Investigation (pi 0.84.4 docs + host auth.json metadata, values never read): provider stacking is native (one auth.json keyed by provider; resolution --api-key > auth.json > env > models.json; OAuth auto-refresh). Multi-account per provider is NOT native -> named-file design: auth.<account>.json + per-launch injection. - scripts/auth.sh: status (provider names, credential types, perms, env-side names informational — never credential material) and accounts (named files, active marker). Exit codes per convention: 3 missing for a read, 2 unparseable, 4 file/environment (symlinks refuse). - scripts/agent.sh --auth <account>: resolves auth.<account>.json and exports PI_AUTH_FILE (the existing compose read-only mount source — no new plumbing); missing/invalid account refuses pre-container. - scripts/test-auth.sh: 13 no-Docker cases; core assertion is the safety property itself — fixture key/token/env VALUES never reach output. - Docs: TOOLS.md Auth section, AGENTS.md command surface + suites. Headless task runs keep the default credential (worker auth selection is a separate policy decision). Real-host smoke: anthropic/openai-codex oauth + zai api_key reported, perms 600, no named accounts yet. Suites 24/90/14/17/13 + verify green. Agreed sequence M16-M19 complete; M20 owner-gated.
160 lines
8.9 KiB
Markdown
160 lines
8.9 KiB
Markdown
# TOOLS.md — command and tool reference
|
||
|
||
On-demand reference for agent sessions (conductors, bootstrapping agents,
|
||
reviewers). `AGENTS.md` routes here; this file carries the depth: usage,
|
||
inputs/outputs, exit codes, and safety notes for every entry point.
|
||
|
||
Reading guide: system entry points are `scripts/*.sh` (bash) or invoked via
|
||
`node scripts/mosaic-task.mjs` (node). Host-side helpers under `tools/`
|
||
(tmux messaging, watchers, prose checker) are covered under Tools
|
||
(host-side) below. Every script fails closed — missing
|
||
or invalid configuration/policy refuses the operation with a nonzero exit
|
||
and changes nothing.
|
||
|
||
## Lifecycle
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/bootstrap.sh` | Create `~/.config/mosaic-dev/config.json` if absent | Idempotent; existing config validated, never rewritten |
|
||
| `scripts/build.sh` | Build the release image | Tag derived from `RELEASE` + pinned pi version |
|
||
| `scripts/hello.sh` | One-shot startup request | Prints model response on stdout |
|
||
| `scripts/verify.sh` | Full gated test | Exit 0 only on exact `MOSAIC_HELLO_OK`; `EXPECTED_MARKER` overrides for negative drills |
|
||
|
||
## Tasks (missions, runs, evidence)
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/run-task.sh run <task.json>` | Execute a task | Immutable run record under `<dataRoot>/runs/` |
|
||
| `scripts/run-task.sh validate <task.json>` | Strict validation | Writes nothing |
|
||
| `node scripts/mosaic-task.mjs show <runId>` | Inspect a run | Full record + snapshots + artifacts |
|
||
| `node scripts/mosaic-task.mjs list` | List runs | task/workspace/session columns |
|
||
| `node scripts/mosaic-task.mjs retry <runId>` | Re-execute a run's snapshot | New run dir; `retriedFrom` lineage recorded |
|
||
| `node scripts/mosaic-task.mjs prune [--keep=N] [--yes]` | Retention | Dry-run default; receipt in `runs/.pruned.log` |
|
||
| `node scripts/mosaic-task.mjs resolve-role <roleFile>` | Validate a role contract | Prints `MOSAIC_ROLE_TOOLS` / `MOSAIC_ROLE_NETWORK`; config-free |
|
||
|
||
Task fields: `prompt` (required), `mission` (path), `expectExact`,
|
||
`timeoutSeconds` (5–600), `workspace` (`:run` or named), `capabilities.tools`
|
||
(allowlist: read write edit bash grep find ls), `session`,
|
||
`sessionForkFrom` (requires `session`). Mission fields: `objective`,
|
||
`directives[]`, optional governing `capabilities.tools`. Policy: a task may
|
||
narrow a mission's tools, never widen; empty intersection = tool-free run.
|
||
|
||
## Agent (interactive TUI)
|
||
|
||
```bash
|
||
scripts/agent.sh <name> [--mission <file>] [--workspace <ws>] [--session <s>] [--tools <list>]
|
||
```
|
||
|
||
Launches an interactive pi TUI inside the container with the four immutable
|
||
contracts + optional mission + agent identity as its system prompt,
|
||
persistent named session, optional workspace. Exit with `/quit`.
|
||
|
||
A seat role (`agent.json` `role`) binds to `roles/<role>.json` (M18): the
|
||
contract's tools are a ceiling the seat definition or `--tools` may narrow,
|
||
never escalate past. Missing/invalid contract refuses the launch; empty
|
||
intersection = loud tool-free seat. An explicit `MOSAIC_AGENTS_DIR` override
|
||
that cannot resolve the named seat also refuses (#46) — unset the override
|
||
for the M13 plain governed TUI. `--auth <account>` injects
|
||
`auth.<account>.json` (beside the active credential file) as the launch's
|
||
`PI_AUTH_FILE`; a missing/invalid account refuses (M19).
|
||
|
||
## Auth (credentials)
|
||
|
||
Credential checkpoint over pi's auth model (one `auth.json` keyed by
|
||
provider; resolution order `--api-key` → `auth.json` → env → models.json).
|
||
No credential material is ever printed — provider names, credential types,
|
||
and env var NAMES only.
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/auth.sh status` | Per-provider credential report | Types (`oauth`/`api_key`), perms, env-side names (informational); exit 3 when the file is missing |
|
||
| `scripts/auth.sh accounts` | List named account files | `auth.<account>.json` beside the credential file; marks the active one |
|
||
|
||
The file is the compose read-only mount source (`PI_AUTH_FILE`, default
|
||
`~/.pi/agent/auth.json`); named accounts ride the same mount per launch via
|
||
`agent.sh --auth`. Headless task runs keep the default credential.
|
||
|
||
## Release
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/release.sh package` | Build + tag the release image | Tag: `mosaic-poc-agent:<pi>-r<release>` |
|
||
| `scripts/release.sh activate` | Health gate → atomic pointer swap | `--fault-injection` proves the refusal path |
|
||
| `scripts/release.sh rollback` | Health-gated return to previous | Refuses if image missing |
|
||
| `scripts/release.sh status` | Release, tag, active pointer, log | Safe on empty state |
|
||
| `scripts/release.sh ensure` | Self-determination: align active pointer to `RELEASE` | Fast path restores a missing/mismatched pointer without a gate; slow path packages + health-gates first. Invoked automatically at launch |
|
||
|
||
## Conductor (worker patches)
|
||
|
||
```bash
|
||
scripts/conductor-apply.sh <runId> [--dry-run]
|
||
```
|
||
|
||
Auto-applies a worker's patch under `roles/conductor-policy.json`:
|
||
succeeded run → clean target tree → path allowlist → syntax gates →
|
||
apply → policy suites → attribution commit. Any failure reverts.
|
||
Push is never automatic.
|
||
|
||
## Maintenance
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/reset.sh` | Delete the data root | Triple-safety-checked (path, symlink, ownership marker) |
|
||
| `scripts/test-config.sh` | Config selftests (no Docker) | 24 cases |
|
||
| `scripts/test-task.sh` | Task selftests + live cases | 90 cases |
|
||
| `scripts/test-release.sh` | Release selftests | 14 cases |
|
||
| `scripts/test-conductor.sh` | Auto-apply selftests (sandboxed) | 17 cases |
|
||
| `scripts/test-auth.sh` | Auth checkpoint selftests (no Docker) | 13 cases |
|
||
| `scripts/gitea-api.sh <METHOD> <path> [body]` | Gitea API helper | Token never on argv/stdout |
|
||
|
||
## Tools (host-side)
|
||
|
||
Host-side helpers under `tools/`, outside the `scripts/` command surface.
|
||
Per-tool READMEs: `tools/tmux/README.md` and `tools/unslop-hook/README.md`.
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `tools/tmux/agent-send.sh` | Inter-agent tmux message with addressing preamble | Reliable submit (bracketed paste, Enter flush, draft detection); ships `send-message.sh` over ssh for remote panes (remote needs only bash + tmux + base64) |
|
||
| `tools/agent-watch/agent-watch.sh` | Condition watcher per agent seat | One transient systemd `--user` timer + service per watch; fires `agent-send.sh` when the condition command exits 0 |
|
||
| `node tools/unslop-hook/unslop-check.js <file>` | Mechanical AI-tell prose check | Dependency-free node CLI + module driven by `lists.json`; `extension.ts` is the pi extension wrapper |
|
||
|
||
`agent-send.sh` prepends the preamble
|
||
`[<src_host>:<src_session> -> <dst_host>:<dst_session>]`; `-C`/`--class` adds
|
||
a ` class=<CLASS>` token (`terminal-log`, `actionable`, `human`, `reaction`,
|
||
`digest`; consumers treat an absent class as `actionable`). Flags: `-s` dst
|
||
session (required) · `-H` ssh target for a remote pane · `-L` named tmux
|
||
socket · `-n` dst hostname for the preamble · `-m`/`-f`/stdin message body ·
|
||
`-S` source-label override · `-r N` Enter-flush attempts (default 2) · `-v`
|
||
verbose · `-h` help. Exit codes: `0` delivered/queued · `1` target not found ·
|
||
`2` still draft · `3` usage error · `4` ambiguous socket (the session exists
|
||
on more than one tmux server; disambiguate with `-L` or `MOSAIC_TMUX_SOCKET`).
|
||
|
||
`agent-watch.sh` subcommands: `start --name <id> --session <session> --when
|
||
'<shell command; exit 0 = met>' --message <text>` with `--class`,
|
||
`--interval` (default 30), `--timeout` (default 3600), `--repeat`,
|
||
`--quiet-timeout`, `--socket` · `list` · `status [--json]` · `stop <name>` ·
|
||
`log <name>` · `meta-install [--interval 300] [--unit-name <unit>]` ·
|
||
`meta-remove [--unit-name <unit>]`. Interval floor is 10s (a watcher is a
|
||
fallback cadence, never a tight poll); hidden `_tick`/`_scan` subcommands run
|
||
inside the systemd services. `status` exit codes: `0` clean · `3` any stale
|
||
watch or dead meta-watch · `6` systemd user bus unreachable. Delivery goes
|
||
through `agent-send.sh`; rc `2` means the text reached the pane as an
|
||
unsubmitted draft, which counts as delivered and is not retried (other
|
||
failures retry twice, then the watch gives up). Watches are one-shot by
|
||
default; `--repeat` re-arms. Notices carry a `[watch:<name>]` prefix.
|
||
|
||
`unslop-check.js` checks a file (or stdin) against the word, phrase,
|
||
punctuation-density, and pattern lists in `lists.json`, stripping fenced and
|
||
inline code first so a quoted mention never flags. Invocation:
|
||
`node tools/unslop-hook/unslop-check.js <file>`; `UNSLOP_LISTS=<path>`
|
||
overrides the lists location. Exit codes: `0` clean · `1` violations (findings
|
||
printed as JSON on stdout) · `2` gate broken (invalid lists or unreadable
|
||
input; error on stderr, never a clean verdict).
|
||
|
||
## Exit-code convention
|
||
|
||
`0` success · `1` operation failed · `2` invalid data/configuration ·
|
||
`3` configuration missing for a read operation · `4` usage/file/environment
|
||
problem. Scripts print diagnostics on stderr; model responses (and only
|
||
model responses) on stdout.
|