English | [日本語](architecture.ja.md)

# Architecture Overview

MAESTRO is an agent orchestrator that runs the tasks a user submits with LLM-driven workflows (Pieces).
For a code map aimed at contributors, see also [../AGENTS.md](../AGENTS.md).

## Execution flow

```
UI (POST /api/local/tasks)
  → bridge/server.ts (Express API)
  → Repository (SQLite: enqueue into the jobs table)
  → Worker.poll() picks up queued jobs
  → piece-classifier.ts: the LLM classifies the task and selects a Piece
  → piece-runner.ts: reads pieces/*.yaml and runs the movements in order
  → agent-loop.ts: the ReAct loop for one movement (LLM ↔ tool calls)
      ├─ intermediate transition: the transition tool
      └─ termination:             the complete tool (success / aborted / needs_user_input)
  → job complete: update DB + post a progress comment. Deliverables go to workspace/output/
```

1. **API intake** — `bridge/server.ts` receives the task and registers it in the `jobs` table as `queued` via the `Repository`.
2. **Worker** — `worker.ts` polls the DB and picks up jobs matching its `profiles`/`task_classes` (multiple workers run in parallel).
3. **Classification** — `piece-classifier.ts` passes the task body and the descriptions of all Pieces to the LLM and chooses the best-fit Piece.
4. **Piece execution** — `piece-runner.ts` iterates through the Piece's movements in order. Feedback from a verify movement is carried over to the next execute, and lessons between movements accumulate via `transition.lessons`.
5. **ReAct loop** — `agent-loop.ts` shuttles between the LLM and tools within one movement. `ContextManager` tracks token usage from the LLM's `usage` and fires warn / prompt / force_transition at thresholds (70/85/95%).

## Piece and Movement

- **Piece** = `pieces/*.yaml`. Composed of a `movements` array.
- Each **Movement** has `allowed_tools` (the tools presented to the LLM), `edit` (whether Write/Edit is allowed), and `rules` (transition conditions). Tools outside `allowed_tools` are invisible to the LLM.
- **Transitions**: an intermediate hop uses `transition` (only the destinations listed in `rules[].next` are selectable); termination uses `complete`. `complete.result` is the only final output visible to the user.
- **`default_next`** is an engine-internal sentinel (forced transition on context overflow, fallback at the ASK limit).
- **Progressive pressure**: as consecutive revisits to the same movement increase, warnings are injected, and exceeding the threshold triggers ABORT.

## Tool runtime

Tools are a set of modules in `src/engine/tools/*.ts`. `tools/index.ts` loads and dispatches them dynamically. Each tool has a one-line description (kept concise because it rides on every LLM call), and detailed instructions live in `docs/tools/<name>.md` (fetched with `ReadToolDoc`). For the main modules, see the list in [../AGENTS.md](../AGENTS.md#tool-modules).

Read-type tools run in parallel. Write/Edit is only presented when the movement has `edit: true`, and writes are mostly limited to `workspace/output/`.

## Bash sandbox

The agent's Bash execution is isolated with the **bwrap sandbox** when available:

- **Filesystem**: only the task's workspace is rw-bound, `/usr` etc. are ro, and other tasks' workspaces and the host `/home` are invisible.
- **Environment variables**: `--clearenv` + injecting only a minimal allowlist (secret env vars are invisible from inside the sandbox).
- **Network**: blocked with `--unshare-net` (outbound communication is consolidated into WebFetch/MCP with SSRF guards).
- **Each Bash call** gets an independent sandbox (volatile `/tmp`, a fresh namespace each time). Only the workspace persists.

`safety.bash_sandbox` selects the mode (`auto`/`always`/`off`). When bwrap is absent, it falls back to a **hardened fallback** (an exec with a command allowlist + path-scope checks + env scrubbing). Runtime `pip`/`npm install` are rejected in all modes, and Python packages are pre-baked from `runtime/python-requirements.txt`. For details, see [operations/bash-sandbox-provisioning.md](operations/bash-sandbox-provisioning.md).

## Workspace structure (during job execution)

```
{worktree_dir}/local/{taskId}/
  input/    where uploads and DownloadFile saves go
  output/   deliverables (the main place where Write/Edit is allowed)
  logs/     activity.log / various histories
  subtasks/ results from SpawnSubTask
  skills/   skill files materialized by ReadSkill
```

## Database

SQLite (better-sqlite3). `db/schema.sql` is the initial schema. Additional columns are applied idempotently in `db/migrate.ts`
with the pattern `PRAGMA table_info` → existence check → `ALTER TABLE ADD COLUMN`
(no version-management table is used). Main tables: `jobs` / `local_tasks` /
`local_task_comments` / `audit_log`, and others.

## Job lifecycle

`queued` → `dispatching` → `running` → `succeeded` / `failed` / `waiting_human` (waiting for an ASK answer) / `waiting_subtasks` (waiting for parallel subtasks). On failure, `retry` re-queues it (up to `retry.max_attempts` times).

## Optional subsystems

- **LLM Gateway** (`src/gateway/`) — exposes MAESTRO itself as an OpenAI-compatible LLM proxy (virtual keys, budgets, Prometheus metrics). For sharing across multiple GPUs/teams. Its env vars and connection type use the historical `AAO_*`/`aao_gateway` prefixes.
- **MCP** — Model Context Protocol server integration (`MCP_ENCRYPTION_KEY` required).
- **Reflection** — the LLM automatically updates user memory after each job completes (OFF by default, revertible).
- **Authentication** — Google/Gitea OAuth via Passport (optional). A `private`/`org`/`public` visibility model.
- **Scheduler** — cron-expression scheduled tasks.

## Frontend

React + Vite + TailwindCSS + @tanstack/react-query. `ui/src/App.tsx` is the root. A two-column (list + detail) layout handles the task list, schedule, settings, and skill/Piece management.
