Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ fi
# Run Composer Guardian (secrets + CI hygiene) before heavier tasks.
bash "$ROOT/scripts/guardian.sh" --trigger pre-commit

mapfile -t STAGED_FORMAT_FILES < <(
# Biome config (biome.json files.include) scopes it to src/test/packages/docs, and
# Biome 1.9.x only handles ts/tsx/js/jsx/mjs/cjs/json/jsonc/css. Restrict the input
# to both, so a commit touching only out-of-scope files (e.g. scripts/*.json, docs/*.md)
# does not trip the "No files were processed" error and block the commit.
mapfile -t BIOME_FILES < <(
git diff --cached --name-only --diff-filter=ACMR |
grep -E "\\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|css|md|yml|yaml)$" || true
grep -E "^(src|test|packages|docs)/.*\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|css)$" || true
)

if [ "${#STAGED_FORMAT_FILES[@]}" -gt 0 ]; then
bunx biome check "${STAGED_FORMAT_FILES[@]}"
if [ "${#BIOME_FILES[@]}" -gt 0 ]; then
bunx biome check "${BIOME_FILES[@]}"
else
echo "No staged Biome-supported files to check."
fi
Expand Down
6 changes: 6 additions & 0 deletions docs/AGENT_PROFILES.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ Oracle configurations must be read-only and reasoning-capable at runtime. The bu
An assistant response completing without an error is not a verified success. Automatic promotion requires at least 20 verified outcomes for the workload. Verification results, explicit user acceptance, rejection, and retries can supply outcome evidence; unverified runs remain useful for latency and cost observations only.

Clients can request a profile through `X-Maestro-Agent-Profile` (or the compatibility `X-Composer-Agent-Profile`) and inspect the versioned resolved profile on the routing decision.

## Outcome-calibrated Oracle experiments

Hosts can enable deterministic session-level control/treatment assignment with `MAESTRO_ORACLE_EXPERIMENT_ID`, `MAESTRO_ORACLE_EXPERIMENT_ALLOCATION`, `MAESTRO_ORACLE_EXPERIMENT_CONTROL_VERSION`, and `MAESTRO_ORACLE_EXPERIMENT_TREATMENT_VERSION`. Configuration must be complete and allocation must be between `0` and `1`.

The assigned arm and policy version appear in routing receipts and bounded telemetry attributes. Assignment is immutable for an experiment/session pair; promotion remains advisory and requires verified outcome gates.
46 changes: 46 additions & 0 deletions docs/CUSTOM_AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Governed Custom Agents

Installed Maestro packages can prepare primary or subagent modes with the capability-scoped API exported from `src/agent/plugin-agent-api.ts`.

Declare agent resource directories in the package manifest. Each child directory is one statically discoverable agent and can be filtered like other package resources:

```json
{
"keywords": ["maestro-package"],
"maestro": { "agents": ["./agents"] }
}
```

For example, `agents/focused-reviewer/agent.json` supplies the static `key`, `label`, and `entry` metadata. Runtime registration must match that metadata before the agent becomes available.

```ts
const agents = createPluginAgentApi({
policy: hostPolicy,
metadata: discoveredAgentMetadata,
});

const reviewer = agents.createAgent({
key: "focused-reviewer",
label: "Focused reviewer",
description: "Reviews a bounded change",
systemPrompt: "Review the requested change and report actionable findings.",
model: "anthropic/claude-sonnet-4-6",
tools: ["read", "search"],
budgets: { maxTurns: 10, maxToolCalls: 20, maxCostUsd: 5 },
approvalMode: "fail",
sandboxMode: "read-only",
});

agents.registerAgentMode({
key: "focused-reviewer",
label: "Focused reviewer",
agent: reviewer,
primary: true,
});
```

`tools: "all"` means every tool already allowed by the host policy, not every installed tool. Explicit tool lists, models, and all three budget limits must remain within the host policy. Approval and sandbox settings may be equal to or more restrictive than the host, never less restrictive.

Registration is atomic and requires a matching static metadata declaration. Duplicate names, metadata mismatches, foreign or forged handles, unknown tools, disallowed models, invalid budgets, and permission escalation are rejected without modifying the registry.

This API configures agent modes only. It does not add executable tools, UI extensions, raw process access, or authority beyond the package installation and host runtime policy. Extensions and MCP remain the supported executable-tool mechanisms.
149 changes: 149 additions & 0 deletions docs/PAINTER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Painter

Painter generates and edits images from inside an agent thread. It is the
visual counterpart to the Oracle: where the Oracle brings a second model's
reasoning, Painter brings an image model's output. Use it for UI mockups,
app icons, illustrations, and editing existing images (for example,
redacting a screenshot before pasting it into a PR).

Painter is a tool the main agent invokes. It is **not** always-on — tell the
agent to use it, the same way you would tell it to use the Oracle.

## Setup

Painter calls an image API and needs a credential in the environment where the
agent runs. The default provider is OpenAI (`gpt-image-2`):

```bash
export OPENAI_API_KEY=sk-...
```

To use FLUX via fal.ai instead:

```bash
export MAESTRO_PAINTER_PROVIDER=flux
export FAL_KEY=<your fal key>
```

## Providers

| Provider | `MAESTRO_PAINTER_PROVIDER` | Generate | Edit | Mask | Credential |
| --- | --- | --- | --- | --- | --- |
| OpenAI (default) | `openai` | yes | yes | yes | `OPENAI_API_KEY` |
| FLUX (fal.ai) | `flux` | yes | no | no | `FAL_KEY` |

Each provider declares a `supports` map; the painter checks it before calling
edit, so asking for an edit on FLUX fails fast with a clear error instead of a
buried API failure.

## What it does

- **Generate** — text prompt → new image.
- **Edit** — one to three input images + a prompt → edited image (OpenAI only).
Restrict changes to a region with a mask (see Masking).
- Outputs are **persisted to disk** and returned as absolute paths. The agent
references those paths in later turns. Transcripts stay small (image bytes
are never inlined as base64 into the conversation).

## Masking

Two ways to restrict an edit to part of an image:

- **Bounding box** — pass `maskRegion: {x, y, width, height}` in pixels. Painter
synthesizes the mask PNG automatically (transparent = editable region,
matching OpenAI's mask semantics). Input dimensions come from the PNG header
(no deps), fall back to the optional `sharp` package, then an explicit
caller-supplied `maskSize`. Coordinates outside the image are clipped.
- **Mask file** — pass `mask` pointing at a PNG whose transparent pixels mark
the editable region.

Omit both for a whole-image edit.

## Output location

Defaults to `~/.maestro/assets/painter/`. Override with
`MAESTRO_PAINTER_OUTPUT_DIR`. Files are named `painter-<timestamp>-<rand>.png`.

## Inline preview

View a generated image inline in a capable terminal:

```bash
maestro painter show ~/.maestro/assets/painter/painter-....png
```

Renders via iTerm2/WezTerm (OSC 1337) or kitty (graphics protocol). Run it from
a plain shell, not inside the full-screen TUI. This writes only to stdout — it
never routes through the agent loop, so it can't waste model tokens or corrupt
the conversation context.

## Spend ceiling (opt-in)

Painter checks a process-level budget before every API call. Enable by setting
both:

- `MAESTRO_PAINTER_MAX_COST_CENTS` — whole-dollar-cents ceiling.
- `MAESTRO_PAINTER_PRICE_TABLE` — JSON mapping
`${model}|${size}|${quality}` → cents per image, with `*` wildcards, e.g.
`{"gpt-image-2|*|high": 8}`.

When the next call would exceed the ceiling, Painter fails fast with a clear
reason and makes no API call. Prices are **not** fabricated: the default table
is empty, and when a ceiling is set but no price matches, the gate fails **open**
(allows the call) rather than blocking on missing data.

## Configuration

| Variable | Default | Purpose |
| --- | --- | --- |
| `OPENAI_API_KEY` / `FAL_KEY` | — | Required provider credential. |
| `MAESTRO_PAINTER_PROVIDER` | `openai` | `openai` or `flux`. |
| `MAESTRO_PAINTER_MODEL` | `gpt-image-2` (OpenAI) / `fal-ai/flux/schnell` (FLUX) | Model id. |
| `MAESTRO_PAINTER_BASE_URL` | — | OpenAI-compatible base URL (proxies). |
| `MAESTRO_PAINTER_OUTPUT_DIR` | `~/.maestro/assets/painter` | Where images are written. |
| `MAESTRO_PAINTER_TIMEOUT_MS` | `180000` | Per-call timeout. |
| `MAESTRO_PAINTER_MAX_COST_CENTS` | — | Opt-in spend ceiling (whole cents). |
| `MAESTRO_PAINTER_PRICE_TABLE` | — | JSON price table for the ceiling. |

## Examples

Ask the agent directly:

- "Use the painter to create a UI mockup for the settings page, dark theme."
- "Use the painter to generate an app icon: dark background, glowing cyan cursor."
- "Redact the API keys in `screenshots/bug.png` using the painter."

For edits, point the agent at one or more image paths (use `@`-mention).
Painter accepts up to three reference images.

## Model notes

`gpt-image-2` does **not** support transparency; requesting
`background: transparent` is rejected by the API. It supports arbitrary `WxH`
sizes where both dimensions are divisible by 16 and the aspect ratio is between
1:3 and 3:1, in addition to the standard sizes.

## Permissions

Painter writes files only inside the configured output directory. Inputs are
read-only — Painter never mutates a file you pass in. Painter is part of the
`advanced` tool category: available to `coder` and `custom` subagent types, and
explicitly denied from `explorer` (read-only), mirroring the Oracle.

## Composing with Conductor (browser automation)

The Maestro-native loop is Painter + Conductor together:

1. **Mockup → diff.** Painter generates a UI mockup; Conductor screenshots the
live page; the agent compares and iterates.
2. **Bug → redact → PR.** Conductor captures a failing-state screenshot;
Painter redacts secrets; the agent opens a PR with the clean image.

## Not yet implemented

- Sixel rasterization (sixel terminals are detected but not encoded; needs a
real image library).
- FLUX editing / inpainting (fal exposes this on a separate endpoint).
- Inline rendering *inside* the full-screen TUI itself (the `maestro painter
show` CLI command covers plain-shell preview; a TUI-integrated viewer is a
separate piece of work in the TUI package).
67 changes: 67 additions & 0 deletions docs/superpowers/plans/2026-07-14-agent-operations-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Agent Operations UI Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add a web operations panel that presents durable parent/child agent lineage, live status, navigation, and safe cancellation.

**Architecture:** Extend the replay-lab wire projection with complete lineage, derive a deterministic forest in a pure web helper, and render it in a focused panel embedded in the composer. Existing session navigation and abort paths remain the only control mechanisms.

**Tech Stack:** TypeScript, Lit, Maestro trajectory/replay contracts, Vitest.

## Global Constraints

- Sparse legacy lineage renders as roots.
- No new execution-control channel or permission bypass.
- HTTP and persisted replay data remain compatible.

---

### Task 1: Complete the web lineage contract

**Files:**
- Modify: `packages/web/src/services/api-client.types.ts`
- Modify: `src/server/agent-trajectory-replay-lab.ts`
- Test: `test/server/agent-trajectory-replay-lab.test.ts`

**Interfaces:**
- Produces: `TrajectoryReplayLabTimelineItem.parentAgentRunId?: string` and existing `childAgentRunId?: string` from server projection.

- [ ] **Step 1: Add a failing projection test** asserting a parent and child timeline record retain both IDs.
- [ ] **Step 2: Run** `bunx vitest --run test/server/agent-trajectory-replay-lab.test.ts` and confirm the parent field assertion fails.
- [ ] **Step 3: Add `parentAgentRunId?: string` to the web type and copy the field in replay-lab projection.**
- [ ] **Step 4: Re-run the focused test** and expect PASS.
- [ ] **Step 5: Commit** with `git commit -m "feat(trajectory): project complete agent lineage"`.

### Task 2: Derive a deterministic operations forest

**Files:**
- Create: `packages/web/src/components/agent-operations-tree.ts`
- Create: `packages/web/src/components/agent-operations-tree.test.ts`

**Interfaces:**
- Produces: `buildAgentOperationsTree(items): AgentOperationsNode[]` where each node contains `runId`, `parentRunId?`, `status`, `latestItem`, and `children`.

- [ ] **Step 1: Write failing tests** for nested runs, missing parents, duplicate events, latest-status selection, and stable timestamp/run-ID ordering.
- [ ] **Step 2: Run** `bunx vitest --run packages/web/src/components/agent-operations-tree.test.ts` and confirm the missing module fails.
- [ ] **Step 3: Implement a pure two-pass builder:** coalesce items by run ID, attach known parents, retain orphans as roots, recursively sort by latest timestamp then run ID.
- [ ] **Step 4: Re-run the focused test** and expect PASS.
- [ ] **Step 5: Commit** with `git commit -m "feat(web): derive agent operations tree"`.

### Task 3: Render and control the operations panel

**Files:**
- Create: `packages/web/src/components/composer-agent-operations-panel.ts`
- Create: `packages/web/src/components/composer-agent-operations-panel.test.ts`
- Modify: `packages/web/src/components/composer-chat.ts`
- Modify: `packages/web/src/components/composer-chat-overlays.ts`

**Interfaces:**
- Consumes: `ApiClient.getSessionReplayLab(sessionId)` and `buildAgentOperationsTree`.
- Emits: `open-session` with `{ sessionId, runId }`, `cancel-run` with `{ runId }`, and `close`.

- [ ] **Step 1: Write failing component tests** that verify nesting, status copy, expansion, navigation events, and that cancellation is absent for terminal runs.
- [ ] **Step 2: Run** `bunx vitest --run packages/web/src/components/composer-agent-operations-panel.test.ts` and confirm the component is missing.
- [ ] **Step 3: Implement the panel and wire navigation/cancellation to existing composer handlers.** Disable cancellation while a request is pending and expose the server error inline.
- [ ] **Step 4: Run** the panel tests plus `packages/web/src/components/composer-chat.test.ts`; expect PASS.
- [ ] **Step 5: Build** with `npx nx run maestro-web:build --skip-nx-cache`; expect PASS.
- [ ] **Step 6: Commit** with `git commit -m "feat(web): add agent operations panel"`.
74 changes: 74 additions & 0 deletions docs/superpowers/plans/2026-07-14-ai-package-build-unblocker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# AI Package Build Unblocker Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Restore the required Maestro test baseline by including painter image-provider dependencies in the AI package TypeScript project.

**Architecture:** Keep the current AI facade boundary and explicitly include the root image-provider directory alongside the other temporary root-owned sources. Protect the include with a source-level regression test.

**Tech Stack:** TypeScript project references, Vitest, Nx.

## Global Constraints

- Do not move painter implementation files in this unblocker.
- Do not skip hooks or required builds.

---

### Task 1: Cover and repair the package input boundary

**Files:**
- Modify: `packages/ai/tsconfig.build.json`
- Create: `test/packages/ai-tsconfig-boundary.test.ts`

**Interfaces:**
- Consumes: `packages/ai/tsconfig.build.json#include`.
- Produces: inclusion of `../../src/services/image-providers/**/*.ts` in the AI composite project.

- [ ] **Step 1: Write the failing test**

```ts
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

describe("@evalops/ai TypeScript boundary", () => {
it("includes painter image-provider dependencies", () => {
const config = JSON.parse(readFileSync("packages/ai/tsconfig.build.json", "utf8"));
expect(config.include).toContain("../../src/services/image-providers/**/*.ts");
});
});
```

- [ ] **Step 2: Verify the test and package build fail**

Run: `bunx vitest --run test/packages/ai-tsconfig-boundary.test.ts`
Expected: FAIL because the include is absent.

Run: `npx nx run @evalops/ai:build --skip-nx-cache`
Expected: FAIL with TS6307 for `src/services/image-providers/*.ts`.

- [ ] **Step 3: Add the minimal include**

```json
"../../src/services/image-providers/**/*.ts",
```

Place it next to the existing `../../src/tools/**/*.ts` facade input.

- [ ] **Step 4: Verify the regression and baseline**

Run: `bunx vitest --run test/packages/ai-tsconfig-boundary.test.ts`
Expected: PASS.

Run: `npx nx run @evalops/ai:build --skip-nx-cache`
Expected: PASS.

Run: `npx nx run maestro:test --skip-nx-cache`
Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add packages/ai/tsconfig.build.json test/packages/ai-tsconfig-boundary.test.ts
git commit -m "fix(ai): include painter providers in package build"
```
Loading
Loading