{"id":49231321,"url":"https://github.com/tomatyss/runloop","last_synced_at":"2026-04-24T12:03:51.514Z","repository":{"id":322101020,"uuid":"1087943069","full_name":"tomatyss/runloop","owner":"tomatyss","description":"An OS for AI agents: Rust + WASM runtime, typed message bus, event‑sourced memory, and a clean TUI.","archived":false,"fork":false,"pushed_at":"2026-02-01T01:44:54.000Z","size":1892,"stargazers_count":2,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-01T13:03:32.336Z","etag":null,"topics":["agent-os","ai-agents","debian","linux","multi-agent","orchestration","os","runloop-os","rust","wasi","wasm"],"latest_commit_sha":null,"homepage":"https://docs.runloop.sh/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomatyss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-11-02T00:47:12.000Z","updated_at":"2026-02-01T01:43:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tomatyss/runloop","commit_stats":null,"previous_names":["tomatyss/runloop"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tomatyss/runloop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomatyss%2Frunloop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomatyss%2Frunloop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomatyss%2Frunloop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomatyss%2Frunloop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomatyss","download_url":"https://codeload.github.com/tomatyss/runloop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomatyss%2Frunloop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32222483,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T10:26:35.452Z","status":"ssl_error","status_checked_at":"2026-04-24T10:25:27.643Z","response_time":64,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-os","ai-agents","debian","linux","multi-agent","orchestration","os","runloop-os","rust","wasi","wasm"],"created_at":"2026-04-24T12:03:50.263Z","updated_at":"2026-04-24T12:03:51.505Z","avatar_url":"https://github.com/tomatyss.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Runloop\n\n\u003e An **agent‑native operating layer** for your machine: terminal‑first,\n\u003e Rust‑powered, agents in lightweight **WASM/WASI** sandboxes, composed into\n\u003e **Openings** (DAGs) over a typed local message bus (RMP).\n\n**Status:** pre‑alpha design + docs. Debian‑first; portable later. Runloop is\n**not a kernel or distro**—it sits above your OS to route prompts to either the\nshell or to AI agents.  \nSee [ROADMAP.md](./ROADMAP.md) for the phased plan and\n[docs/perf.md](./docs/perf.md) for the performance harness.\n\n---\n\n## Documentation (mdBook)\n\nThe documentation under `docs/` is organized as an mdBook.\n\n- Build: `mdbook build docs`\n- Serve locally: `mdbook serve docs -n 127.0.0.1 -p 3000`\n\nIf you use `just`, convenient tasks are available:\n\n- `just docs-book` – builds the book into `docs/book/`\n- `just docs-serve` – serves with live-reload for local editing\n\n---\n\n## What this repo is / is not\n\n**Is:** a terminal‑first layer that:\n\n- routes your prompt to the shell _or_ to agents,\n- runs many small agents (WASM/WASI sandboxes) with least‑privilege\n  capabilities,\n- composes agents into **Openings** (typed DAGs) you can run, pause, replay,\n- maintains a **personal ops graph (POG)**: an event‑sourced knowledge base with\n  provenance and semantic search.\n\n**Is not:** a new kernel, a full Linux distro, or a desktop/windowing\nenvironment.\n\n---\n\n## Quick start\n\n### From source (dev / user mode)\n\nRequirements: Rust (edition 2024), `cargo`, and a recent WASI runtime (e.g.,\nWasmtime).\n\n```bash\ngit clone https://github.com/tomatyss/runloop.git\ncd runloop\ncargo build --workspace\n```\n\nRun the daemon and CLI locally (user mode uses `~/.runloop` for\nconfig/artifacts):\n\n```bash\n# daemon (user mode)\ncargo run -p runloopd\n\n# CLI (daemon-first)\ncargo run -p rlp -- help\n\n# Run an opening locally (daemon offline)\ncargo run -p rlp -- run examples/openings/compose_email.yaml --local --params '{\"recipient\":\"john\"}'\n\n# monitor (agent-top)\nrlp run ... \u003e run.ndjson\ncargo run -p agtop -- --input run.ndjson\n\n# inspect resolved config layers\ncargo run -p rlp -- config path --all\n```\n\n\u003e **Note:** `rlp run` now probes the daemon socket before doing any local work.\n\u003e Provide `--local` explicitly when you want inline execution; both modes stream\n\u003e NDJSON `RunEvent` records so monitors such as `agtop` can consume the same\n\u003e schema (pipe `rlp run ... \u003e run.ndjson` for live monitoring or feed stdin).\n\n### Agent bundles (wasm)\n\nThe canonical `compose_email` agents now ship as wasm32-wasip1 bundles generated\nfrom the helper crates under `crates/agents-wasm/*` (kept outside the default\nworkspace so host builds remain fast). Run `just build-agents-wasm` to\ncross-compile the binaries, copy them into `agents/*/bin/*.wasm`, and refresh\nthe manifest digests. This command requires the `wasm32-wasip1` target to be\ninstalled (`rustup target add wasm32-wasip1`). Each bundle is a self-contained\nCLI that prints JSON to stdout, which the runtime captures when executing an\nopening. Use `just test-agents-wasm` to rebuild the bundles (if needed) and run\n`compose_email` end-to-end via `rlp --local` as a smoke test. When new bundles\nare produced, commit the `.wasm` artifacts plus their updated BLAKE3 digests so\nthe manifests continue to verify.\n\nScaffold your own agent and run the starter opening:\n\n```bash\nrlp agent scaffold note_taker --opening\nrlp agent build note_taker\ncargo run -p rlp -- run examples/openings/note_taker.yaml --local \\\n  --params '{\"prompt\":\"draft a standup note\"}'\n```\n\nInstall a prebuilt bundle into a registry dir (directory or `.tar`/`.tar.gz`):\n\n```bash\nrlp agent install /path/to/agent.bundle.tar\n```\n\n### Packages \u0026 images (daemon / system mode)\n\nWhen installed from a .deb or image, the service runs as **`runloop:runloop`**\nand writes state under **`/var/lib/runloop`**; its UDS socket lives at\n`/run/runloop/rmp.sock`. User mode continues to use `~/.runloop` for\nconfig/artifacts. Runtime socket discovery precedence:\n\n1. `runtime.socket_path` (short‑circuit; error if unreachable)\n2. `${runtime.sockets_dir}/rmp.sock`\n3. `~/.runloop/sock/rmp.sock`\n4. `/run/runloop/rmp.sock`\n\n#### Debian 13 (`trixie`) packages\n\nBuild the .deb via `dpkg-buildpackage` (convenience target provided):\n\n```bash\njust deb\n# artifacts land in ../runloop_\u003cversion\u003e_\u003carch\u003e.deb\n```\n\nInstall and manage the daemon:\n\n```bash\nsudo apt install ../runloop_0.1.0~alpha1-1_amd64.deb\nsudo systemctl status runloopd\nsudo systemctl restart runloopd   # when updating /etc/runloop/config.yaml\n```\n\nThe package ships `runloopd`, `rlp`, and `agtop`, configures the `runloop`\nsystem user, and writes state under `/var/lib/runloop`. Remove with\n`sudo apt purge runloop` to drop both configuration and data.\n\n---\n\n## Configuration (Config v1)\n\nCreate `~/.runloop/config.yaml` for user mode, or `/etc/runloop/config.yaml` for\nsystem mode:\n\n```yaml\nversion: 1\n\nruntime:\n  base: \"debian\"\n  agent_container: \"wasm32-wasip1\"\n\nmodels:\n  default: \"local:llama3.1-8b\"\n  broker:\n    providers:\n      - id: \"openai\"\n        kind: \"http\"\n        base_url: \"https://api.openai.com\"\n        secret_id: \"runloop/models/openai\"\n      # Gemini (text-only) example:\n      # - id: \"gemini\"\n      #   kind: \"http_gemini\"\n      #   base_url: \"https://generativelanguage.googleapis.com\"\n      #   secret_id: \"runloop/models/gemini\"\n    route:\n      - pattern: \"*\"\n        provider: \"openai\"\n    cache:\n      ttl_ms: 600000\n      capacity: 1024\n    budgets:\n      default_tokens: 8000\n      hard_cap_usd: 0.50\n\nkb:\n  # root_dir differs by mode; user mode defaults to \"~/.runloop/pog\",\n  # system mode defaults to \"/var/lib/runloop/pog\"\n  root_dir: \"~/.runloop/pog\"\n  events_db: \"events.sqlite\" # append-only event log\n  view_db: \"pog.sqlite\" # materialized views\n\nlogging:\n  level: \"info\" # error | warn | info | debug | trace\n  format: \"auto\" # auto | json | text (auto picks JSON when stdout is not a TTY)\n  file: \"\" # optional path\n\nobservability:\n  traces:\n    enabled: false\n    otlp_endpoint: \"\" # e.g., http://localhost:4317\n    sampling: \"parent\" # parent | always_on | ratio:0.1\n\nsecurity:\n  confirm_external_actions: true\n  secrets:\n    provider: \"os-keyring\" # stub | os-keyring | age\n    root: \"~/.runloop/secrets\" # only used by 'age' or 'stub'\n\nrouter:\n  fastpath_shell: true\n  default_opening: \"compose_email\"\n  allowlist: []\n  denylist: []\n  known_commands: []\n\nui:\n  theme: \"mono\"\n```\n\nRuntime socket settings: prefer `runtime.socket_path` (explicit file). If unset,\n`runtime.sockets_dir` is used with implied filename `rmp.sock`. Defaults for\nuser mode favor `~/.runloop/sock/rmp.sock`; system mode uses\n`/run/runloop/rmp.sock`.\n\n**Aliases (compatibility):** `kb.ledger` → `\u003croot_dir\u003e/\u003cevents_db\u003e`,\n`kb.materialized` → `\u003croot_dir\u003e/\u003cview_db\u003e`. The config loader maps old keys and\nwarns; aliases are kept for compatibility. **Environment overrides:** any key\nvia `RUNLOOP__SECTION__SUBKEY=value` (e.g., `RUNLOOP__LOGGING__LEVEL=debug`).\n\n---\n\n## Architecture at a glance\n\n- **Daemon (`runloopd`)** – hosts the local bus, schedules agents, enforces\n  capabilities.\n- **Runtime** – spawns agents as **WASM/WASI** tasks (fast start, low RSS,\n  sandboxed).\n- **SDK \u0026 Shim** – `runloop-sdk` + the `agent-shim` bootstrap allow MVP native\n  agents to speak the bus/RMP protocol with the same capability envelope until\n  their WASM bundles land.\n- **RMP (Runloop Message Protocol)** – typed, traceable messages over UDS:\n  headers carry trace/budget/TTL; bodies are schema‑tagged.\n- **Openings** – declarative DAGs that define a crew of agents and their\n  crossings; supports retries, timeouts, budgets, and deterministic replay.\n- **POG (knowledge base)** – local‑first event log + materialized views, with\n  embeddings for semantic recall and full provenance.\n- **Model broker** – centralizes model/provider selection, budgets, caching.\n\n---\n\n## Key concepts\n\n- **Trajectories** – individual agents with goal + budget.\n- **Crossings** – typed interactions between agents (messages, artifacts).\n- **Openings** – a plan (DAG) of agents + crossings you can run/pause/replay.\n\nExample Opening:\n\n```text\nopening \"compose_email\" {\n  goals: [\"email to john about q4 plan\"]\n  nodes:\n    contacts := agent(\"contact_resolver\")\n    context  := agent(\"context_gatherer\", topic=\"{{params.topic}}\")\n    draft    := agent(\"writer\", model=\"mixtral-8x7b\", topic=\"{{params.topic}}\", tone=\"neutral-friendly\")\n    review   := agent(\"critic\")\n    send     := agent(\"mailer\", require_human_confirm=true, topic=\"{{params.topic}}\")\n  edges:\n    contacts.out -\u003e draft.recipients\n    contacts.out -\u003e context.contact\n    context.out  -\u003e draft.context\n    draft.out    -\u003e review.in\n    draft.out    -\u003e send.draft\n    review.review -\u003e send.review\n    contacts.out -\u003e send.contact\n    review.ok    -\u003e send.in\n}\n```\n\nSee the canonical YAML at `examples/openings/compose_email.yaml` for the\nnormative form used by the parser.\n\n---\n\n## Message Protocol (RMP)\n\n**RMP v0 is frozen**: stream transports carry a `u32 frame_len` prefix, a fixed\n64-byte header, and a MsgPack body (`frame_len = header_len + body_len`). All\nintegers are big-endian; anything else is rejected.\n\n\u003c!-- markdownlint-disable MD013 --\u003e\n\n| Offset | Size | Field            | Notes                                                  |\n| -----: | ---: | ---------------- | ------------------------------------------------------ |\n|      0 |    4 | `magic`          | ASCII `\"RMP0\"`                                         |\n|      4 |    2 | `header_version` | `0` only; mismatch → `UnsupportedVersion`              |\n|      6 |    2 | `header_len`     | `64`; compare literally                                |\n|      8 |    4 | `flags`          | MUST be `0` in v0; otherwise `InvalidHeaderFlags`      |\n|     12 |    2 | `schema_id`      | Primitive family ID (see `docs/rmp-registry.md`)       |\n|     14 |    2 | `reserved2`      | MUST be `0`                                            |\n|     16 |    4 | `body_len`       | Length of MsgPack body                                 |\n|     20 |    8 | `created_at_ms`  | Sender clock (epoch ms)                                |\n|     28 |    8 | `ttl_ms`         | Relative TTL; `0` rejected, overflow → `InvalidExpiry` |\n|     36 |   16 | `trace_id`       | u128 trace for dedupe/telemetry                        |\n|     52 |    8 | `msg_id`         | u64 monotonic per publisher                            |\n|     60 |    4 | `reserved4`      | MUST be `0`                                            |\n\n\u003c!-- markdownlint-enable MD013 --\u003e\n\n**Body envelope.** MsgPack map\n`{ \"type\": \"\u003cfamily.kind.vN\u003e\", \"payload\": \u003cobject\u003e, \"meta\"?: \u003cmap\u003e }`.\n`schema_id` picks the primitive family (Observation, Intent, Artifact,\nToolResult, Critique, StateDelta, ErrorReport, etc.); the body `type` string is\nthe registry entry (e.g., `\"error.report.v1\"`). Implementations MUST cross-check\nfamily ↔ kind (`BodyTypeMismatch` on failure). `meta` is optional and\nforward-compatible; `opening_id`, `priority`, and diagnostics live here—not in\nthe fixed header.\n\n**Framing \u0026 safety.** `frame_len` MUST equal `header_len + body_len` or the\nframe is dropped with `LengthMismatch`. TTL uses u128 math (`InvalidTtl` when 0,\n`InvalidExpiry` on overflow); receivers drop messages once `now \u003e= expires_at`.\nDedupe caches key `(trace_id, msg_id)` per (topic + subscriber); `Duplicate`\ndrops, TTL expirations, and back-pressure timeouts increment drop counters and\npublish `rlp/sys/drops {reason, topic, trace_id, msg_id, expires_at_ms?}`\n(rate-limited).\n\n**Limits.** Default body cap is **8 MiB** (`BodyTooLarge`). Unknown `schema_id`\nis rejected; non-zero flags/reserved words throw `InvalidHeaderFlags`. MsgPack\nfailures surface as `BodyDecodeError`. Implementations must treat the error\ntaxonomy (`InvalidMagic`, `UnsupportedVersion`, `TruncatedHeader`,\n`InvalidHeaderFlags`, `LengthMismatch`, `UnknownSchema`, `BodyTooLarge`,\n`InvalidTtl`, `InvalidExpiry`, `Expired`, `Duplicate`, `BodyDecodeError`,\n`BodyTypeMismatch`) as normative test cases. See\n[`docs/message-protocol.md`](docs/message-protocol.md) for the frozen spec,\nhex-dump golden vector, and TTL/duplicate walkthrough.\n\n---\n\n## Knowledge Base (POG)\n\nLocal‑first storage with:\n\n- **Events** (append‑only, SQLite) and **Views** (materialized tables), plus a\n  vector index for semantic recall.\n- All state changes are proposed as `StateDelta` with provenance; a validator\n  stamps \u0026 applies them.\n- Hashing uses **BLAKE3** (binary `BLOB(32)`); hex is a UI/log rendering.\n\n---\n\n## CLI \u0026 TUI\n\n- **`rlp`** – prompt entry (routes to shell fast-path or to an Opening), budget\n  flags, dry-run.\n  - Explain routing decisions with `cargo run -p rlp -- why \"ls -la\"` (plain\n    text) or append `--json` for machine-readable output.\n  - Route prompts programmatically with\n    `cargo run -p rlp -- route \"draft email\"` (or `--stdin` to read the buffer).\n    The command prints JSON like\n    `{ \"version\": 1, \"route\": \"agent\", \"rule\": \"fallback:opening\", \"blocked\": false }`\n    and exits `10` for shell decisions or `11` for agent decisions so shells can\n    branch without parsing stdout.\n  - See [`docs/router-shell.md`](docs/router-shell.md) for opt-in shell\n    integration (zsh/bash widgets, env toggles, and the `rlp shell enable`\n    helper).\n  - Run an Opening locally with\n    `cargo run -p rlp -- run examples/openings/compose_email.yaml --params '{\"recipient\":\"john\",\"topic\":\"Q4 plan\"}' --trace-out trace.json`.\n    The command now drives the full compose-email stack (contact resolver →\n    context gatherer → writer → critic → mailer), prints per-node status, and\n    writes a replayable trace whether the run executes inline or via the daemon\n    (daemon mode pulls the canonical `run.trace` from the KB once it is\n    persisted). Make sure `runloop.json` points to a writable KB folder, that\n    the model broker has at least one provider (or rely on the writer's\n    heuristic fallback), and export any provider secrets to the environment so\n    the CLI secret resolver can read them (either the exact `secret_id` or its\n    upper-snake variant such as `RUNLOOP_MODELS_GEMINI`). Mail send still runs\n    as a dry-run and prompts for approval unless\n    `security.confirm_external_actions=false`.\n  - Replay a recorded run with either a stored trace ID or a JSON file:\n    `cargo run -p rlp -- replay trace:\u003ctrace_uuid\u003e --opening examples/openings/compose_email.yaml`\n    pulls the canonical `run.trace` payload from the KB, while passing a file\n    path (e.g. `trace.json`) keeps the previous developer workflow. Mismatches\n    are reported per node with output hashes.\n  - Knowledge base helpers: `rlp kb migrate`, `rlp kb query \"\u003cSQL\u003e\"`,\n    `rlp kb search \u003ckeyword\u003e`, and `rlp kb why \u003centity\u003e` all operate on the\n    local POG databases.\n- **`agtop`** – live NDJSON TUI; point it at the `rlp run` stream to watch\n  per-node status.\n- **Tracing** – `runloop trace \u003cid\u003e` prints a ladder diagram of crossings.\n\n---\n\n## Repository layout\n\n```text\ncrates/\n  runloopd/      # daemon\n  rlp/           # CLI\n  agtop/         # TUI monitor\n  core/          # shared types \u0026 capabilities\n  bus/           # local message bus \u0026 codecs\n  openings/      # opening engine \u0026 DSL\n  runtime/       # WASM/WASI execution\n  rmp/           # message protocol helpers\n  kb/            # knowledge base layer\n  model-broker/  # provider abstraction \u0026 caching\n  sdk/           # agent SDK\n```\n\nThe README lists **`core`**, **`bus`**, and **`openings`** explicitly to match\nthe workspace plan.\n\n---\n\n## Security \u0026 privacy\n\n- Strict capability grants per agent/opening (FS/net/time/kb/secrets).\n- **Confirm external actions** (sending, deleting, spending) unless explicitly\n  allowed.\n- Secrets are referenced by **opaque IDs** and stored in OS keyring or an\n  encrypted vault.\n\n---\n\n## Roadmap, contributing, and community\n\n- See [ROADMAP.md](./ROADMAP.md) for phases (Seed → Openings/SDK → KB →\n  Reliability/Security → Beta → 1.0).\n- CONTRIBUTING, CODE OF CONDUCT, and SECURITY guidelines live in the repo root.\n- Please open design questions as “discussions” with links to ADRs.\n\n---\n\n## License\n\nSee [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomatyss%2Frunloop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomatyss%2Frunloop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomatyss%2Frunloop/lists"}