AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Run a coding agent from a chat channel. One thread per session, each in a sandbox it cannot escape.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Run a coding agent from a chat channel, in a sandbox it cannot escape.
A message in one configured channel opens a thread and starts a session. The agent works in a project directory of its own and nowhere else. Replies in the thread are prompts; what the agent says, runs, and changes comes back to the same thread.
Full documentation, including every configuration field, is in
docs, built with VitePress from docs.
sandbox.backend picks which, and defaults to bailey.bailey confines a session as a host process, using Landlock for the filesystem and seccomp for the syscall surface, so sessions use the host's own tools and there is no image to build. podman runs the session in a container from an image you provide instead. Sandboxing covers what each one enforces, and what the daemon does when a host cannot enforce it.
errand run # run the daemon until it is told to stop
errand threads # list, inspect, remove and collect what sessions left behind
errand help
There is a file to copy in config.example.json, and a schema beside it that gives an editor completion and checking.
The configuration file is read from ~/.config/errand/config.json, then
/etc/errand/config.json, then config.json in the working directory.
ERRAND_CONFIG names one outright and skips the search. A minimal one:
{
"chat": {
"token": "the bot token",
"channelId": "the one channel to serve",
"allowedUserIds": ["accounts that may drive sessions"]
},
"agent": {
"provider": "anthropic",
"providers": {
"anthropic": {
"credentialName": "ANTHROPIC_API_KEY",
"credential": "the provider key"
}
}
},
"projectRoot": "/srv/errand/projects",
"stateDir": "/var/lib/errand"
}
Everything else has a documented default. The daemon refuses to start rather
than run with a guarantee it cannot keep: if the backend cannot enforce
everything configured on this host, it says which and stops, unless
sandbox.requireFullEnforcement is set to false.
A session can ask a cheaper model of the same provider one question about one thing that already exists: a long log, a large file, a diff. It is shown that one thing and nothing else, has no way to run or read anything, and answers in text, so what comes back is a description to check rather than a decision to follow. The point is what it keeps out of the session's own context, which is paid for again on every later turn.
{
"agent": {
"delegate": { "model": "glm-5.3-flash", "perTurn": 8, "deadlineMs": 60000 }
}
}
Absent means no delegation at all. With it, the agent gets a delegate command
and is told how to use it; !status reports what it cost and how much it kept
out. To have the cheaper model do the work rather than describe it, use
!model instead.
An optional local web interface reads a session as it happens, browses the
project, and starts new ones. It has no login: the address it binds to is the
access control, and a public bind is refused rather than warned about. Build it
once (cd web && bun install && bun run build, then
cargo build --release), then add a web section to the configuration:
{ "web": { "host": "127.0.0.1", "port": 8787 } }
Set "observer": true to serve one that can watch and read but change nothing.
!help lists what can be typed, !usage says how much of the provider's usage
window is left and when it resets, and !model moves the session to another
model of the same provider, keeping the conversation. Plan on the capable one,
switch to the cheap one to carry it out, switch back to review. The same commands are registered as slash
commands, so they can be picked rather than remembered. A message starting
!!! is an aside: the people in the thread see it and the agent is never told.
!help, !usage and !model also work in the channel, where they are
answered by the daemon rather than starting a session. !model lists the
models there; switching one still belongs to a session, so it asks for a
thread.
The daemon reads its configuration file again when it changes, so most edits
take effect without a restart. The next session, and the daemon's own answers
to !usage, !model and the rest, use the new file.
A file that cannot be read is not fatal. An edit caught half-written, a field left out, a path that does not resolve: each is refused whole, the log says every reason, and the daemon carries on serving under the configuration it already had. Only a configuration that cannot be read at startup stops it, since a daemon with nothing to run on cannot serve.
What a reload does not reach, because it was committed to before the daemon was serving:
The model list is derived from the file again after a change, so an edited
models list is what !model lists straight away. !models refresh is for
asking the providers themselves again, over the network.
Full documentation, including every configuration field, is in
docs, built with VitePress from docs.
Definitions for OpenRC and systemd are in packaging, along with what to prepare and what its exit codes mean.
Early, but complete enough to run: chat, sandboxed sessions, the interface, and service definitions for both init systems.
cargo fmt --check # formatting
cargo clippy # lint, pedantic, warnings denied
cargo test # the test suite
cargo run -- run # run the daemon from the checkout
cargo build --release # the daemon binary, into target/release/
cargo build --release produces target/release/errand. The web interface
builds out of web into dist/web:
cd web && bun install && bun run build
The reference pages are generated from the schema, the command table, and the
character table; cargo test fails when what is committed no longer matches,
and cargo test regenerate_the_reference_pages -- --ignored brings them back
in step.
The documentation site builds with VitePress from docs, with bun:
cd docs && bun install && bun run build
Project rules are in AGENTS.md.
MIT OR Apache-2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.