omw-cli 0.1.1

OMW = OpenAI + MCP + WASM
omw-cli-0.1.1 is not a library.

OMW

OMW = OpenAI + MCP + WASM.

omw is an agent runtime. You declare agents in a TOML configuration, each wiring a provider (an OpenAI-family chat service), tooling (MCP tool servers), and a brain — either a compiled WASM component, a rhai script, or a JavaScript script. omw then drives each agent through an actor model: every agent owns a single inbox, and chat streams, tool results, timers, and messages from other agents all arrive there as tagged events the brain consumes.

Who it is for

omw is for people who want a small, local agent runtime that is genuine about its inputs and outputs: the brain is real WASM, tooling speaks MCP, and the configuration is plain TOML. It is not a framework — there is no DSL to learn and no orchestration layer. You bring a provider key, a couple of MCP servers, and a brain, and omw runs it for one iteration (run) or keeps it going (loop).

How it works

  • Providers are OpenAI-family chat services. provider.chat-stream opens a streaming response whose deltas arrive as events in the agent's inbox; the in-band provider.chat blocks for the full result.
  • Tooling is MCP tool servers. They expose callable tools and readable resources; resource subscriptions deliver change events.
  • Brains are runtimes. The wasm runtime loads an agent as a compiled component; the rhai runtime evaluates a script on an interpreter that ships as an opt-in variant, as does the js runtime. The default omw package/binary ships with the runtime-wasm, provider-openai, tooling-mcp, and endpoint-openai back ends but without either script runtime, whereas the omw-rhai package / omw-rhai-<arch>.tar.gz binary (--features runtime-rhai) includes the rhai interpreter and the omw-js package / omw-js-<arch>.tar.gz binary (--features runtime-js) includes the js interpreter. All three see the same omw host interface (rhai in snake_case, js in camelCase). To write a pure Rust brain, depend on the omw-wasm-rust guest SDK crate instead of running wit-bindgen yourself; see Rust brains.
  • Agents are actors. They subscribe to each other explicitly, so a message only ever reaches an agent that chose to listen.
  • Endpoint is an optional OpenAI-compatible HTTP server. Set [endpoint] with kind = "openai" plus a listen address and agents can subscribe themselves under model names: inbound chat requests arrive in the agent's inbox as events, and the agent streams its reply back (SSE or buffered JSON). Any OpenAI-compatible client can then drive an agent.
  • Hot reload is --watch on run / loop. When a brain script changes, the agent's run restarts on the new script while inboxes, subscriptions, and sessions survive. The new script is validated before the live run ends, so a bad edit never kills a good run — and a broken script never starts.

Installation

omw is packaged as a Nix flake. Run it directly without installing:

nix run github:haras-unicorn/omw

or build the omw binary with:

nix build github:haras-unicorn/omw

Releases

Prebuilt binaries for x86_64-linux and aarch64-linux are attached to each GitHub release as tarballs containing the omw binary. The default omw-<arch>.tar.gz ships no rhai runtime; grab the omw-rhai-<arch>.tar.gz tarball (or the rhai Nix package) when your brains are rhai scripts, or the omw-js-<arch>.tar.gz tarball (or the js Nix package) when your brains are JavaScript scripts:

curl -L -o omw.tar.gz \
  https://github.com/haras-unicorn/omw/releases/latest/download/omw-x86_64-linux.tar.gz
tar -xzf omw.tar.gz
./omw-x86_64-linux

The rhai variant is the same shape, with the -rhai name:

curl -L -o omw-rhai.tar.gz \
  https://github.com/haras-unicorn/omw/releases/latest/download/omw-rhai-x86_64-linux.tar.gz
tar -xzf omw-rhai.tar.gz
./omw-rhai-x86_64-linux

The js variant is the same shape, with the -js name:

curl -L -o omw-js.tar.gz \
  https://github.com/haras-unicorn/omw/releases/latest/download/omw-js-x86_64-linux.tar.gz
tar -xzf omw-js.tar.gz
./omw-js-x86_64-linux

Usage

Configuration lives in a TOML file (default omw.toml in the current directory, overridable with --config). It declares named providers, tooling, and runtimes plus a list of agents:

[providers.openai]
kind = "openai"
api_key = "sk-…"
model = "gpt-4o"

[tooling.mcp]
kind = "mcp"
transport = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-everything"]

[runtime.rhai]
kind = "rhai"

[[agents]]
name = "alice"
runtime = "rhai"
script = "brain.rhai"

Then drive it:

omw run    # run every agent once
omw loop   # keep every agent running, restarting on failure

Serve agents over HTTP with the optional endpoint: set kind plus a listen address, have a brain subscribe itself under a model name, then any OpenAI-compatible client can call it:

[endpoint]
kind = "openai"
listen = "127.0.0.1:8080"
let sub = omw::host::subscribe_endpoint("gpt-4o");

Inbound requests arrive in the agent's inbox as endpoint-message events; the brain streams its reply back with stream_endpoint (SSE for stream: true, one buffered JSON completion otherwise). GET /v1/models lists subscribed models.

Edit brains live with --watch on either mode: when a brain file changes, the agent's current run ends and restarts on the new script, while inboxes, subscriptions, and sessions survive on the shared bus. The new script is validated before the live run ends, so a bad edit keeps the good run alive (plus an error event if the brain subscribed to lifecycle events) — and a broken script never starts (parks under --watch, fails fast without it). Brains opt in to reload / shutdown notices with subscribe_lifecycle.

See the endpoint and hot reload pages for the full reference.

To write a pure Rust brain, depend on the omw-wasm-rust guest SDK crate instead of running wit-bindgen yourself; see Rust brains.

Configuration can also be layered from the environment (OMW__ prefix) or generated as a JSON schema:

omw schema --output config.schema.json

See the docs for the full reference.

NixOS

The flake ships a NixOS module exposing services.omw — a hardened systemd unit that runs omw from a config file, layering OMW__-prefixed environment variables over it so secrets never live in the Nix store. Plain-systemd and Docker deployments are covered too — see Deployment in the documentation:

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
    omw.url = "github:haras-unicorn/omw";
  };

  nixosConfigurations.my-machine = nixpkgs.lib.nixosSystem {
    modules = [
      omw.nixosModules.default
      {
        services.omw = {
          enable = true;
          settingsFile = "/etc/omw.toml";
          environmentFile = "/var/lib/omw/env";
        };
      }
    ];
  };
}

See The NixOS module in the documentation for the full option set, including settings vs settingsFile, mode, user/group, and stateDir.

Library

omw can be used as a library in your own crate by adding omw to dependencies and enabling the runtime features you want. See the library page for details.

Examples and testing

The examples are runnable agents that exercise the whole stack — provider, tooling, endpoint, agents — with no keys, no network, and no external services, each in rhai, js, and wasm. The omw-test binary runs them deterministically against in-process scripted doubles and checks what every agent saw and did against an [assertions] section; the testing pages cover the binary, the assertion language, and each mock. See examples for the tour.

Binary cache

Builds are cached on the haras cachix cache. When the flake is used directly (for example with nix run github:haras-unicorn/omw), the cache is configured automatically through the flake's nixConfig. To use it when the package comes from an overlay, add the following to your nix configuration:

{
  nix.settings = {
    substituters = [ "https://haras.cachix.org" ];
    trusted-public-keys = [
      "haras.cachix.org-1:/HIo1JYqOIH1Nwk1EGXhuPPvDW0WekxIbY5CiXUZbYw="
    ];
  };
}

Documentation

The documentation is available at https://haras-unicorn.github.io/omw/.