- Rust 94.6%
- Nix 5.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Update the git forge integration backlog entry to reflect that all three forge backends are now complete. - Retitle the forge integration section from 'partial' to 'backends shipped' - Document that Forgejo (default), GitHub, and GitLab all ship behind the shared Forge trait with identical tool names/schemas - Note backend selection via the provider setting - Reframe remaining work as provider-specific polish (state filters, hosted-instance edge cases) Files changed: TODO.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-047 Fusion-Task-Lineage: ba5a2e2b-da7c-4bf8-bd75-7dda4ca8d61e Co-authored-by: Fusion <noreply@runfusion.ai> |
||
| crates | ||
| docs | ||
| examples | ||
| nix | ||
| profiles | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
| TODO.md | ||
lambda
A minimal, Nix-first, self-evolving coding-agent runtime in Rust.
lambda is a framework: a small core plus extensions. The core is just the agent
loop, an extension registry, and the typed contract everything registers through.
Tools, hooks, commands, providers, skills, MCP clients — and the interface
itself — are all extensions, written as native Rust crates or as Luau the
agent can author and reload itself. The TUI is just one interface built on the
core; a headless server interface ships too, and you can write your own.
Configuration is 100% Nix (Nixvim-style): you write typed options, a
home-manager or NixOS module generates the config.toml lambda reads.
Status: early but the foundation is complete and tested — core + contract + registry, streaming providers, a markdown-rendering TUI, Luau front-end, skill bundles, MCP client, slash commands,
AGENTS.mdproject context, resumable session persistence, health, layered config + Nix module, and hot-reload (the agent edits a.luau, calls thereloadtool, and the next turn picks it up — validate-then-swap keeps the old version on a bad reload). The TUI runs on a persistent runtime, so the registry, reloads, MCP connections, and compaction all survive across turns. Ambient timers and subagents remain as follow-ups (largely agent-buildable as extensions on the existing substrate). Live LLM use runs against OpenAI via existing Codex subscription auth (~/.codex/auth.json); Anthropic is built but needs an API key.
Quickstart (Nix)
nix build # build the binary → ./result/bin/lmda
nix develop # dev shell with cargo/rustc/clippy/rustfmt
nix flake check # run the Nix module + config-generation checks
nix develop --command cargo test # ~300 tests
./result/bin/lmda # launch the interactive TUI (the default)
./result/bin/lmda "your prompt" # one-shot agent run (prints, exits)
./result/bin/lmda health # diagnostics: what loaded, what shadowed, auth readiness
./result/bin/lmda --help # full usage (clap)
bare lmda launches the TUI. LAMBDA_DEMO=1 lmda runs an offline mock demo
(no network/credentials). Vim-style modal editing: --vim, or set it as the
default with [tui] vim = true in config.toml.
Sessions (resume a conversation)
By default lambda is stateless. Opt into persistence with --session <id> (resume
or create a named session) or --continue (resume the most-recently-updated one);
the full transcript is saved after every turn to
$XDG_STATE_HOME/lambda/sessions/<id>.json (versioned JSON; corrupt files are
reported and replaced, never fatal). Works for both the TUI and one-shot runs.
./result/bin/lmda --session work "start a refactor plan"
./result/bin/lmda --session work "now implement step 1" # continues where it left off
./result/bin/lmda --continue # resume the latest session in the TUI
./result/bin/lmda sessions # list saved sessions
First run / auth
The default provider is OpenAI via your existing Codex subscription — lambda
reads ~/.codex/auth.json (so you must have the Codex CLI installed and logged in;
lambda has no login flow of its own yet). No config or API key needed:
./result/bin/lmda health # auth.openai should read "codex-auth ready"
./result/bin/lmda "say hello" # live one-shot run (default model gpt-5.5)
This live path is verified working end-to-end (text replies and tool-use
round-trips). The default model gpt-5.5 must be available to your ChatGPT
account; if a model isn't, the API returns a clear error and you can pass
--model <id>. Anthropic needs ANTHROPIC_API_KEY (otherwise it's listed but
unusable — lmda health shows a WARN).
Architecture
lambda is a framework: the core is the product, and the interface (the TUI) is
just one extension built on top — fully swappable. The dependency direction is
core ← host ← extension/interface ← app/bin.
| Crate | Responsibility |
|---|---|
lambda-core |
The contract (ToolOutput, ExtError, descriptors, hooks, Ctx, Layer, CONTRACT_VERSION), the Registry (registration, shadowing, dispatch, remove_extension, the single-active interface slot), the streaming Provider trait, the agent loop (run_agent), and the Interface trait + UI-neutral InterfaceSession. |
lambda-host |
Generic reusable assembly: load config, run extension startup tasks, select provider, resolve sessions, build the AgentRuntime, compute neutral ContextMetadata. No first-party extension or interface deps. |
lambda-app |
Reusable CLI runner: parses args, asks a caller-supplied register_all to install extensions/interfaces, then runs one-shot/health/sessions or the single registered interface. |
lambda (bin) |
Thin default composition wrapper. Nix decides which ext-* Cargo features are compiled. |
lambda-config |
Layered runtime config: defaults < config.toml < config.local.toml < env/CLI. |
lambda-openai |
OpenAI provider (ChatGPT Codex Responses transport; reuses ~/.codex/auth.json; optional codex_auth_file). |
lambda-anthropic |
Anthropic provider (Messages transport; API key from env or an out-of-store api_key_file). |
lambda-mcp |
stdio MCP client — registers a server's tools as native tools. |
lambda-highlight |
syntect-backed syntax highlighter (a registry service interfaces can render with). |
lambda-luau |
Luau extension loader, VM actor, bridge, and hot-reload reloader. |
lambda-skills |
Prompt-only skill bundles (manifest + SKILL.md) surfaced through context assembly. |
lambda-tui |
terminal interface: ratatui/crossterm, markdown rendering, Vim mode, persistent runtime. Depends on lambda-core only. |
lambda-headless |
non-terminal interface for servers: runs an optional startup prompt, then polls a queue directory for prompt files (success → done/, failure → failed/), streaming to journald. Depends on lambda-core only. |
Interfaces (swap or write your own)
An interface owns the interactive loop. Exactly one is active — registering a
second is an error. Interfaces are ordinary native extensions (ext-tui,
ext-headless, or your own) that register into the single-active interface slot.
To completely replace the UI:
- Write a crate implementing
lambda_core::interface::Interface(onerun(self, InterfaceSession)method) on top oflambda-core— seelambda-tui/lambda-headlessfor templates. - Ship an
extension.nixusinglib.lambda.extensions.mkRustExtensionwithprovides = [ "interface" ]. - Compose the binary through Nix (
mkLambdaWith { extraExtensions = [ ... ]; }) or enable the corresponding first-partyextensions.<name>.
No core changes needed. InterfaceSession carries only runtime primitives +
neutral ContextMetadata — no terminal concepts — so a headless/daemon interface
is as first-class as the TUI.
Layers (Store < Overlay < ProjectLocal)
Extensions register at a layer; higher layers shadow lower ones. Nix owns
Store (read-only, declarative); the agent owns Overlay
($XDG_DATA_HOME/lambda/…, mutable — the self-evolution channel); ./.lambda/… is
ProjectLocal. This lets the agent iterate on a Nix-installed extension without
editing the store.
Configuration
lambda reads $XDG_CONFIG_HOME/lambda/config.toml (override with LAMBDA_CONFIG):
default_provider = "openai"
default_model = "gpt-5.5"
system_prompt = "You are lambda. Be direct and use tools when useful."
[extensions]
store_skill_dirs = ["/nix/store/…/skills"] # Store-layer (Nix-declared)
store_luau_dirs = ["/nix/store/…/ext"]
[[mcp.servers]]
name = "fs"
command = "mcp-fs"
args = ["--root", "/tmp"]
[compaction] # auto-summarize old turns near the context limit (on by default)
enabled = true
context_window = 200000 # set to your model's real window (no per-model metadata yet)
reserve_tokens = 16384
keep_recent_tokens = 20000
[tui]
vim = true # Vim-style modal editing by default (--vim forces it on)
[providers.anthropic]
api_key_file = "/run/credentials/lambda.service/anthropic-api-key" # file path, never the key
[providers.openai]
codex_auth_file = "/run/credentials/lambda.service/openai-codex-auth.json"
[headless] # used when the headless interface is compiled in
startup_prompt = "…" # run once on start
queue_dir = "/var/lib/lambda/queue"
poll_interval_secs = 5
session_id = "headless"
This file is generated by the Nix module — you configure via programs.lambda/
services.lambda, not by editing it. CLI flags (--provider, --model) and
LAMBDA_PROVIDER/LAMBDA_MODEL override at runtime.
Project context (AGENTS.md)
lambda follows the cross-agent AGENTS.md convention: on
startup it walks from the working directory up to the project root (nearest
.git ancestor), collects every AGENTS.md along the way, and appends them to
the system prompt (root first, so the most specific file wins). Without a .git
root only the working directory is consulted, so discovery never escapes upward.
Drop an AGENTS.md in your repo to give the agent project-specific conventions.
Nix modules
Nix is the only supported configuration surface — the generated config.toml
and any env vars are internal artifacts you never edit by hand. The flake exposes
both homeManagerModules.lambda (desktop) and nixosModules.lambda (system
service), sharing one typed option schema (nix/lambda-options.nix) plus a freeform
settings escape hatch. Extensions and interfaces are ordinary Nix modules under
extensions.<name>.
Home-manager (desktop, defaults to extensions.tui.enable = true):
programs.lambda = {
enable = true;
defaultProvider = "openai";
stateDir = "/var/lib/lambda/state"; # optional runtime session/agent state directory
skills = [ ./skills/writer ];
luauExtensions = [ ./ext/linter.luau ];
extensions.mcp.settings.servers = [
{ name = "fs"; command = "mcp-fs"; args = [ "--root" "/tmp" ]; }
];
settings = { }; # freeform config.toml escape hatch, deep-merged last
};
NixOS (headless server, hardened systemd service, defaults to the headless
interface). Provider secrets stay out of the Nix store — give a path
(agenix/sops-decrypted) and it's loaded via systemd LoadCredential; the rendered
config points at the credential path, never the value:
services.lambda = {
enable = true;
defaultProvider = "anthropic";
extensions.anthropic.settings.api_key_file = "/run/secrets/anthropic"; # NOT in /nix/store
workspaces = [ "/srv/project" ]; # dirs the agent may read+write
extensions.headless.settings = {
startup_prompt = "summarize today's commits";
queue_dir = "/var/lib/lambda/queue"; # drop prompt files here; they run then move to done/ or failed/
poll_interval_secs = 5;
};
headless.sessionId = "server";
};
The home-manager module renders config to the user's XDG path; the NixOS module
renders to /etc/lambda/config.toml and runs a hardened service (system user,
StateDirectory, explicit ReadWritePaths workspaces). Both materialize declared
skills/extensions into Store-layer dirs.
Extending
- Skill bundle: a directory with
skill.toml(name/version/description/contract_version) andSKILL.mdsurfaced to the model. Drop it in a skills dir; it loads at startup. - Standalone Luau: a
.luaufile in a luau-extensions dir. - Native crate: a crate exposing
register_extension(&mut ExtensionHost); ship anextension.nixusinglib.lambda.extensions.mkRustExtension. - Interface: a native extension that registers into the single-active
interface slot (see
lambda-tui/lambda-headless) — completely replace or rewrite the UI without touching the core. - MCP server: declare it in
extensions.mcp.settings.servers; its tools register asmcp__<server>__<tool>. - Hot-reload: after editing an Overlay
.luau, call thereloadtool (reload{ext_id}); the change applies at the next turn boundary.
Design docs
Full design specs live in docs/superpowers/specs/ — the master design, the
extension contract, providers/auth, the Luau front-end, skills, the Nix
config-module, and hot-reload/ambient.
Development notes
- All cargo commands run inside
nix develop. - The Nix flake only sees git-tracked files;
git addnew files beforenix build. - This repo is developed with a two-agent workflow (Claude + Codex as co-equal peer implementers/reviewers); the local AgentBridge config is git-ignored.