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

5.4 KiB
Raw Blame History

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:

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

- 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.