# rho v0.1.0 - Table of Contents > An agent core for Elixir apps: pi-agent-core's loop, omp/pi JSONL sessions, compaction, tools, and model sign-in (subscription OAuth or API keys) on req_llm. ## Pages - [rho](readme.md) - [Changelog](changelog.md) ## Modules - Loop & sessions - [Rho](Rho.md): An agent core for Elixir apps: pi-agent-core's loop (`Rho.Loop`), omp/pi JSONL sessions (`Rho.Session`), compaction, tools, and model sign-in (subscription OAuth or API keys, `Rho.Auth`) on req_llm. - [Rho.Artifacts](Rho.Artifacts.md): The output budget: a tool result over 16 KiB goes to the model as its head and tail with a marker in the middle; the full text is kept as an artifact the agent reads back with the `read_artifact` tool, a page of lines at a time. - [Rho.Compaction](Rho.Compaction.md): Context compaction, ported from pi's local summary method (`pi-agent-core/src/compaction/compaction.ts`; omp writes it as `method: "soft"`). When a request nears the model's context window, the older part of the branch is summarized by the model into a `compaction` entry (`Rho.Session.compaction/4`), and `Rho.Session.context/1` sends that summary plus the recent tail from then on. - [Rho.Events](Rho.Events.md): What a loop tells its subscribers, as `{:rho, session_id, event}` messages (plain `send`, no PubSub). The lifecycle is pi's (`pi-agent-core/src/agent-loop.ts`) - [Rho.Loop](Rho.Loop.md): The agent loop for one session: a GenServer with pi's lifecycle (`pi-agent-core/src/agent-loop.ts`; events in `Rho.Events`). - [Rho.Session](Rho.Session.md): Sessions in omp/pi's JSONL format (`pi-coding-agent/src/session/ session-entries.ts`, `CURRENT_SESSION_VERSION = 3`), so a session can move between omp on a computer and a rho host (Operator on a phone, say). - Tools - [Rho.Tool](Rho.Tool.md): A tool the agent can call. Implementations are plain modules, so a host can register one at runtime by module, or serve changing sets of them from a source (see `Rho.ToolRegistry`). - [Rho.ToolRegistry](Rho.ToolRegistry.md): The tools the agent can call, by name, from three places - [Rho.ToolRegistry.Source](Rho.ToolRegistry.Source.md): A source of tools the registry asks on every call, for tool sets that change while the app runs (the Dyn layer's generations, `Rho.Dyn.Source`: tools the agent writes, compiles and activates on the device). Configure with `{Rho.Supervisor, tool_sources: [MySource]}` or `config :rho, :tool_sources, [MySource]`. - [Rho.ToolRunner](Rho.ToolRunner.md): Runs one tool call in its own task (under a `Task.Supervisor`, not linked to the loop). The `before_tool_call` gate runs first, inside the task, so a gate that waits (a biometric approval) never blocks the loop; once the gate allows, the task tells the loop (`{:tool_running, pid}`), which starts the tool's timeout clock from then. - [Rho.Tools.ReadArtifact](Rho.Tools.ReadArtifact.md): Core tool: read a page of a tool output that was too big to give whole (`Rho.Artifacts`). A page stays under the output budget, so reading never spills again. - [Rho.Tools.Todo](Rho.Tools.Todo.md): Core tool: the session's todo list (omp's `todo`), kept in `todos/.json` in the app's data dir. Writing the steps of a multi-step job down and ticking them off keeps the agent from stopping halfway, or forgetting a step after a compaction; and the list survives a restart of the app. One item at a time is in progress: the first open one, moved on whenever an item is done or removed. - Models & usage - [Rho.Budget](Rho.Budget.md): The per-day cost cap: what the model calls cost today, priced by req_llm from each reply's usage (notional on a Claude or ChatGPT subscription), against the host's `Rho.Settings.daily_cap/1`. - [Rho.LLM](Rho.LLM.md): The model client the loop talks to. `Rho.LLM.ReqLLM` is the real one (Anthropic, OpenAI, OpenAI Codex and Google through req_llm, with the credential from `Rho.Auth`); tests inject a scripted fake. - [Rho.LLM.ReqLLM](Rho.LLM.ReqLLM.md): The model calls through req_llm's providers, streamed, with the provider's credential from `Rho.Auth` (fetched, and for a subscription refreshed when due, per call) - [Rho.LLMCatalog](Rho.LLMCatalog.md): Gives llm_db (the model catalog req_llm resolves models through) a snapshot that works on a phone. - [Rho.Models](Rho.Models.md): The models rho can call right now (like omp's `/models`): the static catalog filtered to the providers that have a credential (`Rho.Auth`). - [Rho.Usage](Rho.Usage.md): How much of each provider subscription is left, and what the model calls used, for the host's usage page and status line. - [Rho.Usage.Fetch](Rho.Usage.Fetch.md): Asks a provider's OAuth usage endpoint what its subscription has left, with the sign-in's access token (`Rho.Auth.credential/1`), as omp does (subscriptions only: an API key has no such endpoint) - Sign-in & credentials - [Rho.Auth](Rho.Auth.md): The model-provider credentials, one per provider, each pi's `auth.json` entry as JSON in the credential store (`Rho.CredentialStore`) under `"auth:"` - [Rho.Auth.Login](Rho.Auth.Login.md): Signing in to a subscription (`:anthropic`, `:openai_codex`): the OAuth flow (`Rho.Auth.OAuthFlow`) with the provider's own localhost redirect, answered by this BEAM. - [Rho.Auth.OAuthFlow](Rho.Auth.OAuthFlow.md): The OAuth authorization-code flows omp signs in with (`/login anthropic`, `/login openai-codex`), built as omp's declarative engine builds them from `pi-catalog`'s `rules/auth/anthropic.kdl` and `openai-codex.kdl` - [Rho.CredentialStore](Rho.CredentialStore.md): Where rho keeps secrets: the provider sign-ins and API keys (`Rho.Auth`), a binary value per string key. A host seam: `config :rho, :credential_store, Module` picks the backend. - [Rho.CredentialStore.File](Rho.CredentialStore.File.md): The default backend: one 0600 file per key under `/secure/`, the key made filename-safe. Written through a temp file created 0600 before the secret goes in, then renamed over the old one. - Host seams - [Rho.Config](Rho.Config.md): The host's settings for rho, `config :rho, ...`, each with a default that works on a computer. `:data_dir`, `:default_model` and `:system_prompt` may be given as `{module, function, args}` (or a 0-arity function), called each time the value is read, so a host can keep them in its own settings and change them at run time. - [Rho.Settings](Rho.Settings.md): The user settings the loop reads, a host seam (`config :rho, :settings, Module`) - [Rho.Settings.File](Rho.Settings.File.md): The default `Rho.Settings`: `settings.json` in the data dir (a missing or unreadable file means the defaults; other keys in it are kept, so a host may share the file). Each call reads the file. - [Rho.Supervisor](Rho.Supervisor.md): rho's processes, for the host to start in its own tree (rho has no OTP application callback: a mob app starts its code by hand) - Dyn - [Rho.Dyn](Rho.Dyn.md): The Dyn layer: code the agent writes, compiles and runs on the device itself (tools and screens), next to the fixed Core (the loop, sessions, sign-in, the core tools, the approval gate and the repair path, which ship in the app and which the agent can't change). The Core changes Dyn only through this module, one generation at a time - [Rho.Dyn.Approval](Rho.Dyn.Approval.md): The approval gate on changing what runs (the pipeline's step 3, see `Rho.Dyn`): activating a proposed generation, or reverting to a chosen one by hand. - [Rho.Dyn.Approval.Biometric](Rho.Dyn.Approval.Biometric.md): The production approval: the system screen-lock prompt (fingerprint, face, or the phone's PIN, pattern, password or passcode) behind an approve control the host shows with the change (Operator's approve chip). This small process (`Rho.Supervisor` starts it with Dyn) holds what the human confirmed; only the host's UI, after the prompt passed, calls `confirm/1`. - [Rho.Dyn.AutoApprove](Rho.Dyn.AutoApprove.md): The "approve all" setting: while on, the host's UI activates each candidate generation as it arrives instead of asking for approval (the same activation, so probation and revert-on-crash still apply). Only Dyn activations: reverting still asks. - [Rho.Dyn.Check](Rho.Dyn.Check.md): The static check every staged file passes before it is compiled (the pipeline's step 2, see `Rho.Dyn`), and `beam/2`, a second pass over the compiled binaries. - [Rho.Dyn.Config](Rho.Dyn.Config.md): The host's settings for the Dyn layer, `config :rho, :dyn, [...]` (see `Rho.Dyn`). Every key has a default; a host that doesn't use Dyn sets none of them. - [Rho.Dyn.Generation](Rho.Dyn.Generation.md): One generation of the Dyn layer: what `gens//manifest.json` holds (see `Rho.Dyn.Store`). - [Rho.Dyn.Keeper](Rho.Dyn.Keeper.md): The Core process that decides which Dyn generation runs (activation, probation, revert and safe mode, steps 3–4 in `Rho.Dyn`). It owns the registry table (`Rho.Dyn.Registry`) and is the only writer of the `current` pointer. - [Rho.Dyn.Screens](Rho.Dyn.Screens.md): How Dyn screens plug into the host's UI: a host seam (`config :rho, :dyn, screens: Module`, `Rho.Dyn.Config`). rho builds, checks, selftests and watches Dyn screens the same way whatever the UI; this says what a screen is and how the UI tells the Keeper a launch got going - [Rho.Dyn.Screens.Mob](Rho.Dyn.Screens.Mob.md): Mob screens as Dyn screens (`config :rho, :dyn, screens: Rho.Dyn.Screens.Mob`, see `Rho.Dyn.Screens`) - [Rho.Dyn.Screens.None](Rho.Dyn.Screens.None.md): No screens (the default `Rho.Dyn.Screens`): Dyn code is tools and modules. Without a UI there is no first frame to wait for, so `c:Rho.Dyn.Screens.after_first_render/1` applies the MFA at once: a launch's clock to "stable" starts at `Rho.Dyn.boot/1`. - [Rho.Dyn.Seed](Rho.Dyn.Seed.md): The seed: the Dyn sources an install starts from, which the host ships (Operator's default front: a welcome screen, a component library, the front's settings). The host embeds them at build time (`Application.app_dir/2` can't resolve priv/ on the device) and hands them to `start/2`. - [Rho.Dyn.Source](Rho.Dyn.Source.md): The Dyn layer as a `Rho.ToolRegistry.Source`: the current generation's tools, read from the Keeper's registry table on every call, so an activation or a revert (by hand, or automatic) changes the tools the loop offers from its next model call. Every tool call's task is handed to the Keeper first (`c:Rho.ToolRegistry.Source.watch/2`), so a Dyn tool's crash, or the kill at its timeout, counts against its generation. - [Rho.Untrusted](Rho.Untrusted.md): Text and data from code rho doesn't trust, the Dyn layer's (`Rho.Dyn`), for the processes it does: the loop, the Keeper, the caller of a compile or a selftest. - Dyn internals - [Rho.Dyn.Compiler](Rho.Dyn.Compiler.md): Compiles a source set into generation `n` (part of the pipeline's step 2, see `Rho.Dyn`) and loads, unloads and purges generations' modules. - [Rho.Dyn.Diff](Rho.Dyn.Diff.md): Unified diff (as `diff -u` and git print it, three lines of context) between two source sets `%{relative_path => source}`: what the approval card and the rescue screen show for a generation. - [Rho.Dyn.LogGuard](Rho.Dyn.LogGuard.md): A primary `:logger` filter that keeps Dyn terms away from the logger's formatting, in whatever process logs them. - [Rho.Dyn.Registry](Rho.Dyn.Registry.md): Reads the Dyn registry: one ETS table owned (and only written) by `Rho.Dyn.Keeper`, named like the Keeper. It maps logical names to the current generation's modules - [Rho.Dyn.Reuse](Rho.Dyn.Reuse.md): Incremental compiles: a proposal recompiles only the files that changed (or depend on a change at compile time) and reuses the rest from its parent generation, renamed. On a Moto G 2021 compiling the default front from source takes about 33 s, and recompiling a module from its stored Erlang code still about 350 ms (the Erlang compiler's passes): 24 s of a one-file change to the 68-module front. So a module is reused by renaming its compiled binary instead; on a Mac that takes 56 ms for the whole front, against 1.9 s through the Erlang compiler. - [Rho.Dyn.Selftest](Rho.Dyn.Selftest.md): Selftests a compiled generation (the end of the pipeline's step 2, see `Rho.Dyn`), one module at a time, each in a fresh process with a `max_heap_size` (killed when exceeded, off-heap binaries included), a timeout and `trap_exit` (a linked helper that crashes doesn't take the test down unreported). - [Rho.Dyn.Store](Rho.Dyn.Store.md): The Dyn layer on disk, under one root dir (`/dyn` by default, `Rho.Config.data_dir/0`) - [Rho.Dyn.Trace](Rho.Dyn.Trace.md): An Elixir compiler tracer for Dyn compiles (`Code.put_compiler_option(:tracers, ...)`): which modules of the generation a file needs **at compile time**, so `Rho.Dyn.Compiler` knows when a file compiled for an earlier generation can be reused as it is (`Rho.Dyn.Reuse`). - Dyn tools - [Rho.Tools.DynCopy](Rho.Tools.DynCopy.md): Core tool: copy a staged Dyn source file to a new path under a new module name (see `Rho.Tools.DynTool`). - [Rho.Tools.DynDelete](Rho.Tools.DynDelete.md): Core tool: delete a staged Dyn source file (see `Rho.Tools.DynTool`). - [Rho.Tools.DynEdit](Rho.Tools.DynEdit.md): Core tool: replace one exact, unique piece of a staged Dyn source file (see `Rho.Tools.DynTool`). - [Rho.Tools.DynFiles](Rho.Tools.DynFiles.md): Core tool: list the Dyn layer's staged source files (see `Rho.Tools.DynTool`). - [Rho.Tools.DynPropose](Rho.Tools.DynPropose.md): Core tool: turn the Dyn staging copy into a candidate generation (`Rho.Dyn.propose/2`: static check, compile, selftests). It never activates anything itself: the human approves on the phone, or has turned approve all on (`Rho.Dyn.AutoApprove`), when the host's UI activates it as it arrives. Description and result say which. - [Rho.Tools.DynRead](Rho.Tools.DynRead.md): Core tool: read a staged Dyn source file with line numbers (see `Rho.Tools.DynTool`). - [Rho.Tools.DynReset](Rho.Tools.DynReset.md): Core tool: put the Dyn staging copy back to the current generation's sources (see `Rho.Tools.DynTool`). - [Rho.Tools.DynStatus](Rho.Tools.DynStatus.md): Core tool: what the Dyn layer runs, what's pending, and its recent crash reports (see `Rho.Tools.DynTool`). - [Rho.Tools.DynTool](Rho.Tools.DynTool.md): What the `dyn_*` Core tools share: the agent edits its Dyn layer's staging copy and proposes it (`Rho.Dyn`). There is deliberately no tool to activate or revert: those need the human's approval (the screen lock). - [Rho.Tools.DynWrite](Rho.Tools.DynWrite.md): Core tool: create or overwrite a staged Dyn source file (see `Rho.Tools.DynTool`).