PROTOCOL.md, DECISIONS.md, README.md, AGENTS.md for plugins, processes, and JSON-RPC
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
|
||||
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.
|
||||
Sausage is a world of small WASI programs ("plugins") that you grow by
|
||||
talking to it. A plugin is a `.wasm` file with JSON-RPC methods. Each call
|
||||
to a plugin starts a new process. The agent is a plugin too: it writes new
|
||||
plugins in Rust, builds them, tests them, activates them, and can change its
|
||||
own code.
|
||||
|
||||
## Start
|
||||
|
||||
@@ -29,8 +29,8 @@ nix run git+https://gitea.surma.technology/surma/sausage -- run # starts the
|
||||
`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`.
|
||||
values go to `world/config/agent.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:
|
||||
|
||||
@@ -39,60 +39,61 @@ nix run git+https://gitea.surma.technology/surma/sausage#loop # ./
|
||||
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.
|
||||
When the host crashes or a plugin calls `core.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
|
||||
same commands are `nix run`, `nix run -- init`, `nix run -- run`, and
|
||||
`nix run .#loop`.
|
||||
|
||||
### Headless
|
||||
|
||||
`--headless` (or `SAUSAGE_HEADLESS=1`) turns the console input off: the host
|
||||
never reads stdin, so end of input does not stop the world. It 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 pass the flag through the loop:
|
||||
`--headless` (or `SAUSAGE_HEADLESS=1`) turns the console input off. The host
|
||||
runs until SIGTERM or SIGINT (exit code 0) or until a plugin calls
|
||||
`core.exit` (exit code 75, so the loop starts the newest version):
|
||||
|
||||
```sh
|
||||
nix run git+https://gitea.surma.technology/surma/sausage#loop -- --headless my-world
|
||||
```
|
||||
|
||||
In the console:
|
||||
## 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
|
||||
```
|
||||
| Input | Effect |
|
||||
| --- | --- |
|
||||
| text | a message to the agent (`agent.chat`) |
|
||||
| `<plugin>.<method> {json}` | a call. The console prints the result. |
|
||||
| `ls [path]`, `cat <path>` | the filesystem as a process sees it, with `/proc` and `/run` |
|
||||
| `methods <plugin>` | the methods of a plugin, with params and docs |
|
||||
| `ps` | the running processes |
|
||||
| `log [plugin or pid] [-n N]` | the journal |
|
||||
| `activate <plugin> <wasm>`, `revert <plugin>` | activate a new version, or go back |
|
||||
| `stop <pid>`, `kill <pid>` | stop a process |
|
||||
| `help`, `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
|
||||
plugins/<name>/plugin.wasm the current version of a plugin
|
||||
plugins/<name>/source/ its source
|
||||
state/<plugin>/ the state of a plugin, by convention
|
||||
state/core/journal.sqlite the journal: stderr of all processes, and core events
|
||||
config/boot.json the services that start with the world
|
||||
config/agent.json the configuration of the agent
|
||||
```
|
||||
|
||||
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.
|
||||
Each process also sees `/proc` (the processes and the state of the core),
|
||||
`/self` (its own folder in `/proc`), and `/run` (shared files in memory).
|
||||
|
||||
To replace a plugin by hand, copy a `.wasm` file to its `plugin.wasm`. That
|
||||
skips the interface checks. `revert <plugin>` goes back to the version
|
||||
before the last activation.
|
||||
|
||||
## 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.
|
||||
- [PROTOCOL.md](PROTOCOL.md): calls, processes, the filesystem, locks,
|
||||
interfaces, and `cargo`. Read this to write a plugin in any language.
|
||||
- [DECISIONS.md](DECISIONS.md): why Sausage is the way it is.
|
||||
- [`sdk/`](sdk/): `sausage-plugin`, the SDK for Rust plugins.
|
||||
- [`examples/`](examples/): small plugins.
|
||||
- [AGENTS.md](AGENTS.md): how to work on Sausage itself.
|
||||
|
||||
Reference in New Issue
Block a user