mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
14 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ec1b3f2084
|
feat(sandbox): secure daytona snapshot names (#429)
## Summary Secures Daytona custom snapshot creation by removing user-controlled snapshot/image references and replacing them with deterministic names Fabro computes internally. Docker image selection now uses `image.docker`, while Daytona only accepts `image.dockerfile` for custom snapshots and continues to use `daytona-medium` when no Dockerfile is configured. ## Changes - Replaces public `image.ref` config/API shape with Docker-specific `image.docker` across Rust settings, OpenAPI, generated TypeScript client, docs, defaults, examples, and web samples. - Adds Daytona snapshot identity generation using HMAC-SHA256 over a canonical manifest keyed by the Daytona API key, producing `fabro-<uuid>` snapshot names without exposing Dockerfile text or key material. - Routes Daytona custom Dockerfiles, including devcontainer-generated Dockerfiles, through the same computed identity path before calling Daytona snapshot APIs. - Updates sandbox initialization events and store projections so initialized run state can show the resolved image and computed Daytona snapshot after startup. - Updates legacy config migration behavior so Docker image refs map to `image.docker`, while Daytona legacy snapshot names are not preserved. ## Breaking Changes - `image.ref` is no longer accepted in new environment config. - Docker environments should use `image.docker` for image selection. - Daytona environments reject `image.docker`; use `image.dockerfile` to request a custom computed snapshot. ## Verification - `cargo build -p fabro-api` - `cd lib/packages/fabro-api-client && bun run generate` - `cd lib/packages/fabro-api-client && bun run typecheck` - `cd apps/fabro-web && bun run typecheck` - `cargo +nightly-2026-04-14 fmt --check --all` - `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` - `ulimit -n 4096 && cargo nextest run --no-fail-fast -p fabro-cli -p fabro-config -p fabro-sandbox -p fabro-workflow -p fabro-store -p fabro-server -p fabro-api` - `cargo insta pending-snapshots` --- [](https://github.com/EveryInc/compound-engineering-plugin) 🤖 Generated with GPT-5 via [Codex](https://openai.com/codex) |
||
|
|
2c851d5a41
|
Merge remote-tracking branch 'origin/main' | ||
|
|
b15d8b8476
|
feat: Add approve/deny run controls to MCP and CLI (#400)
## Summary
Exposes the existing `POST /api/v1/runs/{id}/approve` and `POST
/api/v1/runs/{id}/deny` REST endpoints through the `fabro_run_interact`
MCP tool and two new top-level CLI commands (`fabro approve`, `fabro
deny`). Workflow agents are explicitly blocked from using these actions
— approval remains a human/user operation.
## What changed
**Client & tool backend** (`fabro-client`, `fabro-tool`): Added
`approve_run` and `deny_run` to `Client` and the `FabroToolBackend`
trait, implemented in `ClientBackend`. `deny_run` passes a
`DenyRunRequest` body; absent, blank, or whitespace-only reasons are
normalised to `None`.
**`fabro_run_interact` MCP tool**: Added `Approve` and `Deny` variants
to `RunInteractAction` / `ValidatedInteractAction`, and an optional
`reason` parameter (only valid for `deny`; validated and trimmed on
input). Both actions return `{ "summary": … }` using the existing shape.
The tool description is updated to list the new actions.
**Workflow-agent guard** (`fabro-workflow`): Before dispatching
`fabro_run_interact`, the handler checks
`validated.action.requires_user()`. If the action is `approve` or
`deny`, it returns an immediate `ToolError` without ever reaching the
backend, keeping the guard explicit and independent of server auth.
**CLI** (`fabro-cli`): Extracted the archive/unarchive batch loop into a
shared `run_resolved_run_batch` helper in `commands/runs/mod.rs`, then
implemented `approval.rs` using the same helper. Both commands follow
the same batch contract as archive: attempt all runs, collect per-run
errors, exit non-zero if any fail, and emit `{ "approved"/"denied": […],
"errors": […] }` in JSON mode.
**Server auth regression** (`fabro-server`): Extended
`run_tools_worker_cannot_call_user_only_non_mcp_routes` to cover `POST
/runs/{id}/deny` alongside the existing `approve` and `timeline` checks.
**Docs** (`mcp.mdx`, `cli.mdx`): Updated the `fabro_run_interact` table
entry and added approve/deny examples, plus reference sections for the
two new CLI commands.
### Plan Summary
- Add `approve_run` / `deny_run` to `Client` and `FabroToolBackend`
- Extend `fabro_run_interact` with `approve`, `deny`, and optional
`reason`
- Block workflow-agent self-approval with an early `ToolError`
- Refactor archive batch loop into shared `run_resolved_run_batch`
helper
- Add `fabro approve` and `fabro deny` CLI commands reusing that helper
- Add integration tests for CLI commands, MCP tool, and server auth
guard
### Fabro Details
<details>
<summary>Ran 9 stages in 63m 57s for $42.33</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 6s | – | 0 |
| preflight_lint | 2m 17s | – | 0 |
| implement | 28m 16s | $32.57 | 0 |
| simplify_opus | 10m 59s | $4.24 | 0 |
| simplify_gpt | 6m 13s | $3.96 | 0 |
| verify | 10m 51s | – | 0 |
| fixup | 2m 29s | $1.55 | 0 |
| **Total** | **63m 57s** | **$42.33** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (11 nodes and 14
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="git fetch origin main 2>&1 && git merge --no-edit --no-stat origin/main 2>&1 && cargo +nightly-2026-04-14 fmt --all 2>&1 && cargo dev docs refresh 2>&1 && cargo +nightly-2026-04-14 fmt --check --all 2>&1 && { command -v rg >/dev/null 2>&1 || { echo 'rg is required for verify'; exit 127; }; } && ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*\"disabled\"' lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml 2>&1 && cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --workspace --status-level slow --profile ci 2>&1 && cargo dev docs check 2>&1 && bun install --frozen-lockfile 2>&1 && (cd apps/fabro-web && bun run typecheck) 2>&1 && (cd apps/fabro-web && bun run test) 2>&1 && (cd lib/packages/fabro-api-client && bun run typecheck) 2>&1 && cargo dev build -- -p fabro-cli --release 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all format, clippy, Rust test, docs, TypeScript typecheck/test, and build failures.", max_visits=3]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> exit [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
|
||
|
|
879969cf54
|
docs: add child runs guide
Document child-run orchestration as a first-class execution concept and link the related MCP, UI, and API surfaces back to it. |
||
|
|
883a11ce4d
|
feat: MCP tool parity for fabro_tools workflow agents (#387)
## Summary
Workflow agents that opt in with `[run.agent] fabro_tools = true` now
see the full seven-tool Fabro run-management catalog — including
`fabro_run_pair` — matching what human MCP clients receive. Auth
extractors have been renamed from the ad-hoc "run tools" vocabulary to
product-level names, and all pair routes now accept run-management
actors instead of requiring a user principal.
### Plan Summary
- **Shared catalog parity** — `FABRO_RUN_PAIR_TOOL_NAME` added to
`TOOL_DEFINITIONS` in `fabro-tool`, making `register_fabro_run_tools()`
register all seven tools.
- **Workflow agent executor** — new `FABRO_RUN_PAIR_TOOL_NAME` match arm
in `execute_fabro_run_tool` parses `FabroRunPairParams`, calls
`fabro_tool::pair_run`, and renders the standard summary.
- **Auth extractor rename** — `RequiredRunToolActor` →
`RequiredRunManagementActor`; `RequireRunScopedOrRunTools` →
`RequireRunManagementTarget`. Semantics are unchanged; names now
describe the product policy.
- **Pair route migration** — all six pair handlers (`get_pair_status`,
`start_pair`, `get_pair`, `end_pair`, `send_pair_message`,
`get_transcript`) switch from `RequiredUser` to
`RequireRunManagementTarget`, removing the `Principal::User(auth.0)`
construction and the now-redundant `parse_run_id_path` calls.
- **Test coverage** — unit tests for the renamed extractors, integration
tests proving run-tools workers can read pair status/transcript
cross-run, that auth is accepted before domain logic
(worker-control-unavailable), that cross-run base workers remain
forbidden, and that run-tools workers still cannot call user-only routes
(approve, timeline).
### Key design decisions
**Forced-child behavior is preserved.** `fabro_run_create` from a
workflow agent still calls `ensure_current_run_parent`; the plan
specifically excludes relaxing this.
**Principal provenance is unchanged.** Workers keep `Principal::Worker {
run_id: … }` when acting through `fabro_tools`; no user principal is
forged.
**Pair handler run-id extraction simplified.** Because
`RequireRunManagementTarget` already extracts and validates the run ID
from the path, the pair handlers no longer repeat that parse — the
second `Path` component for pair-specific routes is bound to `_id` and
discarded.
**Twin-OpenAI gains `instructions_text` logging.** The integration test
for project-skill discovery needed to inspect the system prompt sent to
OpenAI; the twin now captures and exposes `instructions_text` in request
logs. This is a supporting change, not part of the auth model.
### Fabro Details
<details>
<summary>Ran 8 stages in 44m 10s for $39.32</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 6s | – | 0 |
| preflight_lint | 2m 17s | – | 0 |
| implement | 23m 9s | $33.23 | 0 |
| simplify_opus | 4m 6s | $1.06 | 0 |
| simplify_gpt | 4m 1s | $5.03 | 0 |
| verify | 7m 56s | – | 0 |
| **Total** | **44m 10s** | **$39.32** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (11 nodes and 14
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="git fetch origin main 2>&1 && git merge --no-edit --no-stat origin/main 2>&1 && cargo +nightly-2026-04-14 fmt --all 2>&1 && cargo dev docs refresh 2>&1 && cargo +nightly-2026-04-14 fmt --check --all 2>&1 && { command -v rg >/dev/null 2>&1 || { echo 'rg is required for verify'; exit 127; }; } && ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*\"disabled\"' lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml 2>&1 && cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --workspace --status-level slow --profile ci 2>&1 && cargo dev docs check 2>&1 && bun install --frozen-lockfile 2>&1 && (cd apps/fabro-web && bun run typecheck) 2>&1 && (cd apps/fabro-web && bun run test) 2>&1 && (cd lib/packages/fabro-api-client && bun run typecheck) 2>&1 && cargo dev build -- -p fabro-cli --release 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all format, clippy, Rust test, docs, TypeScript typecheck/test, and build failures.", max_visits=3]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> exit [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
|
||
|
|
990324710c
|
Add legacy SSE MCP transport support (#386)
## Summary Adds support for MCP servers that use the SSE-based HTTP transport, including Playwright MCP. Fabro already supports stdio and Streamable HTTP MCP servers. Some MCP servers still expose the SSE transport shape where the client opens an SSE stream, receives an `endpoint` event, and sends JSON-RPC requests back to that endpoint. This PR adds an explicit `protocol = "sse"` option while keeping Streamable HTTP as the default. ## What Changed - Added `McpHttpProtocol` with `streamable_http` as the default and `sse` as an opt-in protocol. - Added an SSE MCP client transport implementation. - Wired HTTP MCP setup to choose Streamable HTTP or SSE based on config. - Added `protocol = "sse"` support for both `http` and `sandbox` MCP entries. - Updated sandbox MCP resolution so SSE sandbox servers connect through the preview `/sse` path. - Documented `protocol = "sse"` for Playwright MCP. - Added an integration test covering SSE initialize, tool listing, and tool calls. ## Example ```toml [run.agent.mcps.playwright] type = "sandbox" protocol = "sse" command = ["npx", "@playwright/mcp@latest", "--port", "3100", "--headless", "--browser", "chromium"] port = 3100 startup_timeout = "60s" tool_timeout = "2m" ``` ## Compatibility Existing MCP configs are unchanged because `protocol` defaults to `streamable_http`. ## Validation - `cargo +nightly-2026-04-14 fmt --check --all` - `cargo nextest run -p fabro-mcp` - `cargo check -p fabro-agent -p fabro-workflow -p fabro-config` --------- Co-authored-by: Bryan Helmkamp <bryan@brynary.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7f84ac5e3f
|
Replace run-scoped sandbox config with named environments (#360)
## Summary
Replaces the `[run.sandbox]` configuration surface with a named,
provider-explicit environment catalog. Runs now select an environment by
slug (`[run.environment] id = "..."`) rather than configuring a sandbox
inline. Fabro resolves the catalog through normal settings precedence,
applies sparse run-level overrides, and creates a concrete sandbox from
the resolved environment.
This is a clean break — no `[run.sandbox]` compatibility layer.
### Plan Summary
- **New config shape:** Top-level `[environments.<slug>]` catalog valid
in `settings.toml`, `.fabro/project.toml`, and `workflow.toml`. Runs
reference a slug via `[run.environment] id = "..."` with optional sparse
overrides under `[run.environment.*]`.
- **Unified environment fields:** `provider`, `image` (ref +
dockerfile), `resources` (cpu/memory/disk), `network` (mode + allow
CIDRs), `lifecycle` (preserve/stop_on_terminal/auto_stop), `labels`,
`volumes`, `env` — replacing the previous split between `[run.sandbox]`,
`[run.sandbox.docker]`, `[run.sandbox.daytona]`, and
`[run.sandbox.daytona.snapshot]`.
- **OpenAPI schema update:** `RunSandboxSettings`, `DockerSettings`,
`DaytonaSettings`, and `DaytonaNetworkLayer` replaced with
`RunEnvironmentSettings`, `EnvironmentSettings`, `EnvironmentProvider`,
`EnvironmentImageSettings`, `EnvironmentResourcesSettings`,
`EnvironmentNetworkSettings`, `EnvironmentLifecycleSettings`, and
`EnvironmentVolumeSettings`.
- **CLI flag rename:** `--sandbox <provider>` → `--environment <slug>`
on `run`, `create`, `preflight`, and `server start/restart`.
- **Provider capability model:** Hard errors for security properties a
provider cannot enforce (local with blocked/CIDR networking; docker with
CIDR allow-lists). Warnings for unsupported resource limits, volumes,
labels, auto-stop, and Docker Dockerfiles.
- **Docs and internal code updated** throughout: `.fabro/project.toml`,
workflow configs, all public docs, CLI args, manifest builders, and the
runner's GitHub credentials check.
### Provider mapping
| Environment field | Local | Docker | Daytona |
|---|---|---|---|
| `image.ref` | Ignored | Docker image | Snapshot name |
| `image.dockerfile` | Ignored | Warning; ignored | Snapshot Dockerfile
(requires `image.ref`) |
| `resources.cpu/memory/disk` | Warning; ignored | cpu_quota / memory
limit / warning | Snapshot sizing |
| `network.mode = block` | **Error** | `network_mode = none` | Daytona
block |
| `network.mode = cidr_allow_list` | **Error** | **Error** | Daytona
CIDR allow-list |
| `labels` | Warning; ignored | Warning; ignored | Daytona labels |
| `volumes` | Warning; ignored | Warning; ignored | Daytona volume
mounts |
| `lifecycle.auto_stop` | Warning; ignored | Warning; ignored | Daytona
auto-stop interval |
| `env` | Process env overlay | Container env | Sandbox env |
### Fabro Details
<details>
<summary>Ran 11 stages in 217m 39s for $129.86</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 4m 7s | – | 0 |
| preflight_lint | 4m 9s | – | 0 |
| fix_lints | 3m 46s | $1.06 | 0 |
| implement | 76m 6s | $57.39 | 0 |
| simplify_opus | 71m 50s | $38.17 | 0 |
| simplify_gpt | 8m 27s | $2.24 | 0 |
| verify | 6m 10s | – | 0 |
| fixup | 42m 1s | $31.00 | 0 |
| fmt | 3s | – | 0 |
| **Total** | **217m 39s** | **$129.86** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
fmt [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> fmt [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
fmt -> exit
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Bryan Helmkamp <bhelmkamp@users.noreply.github.com>
|
||
|
|
0754f1ca4a
|
Add fabro_run_get read-only run inspection tool (#358)
## Summary
Adds a new `fabro_run_get` MCP tool that returns a run's summary,
resolved ID, projection, and pending questions without any mutation
capability. This separates read-only inspection from operational
control, allowing Ask Fabro sessions to inspect runs safely without
access to write operations.
## What changed and why
**New tool (`fabro-tool/src/get.rs`):** `FabroRunGetParams` /
`ValidatedRunGet` / `RunGetResult` follow the same
validation-and-dispatch pattern as other run tools. The implementation
resolves a selector, then fans out to three read-only API calls
(retrieve run, get state, list questions) and assembles them into a
single structured result.
**Tool registry and dispatch:** `FABRO_RUN_GET_TOOL_NAME` is exported
from `common.rs` and `lib.rs`, added to `TOOL_DEFINITIONS`, wired into
the MCP stdio server (`fabro-mcp-server/src/server.rs`), and dispatched
in the LLM agent executor (`fabro-workflow/src/handler/llm/api.rs`).
**Ask Fabro access policy (`fabro-server/.../sessions.rs`):** The
session now registers and allows only `fabro_run_events` +
`fabro_run_get` via the new `ASK_FABRO_RUN_TOOL_NAMES` constant.
`fabro_run_interact` is explicitly moved to the denied set, closing off
mutation from that session type. The policy match arm is refactored from
a hardcoded `|`-chain to a slice `contains` check so the constant is the
single source of truth.
**Docs:** `mcp.mdx` now lists `fabro_run_get` as the inspection tool and
redescribes `fabro_run_interact` as control-oriented.
**Backward compatibility:** `fabro_run_interact` (including `get` and
`get_questions` actions) is unchanged and still fully operational for
contexts that allow it.
### Plan Summary
- New `get.rs` module in `fabro-tool` with validation, async fetch, and
unit tests
- Constants + schema registration in `common.rs` / `lib.rs`
- MCP server and LLM dispatch branches added for
`FABRO_RUN_GET_TOOL_NAME`
- Ask Fabro session swaps `FABRO_RUN_INTERACT_TOOL_NAME` →
`FABRO_RUN_GET_TOOL_NAME` in registry and policy
- MCP integration tests: tool count constant, schema assertions, two new
end-to-end tests
(`mcp_get_resolves_selector_and_returns_summary_projection_and_questions`,
`mcp_get_rejects_blank_run_id_before_auth_or_network`)
- Public MCP docs updated
### Fabro Details
<details>
<summary>Ran 9 stages in 43m 30s for $13.42</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 2s | – | 0 |
| preflight_lint | 2m 14s | – | 0 |
| implement | 19m 43s | $8.85 | 0 |
| simplify_opus | 5m 52s | $1.45 | 0 |
| simplify_gpt | 10m 16s | $3.12 | 0 |
| verify | 2m 53s | – | 0 |
| fmt | 3s | – | 0 |
| **Total** | **43m 30s** | **$13.42** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD."]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
fmt [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> fmt [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
fmt -> exit
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
|
||
|
|
203ed5d029
|
docs: document agent pair and todo tools | ||
|
|
511371bd9a
|
feat(mcp): support goal files in run create
Allow fabro_run_create object specs to pass goal_file, reject goal and goal_file together, and preserve file-sourced goal semantics when building run manifests. |
||
|
|
c6356cbd77
|
feat: improve run board, thread, and MCP create flows (#347)
## Summary This branch improves several run-management surfaces that agents and users rely on: archived runs now stay visible and ordered correctly in the board view, pair-session messages appear in the stage Thread tab, and the `fabro_run_create` MCP tool accepts the workflow-string shorthand it advertises. ## Changes - Updates the web board cache invalidation and archived-column handling so archive/unarchive actions refresh both active and archived board queries and keep archived runs in a predictable column position. - Adds pair user/system message events to stage activity parsing, Thread rendering, search, details, and DNA timeline items. - Aligns `fabro_run_create` MCP runtime deserialization and `tools/list` schema so each run entry may be either a workflow string or a full create spec object. ## Test Plan - `cargo nextest run -p fabro-tool -p fabro-mcp-server` - `cargo nextest run -p fabro-cli stdio_server_initializes_and_lists_run_tools mcp_create_string_shorthand_deserializes_before_auth mcp_create_validation_errors_happen_before_auth_or_network mcp_create_and_search_manage_real_runs_with_cli_auth` - `cargo +nightly-2026-04-14 fmt --check --all` --- [](https://github.com/EveryInc/compound-engineering-plugin) 🤖 Generated with GPT-5 via [Codex](https://openai.com/codex) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
73ebb7d28b
|
feat(mcp): support run parent relationships (#295)
## Summary - Add parent metadata (`parent_id`, `children_count`) to Fabro MCP run summaries, search summaries, and created-run results. - Allow MCP clients to create child runs, search direct children, and link or unlink an existing run's parent through the existing run tools. - Update MCP docs and tool descriptions for the parent-aware create/search/interact behavior. ## Test Plan - [x] `cargo +nightly-2026-04-14 fmt --check --all` - [x] `cargo nextest run -p fabro-mcp-server` - [x] `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5e839ef4ea
|
docs(mcp): document Fabro MCP server | ||
|
|
283eab181f
|
refactor(docs): split docs/ into public/ and internal/
Invert the docs convention so the Mintlify-published site lives under docs/public/ and internal artifacts (strategy docs, brainstorms, plans, etc.) sit at docs/ root or docs/internal/. Tools that default to writing into docs/ now land in the catch-all instead of leaking into the published tree. - Move Mintlify content (administration/, agents/, api-reference/, changelog/, core-concepts/, examples/, execution/, getting-started/, human-tools/, integrations/, languages/, reference/, tutorials/, workflows/, images/, logo/, docs.json, favicon.svg, dot-highlight.js) into docs/public/. - Collapse docs-internal/ into docs/internal/. - Update Rust path references (fabro-api/build.rs, fabro-server, fabro-dev), TypeScript generator arg, CI path filters, clippy.toml reasons, AGENTS.md/CLAUDE.md, and README.md image refs. Mintlify dashboard project root must be updated to docs/public/ in a follow-up. .mintignore move/trim and .claude/skills/ updates land in a separate commit. |
Renamed from docs/agents/mcp.mdx (Browse further)