Files
sausage/README.md
T
surma 895be183c9
ci / check (push) Successful in 9m11s
agent: a thinking level in agent.yaml and per call
- thinking (off, low, medium, high, xhigh, max) is an enum: serde rejects
  unknown levels in the config, and the schema of agent.chat lists them.
  reasoning_effort, a second name for the same setting, is gone.
- agent.chat takes an optional thinking level for one turn.
- off really switches thinking off: OpenAI gets the effort none, and
  Anthropic gets disabled thinking (newer Claude models think by
  default). A model that cannot switch thinking off gets low.
- Unset means the model's default. Anthropic thinking blocks now return
  unless thinking is off, because newer models think by default.
- The template sets thinking: medium and shows it in each example, with
  current model names.
- Checked against the proxy with gpt-6-luna, glm-5.3-flash, and
  claude-haiku-5-5.
2026-10-23 13:01:36 +01:00

106 lines
4.3 KiB
Markdown

# Sausage
You can see how the sausage is made.
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
You need [Nix](https://nixos.org/download) with flakes, and an LLM: the OpenAI
Responses API, an OpenAI-compatible Chat Completions API (OpenRouter, Ollama,
and most other providers), or the 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: the
first build on a machine takes a few minutes, later worlds reuse the compiled
crates), 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 nothing. It writes `world/config/agent.yaml`, with a comment for
each field and an example for each API. Edit it before the first chat: choose
the API, the base URL, the model, the key, and the thinking level. Until then, a chat answers with
an error that names the missing field. Both commands take another folder as
an argument, for example `... -- run my-world`.
Compiled code is cached outside the world, in `$XDG_CACHE_HOME/sausage/`
(`~/.cache/sausage/` without the variable). All worlds share it.
`--cache-dir <folder>` changes it for `init` and `run`.
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
```
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
`nix run .#loop`.
### Headless
`--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
```
## The console
| 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
```text
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.yaml the services that start with the world
config/agent.yaml the configuration of the agent
```
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): 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.