102 lines
4.1 KiB
Markdown
102 lines
4.1 KiB
Markdown
# Sausage
|
|
|
|
You can see how the sausage is made.
|
|
|
|
Sausage is a world of small programs ("actors") that you grow by talking to
|
|
it. Each actor is a WASI program with stdin and stdout. Actors send each other
|
|
JSON messages, one per line, through a broker. The agent is an actor too: it
|
|
writes new actors in Rust, builds them, tests them, starts them, and can
|
|
change its own code.
|
|
|
|
## Start
|
|
|
|
You need [Nix](https://nixos.org/download) with flakes, and an API token for
|
|
an LLM (OpenAI Responses or Anthropic Messages API). No clone needed:
|
|
|
|
```sh
|
|
nix run git+https://gitea.surma.technology/surma/sausage
|
|
```
|
|
|
|
This creates a world in `./world` the first time (it builds the agent, which
|
|
takes a few minutes), then starts it with a console. Later runs start the
|
|
existing world. The two steps on their own:
|
|
|
|
```sh
|
|
nix run git+https://gitea.surma.technology/surma/sausage -- init # creates ./world and builds the agent
|
|
nix run git+https://gitea.surma.technology/surma/sausage -- run # starts the world and a console
|
|
```
|
|
|
|
`init` asks for the API shape (`openai-responses` or `anthropic-messages`),
|
|
the base URL, the model, and the token. It reads `SAUSAGE_API`,
|
|
`SAUSAGE_BASE_URL`, `SAUSAGE_MODEL`, and `SAUSAGE_API_TOKEN` first. The
|
|
values go to `world/actor/agent/data/config.json`. Both commands take another
|
|
folder as an argument, for example `... -- run my-world`.
|
|
|
|
To keep a world on the newest version, run it in a loop:
|
|
|
|
```sh
|
|
nix run git+https://gitea.surma.technology/surma/sausage#loop # ./world, created if needed
|
|
nix run git+https://gitea.surma.technology/surma/sausage#loop -- my-world # an existing world
|
|
```
|
|
|
|
The loop runs `nix run --refresh git+https://gitea.surma.technology/surma/sausage -- run my-world`.
|
|
When the host crashes or an actor sends `@broker {"type":"exit"}`, the loop
|
|
fetches the newest commit and starts it again. `/quit` ends the loop. Set
|
|
`SAUSAGE_FLAKE` to run another flake URL instead, for example your fork.
|
|
|
|
From a clone (`git clone https://gitea.surma.technology/surma/sausage`), the
|
|
same commands are `nix run`, `nix run -- init`, `nix run -- run` and
|
|
`nix run .#loop`.
|
|
|
|
### As a service (headless)
|
|
|
|
Without a terminal (systemd, a container), stdin is `/dev/null` and EOF would
|
|
stop the world. `--headless` (or `SAUSAGE_HEADLESS=1`) turns the console
|
|
input off: the host runs until SIGTERM/SIGINT (clean stop, exit code 0) or a
|
|
broker `exit` request (exit code 75, so the loop starts the newest version).
|
|
Create the world first (`init`), then run the loop:
|
|
|
|
```sh
|
|
nix run git+https://gitea.surma.technology/surma/sausage#loop -- --headless my-world
|
|
```
|
|
|
|
[`examples/systemd/`](examples/systemd/) has a plain systemd unit and a NixOS
|
|
module that run exactly this.
|
|
|
|
In the console:
|
|
|
|
```text
|
|
write an actor "notes" that stores notes, and start it → text to the agent
|
|
@notes {"type":"add","text":"milk"} → a JSON message to an actor
|
|
@agent hello → text to an actor
|
|
/quit
|
|
```
|
|
|
|
## The world folder
|
|
|
|
The folder is the source of truth. Every actor lives in `world/actor/<id>/`:
|
|
|
|
```text
|
|
actor.wasm the current version
|
|
actor.json installed version, running, subscriptions
|
|
source/ the source of the current version
|
|
public/ files that the actor publishes
|
|
data/ actor state; owner-writable, world-readable via /actor/<id>/data/ (not private)
|
|
stderr.log what the actor and the broker write about it
|
|
versions/<n>/ every installed version: actor.wasm, source/, meta.json
|
|
```
|
|
|
|
To add or replace an actor by hand, put an `actor.wasm` (and its `source/`,
|
|
with the tests in `source/tests/`) into its folder; the host watches the
|
|
world folder and picks the file up once it stops changing. The broker tests
|
|
the file before it uses it.
|
|
If the tests fail, the old version keeps running, and `stderr.log` says why.
|
|
|
|
## More
|
|
|
|
- [PROTOCOL.md](PROTOCOL.md): messages, the filesystem, the broker, `cargo`.
|
|
Read this to write an actor in any language.
|
|
- [`sdk/`](sdk/): `sausage-actor`, the SDK for Rust actors.
|
|
- [`examples/notes`](examples/notes): a small actor with a test.
|
|
- [AGENTS.md](AGENTS.md): how to work on Sausage itself.
|