Files
maestro/pieces/SCHEMA.md
T
oss-sync 29ccaf1e92
CI / build-and-test (push) Has been cancelled
sync: update from private repo (dfadcd5f)
2026-06-23 06:38:48 +00:00

130 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Piece YAML Schema
This is the reference for the piece YAML format consumed by
`src/engine/piece-runner.ts` (`loadPiece` / `validatePieceDef`) and the
`/api/pieces` HTTP layer (`src/bridge/pieces-api.ts` `validatePiece`).
Field names are snake_case in the YAML; the engine maps them to
camelCase internally (see `Movement` in `src/engine/agent-loop.ts`).
## Top-level
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `name` | string | yes | lowercase `[a-z0-9-]+` |
| `description` | string | yes | shown in the piece classifier |
| `max_movements` | positive integer | yes | hard cap on movement count per run |
| `initial_movement` | string | yes | must reference a `movements[].name` |
| `triggers.keywords` | string[] | no | classifier hint only |
| `required_mcp` | string[] | no | `[a-z0-9_-]{1,64}` server slugs |
| `shared_tools` | string[] | no | piece-level tools merged into **every** movement's effective `allowed_tools` (union). See below. |
| `model` | string | no | preferred LLM model |
| `movements` | Movement[] | yes | non-empty array |
### `shared_tools`
Tools listed here are added to every movement's allowed set, so they don't have
to be repeated in each `movements[].allowed_tools`. The effective set for a
movement is:
```
shared_tools movements[].allowed_tools META_TOOLS
```
`prepareMovementContext` (`src/engine/piece-runner.ts`) unions the first two
via `mergeToolNames` (de-duplicates, preserves order) into
`movement.allowedTools`; the always-on `META_TOOLS` are added later, when
`getToolDefs` (`src/engine/tools/index.ts`) builds the tool list.
The per-movement gates still apply on top of the union:
- A movement with `edit: false` never exposes `Write`/`Edit`, even if listed in
`shared_tools`.
- SSH tools (`SshExec` etc.) still require the movement's own
`allowed_ssh_connections` — a shared SSH tool is rejected at runtime in any
movement that has not declared connections (defense in depth).
- Unknown tool names are silently dropped by `getToolDefs` (`name in allDefs`).
Handled by two complementary layers (mirrors `required_mcp`): file-backed
pieces are cleaned leniently at load time by `normalizeSharedTools`
(`piece-runner.ts`, drops bad entries + warns), while API writes
(`POST`/`PUT /api/pieces`, `CreatePiece`/`UpdatePiece`) are validated strictly
by `validatePiece` (`pieces-api.ts`, rejects a non-array or non-string entry).
## Movement
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `name` | string | yes | unique within the piece |
| `edit` | boolean | yes | when true, Write/Edit are exposed |
| `persona` | string | yes | system-prompt persona |
| `instruction` | string | yes | the movement's task description |
| `allowed_tools` | string[] | yes | tool names; `'mcp__*'` wildcard allowed |
| `allowed_commands` | string[] | no | Bash command allowlist (overrides default) |
| `allowed_ssh_connections` | string[] | conditional | see below |
| `rules` | Rule[] | yes | transition rules; may be empty |
| `default_next` | string | no | engine-internal fallback (sentinel-friendly) |
| `max_consecutive_revisits` | number | no | loop-detection threshold override |
## `allowed_ssh_connections`
Per-movement SSH connection allowlist (Phase 4 of the SSH tool integration
| Value | Meaning |
|-------|---------|
| `undefined` (field omitted) | SSH tools reject with `no_allowed_connections_declared`. |
| `[]` (empty array) | SSH tools reject with `no_allowed_connections_declared`. The empty form is preferred over omission when the movement intentionally denies all connections (intent is explicit). |
| `['<connection-id>', ...]` | Only listed connection IDs may be passed to SSH tools. |
| `['*']` | Any registered connection may be passed. Still subject to ownership and grant checks (defense in depth). Use sparingly — typically only `ssh-ops`-style pieces. |
**Required**: If a movement's `allowed_tools` contains any of `SshExec`,
`SshUpload`, or `SshDownload`, then `allowed_ssh_connections` MUST be
present. `validatePieceDef` and `validatePiece` both reject pieces that
omit it for SSH-using movements.
**Format**: each entry must be `'*'` or a lowercase hex/hyphen id with
8+ characters (loose match against `randomUUID()` output).
Example:
```yaml
movements:
- name: ops
edit: false
persona: ops-operator
instruction: Run health checks on production hosts.
allowed_tools: [SshExec, Read]
allowed_ssh_connections:
- 6f9619ff-8b86-d011-b42d-00c04fc964ff
- 7a8b9cde-1234-4567-89ab-cdef12345678
rules:
- condition: all checks pass
next: COMPLETE
```
## Rule
```yaml
- condition: <human-readable description shown to the LLM>
next: <movement name | WAIT_SUBTASKS>
```
`rules[].next` may NOT use the reserved terminal sentinels
`COMPLETE` / `ABORT` / `ASK` — those are reachable only through the
`complete` tool (status: `success` / `aborted` / `needs_user_input`).
`default_next` does accept the terminal sentinels because it is an
engine-internal fallback (context overflow, ASK limit, SpawnSubTask
unavailable).
## Validation paths
Two validators implement the same rules:
- `validatePieceDef` in `src/engine/piece-runner.ts` — runs on every
`loadPiece` (file-backed) and `CreatePiece` (runtime).
- `validatePiece` in `src/bridge/pieces-api.ts` — runs on `PUT
/api/pieces/:name` (UI editor).
Both must stay in sync. When changing the schema, update both and add
test coverage in `src/engine/piece-runner.test.ts` and
`src/bridge/pieces-api.test.ts`.