diff --git a/.ai/prompts/code-review-deep-1.md b/.ai/prompts/code-review-deep-1.md index 1d837cfe7..c15a499b6 100644 --- a/.ai/prompts/code-review-deep-1.md +++ b/.ai/prompts/code-review-deep-1.md @@ -17,7 +17,7 @@ Output a report with all the bugs using this format: title of bug brief description of bug - lib/crates/fabro-cli/src/commands/resume.rs + lib/apps/fabro-cli/src/commands/resume.rs 115 115 diff --git a/.ai/prompts/code-review-deep-2.md b/.ai/prompts/code-review-deep-2.md index 6bc81586b..aa7000436 100644 --- a/.ai/prompts/code-review-deep-2.md +++ b/.ai/prompts/code-review-deep-2.md @@ -19,7 +19,7 @@ Output a report with all the bugs using this format: important OR nit yes OR no - lib/crates/fabro-cli/src/commands/resume.rs + lib/apps/fabro-cli/src/commands/resume.rs 115 115 @@ -51,7 +51,7 @@ Here is a real-world example: important no - lib/crates/fabro-cli/src/commands/resume.rs + lib/apps/fabro-cli/src/commands/resume.rs 208 208 diff --git a/.claude/skills/docs/references/mapping.md b/.claude/skills/docs/references/mapping.md index 0a7c9e329..0d6a0d3cf 100644 --- a/.claude/skills/docs/references/mapping.md +++ b/.claude/skills/docs/references/mapping.md @@ -4,32 +4,32 @@ Which source files affect which doc pages. Use this as guidance — also apply j | Source | Docs | |--------|------| -| `lib/crates/fabro-cli/src/main.rs`, `lib/crates/fabro-workflow/src/cli/mod.rs`, `lib/crates/fabro-workflow/src/cli/run.rs` | `docs/public/reference/cli.mdx` | -| `lib/crates/fabro-cli/src/cli_config.rs` | `docs/public/reference/cli-configuration.mdx` | -| `lib/crates/fabro-llm/src/cli.rs` | `docs/public/reference/cli.mdx` | -| `lib/crates/fabro-api/src/serve.rs` | `docs/public/reference/cli.mdx` | -| `lib/crates/fabro-workflow/src/parser/*.rs` | `docs/public/reference/dot-language.mdx` | -| `lib/crates/fabro-workflow/src/condition.rs` | `docs/public/reference/dot-language.mdx` | -| `lib/crates/fabro-workflow/src/cli/validate.rs` | `docs/public/reference/dot-language.mdx` | -| `lib/crates/fabro-workflow/src/stylesheet.rs` | `docs/public/workflows/stylesheets.mdx` | -| `lib/crates/fabro-workflow/src/transform.rs` | `docs/public/workflows/variables.mdx` | -| `lib/crates/fabro-workflow/src/handler/*.rs` | `docs/public/workflows/stages-and-nodes.mdx`, `docs/public/reference/dot-language.mdx` | -| `lib/crates/fabro-workflow/src/handler/human.rs` | `docs/public/workflows/human-in-the-loop.mdx` | -| `lib/crates/fabro-workflow/src/cli/run_config.rs` | `docs/public/execution/run-configuration.mdx` | -| `lib/crates/fabro-workflow/src/engine.rs` | `docs/public/core-concepts/how-arc-works.mdx` | -| `lib/crates/fabro-workflow/src/context/*.rs` | `docs/public/execution/context.mdx` | -| `lib/crates/fabro-workflow/src/checkpoint.rs` | `docs/public/execution/checkpoints.mdx` | -| `lib/crates/fabro-workflow/src/retro.rs`, `lib/crates/fabro-workflow/src/retro_agent.rs` | `docs/public/execution/retros.mdx` | -| `lib/crates/fabro-workflow/src/interviewer/*.rs` | `docs/public/execution/interviews.mdx` | -| `lib/crates/fabro-workflow/src/hook/*.rs` | `docs/public/agents/hooks.mdx` | -| `lib/crates/fabro-workflow/src/daytona_sandbox.rs` | `docs/public/integrations/daytona.mdx`, `docs/public/execution/environments.mdx` | -| `lib/crates/fabro-agent/src/tools.rs`, `lib/crates/fabro-agent/src/tool_registry.rs`, `lib/crates/fabro-agent/src/tool_execution.rs` | `docs/public/agents/tools.mdx` | -| `lib/crates/fabro-agent/src/v4a_patch.rs` | `docs/public/agents/tools.mdx` | -| `lib/crates/fabro-agent/src/cli.rs` | `docs/public/agents/permissions.mdx` | -| `lib/crates/fabro-agent/src/subagent.rs` | `docs/public/agents/subagents.mdx` | -| `lib/crates/fabro-agent/src/mcp_integration.rs` | `docs/public/agents/mcp.mdx` | -| `lib/crates/fabro-llm/src/catalog.rs`, `lib/crates/fabro-llm/src/providers/*.rs` | `docs/public/core-concepts/models.mdx` | -| `lib/crates/fabro-slack/src/*.rs` | `docs/public/integrations/slack.mdx` | -| `lib/crates/fabro-mcp/src/*.rs` | `docs/public/agents/mcp.mdx` | -| `lib/crates/fabro-api/src/*.rs` | `docs/public/api-reference/overview.mdx`, `docs/public/api-reference/demo-mode.mdx` | -| `lib/crates/fabro-api/src/server_config.rs` | `docs/public/administration/server-configuration.mdx` | +| `lib/apps/fabro-cli/src/main.rs`, `lib/components/fabro-workflow/src/cli/mod.rs`, `lib/components/fabro-workflow/src/cli/run.rs` | `docs/public/reference/cli.mdx` | +| `lib/apps/fabro-cli/src/cli_config.rs` | `docs/public/reference/cli-configuration.mdx` | +| `lib/components/fabro-llm/src/cli.rs` | `docs/public/reference/cli.mdx` | +| `lib/foundation/fabro-api/src/serve.rs` | `docs/public/reference/cli.mdx` | +| `lib/components/fabro-workflow/src/parser/*.rs` | `docs/public/reference/dot-language.mdx` | +| `lib/components/fabro-workflow/src/condition.rs` | `docs/public/reference/dot-language.mdx` | +| `lib/components/fabro-workflow/src/cli/validate.rs` | `docs/public/reference/dot-language.mdx` | +| `lib/components/fabro-workflow/src/stylesheet.rs` | `docs/public/workflows/stylesheets.mdx` | +| `lib/components/fabro-workflow/src/transform.rs` | `docs/public/workflows/variables.mdx` | +| `lib/components/fabro-workflow/src/handler/*.rs` | `docs/public/workflows/stages-and-nodes.mdx`, `docs/public/reference/dot-language.mdx` | +| `lib/components/fabro-workflow/src/handler/human.rs` | `docs/public/workflows/human-in-the-loop.mdx` | +| `lib/components/fabro-workflow/src/cli/run_config.rs` | `docs/public/execution/run-configuration.mdx` | +| `lib/components/fabro-workflow/src/engine.rs` | `docs/public/core-concepts/how-arc-works.mdx` | +| `lib/components/fabro-workflow/src/context/*.rs` | `docs/public/execution/context.mdx` | +| `lib/components/fabro-workflow/src/checkpoint.rs` | `docs/public/execution/checkpoints.mdx` | +| `lib/components/fabro-workflow/src/retro.rs`, `lib/components/fabro-workflow/src/retro_agent.rs` | `docs/public/execution/retros.mdx` | +| `lib/components/fabro-workflow/src/interviewer/*.rs` | `docs/public/execution/interviews.mdx` | +| `lib/components/fabro-workflow/src/hook/*.rs` | `docs/public/agents/hooks.mdx` | +| `lib/components/fabro-workflow/src/daytona_sandbox.rs` | `docs/public/integrations/daytona.mdx`, `docs/public/execution/environments.mdx` | +| `lib/components/fabro-agent/src/tools.rs`, `lib/components/fabro-agent/src/tool_registry.rs`, `lib/components/fabro-agent/src/tool_execution.rs` | `docs/public/agents/tools.mdx` | +| `lib/components/fabro-agent/src/v4a_patch.rs` | `docs/public/agents/tools.mdx` | +| `lib/components/fabro-agent/src/cli.rs` | `docs/public/agents/permissions.mdx` | +| `lib/components/fabro-agent/src/subagent.rs` | `docs/public/agents/subagents.mdx` | +| `lib/components/fabro-agent/src/mcp_integration.rs` | `docs/public/agents/mcp.mdx` | +| `lib/components/fabro-llm/src/catalog.rs`, `lib/components/fabro-llm/src/providers/*.rs` | `docs/public/core-concepts/models.mdx` | +| `lib/components/fabro-slack/src/*.rs` | `docs/public/integrations/slack.mdx` | +| `lib/components/fabro-mcp/src/*.rs` | `docs/public/agents/mcp.mdx` | +| `lib/foundation/fabro-api/src/*.rs` | `docs/public/api-reference/overview.mdx`, `docs/public/api-reference/demo-mode.mdx` | +| `lib/foundation/fabro-api/src/server_config.rs` | `docs/public/administration/server-configuration.mdx` | diff --git a/.gitattributes b/.gitattributes index 068cf7f02..96454cdd1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -lib/crates/fabro-spa/assets/** linguist-generated=true -diff +lib/apps/fabro-spa/assets/** linguist-generated=true -diff lib/packages/fabro-api-client/src/** linguist-generated=true diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1580d81df..12ba1b9cc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,7 +4,9 @@ on: push: branches: [main] paths: - - "lib/crates/**" + - "lib/apps/**" + - "lib/components/**" + - "lib/foundation/**" - "test/**" - "Cargo.toml" - "Cargo.lock" @@ -18,7 +20,9 @@ on: pull_request: branches: [main] paths: - - "lib/crates/**" + - "lib/apps/**" + - "lib/components/**" + - "lib/foundation/**" - "test/**" - "Cargo.toml" - "Cargo.lock" @@ -76,7 +80,7 @@ jobs: - name: Verify legacy auth identity removal run: | if git grep -nE '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; then + -- lib/apps lib/components lib/foundation apps lib/packages docs/public/api-reference/fabro-api.yaml; then echo "::error::Legacy auth identities remain in the repository" exit 1 else diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml index e2f63a047..064c38c3d 100644 --- a/.github/workflows/typescript.yml +++ b/.github/workflows/typescript.yml @@ -5,7 +5,7 @@ on: branches: [main] paths: - "apps/**" - - "lib/crates/fabro-spa/**" + - "lib/apps/fabro-spa/**" - "lib/packages/**" - "package.json" - "bun.lock" @@ -17,7 +17,7 @@ on: branches: [main] paths: - "apps/**" - - "lib/crates/fabro-spa/**" + - "lib/apps/fabro-spa/**" - "lib/packages/**" - "package.json" - "bun.lock" diff --git a/.gitignore b/.gitignore index 5c49b62a9..d9b235fc2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ target node_modules apps/fabro-web/dist apps/fabro-web/.dist-builds/ -lib/crates/fabro-spa/assets/* -!lib/crates/fabro-spa/assets/.gitkeep +lib/apps/fabro-spa/assets/* +!lib/apps/fabro-spa/assets/.gitkeep tmp evals/swe-bench/repos/ evals/swe-bench/results/ diff --git a/AGENTS.md b/AGENTS.md index d77efe095..bdc9ab53f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ The OpenAPI spec at `docs/public/api-reference/fabro-api.yaml` is the source of 1. Edit `docs/public/api-reference/fabro-api.yaml` 2. `cargo build -p fabro-api` — build.rs regenerates Rust types and client via progenitor -3. Write/update handler in `lib/crates/fabro-server/src/server.rs`, add route to `build_router()` +3. Write/update handler in `lib/apps/fabro-server/src/server.rs`, add route to `build_router()` 4. `cargo nextest run -p fabro-server` — conformance test catches spec/router drift 5. `cd lib/packages/fabro-api-client && bun run generate` — regenerates TypeScript Axios client @@ -64,7 +64,7 @@ The OpenAPI spec at `docs/public/api-reference/fabro-api.yaml` is the source of - Treat OpenAPI as the source of truth for the wire contract, not as the automatic owner of Rust types. - Before adding or keeping a generated schema type, search the workspace for an existing hand-written Rust type with the same product meaning. -- If the schema and an existing Rust type have the same semantics and serde shape, reuse the existing type via `lib/crates/fabro-api/build.rs` `with_replacement(...)` instead of generating a parallel API type. +- If the schema and an existing Rust type have the same semantics and serde shape, reuse the existing type via `lib/foundation/fabro-api/build.rs` `with_replacement(...)` instead of generating a parallel API type. - If two types are close but not identical, prefer proposing changes that align them into one canonical type rather than accepting small drift. It is usually better to iterate the API now than to create permanently split Rust/API types. - Keep a separate API DTO only when the API is intentionally a projection, summary, or presentation-specific view of internal state. In that case, give it a distinct API-facing name instead of reusing the internal concept name. - Treat `ApiFoo` aliases and `foo_to_api` / `foo_from_api` adapters as a smell unless they represent a real semantic boundary. They should not exist only to bridge accidental duplicate types. @@ -107,7 +107,7 @@ Before merging changes that add or move shared test helpers, verify: Fabro is an AI-powered workflow orchestration platform. Workflows are defined as Graphviz graphs, where each node is a stage (agent, prompt, command, conditional, human, parallel, etc.) executed by the workflow engine. -### Rust crates (`lib/crates/`) +### Rust crates (`lib/apps/`, `lib/components/`, and `lib/foundation/`) - **fabro-cli** — CLI entry point. Commands: `run`, `exec`, `serve`, `validate`, `parse`, `cp`, `model`, `doctor`, `install`, `ps`, `system prune` - **fabro-workflow** — Core workflow engine. Parses Graphviz graphs, runs stages, manages checkpoints/resume, hooks, and human-in-the-loop interactions - **fabro-agent** — AI coding agent with tool use (Bash, Read, Write, Edit, Glob, Grep, WebFetch). `Sandbox` trait abstracts execution environments diff --git a/Cargo.toml b/Cargo.toml index 6cdec975b..e9f63c9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,12 @@ [workspace] -members = ["lib/crates/*", "test/twin/openai", "test/twin/github"] -default-members = ["lib/crates/fabro-cli"] +members = [ + "lib/apps/*", + "lib/components/*", + "lib/foundation/*", + "test/twin/openai", + "test/twin/github", +] +default-members = ["lib/apps/fabro-cli"] resolver = "2" [workspace.package] @@ -84,7 +90,7 @@ hmac = "0.12" sha2 = "0.10" hex = "0.4" insta = "1" -fabro-test = { path = "lib/crates/fabro-test" } +fabro-test = { path = "lib/foundation/fabro-test" } twin-openai = { path = "test/twin/openai" } twin-github = { path = "test/twin/github" } tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] } @@ -100,11 +106,11 @@ rust-embed = "8" percent-encoding = "2" minijinja = "=2.19.0" miette = { version = "7.6", features = ["fancy"] } -fabro-http = { path = "lib/crates/fabro-http" } -fabro-environment = { path = "lib/crates/fabro-environment" } -fabro-options-metadata = { path = "lib/crates/fabro-options-metadata" } -fabro-redact = { path = "lib/crates/fabro-redact" } -fabro-static = { path = "lib/crates/fabro-static" } +fabro-http = { path = "lib/foundation/fabro-http" } +fabro-environment = { path = "lib/components/fabro-environment" } +fabro-options-metadata = { path = "lib/foundation/fabro-options-metadata" } +fabro-redact = { path = "lib/foundation/fabro-redact" } +fabro-static = { path = "lib/foundation/fabro-static" } graphviz-sys = { git = "https://github.com/fabro-sh/graphviz-sys" } ref-cast = "1" strum = { version = "0.28", features = ["derive"] } diff --git a/apps/fabro-web/app/routes/run-overview.tsx b/apps/fabro-web/app/routes/run-overview.tsx index 25262b748..0150d19ea 100644 --- a/apps/fabro-web/app/routes/run-overview.tsx +++ b/apps/fabro-web/app/routes/run-overview.tsx @@ -23,7 +23,7 @@ export const handle = { wide: true, fullHeight: true }; type Direction = "LR" | "TB"; -// Mirrors fabro-graphviz's RANKDIR_RE (lib/crates/fabro-graphviz/src/render.rs) — +// Mirrors fabro-graphviz's RANKDIR_RE (lib/components/fabro-graphviz/src/render.rs) — // keep the accepted `rankdir=` syntax in sync with that regex. const RANKDIR_RE = /rankdir\s*=\s*(\w+)/; diff --git a/docs/internal/cli-workflow-coupling-audit.md b/docs/internal/cli-workflow-coupling-audit.md index 7fdf953ba..198e658f3 100644 --- a/docs/internal/cli-workflow-coupling-audit.md +++ b/docs/internal/cli-workflow-coupling-audit.md @@ -2,7 +2,7 @@ ## Scope -- Production imports under `lib/crates/fabro-cli/src/**` that still reference `fabro_workflow::*` after the server-owned selector/export refactor. +- Production imports under `lib/apps/fabro-cli/src/**` that still reference `fabro_workflow::*` after the server-owned selector/export refactor. - Test-only imports are listed separately so the remaining architectural debt is explicit. ## Completed In This Change @@ -16,30 +16,30 @@ | Path | Direct dependency | Why it still exists | Required remediation track | | --- | --- | --- | --- | -| `lib/crates/fabro-cli/src/commands/pr/create.rs` | `StageOutcome`, `pull_request::maybe_open_pull_request` | CLI still reconstructs store state and runs PR creation logic from the workflow pipeline directly. | Replace with a server API, or extract PR orchestration into a non-engine shared service crate plus API. | -| `lib/crates/fabro-cli/src/commands/run/runner.rs` | `artifact_snapshot::CapturedArtifactInfo`, `artifact_upload::{ArtifactSink, StageArtifactUploader}`, `event::{Emitter, RunEventSink}`, `operations::{self, StartServices}`, `run_control::RunControlState`, `runtime_store::{RunStoreBackend, RunStoreHandle}` | Hidden worker subprocess path still lives inside the CLI crate and embeds the workflow engine directly. | Re-home worker/runtime code outside the user CLI surface, ideally into a dedicated worker crate or binary. | -| `lib/crates/fabro-cli/src/manifest_builder.rs` | `git::{GitSyncStatus, head_sha, sync_status}` | Manifest submission still relies on git helper logic that happens to live in `fabro_workflow`. | Extract git-sync inspection helpers into a non-workflow shared crate/module. | -| `lib/crates/fabro-cli/src/server_client.rs` | `artifact_snapshot::CapturedArtifactInfo` | The upload client reuses a workflow-owned artifact snapshot DTO. | Extract shared artifact snapshot DTOs into `fabro-store`, `fabro-types`, or a dedicated shared crate. | -| `lib/crates/fabro-cli/src/commands/runs/inspect.rs` | `run_status::RunStatus` | CLI output types still depend on engine-owned run status enums. | Extract shared status types into `fabro-types` or switch to API-generated/public store types. | -| `lib/crates/fabro-cli/src/commands/runs/list.rs` | `run_status::RunStatus` | List rendering still depends on engine-owned run status enums. | Extract shared status types into `fabro-types` or switch to API-generated/public store types. | -| `lib/crates/fabro-cli/src/commands/run/attach.rs` | `StageOutcome`, `run_status::RunStatus` | Attach/replay logic still formats engine-owned terminal status types directly. | Extract shared run/conclusion status types into `fabro-types`. | -| `lib/crates/fabro-cli/src/commands/run/output.rs` | `StageOutcome`, `records::Conclusion` | Human-readable completion output still consumes workflow-owned conclusion/status records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. | -| `lib/crates/fabro-cli/src/commands/run/wait.rs` | `records::Conclusion`, `run_status::RunStatus` | Wait output still depends on workflow-owned status/conclusion records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. | -| `lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs` | `StageOutcome`, `format_cost` | Progress UI still depends on shared stage outcome and workflow-owned cost-formatting helper code. | Move formatting helpers into `fabro-util`. | -| `lib/crates/fabro-cli/src/commands/run/run_progress/info_display.rs` | `event::RunNoticeLevel` | Progress UI still formats workflow-owned notice levels directly. | Extract shared notice/event enums into `fabro-types`. | -| `lib/crates/fabro-cli/src/commands/run/run_progress/event.rs` | `event::RunNoticeLevel` | Progress event translation still depends on workflow-owned notice levels. | Extract shared notice/event enums into `fabro-types`. | +| `lib/apps/fabro-cli/src/commands/pr/create.rs` | `StageOutcome`, `pull_request::maybe_open_pull_request` | CLI still reconstructs store state and runs PR creation logic from the workflow pipeline directly. | Replace with a server API, or extract PR orchestration into a non-engine shared service crate plus API. | +| `lib/apps/fabro-cli/src/commands/run/runner.rs` | `artifact_snapshot::CapturedArtifactInfo`, `artifact_upload::{ArtifactSink, StageArtifactUploader}`, `event::{Emitter, RunEventSink}`, `operations::{self, StartServices}`, `run_control::RunControlState`, `runtime_store::{RunStoreBackend, RunStoreHandle}` | Hidden worker subprocess path still lives inside the CLI crate and embeds the workflow engine directly. | Re-home worker/runtime code outside the user CLI surface, ideally into a dedicated worker crate or binary. | +| `lib/apps/fabro-cli/src/manifest_builder.rs` | `git::{GitSyncStatus, head_sha, sync_status}` | Manifest submission still relies on git helper logic that happens to live in `fabro_workflow`. | Extract git-sync inspection helpers into a non-workflow shared crate/module. | +| `lib/apps/fabro-cli/src/server_client.rs` | `artifact_snapshot::CapturedArtifactInfo` | The upload client reuses a workflow-owned artifact snapshot DTO. | Extract shared artifact snapshot DTOs into `fabro-store`, `fabro-types`, or a dedicated shared crate. | +| `lib/apps/fabro-cli/src/commands/runs/inspect.rs` | `run_status::RunStatus` | CLI output types still depend on engine-owned run status enums. | Extract shared status types into `fabro-types` or switch to API-generated/public store types. | +| `lib/apps/fabro-cli/src/commands/runs/list.rs` | `run_status::RunStatus` | List rendering still depends on engine-owned run status enums. | Extract shared status types into `fabro-types` or switch to API-generated/public store types. | +| `lib/apps/fabro-cli/src/commands/run/attach.rs` | `StageOutcome`, `run_status::RunStatus` | Attach/replay logic still formats engine-owned terminal status types directly. | Extract shared run/conclusion status types into `fabro-types`. | +| `lib/apps/fabro-cli/src/commands/run/output.rs` | `StageOutcome`, `records::Conclusion` | Human-readable completion output still consumes workflow-owned conclusion/status records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. | +| `lib/apps/fabro-cli/src/commands/run/wait.rs` | `records::Conclusion`, `run_status::RunStatus` | Wait output still depends on workflow-owned status/conclusion records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. | +| `lib/apps/fabro-cli/src/commands/run/run_progress/stage_display.rs` | `StageOutcome`, `format_cost` | Progress UI still depends on shared stage outcome and workflow-owned cost-formatting helper code. | Move formatting helpers into `fabro-util`. | +| `lib/apps/fabro-cli/src/commands/run/run_progress/info_display.rs` | `event::RunNoticeLevel` | Progress UI still formats workflow-owned notice levels directly. | Extract shared notice/event enums into `fabro-types`. | +| `lib/apps/fabro-cli/src/commands/run/run_progress/event.rs` | `event::RunNoticeLevel` | Progress event translation still depends on workflow-owned notice levels. | Extract shared notice/event enums into `fabro-types`. | ## Test-Only Couplings | Path | Direct dependency | Why it still exists | Suggested handling | | --- | --- | --- | --- | -| `lib/crates/fabro-cli/src/commands/dump.rs` test module | `event::{Event, append_event}` | Unit tests synthesize workflow events directly. | Low priority; keep until a lighter-weight event fixture helper exists. | -| `lib/crates/fabro-cli/src/commands/run/wait.rs` test module | `StageOutcome`, `records::Conclusion`, `run_status::RunStatusRecord` | Output tests construct workflow-owned records directly. | Replace with shared fixture builders once conclusion DTOs move out. | -| `lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs` test module | `event::{Event, RunNoticeLevel, to_run_event, to_run_event_at}`, `outcome::billed_model_usage_from_llm` | Progress tests build engine events directly. | Replace with shared event fixture helpers after event DTO extraction. | -| `lib/crates/fabro-cli/src/commands/run/run_progress/event.rs` test module | `event::{Event, to_run_event}` | Event rendering tests depend on engine event constructors. | Replace with shared event fixture helpers after event DTO extraction. | -| `lib/crates/fabro-cli/src/commands/run/runner.rs` test module | `artifact_upload::StageArtifactUploader` | Worker tests still reach into workflow upload internals. | Keep with worker re-home work; not worth separating first. | -| `lib/crates/fabro-cli/tests/it/workflow/real_cli.rs` | `context::Context`, `event::Emitter`, `handler::agent::{CodergenBackend, CodergenResult}`, `handler::llm::cli::AgentCliBackend` | Integration test exercises the real workflow engine directly through CLI harnesses. | Accept as engine integration coverage or move under workflow-owned test support later. | -| `lib/crates/fabro-cli/tests/it/scenario/recovery.rs` | `operations::{RunTimeline, build_timeline}` | Scenario test inspects rewind timeline internals directly. | Replace after server-owned rewind/timeline APIs exist. | +| `lib/apps/fabro-cli/src/commands/dump.rs` test module | `event::{Event, append_event}` | Unit tests synthesize workflow events directly. | Low priority; keep until a lighter-weight event fixture helper exists. | +| `lib/apps/fabro-cli/src/commands/run/wait.rs` test module | `StageOutcome`, `records::Conclusion`, `run_status::RunStatusRecord` | Output tests construct workflow-owned records directly. | Replace with shared fixture builders once conclusion DTOs move out. | +| `lib/apps/fabro-cli/src/commands/run/run_progress/mod.rs` test module | `event::{Event, RunNoticeLevel, to_run_event, to_run_event_at}`, `outcome::billed_model_usage_from_llm` | Progress tests build engine events directly. | Replace with shared event fixture helpers after event DTO extraction. | +| `lib/apps/fabro-cli/src/commands/run/run_progress/event.rs` test module | `event::{Event, to_run_event}` | Event rendering tests depend on engine event constructors. | Replace with shared event fixture helpers after event DTO extraction. | +| `lib/apps/fabro-cli/src/commands/run/runner.rs` test module | `artifact_upload::StageArtifactUploader` | Worker tests still reach into workflow upload internals. | Keep with worker re-home work; not worth separating first. | +| `lib/apps/fabro-cli/tests/it/workflow/real_cli.rs` | `context::Context`, `event::Emitter`, `handler::agent::{CodergenBackend, CodergenResult}`, `handler::llm::cli::AgentCliBackend` | Integration test exercises the real workflow engine directly through CLI harnesses. | Accept as engine integration coverage or move under workflow-owned test support later. | +| `lib/apps/fabro-cli/tests/it/scenario/recovery.rs` | `operations::{RunTimeline, build_timeline}` | Scenario test inspects rewind timeline internals directly. | Replace after server-owned rewind/timeline APIs exist. | ## Follow-Up Order diff --git a/docs/internal/event-schema-competitive-analysis.md b/docs/internal/event-schema-competitive-analysis.md index 9763fdf30..60a1c02ab 100644 --- a/docs/internal/event-schema-competitive-analysis.md +++ b/docs/internal/event-schema-competitive-analysis.md @@ -48,9 +48,9 @@ That envelope is stronger than most comparator systems. It gives Fabro stable to Relevant current Fabro sources: - `docs-internal/events-strategy.md` -- `lib/crates/fabro-workflow/src/event.rs` -- `lib/crates/fabro-types/src/run_event/mod.rs` -- `lib/crates/fabro-agent/src/types.rs` +- `lib/components/fabro-workflow/src/event.rs` +- `lib/foundation/fabro-types/src/run_event/mod.rs` +- `lib/components/fabro-agent/src/types.rs` ## Comparison Matrix diff --git a/docs/internal/events.md b/docs/internal/events.md index 4af5ab491..7850a5411 100644 --- a/docs/internal/events.md +++ b/docs/internal/events.md @@ -1252,26 +1252,6 @@ Emitted when the agent detects a tool-use loop. No properties. -### `agent.turn.limit` - -Emitted when the agent reaches its maximum turn count. - -```json -{ - "id": "...", "ts": "...", "run_id": "...", - "event": "agent.turn.limit", - "node_id": "code", "node_label": "code", - "session_id": "ses_abc", - "properties": { - "max_turns": 25 - } -} -``` - -| Property | Type | Description | -|----------|------|-------------| -| `max_turns` | number | Maximum turns allowed | - ### `agent.skill.expanded` ```json diff --git a/docs/internal/fabro-event-schema-v2-concrete-shape.md b/docs/internal/fabro-event-schema-v2-concrete-shape.md index 4d8541b90..78134f619 100644 --- a/docs/internal/fabro-event-schema-v2-concrete-shape.md +++ b/docs/internal/fabro-event-schema-v2-concrete-shape.md @@ -269,21 +269,21 @@ The durable model remains simple: replay ordered events, no duplicate truth laye An engineer implementing this proposal should make only these structural changes unless a later section explicitly says otherwise. -1. Update [`RunEvent`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/mod.rs) to add: +1. Update [`RunEvent`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/mod.rs) to add: - `stage_id` - `parallel_group_id` - `parallel_branch_id` - `tool_call_id` - `actor` -2. Update `RunEvent::to_value()` and `RunEvent` parsing in [`run_event/mod.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/mod.rs) so the new envelope fields serialize and deserialize. -3. Extend `StoredEventFields` and `stored_event_fields()` in [`event.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-workflow/src/event.rs) to populate: +2. Update `RunEvent::to_value()` and `RunEvent` parsing in [`run_event/mod.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/mod.rs) so the new envelope fields serialize and deserialize. +3. Extend `StoredEventFields` and `stored_event_fields()` in [`event.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/components/fabro-workflow/src/event.rs) to populate: - `stage_id` - `parallel_group_id` - `parallel_branch_id` - `tool_call_id` on tool-lifecycle events - `actor` when there is a clear primary actor These values should come from the emitter's current execution context for stage and parallel scope, and from event-specific payloads for `tool_call_id`. -4. Leave [`EventEnvelope`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-store/src/types.rs) structurally unchanged: +4. Leave [`EventEnvelope`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/components/fabro-store/src/types.rs) structurally unchanged: - `seq: u32` - `payload: EventPayload` 5. Update API/SSE envelope serialization so wire JSON is flattened: @@ -304,11 +304,11 @@ An engineer implementing this proposal should make only these structural changes V2 should keep the current hand-coded domain split for prop structs: -- run props in [`run.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/run.rs) -- stage and checkpoint props in [`stage.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/stage.rs) -- agent props in [`agent.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/agent.rs) -- infra/setup props in [`infra.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/infra.rs) -- parallel/interview/git/misc props in [`misc.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/crates/fabro-types/src/run_event/misc.rs) +- run props in [`run.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/run.rs) +- stage and checkpoint props in [`stage.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/stage.rs) +- agent props in [`agent.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/agent.rs) +- infra/setup props in [`infra.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/infra.rs) +- parallel/interview/git/misc props in [`misc.rs`](/Users/bhelmkamp/p/fabro-sh/fabro/lib/foundation/fabro-types/src/run_event/misc.rs) That split is part of the design quality. V2 should keep adding hand-coded prop structs, not collapse everything into generic maps. @@ -374,7 +374,6 @@ V2 keeps the current durable family surface broadly intact. - `agent.error` - `agent.warning` - `agent.loop.detected` -- `agent.turn.limit` - `agent.steering.injected` - `agent.compaction.started` - `agent.compaction.completed` diff --git a/docs/internal/mcp-server-qa-test-plan.md b/docs/internal/mcp-server-qa-test-plan.md index de2fcfc75..c5d7b8e74 100644 --- a/docs/internal/mcp-server-qa-test-plan.md +++ b/docs/internal/mcp-server-qa-test-plan.md @@ -1,6 +1,6 @@ # Fabro MCP Server — QA Test Plan -One-time manual QA pass for the 5 tools exposed by `fabro-mcp-server`. Source of truth: `lib/crates/fabro-mcp-server/src/run_tools/`. +One-time manual QA pass for the 5 tools exposed by `fabro-mcp-server`. Source of truth: `lib/apps/fabro-mcp-server/src/run_tools/`. This plan is **not** a template for adding automated test coverage — it exists to drive a single hands-on sweep against a real running server. Tick boxes as scenarios pass; add notes inline for failures or surprising behavior. Open bugs/PRs for issues found; do not port these scenarios into the Rust test suite. diff --git a/docs/internal/migrations-strategy.md b/docs/internal/migrations-strategy.md index 1aa73370f..318463a58 100644 --- a/docs/internal/migrations-strategy.md +++ b/docs/internal/migrations-strategy.md @@ -9,7 +9,7 @@ Migrations are product-facing compatibility code. Treat them like startup and st Each crate owns the migrations for the data it owns. ```text -lib/crates// +lib/// migrations/ YYYYMMDDSS_descriptive_name.rs src/migrations.rs diff --git a/docs/internal/plan-events-as-source-of-truth-follow-ups.md b/docs/internal/plan-events-as-source-of-truth-follow-ups.md index 650eca25d..1ca2b9fc2 100644 --- a/docs/internal/plan-events-as-source-of-truth-follow-ups.md +++ b/docs/internal/plan-events-as-source-of-truth-follow-ups.md @@ -170,14 +170,14 @@ Required payload fields: Emit from the same places that currently call `put_status`: -- `lib/crates/fabro-workflow/src/operations/create.rs` -- `lib/crates/fabro-workflow/src/operations/start.rs` -- `lib/crates/fabro-workflow/src/operations/resume.rs` -- `lib/crates/fabro-workflow/src/pipeline/finalize.rs` -- `lib/crates/fabro-workflow/src/lifecycle/disk.rs` +- `lib/components/fabro-workflow/src/operations/create.rs` +- `lib/components/fabro-workflow/src/operations/start.rs` +- `lib/components/fabro-workflow/src/operations/resume.rs` +- `lib/components/fabro-workflow/src/pipeline/finalize.rs` +- `lib/components/fabro-workflow/src/lifecycle/disk.rs` - CLI administrative flows that directly mutate status: - - `lib/crates/fabro-cli/src/commands/runs/rm.rs` - - `lib/crates/fabro-cli/src/commands/run/rewind.rs` + - `lib/apps/fabro-cli/src/commands/runs/rm.rs` + - `lib/apps/fabro-cli/src/commands/run/rewind.rs` ### 2. Enrich `checkpoint.completed` to carry a full checkpoint snapshot @@ -200,11 +200,11 @@ Add fields covering: Emitter seam: -- `lib/crates/fabro-workflow/src/lifecycle/event.rs` +- `lib/components/fabro-workflow/src/lifecycle/event.rs` Producer seam for the source checkpoint object: -- `lib/crates/fabro-workflow/src/lifecycle/disk.rs` +- `lib/components/fabro-workflow/src/lifecycle/disk.rs` Design rule: @@ -231,7 +231,7 @@ Add: Producer seam: -- `lib/crates/fabro-workflow/src/pipeline/pull_request.rs` +- `lib/components/fabro-workflow/src/pipeline/pull_request.rs` After this lands, `put_pull_request` should become removable during the later memoized-state cutover. @@ -245,7 +245,7 @@ Do not add a separate storage-shaped event. The final patch is run-level termina Enrich `run.completed`, using the patch already computed from: -- `lib/crates/fabro-workflow/src/lifecycle/git.rs` +- `lib/components/fabro-workflow/src/lifecycle/git.rs` Required payload: @@ -287,9 +287,9 @@ Required projected output: Likely seams: -- `lib/crates/fabro-workflow/src/handler/agent.rs` -- `lib/crates/fabro-workflow/src/handler/llm/api.rs` -- `lib/crates/fabro-workflow/src/pipeline/retro.rs` if retro uses the same forwarded agent session path +- `lib/components/fabro-workflow/src/handler/agent.rs` +- `lib/components/fabro-workflow/src/handler/llm/api.rs` +- `lib/components/fabro-workflow/src/pipeline/retro.rs` if retro uses the same forwarded agent session path - any CLI-backed LLM path if it still produces `provider_used.json` Do not keep the current “read JSON sidecar, then `put_node_provider_used`” pattern once this event exists. @@ -304,7 +304,7 @@ Use the existing terminal parallel event rather than adding a storage-shaped eve Add to `parallel.completed`, emitted from: -- `lib/crates/fabro-workflow/src/handler/parallel.rs` +- `lib/components/fabro-workflow/src/handler/parallel.rs` Required new payload: @@ -346,19 +346,19 @@ Likely files to touch: - `docs-internal/events.md` - `docs-internal/run-directory-keys.md` - `docs-internal/events-strategy.md` -- `lib/crates/fabro-workflow/src/event.rs` -- `lib/crates/fabro-workflow/src/lifecycle/event.rs` -- `lib/crates/fabro-workflow/src/lifecycle/disk.rs` -- `lib/crates/fabro-workflow/src/lifecycle/git.rs` -- `lib/crates/fabro-workflow/src/operations/create.rs` -- `lib/crates/fabro-workflow/src/operations/start.rs` -- `lib/crates/fabro-workflow/src/operations/resume.rs` -- `lib/crates/fabro-workflow/src/pipeline/finalize.rs` -- `lib/crates/fabro-workflow/src/pipeline/pull_request.rs` -- `lib/crates/fabro-workflow/src/handler/agent.rs` -- `lib/crates/fabro-workflow/src/handler/parallel.rs` -- `lib/crates/fabro-cli/src/commands/runs/rm.rs` -- `lib/crates/fabro-cli/src/commands/run/rewind.rs` +- `lib/components/fabro-workflow/src/event.rs` +- `lib/components/fabro-workflow/src/lifecycle/event.rs` +- `lib/components/fabro-workflow/src/lifecycle/disk.rs` +- `lib/components/fabro-workflow/src/lifecycle/git.rs` +- `lib/components/fabro-workflow/src/operations/create.rs` +- `lib/components/fabro-workflow/src/operations/start.rs` +- `lib/components/fabro-workflow/src/operations/resume.rs` +- `lib/components/fabro-workflow/src/pipeline/finalize.rs` +- `lib/components/fabro-workflow/src/pipeline/pull_request.rs` +- `lib/components/fabro-workflow/src/handler/agent.rs` +- `lib/components/fabro-workflow/src/handler/parallel.rs` +- `lib/apps/fabro-cli/src/commands/runs/rm.rs` +- `lib/apps/fabro-cli/src/commands/run/rewind.rs` - tests in `fabro-workflow`, `fabro-cli`, and `fabro-store` ## Phases diff --git a/docs/internal/run-directory-keys.md b/docs/internal/run-directory-keys.md index 91ceeb2a8..1b78fd560 100644 --- a/docs/internal/run-directory-keys.md +++ b/docs/internal/run-directory-keys.md @@ -5,8 +5,8 @@ This document maps the files that still live under a run scratch directory. Dura Scope: - Scratch root: `~/.fabro/scratch/YYYYMMDD-{run_id}/` - This covers local run files only -- Persistent store keys live in `lib/crates/fabro-store/src/keys.rs` -- Artifact object-store keys live in `lib/crates/fabro-store/src/artifact_store.rs` +- Persistent store keys live in `lib/components/fabro-store/src/keys.rs` +- Artifact object-store keys live in `lib/components/fabro-store/src/artifact_store.rs` There is no `_init.json` anymore. Run existence in the database is determined by stored run events, and local scratch directories are managed separately under `scratch/`. diff --git a/docs/internal/slow-test-opportunities-2026-04-07.md b/docs/internal/slow-test-opportunities-2026-04-07.md index 2fe511ff4..790a80c20 100644 --- a/docs/internal/slow-test-opportunities-2026-04-07.md +++ b/docs/internal/slow-test-opportunities-2026-04-07.md @@ -22,7 +22,7 @@ Method: ### [x] 1. Change two slow `exec` mock responses from retriable `500` to non-retriable `400` Files: -- `lib/crates/fabro-cli/tests/it/cmd/exec.rs` +- `lib/apps/fabro-cli/tests/it/cmd/exec.rs` Measured evidence: - `fabro-cli::it::cmd::exec::exec_cli_server_target_overrides_configured_server_target`: `6.858s` median @@ -32,7 +32,7 @@ Measured evidence: - mocked `400`: `0.053s` median Implementation status: -- Implemented in `lib/crates/fabro-cli/tests/it/cmd/exec.rs` +- Implemented in `lib/apps/fabro-cli/tests/it/cmd/exec.rs` - Verified with `ulimit -n 4096` via 5 targeted nextest runs per test - Post-change nextest exec-time medians: - `exec_server_target_uses_remote_transport_instead_of_local_api_key_resolution`: `1.580s` @@ -57,7 +57,7 @@ Cons: ### 2. Short-circuit delete-path worker grace for already-terminal runs Files: -- `lib/crates/fabro-server/src/server.rs` +- `lib/apps/fabro-server/src/server.rs` Measured evidence: - `fabro-cli::it::cmd::system_prune::system_prune_yes_deletes_matching_runs`: `10.477s` @@ -87,11 +87,11 @@ Cons: ### [x] 3. Collapse the five abnormally slow `help` integration tests into one smoke test or a lighter harness Files: -- `lib/crates/fabro-cli/tests/it/cmd/artifact.rs` -- `lib/crates/fabro-cli/tests/it/cmd/artifact_list.rs` -- `lib/crates/fabro-cli/tests/it/cmd/artifact_cp.rs` -- `lib/crates/fabro-cli/tests/it/cmd/config.rs` -- `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- `lib/apps/fabro-cli/tests/it/cmd/artifact.rs` +- `lib/apps/fabro-cli/tests/it/cmd/artifact_list.rs` +- `lib/apps/fabro-cli/tests/it/cmd/artifact_cp.rs` +- `lib/apps/fabro-cli/tests/it/cmd/config.rs` +- `lib/apps/fabro-cli/tests/it/cmd/attach.rs` Measured evidence: - Slow `help` tests: @@ -131,8 +131,8 @@ Cons: ### [x] 4. Replace `doctor_no_color_when_no_color_set` with a render-path assertion Files: -- `lib/crates/fabro-cli/tests/it/cmd/doctor.rs` -- `lib/crates/fabro-util/src/check_report.rs` +- `lib/apps/fabro-cli/tests/it/cmd/doctor.rs` +- `lib/foundation/fabro-util/src/check_report.rs` Measured evidence: - `fabro-cli::it::cmd::doctor::doctor_no_color_when_no_color_set`: `5.131s` @@ -144,7 +144,7 @@ Estimated impact: Implementation status: - Implemented by deleting `fabro-cli::it::cmd::doctor::doctor_no_color_when_no_color_set` -- Added a unit-level render assertion in `lib/crates/fabro-cli/src/commands/doctor.rs`: +- Added a unit-level render assertion in `lib/apps/fabro-cli/src/commands/doctor.rs`: - `render_report_text_without_color_has_no_ansi` - Verified with `ulimit -n 4096; cargo nextest run -p fabro-cli render_report_text_without_color_has_no_ansi --status-level fail --final-status-level fail --show-progress none` - Verification result: `1 passed` @@ -166,8 +166,8 @@ Cons: ### [x] 5. Fix local Unix-socket autostart so it doesn't burn the full 5s readiness wait Files: -- `lib/crates/fabro-cli/src/server_client.rs` -- `lib/crates/fabro-cli/tests/it/cmd/server_start.rs` +- `lib/apps/fabro-cli/src/server_client.rs` +- `lib/apps/fabro-cli/tests/it/cmd/server_start.rs` Measured evidence: - Pre-fix 5-run timing for `fabro-cli::it::cmd::server_start::concurrent_autostart_converges_on_one_shared_daemon_and_cleans_up`: @@ -197,7 +197,7 @@ Implementation status: - Implemented by splitting the Unix-socket connection path into: - a single immediate health probe before autostart - the existing retrying readiness wait after autostart -- Kept the original integration test coverage in `lib/crates/fabro-cli/tests/it/cmd/server_start.rs` +- Kept the original integration test coverage in `lib/apps/fabro-cli/tests/it/cmd/server_start.rs` - Verified with `ulimit -n 4096; cargo nextest run -p fabro-cli concurrent_autostart_converges_on_one_shared_daemon_and_cleans_up --status-level fail --final-status-level fail --show-progress none` - Verification result: `1 passed` - Post-change 5-run timing for `fabro-cli::it::cmd::server_start::concurrent_autostart_converges_on_one_shared_daemon_and_cleans_up`: @@ -217,7 +217,7 @@ Implementation status: ### [x] 6. Collapse three lightweight `attach` smoke tests into one scenario-style test Files: -- `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- `lib/apps/fabro-cli/tests/it/cmd/attach.rs` Measured evidence: - `attach_requires_run_arg`: `1.595s` @@ -230,7 +230,7 @@ Estimated impact: - Conservative recoverable time: about `3.15s` Implementation status: -- Implemented by removing the 3 command-owned smoke tests from `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- Implemented by removing the 3 command-owned smoke tests from `lib/apps/fabro-cli/tests/it/cmd/attach.rs` - Added `fabro-cli::it::scenario::smoke::attach_smoke_covers_arg_validation_and_remote_server_behaviors` - Verified with `ulimit -n 4096; cargo nextest run -p fabro-cli attach_smoke_covers_arg_validation_and_remote_server_behaviors --status-level fail --final-status-level fail --show-progress none` - Verification result: `1 passed` @@ -256,7 +256,7 @@ Cons: ### [x] 7. Collapse the three `completion` tests Files: -- `lib/crates/fabro-cli/tests/it/cmd/completion.rs` +- `lib/apps/fabro-cli/tests/it/cmd/completion.rs` Measured evidence: - `completion::generates_zsh_completions`: `1.567s` @@ -295,7 +295,7 @@ Cons: ### [x] 8. Remove or merge the duplicate attach replay test Files: -- `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- `lib/apps/fabro-cli/tests/it/cmd/attach.rs` Measured evidence: - `attach_replays_completed_detached_run`: `2.696s` @@ -303,7 +303,7 @@ Measured evidence: - The two tests are currently identical in code and assertions Implementation status: -- Implemented by removing the duplicate test from `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- Implemented by removing the duplicate test from `lib/apps/fabro-cli/tests/it/cmd/attach.rs` - Verified with `ulimit -n 4096; cargo nextest run -p fabro-cli attach_replays_completed_detached_run --status-level fail --final-status-level fail --show-progress none` - Verification result: `1 passed` @@ -325,7 +325,7 @@ Cons: ### [x] 9. Make `attach_before_completion_streams_to_finished_state` event-driven instead of sleep-driven Files: -- `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- `lib/apps/fabro-cli/tests/it/cmd/attach.rs` Measured evidence: - `attach_before_completion_streams_to_finished_state`: `3.043s` @@ -333,7 +333,7 @@ Measured evidence: - `write_gated_workflow()` adds another fixed `sleep 0.2` Implementation status: -- Implemented by replacing the fixed 1-second gate-release sleep with a real attach-output signal in `lib/crates/fabro-cli/tests/it/cmd/attach.rs` +- Implemented by replacing the fixed 1-second gate-release sleep with a real attach-output signal in `lib/apps/fabro-cli/tests/it/cmd/attach.rs` - The test now spawns `fabro attach`, waits for replayed stderr output (`✓ start`), then releases the workflow gate - Verified with `ulimit -n 4096; cargo nextest run -p fabro-cli attach_before_completion_streams_to_finished_state --status-level fail --final-status-level fail --show-progress none` - Verification result: `1 passed` @@ -361,14 +361,14 @@ Cons: ### [x] 10. Remove or parameterize the fixed `sleep 0.2` in `write_gated_workflow()` Files: -- `lib/crates/fabro-cli/tests/it/cmd/support.rs` +- `lib/apps/fabro-cli/tests/it/cmd/support.rs` Measured evidence: - `write_gated_workflow()` hardcodes `sleep 0.2` - The helper is used in 6 cmd tests Implementation status: -- Implemented by deleting the fixed `sleep 0.2` from `write_gated_workflow()` in `lib/crates/fabro-cli/tests/it/cmd/support.rs` +- Implemented by deleting the fixed `sleep 0.2` from `write_gated_workflow()` in `lib/apps/fabro-cli/tests/it/cmd/support.rs` - Verified with `ulimit -n 4096; cargo nextest run -p fabro-cli -E 'test(attach_before_completion_streams_to_finished_state) | test(ctrl_c_cancels_active_run_via_server) | test(rm_force_terminates_active_run_worker) | test(start_rejects_already_active_or_completed_run) | test(start_runs_under_server_ownership_without_launcher_record)' --status-level fail --final-status-level fail --show-progress none` - Verification result: `5 passed` - Targeted 5-pass benchmark with `ulimit -n 4096` over the 5 tests that currently use the helper: diff --git a/docs/internal/testing-strategy.md b/docs/internal/testing-strategy.md index 5ec1534f5..b5fa9488a 100644 --- a/docs/internal/testing-strategy.md +++ b/docs/internal/testing-strategy.md @@ -37,7 +37,7 @@ This is the right place for: If the setup requires direct writes to internal run files or runtime directories, prefer this layer over `fabro-cli/tests/it`. -### `lib/crates/fabro-cli/tests/it/cmd/*.rs` +### `lib/apps/fabro-cli/tests/it/cmd/*.rs` `cmd/*` tests are command-owned tests. @@ -64,7 +64,7 @@ Bad command-test assertions: - behavior primarily owned by another command - runtime internals that only exist because the test planted them by hand -### `lib/crates/fabro-cli/tests/it/workflow/*.rs` +### `lib/apps/fabro-cli/tests/it/workflow/*.rs` `workflow/*` tests are black-box workflow-behavior tests. @@ -79,7 +79,7 @@ Examples: These tests should focus on the workflow's observed behavior, not on CLI help text or command argument validation. -### `lib/crates/fabro-cli/tests/it/scenario/*.rs` +### `lib/apps/fabro-cli/tests/it/scenario/*.rs` `scenario/*` tests are cross-command lifecycle tests. diff --git a/docs/public/agents/outputs.mdx b/docs/public/agents/outputs.mdx index 28e3659a6..b726a9ffd 100644 --- a/docs/public/agents/outputs.mdx +++ b/docs/public/agents/outputs.mdx @@ -66,7 +66,7 @@ JSON objects without recognized fields are ignored. ### Validated routing output -Set `output_schema="routing"` on an agent or prompt node to require Fabro's built-in routing directive schema: +Set `output_schema="routing"` on an agent, prompt, or command node to require Fabro's built-in routing directive schema: ```dot review [ @@ -81,7 +81,9 @@ With `output_schema="routing"`, the routing JSON must be an object with at least Validated routing uses the same reverse scan as normal routing extraction: Fabro validates the last parsable JSON object that contains a recognized routing field. If a routing object is present but malformed or has invalid field types, Fabro repairs that response instead of falling through to a file fallback. -Fabro repairs invalid structured output inside the same LLM context before failing the node. For prompt nodes, Fabro appends the invalid assistant response and a corrective user message to the same message list. For agent nodes using the API backend, Fabro sends the corrective message to the same live agent session. `output_retries` controls these repair turns and defaults to `2`; `output_retries=0` validates once and fails without a repair turn. Negative values are treated as `0`. These repair turns are separate from workflow `max_retries` and do not consume node retry attempts. +Fabro repairs invalid structured output inside the same LLM context before failing an agent or prompt node. For prompt nodes, Fabro appends the invalid assistant response and a corrective user message to the same message list. For agent nodes using the API backend, Fabro sends the corrective message to the same live agent session. `output_retries` controls these repair turns and defaults to `2`; `output_retries=0` validates once and fails without a repair turn. Negative values are treated as `0`. These repair turns are separate from workflow `max_retries` and do not consume node retry attempts. + +Command nodes instead validate only after an exit-code-`0` script, applying the same reverse scan to merged stdout and stderr. Print the intended JSON object last. Invalid output fails deterministically without a repair turn, command retry, or `status.json` fallback; `output_retries`, `retry_policy`, and `max_retries` do not retry the validation failure. Nonzero exits keep their normal command failure behavior without schema validation. ### Routing fallback sources @@ -97,7 +99,7 @@ This fallback chain applies to normal routing extraction and to `output_schema=" The last-file fallback only reads `.json` and `.md` files (case-insensitive), and the routing JSON must be the final JSON object in the file, with only whitespace after it. Fabro ignores other file types and routing JSON followed by any other content. These restrictions do not apply to the dedicated `status.json` fallback. -Prompt nodes do not use file fallbacks; they validate or extract routing directives from the response text only. +Prompt nodes do not use file fallbacks; they validate or extract routing directives from the response text only. Command nodes likewise have no file fallback and validate only their merged stdout and stderr. If no source provides routing directives, the transition falls through to condition matching, unconditional edges, or weight-based tiebreaking as described in [Transitions](/workflows/transitions). @@ -121,7 +123,7 @@ review -> approve [label="Approve"] ## Custom structured outputs -Agent and prompt nodes can also validate their final JSON object against a JSON Schema file: +Agent, prompt, and command nodes can also validate their final JSON object against a JSON Schema file: ```dot audit [ @@ -132,9 +134,9 @@ audit [ ] ``` -`output_schema="@path/to/schema.json"` uses the same workflow file-reference rules as prompt files: the schema is loaded relative to the workflow file and inlined before execution. The final JSON object in the LLM response is validated with `jsonschema`. +`output_schema="@path/to/schema.json"` uses the same workflow file-reference rules as prompt files: the schema is loaded relative to the workflow file and inlined before execution. The final JSON object in the LLM response, or in a successful command's merged stdout and stderr, is validated with `jsonschema`. -Custom schema validation only reads the response text. It does not fall back to `status.json` or the last file touched by the agent. +Custom schema validation only reads response or command output text. It does not fall back to `status.json` or the last file touched by the agent. When custom schema validation succeeds, Fabro stores the parsed JSON value in context at: @@ -142,12 +144,12 @@ When custom schema validation succeeds, Fabro stores the parsed JSON value in co |---|---| | `output.{node_id}` | The parsed JSON object that matched the custom schema | -For example, node `audit` writes its parsed custom output to `output.audit`. Fabro still stores the raw response text at `response.audit`. +For example, node `audit` writes its parsed custom output to `output.audit`. Fabro still stores raw LLM response text at `response.audit`; command output remains available through `command.output`. -If custom schema validation fails, Fabro sends concise validation feedback to the same prompt conversation or agent session and asks for corrected JSON. After `output_retries` repair turns are exhausted, the node fails terminally with `output schema validation failed after N repair attempt(s)`. +If custom schema validation fails for an agent or prompt node, Fabro sends concise validation feedback to the same prompt conversation or agent session and asks for corrected JSON. After `output_retries` repair turns are exhausted, the node fails terminally with `output schema validation failed after N repair attempt(s)`. A command node instead fails deterministically after its first validation, without a repair turn or command retry. -Structured output validation currently applies to agent and prompt nodes. `backend="acp"` does not support `output_schema` in this release. Custom schemas update `output.{node_id}`; routing schemas update routing fields and `context_updates` instead. +Structured output validation applies to agent, prompt, and command nodes. `backend="acp"` does not support `output_schema` in this release. Custom schemas update `output.{node_id}`; routing schemas update routing fields and merge `context_updates` as flat context keys that edge conditions can read. Edge conditions cannot traverse the custom `output.{node_id}` object. ## Output logging diff --git a/docs/public/agents/subagents.mdx b/docs/public/agents/subagents.mdx index 11cf30c38..597bd96e3 100644 --- a/docs/public/agents/subagents.mdx +++ b/docs/public/agents/subagents.mdx @@ -44,7 +44,6 @@ Sub-agent failures do not automatically fail the parent stage. The parent receiv Common cases: -- **Hits `max_turns`** -- returns normally with its last output - **Panics or errors** -- returned as a failed `wait` result - **`spawn_agent` fails** -- returned immediately as a tool result @@ -97,5 +96,5 @@ Sub-agents are most useful for: Use [child runs](/execution/child-runs) instead when the delegated work should be a separate Fabro run with its own workflow, lifecycle, sandbox, checkpoints, and outputs. -Sub-agents run with no turn limit by default. Pass `max_turns` when you want predictable cost or time bounds. All active sub-agents are cleaned up automatically when the parent session closes. +Sub-agents run until they complete, fail, are cancelled, or hit the session's wall-clock timeout. All active sub-agents are cleaned up automatically when the parent session closes. diff --git a/docs/public/api-reference/client-sdks.mdx b/docs/public/api-reference/client-sdks.mdx index e0fcb4a94..b46291669 100644 --- a/docs/public/api-reference/client-sdks.mdx +++ b/docs/public/api-reference/client-sdks.mdx @@ -43,7 +43,7 @@ The `fabro-api` crate generates Rust structs, enums, and a `reqwest`-based HTTP A `build.rs` script reads `docs/public/api-reference/fabro-api.yaml`, patches it from OpenAPI 3.1 to 3.0 for progenitor compatibility, and generates both types and a client. The generated code is written to `OUT_DIR` and included via: ```rust -// lib/crates/fabro-api/src/lib.rs +// lib/foundation/fabro-api/src/lib.rs include!(concat!(env!("OUT_DIR"), "/codegen.rs")); ``` diff --git a/docs/public/api-reference/fabro-api.yaml b/docs/public/api-reference/fabro-api.yaml index a2e5af539..519ee4188 100644 --- a/docs/public/api-reference/fabro-api.yaml +++ b/docs/public/api-reference/fabro-api.yaml @@ -8238,6 +8238,7 @@ components: - reasoning - reasoning_effort - prompt_cache + - cache_control_breakpoints - sampling_params properties: tools: @@ -8254,6 +8255,12 @@ components: prompt_cache: type: boolean description: Whether the model endpoint supports prompt caching. + cache_control_breakpoints: + type: boolean + description: >- + Whether the endpoint only caches when the request marks the + cacheable prefix with Anthropic-style cache_control breakpoints + (e.g. Claude via OpenRouter). sampling_params: type: boolean description: Whether the model accepts classic sampling parameters (temperature, top_p). @@ -10541,7 +10548,11 @@ components: oneOf: - $ref: "#/components/schemas/TodoListProjection" - type: "null" - description: Projected todo / task list for this stage. + description: | + Todo / task list owned by this stage's root agent session. OpenAI + child sessions have separate per-session plans that do not appear + here. Anthropic task lists are root-scoped and shared with child + sessions, so child mutations of that shared list do appear here. subagents: type: array description: Subagents spawned by this stage, in replay/insertion order. diff --git a/docs/public/changelog/2026-03-23.mdx b/docs/public/changelog/2026-03-23.mdx index e3d47ecda..359f5bbc1 100644 --- a/docs/public/changelog/2026-03-23.mdx +++ b/docs/public/changelog/2026-03-23.mdx @@ -5,7 +5,7 @@ date: "2026-03-23" ## Unlimited agent tool rounds -Agent stages no longer have an artificial cap on tool rounds per input or subagent turns. Previously, the default limits could cause agents to stop mid-task on complex operations. Both `max_tool_rounds_per_input` and subagent `max_turns` now default to unlimited, so agents run until they complete their work or hit the context window. +Agent stages no longer have an artificial cap on tool rounds per input or subagent turns, so agents run until they complete their work or are interrupted. ## More diff --git a/docs/public/examples/repl-handoff.mdx b/docs/public/examples/repl-handoff.mdx index a89942013..bf05c425e 100644 --- a/docs/public/examples/repl-handoff.mdx +++ b/docs/public/examples/repl-handoff.mdx @@ -73,7 +73,7 @@ Use Claude Code's plan mode to collaborate on an implementation plan. Go back an > /plan Add retry logic to the webhook delivery system Planning... -1. Add RetryPolicy struct to lib/crates/fabro-webhooks/src/policy.rs +1. Add RetryPolicy struct to lib/components/fabro-webhooks/src/policy.rs 2. Implement exponential backoff with jitter 3. Add max_retries field to WebhookConfig 4. Write tests for retry timing, max attempts, and jitter bounds diff --git a/docs/public/reference/dot-language.mdx b/docs/public/reference/dot-language.mdx index 3dccb9734..fb6152163 100644 --- a/docs/public/reference/dot-language.mdx +++ b/docs/public/reference/dot-language.mdx @@ -206,7 +206,7 @@ Start nodes can also be identified by ID (`start` or `Start`). Exit nodes can be | `model` | String | Explicit model ID (overrides stylesheet) | | `provider` | String | Explicit provider name (overrides stylesheet). Auto-inferred from the model catalog when omitted. | | `project_memory` | Boolean | When `true` (default), prompt nodes discover and include project docs (`AGENTS.md`, `CLAUDE.md`, etc.) as a system prompt. Set to `false` to disable. | -| `output_schema` | String | Optional structured output validation. Use `routing` for Fabro's built-in routing directive schema, `@path/to/schema.json` for a JSON Schema file, or an inline JSON Schema object string. Supported on agent and prompt nodes. | +| `output_schema` | String | Optional structured output validation. Use `routing` for Fabro's built-in routing directive schema, `@path/to/schema.json` for a JSON Schema file, or an inline JSON Schema object string. Supported on agent, prompt, and command nodes. | | `output_retries` | Integer | Corrective structured-output turns inside the same prompt conversation or agent session. Default `2`; `0` validates once and fails without repair; negative values are treated as `0`. Separate from `max_retries`. | | `backend` | String | Agent execution backend: `api` (default) or `acp`. `api` runs Fabro's tool loop through provider APIs; `acp` runs an Agent Client Protocol stdio agent inside the active sandbox. Prompt nodes are API-only. See [Agents — Backends](/core-concepts/agents#backends). | | `acp.command` | String | Shell command for nodes with `backend="acp"`. Mutually exclusive with `acp.config`. The value is always parsed as a command string, not JSON. | @@ -214,7 +214,7 @@ Start nodes can also be identified by ID (`start` or `Start`). Exit nodes can be #### Structured output validation -`output_schema` opts an agent or prompt node into strict JSON validation: +`output_schema` opts an agent, prompt, or command node into strict JSON validation: ```dot review [ @@ -236,6 +236,8 @@ audit [ - `output_retries` defaults to `2` and controls only these corrective structured-output turns. Negative values are treated as `0`. It is not the same as `max_retries` and does not consume workflow retry attempts. - Custom schema output is stored in context at `output.{node_id}`. Routing schema output updates routing fields and any `context_updates`. - Agent routing fallbacks still apply to `output_schema="routing"`: response text first, then `status.json`, then the last file touched by the agent. The last-file fallback only accepts `.json` and `.md` files (case-insensitive) whose final JSON object contains the routing directive; only whitespace may follow it. Custom schemas and prompt nodes validate response text only. +- Command nodes validate merged stdout and stderr only after the script exits with code `0`, using the same object selection as agents and prompts: custom schemas validate the last JSON object, and `routing` validates the last JSON object containing a recognized routing field. Print the intended JSON object last. A validation error is a deterministic, non-retryable failure with no repair turn or `status.json` fallback. `output_retries`, `retry_policy`, and `max_retries` do not retry it. Nonzero exits retain normal command failure behavior without schema validation. +- For commands, custom schema output is stored at `output.{node_id}`; edge conditions cannot traverse into its fields. The `routing` schema applies routing fields and merges `context_updates` into flat context keys, which conditions can read (for example, `context.kept_count`). - `backend="acp"` with `output_schema` is unsupported in this release. ### Command nodes @@ -244,6 +246,7 @@ audit [ |---|---|---| | `script` | String | Shell command to execute | | `language` | String | `"shell"` (default) or `"python"` | +| `output_schema` | String | Optional structured output validation. Accepts `routing`, `@path/to/schema.json`, or an inline JSON Schema object string. See [Structured output validation](#structured-output-validation). | ### Parallel (fan-out) nodes diff --git a/docs/public/reference/sdk.mdx b/docs/public/reference/sdk.mdx index 4332f3adc..9f88c7696 100644 --- a/docs/public/reference/sdk.mdx +++ b/docs/public/reference/sdk.mdx @@ -84,7 +84,7 @@ pub fn new( | Method | Description | |---|---| | `initialize().await` | Discovers project docs, skills, and MCP servers. Call before `process_input`. | -| `process_input(input).await` | Sends user input and runs the agent loop until the model stops or a limit is hit. | +| `process_input(input).await` | Sends user input and runs the agent loop until the model stops, the session is interrupted, or an error occurs. | | `close()` | Ends the session and emits `SessionEnded`. | | `interrupt()` | Cancels the current `process_input` call. | | `cancel_token()` | Returns a `CancellationToken` for external cancellation. | @@ -110,8 +110,6 @@ All fields are public. Key settings with their defaults: | Field | Default | Description | |---|---|---| -| `max_turns` | `0` (unlimited) | Maximum conversation turns before stopping. | -| `max_tool_rounds_per_input` | `200` | Maximum tool execution rounds per `process_input` call. | | `default_command_timeout_ms` | `10,000` | Default timeout for Bash tool commands. | | `max_command_timeout_ms` | `600,000` | Maximum allowed timeout for Bash tool commands. | | `enable_loop_detection` | `true` | Detect and break out of repetitive tool call patterns. | @@ -221,7 +219,6 @@ Key `AgentEvent` variants: | `ToolCallCompleted { tool_name, tool_call_id, output, is_error }` | A tool call finished. | | `Error { error }` | An `AgentError` occurred. | | `LoopDetected` | The agent is repeating itself. | -| `TurnLimitReached { max_turns }` | Turn limit hit. | | `CompactionStarted` / `CompactionCompleted` | Context window compaction. | | `SubAgentSpawned` / `SubAgentCompleted` | Sub-agent lifecycle. | | `McpServerReady` / `McpServerFailed` | MCP server connection status. | @@ -296,7 +293,7 @@ All fallible `Session` methods return `Result`: | `SessionClosed` | `process_input` was called on a closed session. | | `InvalidState(String)` | The session is in an unexpected state. | | `ToolExecution(String)` | A tool execution failed. | -| `Interrupted(InterruptReason)` | The session was cancelled (`Cancelled`) or timed out (`WallClockTimeout`). | +| `Interrupted(InterruptReason)` | The session was cancelled or timed out. | --- diff --git a/lib/crates/fabro-cli/Cargo.toml b/lib/apps/fabro-cli/Cargo.toml similarity index 55% rename from lib/crates/fabro-cli/Cargo.toml rename to lib/apps/fabro-cli/Cargo.toml index 4c84def39..44c9982bc 100644 --- a/lib/crates/fabro-cli/Cargo.toml +++ b/lib/apps/fabro-cli/Cargo.toml @@ -18,40 +18,40 @@ sleep_inhibitor = ["dep:core-foundation"] workspace = true [dependencies] -fabro-auth = { path = "../fabro-auth" } -fabro-config = { path = "../fabro-config" } -fabro-environment = { path = "../fabro-environment" } -fabro-llm = { path = "../fabro-llm" } -fabro-model = { path = "../fabro-model" } -fabro-oauth = { path = "../fabro-oauth" } -fabro-github = { path = "../fabro-github" } -fabro-agent = { path = "../fabro-agent" } -fabro-dump = { path = "../fabro-dump" } -fabro-hooks = { path = "../fabro-hooks" } -fabro-install = { path = "../fabro-install" } -fabro-interview = { path = "../fabro-interview" } -fabro-mcp = { path = "../fabro-mcp" } +fabro-auth = { path = "../../foundation/fabro-auth" } +fabro-config = { path = "../../foundation/fabro-config" } +fabro-environment = { path = "../../components/fabro-environment" } +fabro-llm = { path = "../../components/fabro-llm" } +fabro-model = { path = "../../foundation/fabro-model" } +fabro-oauth = { path = "../../foundation/fabro-oauth" } +fabro-github = { path = "../../components/fabro-github" } +fabro-agent = { path = "../../components/fabro-agent" } +fabro-dump = { path = "../../components/fabro-dump" } +fabro-hooks = { path = "../../components/fabro-hooks" } +fabro-install = { path = "../../components/fabro-install" } +fabro-interview = { path = "../../components/fabro-interview" } +fabro-mcp = { path = "../../components/fabro-mcp" } fabro-mcp-server = { path = "../fabro-mcp-server" } -fabro-manifest = { path = "../fabro-manifest" } -fabro-proc = { path = "../fabro-proc" } -fabro-sandbox = { path = "../fabro-sandbox", features = ["daytona"] } -fabro-checkpoint = { path = "../fabro-checkpoint" } -fabro-graphviz = { path = "../fabro-graphviz" } -fabro-validate = { path = "../fabro-validate" } -fabro-workflow = { path = "../fabro-workflow" } +fabro-manifest = { path = "../../components/fabro-manifest" } +fabro-proc = { path = "../../foundation/fabro-proc" } +fabro-sandbox = { path = "../../components/fabro-sandbox", features = ["daytona"] } +fabro-checkpoint = { path = "../../components/fabro-checkpoint" } +fabro-graphviz = { path = "../../components/fabro-graphviz" } +fabro-validate = { path = "../../components/fabro-validate" } +fabro-workflow = { path = "../../components/fabro-workflow" } fabro-server = { path = "../fabro-server" } -fabro-client = { path = "../fabro-client" } -fabro-api = { path = "../fabro-api" } -fabro-telemetry = { path = "../fabro-telemetry" } -fabro-store = { path = "../fabro-store" } -fabro-vault = { path = "../fabro-vault" } -fabro-types = { path = "../fabro-types", features = ["clap"] } +fabro-client = { path = "../../foundation/fabro-client" } +fabro-api = { path = "../../foundation/fabro-api" } +fabro-telemetry = { path = "../../foundation/fabro-telemetry" } +fabro-store = { path = "../../components/fabro-store" } +fabro-vault = { path = "../../foundation/fabro-vault" } +fabro-types = { path = "../../foundation/fabro-types", features = ["clap"] } fabro-redact.workspace = true -fabro-util = { path = "../fabro-util" } +fabro-util = { path = "../../foundation/fabro-util" } fabro-http.workspace = true fabro-static.workspace = true -fabro-template = { path = "../fabro-template" } -fabro-tool = { path = "../fabro-tool" } +fabro-template = { path = "../../foundation/fabro-template" } +fabro-tool = { path = "../../components/fabro-tool" } clap.workspace = true clap_complete.workspace = true cli-table.workspace = true @@ -111,16 +111,16 @@ core-foundation = { version = "0.9", optional = true } openssl = { version = "0.10", features = ["vendored"] } [build-dependencies] -fabro-build-support = { path = "../build-support" } +fabro-build-support = { path = "../../foundation/build-support" } chrono = { workspace = true } [dev-dependencies] assert_cmd = "2" -fabro-acp = { path = "../fabro-acp", features = ["test-support"] } -fabro-build-support = { path = "../build-support" } +fabro-acp = { path = "../../components/fabro-acp", features = ["test-support"] } +fabro-build-support = { path = "../../foundation/build-support" } fabro-server = { path = "../fabro-server", features = ["test-support"] } -fabro-workflow = { path = "../fabro-workflow", features = ["test-support"] } -fabro-types = { path = "../fabro-types", features = ["clap", "test-support"] } +fabro-workflow = { path = "../../components/fabro-workflow", features = ["test-support"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["clap", "test-support"] } insta = { workspace = true, features = ["filters"] } paste = "1" predicates = "3" @@ -129,7 +129,7 @@ tempfile = "3" temp-env = "0.3" httpmock = "0.8" fabro-test = { workspace = true } -fabro-macros = { path = "../fabro-macros" } +fabro-macros = { path = "../../foundation/fabro-macros" } hkdf.workspace = true reqwest = { workspace = true, features = ["cookies"] } tokio = { workspace = true, features = ["test-util", "macros"] } diff --git a/lib/crates/fabro-cli/build.rs b/lib/apps/fabro-cli/build.rs similarity index 100% rename from lib/crates/fabro-cli/build.rs rename to lib/apps/fabro-cli/build.rs diff --git a/lib/crates/fabro-cli/src/args.rs b/lib/apps/fabro-cli/src/args.rs similarity index 100% rename from lib/crates/fabro-cli/src/args.rs rename to lib/apps/fabro-cli/src/args.rs diff --git a/lib/crates/fabro-cli/src/command_context.rs b/lib/apps/fabro-cli/src/command_context.rs similarity index 100% rename from lib/crates/fabro-cli/src/command_context.rs rename to lib/apps/fabro-cli/src/command_context.rs diff --git a/lib/crates/fabro-cli/src/commands/artifact/cp.rs b/lib/apps/fabro-cli/src/commands/artifact/cp.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/artifact/cp.rs rename to lib/apps/fabro-cli/src/commands/artifact/cp.rs diff --git a/lib/crates/fabro-cli/src/commands/artifact/list.rs b/lib/apps/fabro-cli/src/commands/artifact/list.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/artifact/list.rs rename to lib/apps/fabro-cli/src/commands/artifact/list.rs diff --git a/lib/crates/fabro-cli/src/commands/artifact/mod.rs b/lib/apps/fabro-cli/src/commands/artifact/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/artifact/mod.rs rename to lib/apps/fabro-cli/src/commands/artifact/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/auth/login.rs b/lib/apps/fabro-cli/src/commands/auth/login.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/auth/login.rs rename to lib/apps/fabro-cli/src/commands/auth/login.rs diff --git a/lib/crates/fabro-cli/src/commands/auth/logout.rs b/lib/apps/fabro-cli/src/commands/auth/logout.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/auth/logout.rs rename to lib/apps/fabro-cli/src/commands/auth/logout.rs diff --git a/lib/crates/fabro-cli/src/commands/auth/mod.rs b/lib/apps/fabro-cli/src/commands/auth/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/auth/mod.rs rename to lib/apps/fabro-cli/src/commands/auth/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/auth/status.rs b/lib/apps/fabro-cli/src/commands/auth/status.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/auth/status.rs rename to lib/apps/fabro-cli/src/commands/auth/status.rs diff --git a/lib/crates/fabro-cli/src/commands/cli_reference.rs b/lib/apps/fabro-cli/src/commands/cli_reference.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/cli_reference.rs rename to lib/apps/fabro-cli/src/commands/cli_reference.rs diff --git a/lib/crates/fabro-cli/src/commands/config/mod.rs b/lib/apps/fabro-cli/src/commands/config/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/config/mod.rs rename to lib/apps/fabro-cli/src/commands/config/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/doctor.rs b/lib/apps/fabro-cli/src/commands/doctor.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/doctor.rs rename to lib/apps/fabro-cli/src/commands/doctor.rs diff --git a/lib/crates/fabro-cli/src/commands/dump.rs b/lib/apps/fabro-cli/src/commands/dump.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/dump.rs rename to lib/apps/fabro-cli/src/commands/dump.rs diff --git a/lib/crates/fabro-cli/src/commands/exec.rs b/lib/apps/fabro-cli/src/commands/exec.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/exec.rs rename to lib/apps/fabro-cli/src/commands/exec.rs diff --git a/lib/crates/fabro-cli/src/commands/graph.rs b/lib/apps/fabro-cli/src/commands/graph.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/graph.rs rename to lib/apps/fabro-cli/src/commands/graph.rs diff --git a/lib/crates/fabro-cli/src/commands/install.rs b/lib/apps/fabro-cli/src/commands/install.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/install.rs rename to lib/apps/fabro-cli/src/commands/install.rs diff --git a/lib/crates/fabro-cli/src/commands/mcp/mod.rs b/lib/apps/fabro-cli/src/commands/mcp/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/mcp/mod.rs rename to lib/apps/fabro-cli/src/commands/mcp/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/mod.rs b/lib/apps/fabro-cli/src/commands/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/mod.rs rename to lib/apps/fabro-cli/src/commands/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/model.rs b/lib/apps/fabro-cli/src/commands/model.rs similarity index 97% rename from lib/crates/fabro-cli/src/commands/model.rs rename to lib/apps/fabro-cli/src/commands/model.rs index 4e5bd6f39..f84bd44e2 100644 --- a/lib/crates/fabro-cli/src/commands/model.rs +++ b/lib/apps/fabro-cli/src/commands/model.rs @@ -538,12 +538,13 @@ mod tests { training: None, knowledge_cutoff: None, features: ModelFeatures { - tools: true, - vision: false, - reasoning: false, - reasoning_effort: ReasoningEffortFeature::None, - prompt_cache: false, - sampling_params: true, + tools: true, + vision: false, + reasoning: false, + reasoning_effort: ReasoningEffortFeature::None, + prompt_cache: false, + cache_control_breakpoints: false, + sampling_params: true, }, costs: ModelCosts { input_cost_per_mtok: Some(1.0), @@ -572,12 +573,13 @@ mod tests { training: None, knowledge_cutoff: None, features: ModelFeatures { - tools: true, - vision: false, - reasoning: false, - reasoning_effort: ReasoningEffortFeature::None, - prompt_cache: false, - sampling_params: true, + tools: true, + vision: false, + reasoning: false, + reasoning_effort: ReasoningEffortFeature::None, + prompt_cache: false, + cache_control_breakpoints: false, + sampling_params: true, }, costs: ModelCosts { input_cost_per_mtok: Some(1.0), diff --git a/lib/crates/fabro-cli/src/commands/parent/link.rs b/lib/apps/fabro-cli/src/commands/parent/link.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/parent/link.rs rename to lib/apps/fabro-cli/src/commands/parent/link.rs diff --git a/lib/crates/fabro-cli/src/commands/parent/mod.rs b/lib/apps/fabro-cli/src/commands/parent/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/parent/mod.rs rename to lib/apps/fabro-cli/src/commands/parent/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/parent/unlink.rs b/lib/apps/fabro-cli/src/commands/parent/unlink.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/parent/unlink.rs rename to lib/apps/fabro-cli/src/commands/parent/unlink.rs diff --git a/lib/crates/fabro-cli/src/commands/parse.rs b/lib/apps/fabro-cli/src/commands/parse.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/parse.rs rename to lib/apps/fabro-cli/src/commands/parse.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/close.rs b/lib/apps/fabro-cli/src/commands/pr/close.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/close.rs rename to lib/apps/fabro-cli/src/commands/pr/close.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/create.rs b/lib/apps/fabro-cli/src/commands/pr/create.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/create.rs rename to lib/apps/fabro-cli/src/commands/pr/create.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/link.rs b/lib/apps/fabro-cli/src/commands/pr/link.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/link.rs rename to lib/apps/fabro-cli/src/commands/pr/link.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/merge.rs b/lib/apps/fabro-cli/src/commands/pr/merge.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/merge.rs rename to lib/apps/fabro-cli/src/commands/pr/merge.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/mod.rs b/lib/apps/fabro-cli/src/commands/pr/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/mod.rs rename to lib/apps/fabro-cli/src/commands/pr/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/unlink.rs b/lib/apps/fabro-cli/src/commands/pr/unlink.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/unlink.rs rename to lib/apps/fabro-cli/src/commands/pr/unlink.rs diff --git a/lib/crates/fabro-cli/src/commands/pr/view.rs b/lib/apps/fabro-cli/src/commands/pr/view.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/pr/view.rs rename to lib/apps/fabro-cli/src/commands/pr/view.rs diff --git a/lib/crates/fabro-cli/src/commands/preflight.rs b/lib/apps/fabro-cli/src/commands/preflight.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/preflight.rs rename to lib/apps/fabro-cli/src/commands/preflight.rs diff --git a/lib/crates/fabro-cli/src/commands/provider/login.rs b/lib/apps/fabro-cli/src/commands/provider/login.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/provider/login.rs rename to lib/apps/fabro-cli/src/commands/provider/login.rs diff --git a/lib/crates/fabro-cli/src/commands/provider/mod.rs b/lib/apps/fabro-cli/src/commands/provider/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/provider/mod.rs rename to lib/apps/fabro-cli/src/commands/provider/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/render_graph.rs b/lib/apps/fabro-cli/src/commands/render_graph.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/render_graph.rs rename to lib/apps/fabro-cli/src/commands/render_graph.rs diff --git a/lib/crates/fabro-cli/src/commands/repo/deinit.rs b/lib/apps/fabro-cli/src/commands/repo/deinit.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/repo/deinit.rs rename to lib/apps/fabro-cli/src/commands/repo/deinit.rs diff --git a/lib/crates/fabro-cli/src/commands/repo/init.rs b/lib/apps/fabro-cli/src/commands/repo/init.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/repo/init.rs rename to lib/apps/fabro-cli/src/commands/repo/init.rs diff --git a/lib/crates/fabro-cli/src/commands/repo/mod.rs b/lib/apps/fabro-cli/src/commands/repo/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/repo/mod.rs rename to lib/apps/fabro-cli/src/commands/repo/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/run/ask.rs b/lib/apps/fabro-cli/src/commands/run/ask.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/ask.rs rename to lib/apps/fabro-cli/src/commands/run/ask.rs diff --git a/lib/crates/fabro-cli/src/commands/run/attach.rs b/lib/apps/fabro-cli/src/commands/run/attach.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/attach.rs rename to lib/apps/fabro-cli/src/commands/run/attach.rs diff --git a/lib/crates/fabro-cli/src/commands/run/checkpoints.rs b/lib/apps/fabro-cli/src/commands/run/checkpoints.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/checkpoints.rs rename to lib/apps/fabro-cli/src/commands/run/checkpoints.rs diff --git a/lib/crates/fabro-cli/src/commands/run/command.rs b/lib/apps/fabro-cli/src/commands/run/command.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/command.rs rename to lib/apps/fabro-cli/src/commands/run/command.rs diff --git a/lib/crates/fabro-cli/src/commands/run/cp.rs b/lib/apps/fabro-cli/src/commands/run/cp.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/cp.rs rename to lib/apps/fabro-cli/src/commands/run/cp.rs diff --git a/lib/crates/fabro-cli/src/commands/run/create.rs b/lib/apps/fabro-cli/src/commands/run/create.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/create.rs rename to lib/apps/fabro-cli/src/commands/run/create.rs diff --git a/lib/crates/fabro-cli/src/commands/run/diff.rs b/lib/apps/fabro-cli/src/commands/run/diff.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/diff.rs rename to lib/apps/fabro-cli/src/commands/run/diff.rs diff --git a/lib/crates/fabro-cli/src/commands/run/events.rs b/lib/apps/fabro-cli/src/commands/run/events.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/events.rs rename to lib/apps/fabro-cli/src/commands/run/events.rs diff --git a/lib/crates/fabro-cli/src/commands/run/fork.rs b/lib/apps/fabro-cli/src/commands/run/fork.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/fork.rs rename to lib/apps/fabro-cli/src/commands/run/fork.rs diff --git a/lib/crates/fabro-cli/src/commands/run/logs.rs b/lib/apps/fabro-cli/src/commands/run/logs.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/logs.rs rename to lib/apps/fabro-cli/src/commands/run/logs.rs diff --git a/lib/crates/fabro-cli/src/commands/run/mod.rs b/lib/apps/fabro-cli/src/commands/run/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/mod.rs rename to lib/apps/fabro-cli/src/commands/run/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/run/output.rs b/lib/apps/fabro-cli/src/commands/run/output.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/output.rs rename to lib/apps/fabro-cli/src/commands/run/output.rs diff --git a/lib/crates/fabro-cli/src/commands/run/overrides.rs b/lib/apps/fabro-cli/src/commands/run/overrides.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/overrides.rs rename to lib/apps/fabro-cli/src/commands/run/overrides.rs diff --git a/lib/crates/fabro-cli/src/commands/run/preview.rs b/lib/apps/fabro-cli/src/commands/run/preview.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/preview.rs rename to lib/apps/fabro-cli/src/commands/run/preview.rs diff --git a/lib/crates/fabro-cli/src/commands/run/resume.rs b/lib/apps/fabro-cli/src/commands/run/resume.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/resume.rs rename to lib/apps/fabro-cli/src/commands/run/resume.rs diff --git a/lib/crates/fabro-cli/src/commands/run/rewind.rs b/lib/apps/fabro-cli/src/commands/run/rewind.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/rewind.rs rename to lib/apps/fabro-cli/src/commands/run/rewind.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/event.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/event.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/event.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/event.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/info_display.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/info_display.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/info_display.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/info_display.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/renderer.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/renderer.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/renderer.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/renderer.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/setup_display.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/setup_display.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/setup_display.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/setup_display.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/stage_display.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/stage_display.rs diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/styles.rs b/lib/apps/fabro-cli/src/commands/run/run_progress/styles.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/run_progress/styles.rs rename to lib/apps/fabro-cli/src/commands/run/run_progress/styles.rs diff --git a/lib/crates/fabro-cli/src/commands/run/runner.rs b/lib/apps/fabro-cli/src/commands/run/runner.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/runner.rs rename to lib/apps/fabro-cli/src/commands/run/runner.rs diff --git a/lib/crates/fabro-cli/src/commands/run/ssh.rs b/lib/apps/fabro-cli/src/commands/run/ssh.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/ssh.rs rename to lib/apps/fabro-cli/src/commands/run/ssh.rs diff --git a/lib/crates/fabro-cli/src/commands/run/start.rs b/lib/apps/fabro-cli/src/commands/run/start.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/start.rs rename to lib/apps/fabro-cli/src/commands/run/start.rs diff --git a/lib/crates/fabro-cli/src/commands/run/steer.rs b/lib/apps/fabro-cli/src/commands/run/steer.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/steer.rs rename to lib/apps/fabro-cli/src/commands/run/steer.rs diff --git a/lib/crates/fabro-cli/src/commands/run/wait.rs b/lib/apps/fabro-cli/src/commands/run/wait.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/run/wait.rs rename to lib/apps/fabro-cli/src/commands/run/wait.rs diff --git a/lib/crates/fabro-cli/src/commands/runs/approval.rs b/lib/apps/fabro-cli/src/commands/runs/approval.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/runs/approval.rs rename to lib/apps/fabro-cli/src/commands/runs/approval.rs diff --git a/lib/crates/fabro-cli/src/commands/runs/archive.rs b/lib/apps/fabro-cli/src/commands/runs/archive.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/runs/archive.rs rename to lib/apps/fabro-cli/src/commands/runs/archive.rs diff --git a/lib/crates/fabro-cli/src/commands/runs/inspect.rs b/lib/apps/fabro-cli/src/commands/runs/inspect.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/runs/inspect.rs rename to lib/apps/fabro-cli/src/commands/runs/inspect.rs diff --git a/lib/crates/fabro-cli/src/commands/runs/list.rs b/lib/apps/fabro-cli/src/commands/runs/list.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/runs/list.rs rename to lib/apps/fabro-cli/src/commands/runs/list.rs diff --git a/lib/crates/fabro-cli/src/commands/runs/mod.rs b/lib/apps/fabro-cli/src/commands/runs/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/runs/mod.rs rename to lib/apps/fabro-cli/src/commands/runs/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/runs/rm.rs b/lib/apps/fabro-cli/src/commands/runs/rm.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/runs/rm.rs rename to lib/apps/fabro-cli/src/commands/runs/rm.rs diff --git a/lib/crates/fabro-cli/src/commands/sandbox/mod.rs b/lib/apps/fabro-cli/src/commands/sandbox/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/sandbox/mod.rs rename to lib/apps/fabro-cli/src/commands/sandbox/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/secret/list.rs b/lib/apps/fabro-cli/src/commands/secret/list.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/secret/list.rs rename to lib/apps/fabro-cli/src/commands/secret/list.rs diff --git a/lib/crates/fabro-cli/src/commands/secret/mod.rs b/lib/apps/fabro-cli/src/commands/secret/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/secret/mod.rs rename to lib/apps/fabro-cli/src/commands/secret/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/secret/rm.rs b/lib/apps/fabro-cli/src/commands/secret/rm.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/secret/rm.rs rename to lib/apps/fabro-cli/src/commands/secret/rm.rs diff --git a/lib/crates/fabro-cli/src/commands/secret/set.rs b/lib/apps/fabro-cli/src/commands/secret/set.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/secret/set.rs rename to lib/apps/fabro-cli/src/commands/secret/set.rs diff --git a/lib/crates/fabro-cli/src/commands/server/foreground.rs b/lib/apps/fabro-cli/src/commands/server/foreground.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/server/foreground.rs rename to lib/apps/fabro-cli/src/commands/server/foreground.rs diff --git a/lib/crates/fabro-cli/src/commands/server/mod.rs b/lib/apps/fabro-cli/src/commands/server/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/server/mod.rs rename to lib/apps/fabro-cli/src/commands/server/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/server/start.rs b/lib/apps/fabro-cli/src/commands/server/start.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/server/start.rs rename to lib/apps/fabro-cli/src/commands/server/start.rs diff --git a/lib/crates/fabro-cli/src/commands/server/status.rs b/lib/apps/fabro-cli/src/commands/server/status.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/server/status.rs rename to lib/apps/fabro-cli/src/commands/server/status.rs diff --git a/lib/crates/fabro-cli/src/commands/server/stop.rs b/lib/apps/fabro-cli/src/commands/server/stop.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/server/stop.rs rename to lib/apps/fabro-cli/src/commands/server/stop.rs diff --git a/lib/crates/fabro-cli/src/commands/system/df.rs b/lib/apps/fabro-cli/src/commands/system/df.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/system/df.rs rename to lib/apps/fabro-cli/src/commands/system/df.rs diff --git a/lib/crates/fabro-cli/src/commands/system/events.rs b/lib/apps/fabro-cli/src/commands/system/events.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/system/events.rs rename to lib/apps/fabro-cli/src/commands/system/events.rs diff --git a/lib/crates/fabro-cli/src/commands/system/info.rs b/lib/apps/fabro-cli/src/commands/system/info.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/system/info.rs rename to lib/apps/fabro-cli/src/commands/system/info.rs diff --git a/lib/crates/fabro-cli/src/commands/system/mod.rs b/lib/apps/fabro-cli/src/commands/system/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/system/mod.rs rename to lib/apps/fabro-cli/src/commands/system/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/system/prune.rs b/lib/apps/fabro-cli/src/commands/system/prune.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/system/prune.rs rename to lib/apps/fabro-cli/src/commands/system/prune.rs diff --git a/lib/crates/fabro-cli/src/commands/system/repair.rs b/lib/apps/fabro-cli/src/commands/system/repair.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/system/repair.rs rename to lib/apps/fabro-cli/src/commands/system/repair.rs diff --git a/lib/crates/fabro-cli/src/commands/uninstall.rs b/lib/apps/fabro-cli/src/commands/uninstall.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/uninstall.rs rename to lib/apps/fabro-cli/src/commands/uninstall.rs diff --git a/lib/crates/fabro-cli/src/commands/upgrade.rs b/lib/apps/fabro-cli/src/commands/upgrade.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/upgrade.rs rename to lib/apps/fabro-cli/src/commands/upgrade.rs diff --git a/lib/crates/fabro-cli/src/commands/validate.rs b/lib/apps/fabro-cli/src/commands/validate.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/validate.rs rename to lib/apps/fabro-cli/src/commands/validate.rs diff --git a/lib/crates/fabro-cli/src/commands/variable/get.rs b/lib/apps/fabro-cli/src/commands/variable/get.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/variable/get.rs rename to lib/apps/fabro-cli/src/commands/variable/get.rs diff --git a/lib/crates/fabro-cli/src/commands/variable/list.rs b/lib/apps/fabro-cli/src/commands/variable/list.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/variable/list.rs rename to lib/apps/fabro-cli/src/commands/variable/list.rs diff --git a/lib/crates/fabro-cli/src/commands/variable/mod.rs b/lib/apps/fabro-cli/src/commands/variable/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/variable/mod.rs rename to lib/apps/fabro-cli/src/commands/variable/mod.rs diff --git a/lib/crates/fabro-cli/src/commands/variable/rm.rs b/lib/apps/fabro-cli/src/commands/variable/rm.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/variable/rm.rs rename to lib/apps/fabro-cli/src/commands/variable/rm.rs diff --git a/lib/crates/fabro-cli/src/commands/variable/set.rs b/lib/apps/fabro-cli/src/commands/variable/set.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/variable/set.rs rename to lib/apps/fabro-cli/src/commands/variable/set.rs diff --git a/lib/crates/fabro-cli/src/commands/version.rs b/lib/apps/fabro-cli/src/commands/version.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/version.rs rename to lib/apps/fabro-cli/src/commands/version.rs diff --git a/lib/crates/fabro-cli/src/commands/workflow/create.rs b/lib/apps/fabro-cli/src/commands/workflow/create.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/workflow/create.rs rename to lib/apps/fabro-cli/src/commands/workflow/create.rs diff --git a/lib/crates/fabro-cli/src/commands/workflow/list.rs b/lib/apps/fabro-cli/src/commands/workflow/list.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/workflow/list.rs rename to lib/apps/fabro-cli/src/commands/workflow/list.rs diff --git a/lib/crates/fabro-cli/src/commands/workflow/mod.rs b/lib/apps/fabro-cli/src/commands/workflow/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/commands/workflow/mod.rs rename to lib/apps/fabro-cli/src/commands/workflow/mod.rs diff --git a/lib/crates/fabro-cli/src/gh.rs b/lib/apps/fabro-cli/src/gh.rs similarity index 100% rename from lib/crates/fabro-cli/src/gh.rs rename to lib/apps/fabro-cli/src/gh.rs diff --git a/lib/crates/fabro-cli/src/landing.rs b/lib/apps/fabro-cli/src/landing.rs similarity index 100% rename from lib/crates/fabro-cli/src/landing.rs rename to lib/apps/fabro-cli/src/landing.rs diff --git a/lib/crates/fabro-cli/src/local_server.rs b/lib/apps/fabro-cli/src/local_server.rs similarity index 100% rename from lib/crates/fabro-cli/src/local_server.rs rename to lib/apps/fabro-cli/src/local_server.rs diff --git a/lib/crates/fabro-cli/src/logging.rs b/lib/apps/fabro-cli/src/logging.rs similarity index 100% rename from lib/crates/fabro-cli/src/logging.rs rename to lib/apps/fabro-cli/src/logging.rs diff --git a/lib/crates/fabro-cli/src/main.rs b/lib/apps/fabro-cli/src/main.rs similarity index 100% rename from lib/crates/fabro-cli/src/main.rs rename to lib/apps/fabro-cli/src/main.rs diff --git a/lib/crates/fabro-cli/src/manifest_args.rs b/lib/apps/fabro-cli/src/manifest_args.rs similarity index 100% rename from lib/crates/fabro-cli/src/manifest_args.rs rename to lib/apps/fabro-cli/src/manifest_args.rs diff --git a/lib/crates/fabro-cli/src/server_client.rs b/lib/apps/fabro-cli/src/server_client.rs similarity index 100% rename from lib/crates/fabro-cli/src/server_client.rs rename to lib/apps/fabro-cli/src/server_client.rs diff --git a/lib/crates/fabro-cli/src/server_runs.rs b/lib/apps/fabro-cli/src/server_runs.rs similarity index 100% rename from lib/crates/fabro-cli/src/server_runs.rs rename to lib/apps/fabro-cli/src/server_runs.rs diff --git a/lib/crates/fabro-cli/src/shared/github.rs b/lib/apps/fabro-cli/src/shared/github.rs similarity index 100% rename from lib/crates/fabro-cli/src/shared/github.rs rename to lib/apps/fabro-cli/src/shared/github.rs diff --git a/lib/crates/fabro-cli/src/shared/mod.rs b/lib/apps/fabro-cli/src/shared/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/shared/mod.rs rename to lib/apps/fabro-cli/src/shared/mod.rs diff --git a/lib/crates/fabro-cli/src/shared/provider_auth.rs b/lib/apps/fabro-cli/src/shared/provider_auth.rs similarity index 100% rename from lib/crates/fabro-cli/src/shared/provider_auth.rs rename to lib/apps/fabro-cli/src/shared/provider_auth.rs diff --git a/lib/crates/fabro-cli/src/shared/repo.rs b/lib/apps/fabro-cli/src/shared/repo.rs similarity index 100% rename from lib/crates/fabro-cli/src/shared/repo.rs rename to lib/apps/fabro-cli/src/shared/repo.rs diff --git a/lib/crates/fabro-cli/src/shared/utilities.rs b/lib/apps/fabro-cli/src/shared/utilities.rs similarity index 100% rename from lib/crates/fabro-cli/src/shared/utilities.rs rename to lib/apps/fabro-cli/src/shared/utilities.rs diff --git a/lib/crates/fabro-cli/src/sleep_inhibitor/dummy.rs b/lib/apps/fabro-cli/src/sleep_inhibitor/dummy.rs similarity index 100% rename from lib/crates/fabro-cli/src/sleep_inhibitor/dummy.rs rename to lib/apps/fabro-cli/src/sleep_inhibitor/dummy.rs diff --git a/lib/crates/fabro-cli/src/sleep_inhibitor/iokit_bindings.rs b/lib/apps/fabro-cli/src/sleep_inhibitor/iokit_bindings.rs similarity index 100% rename from lib/crates/fabro-cli/src/sleep_inhibitor/iokit_bindings.rs rename to lib/apps/fabro-cli/src/sleep_inhibitor/iokit_bindings.rs diff --git a/lib/crates/fabro-cli/src/sleep_inhibitor/linux.rs b/lib/apps/fabro-cli/src/sleep_inhibitor/linux.rs similarity index 100% rename from lib/crates/fabro-cli/src/sleep_inhibitor/linux.rs rename to lib/apps/fabro-cli/src/sleep_inhibitor/linux.rs diff --git a/lib/crates/fabro-cli/src/sleep_inhibitor/macos.rs b/lib/apps/fabro-cli/src/sleep_inhibitor/macos.rs similarity index 100% rename from lib/crates/fabro-cli/src/sleep_inhibitor/macos.rs rename to lib/apps/fabro-cli/src/sleep_inhibitor/macos.rs diff --git a/lib/crates/fabro-cli/src/sleep_inhibitor/mod.rs b/lib/apps/fabro-cli/src/sleep_inhibitor/mod.rs similarity index 100% rename from lib/crates/fabro-cli/src/sleep_inhibitor/mod.rs rename to lib/apps/fabro-cli/src/sleep_inhibitor/mod.rs diff --git a/lib/crates/fabro-cli/src/user_config.rs b/lib/apps/fabro-cli/src/user_config.rs similarity index 100% rename from lib/crates/fabro-cli/src/user_config.rs rename to lib/apps/fabro-cli/src/user_config.rs diff --git a/lib/crates/fabro-cli/tests/build_git_metadata.rs b/lib/apps/fabro-cli/tests/build_git_metadata.rs similarity index 100% rename from lib/crates/fabro-cli/tests/build_git_metadata.rs rename to lib/apps/fabro-cli/tests/build_git_metadata.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/approve.rs b/lib/apps/fabro-cli/tests/it/cmd/approve.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/approve.rs rename to lib/apps/fabro-cli/tests/it/cmd/approve.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/archive.rs b/lib/apps/fabro-cli/tests/it/cmd/archive.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/archive.rs rename to lib/apps/fabro-cli/tests/it/cmd/archive.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/artifact_cp.rs b/lib/apps/fabro-cli/tests/it/cmd/artifact_cp.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/artifact_cp.rs rename to lib/apps/fabro-cli/tests/it/cmd/artifact_cp.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/artifact_list.rs b/lib/apps/fabro-cli/tests/it/cmd/artifact_list.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/artifact_list.rs rename to lib/apps/fabro-cli/tests/it/cmd/artifact_list.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/attach.rs b/lib/apps/fabro-cli/tests/it/cmd/attach.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/attach.rs rename to lib/apps/fabro-cli/tests/it/cmd/attach.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/auth.rs b/lib/apps/fabro-cli/tests/it/cmd/auth.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/auth.rs rename to lib/apps/fabro-cli/tests/it/cmd/auth.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/cli_reference.rs b/lib/apps/fabro-cli/tests/it/cmd/cli_reference.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/cli_reference.rs rename to lib/apps/fabro-cli/tests/it/cmd/cli_reference.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/config.rs b/lib/apps/fabro-cli/tests/it/cmd/config.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/config.rs rename to lib/apps/fabro-cli/tests/it/cmd/config.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/create.rs b/lib/apps/fabro-cli/tests/it/cmd/create.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/create.rs rename to lib/apps/fabro-cli/tests/it/cmd/create.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/deny.rs b/lib/apps/fabro-cli/tests/it/cmd/deny.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/deny.rs rename to lib/apps/fabro-cli/tests/it/cmd/deny.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/diff.rs b/lib/apps/fabro-cli/tests/it/cmd/diff.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/diff.rs rename to lib/apps/fabro-cli/tests/it/cmd/diff.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/discord.rs b/lib/apps/fabro-cli/tests/it/cmd/discord.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/discord.rs rename to lib/apps/fabro-cli/tests/it/cmd/discord.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/docs.rs b/lib/apps/fabro-cli/tests/it/cmd/docs.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/docs.rs rename to lib/apps/fabro-cli/tests/it/cmd/docs.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/doctor.rs b/lib/apps/fabro-cli/tests/it/cmd/doctor.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/doctor.rs rename to lib/apps/fabro-cli/tests/it/cmd/doctor.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/dump.rs b/lib/apps/fabro-cli/tests/it/cmd/dump.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/dump.rs rename to lib/apps/fabro-cli/tests/it/cmd/dump.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/events.rs b/lib/apps/fabro-cli/tests/it/cmd/events.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/events.rs rename to lib/apps/fabro-cli/tests/it/cmd/events.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/exec.rs b/lib/apps/fabro-cli/tests/it/cmd/exec.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/exec.rs rename to lib/apps/fabro-cli/tests/it/cmd/exec.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/fabro.rs b/lib/apps/fabro-cli/tests/it/cmd/fabro.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/fabro.rs rename to lib/apps/fabro-cli/tests/it/cmd/fabro.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/fork.rs b/lib/apps/fabro-cli/tests/it/cmd/fork.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/fork.rs rename to lib/apps/fabro-cli/tests/it/cmd/fork.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/graph.rs b/lib/apps/fabro-cli/tests/it/cmd/graph.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/graph.rs rename to lib/apps/fabro-cli/tests/it/cmd/graph.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/inspect.rs b/lib/apps/fabro-cli/tests/it/cmd/inspect.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/inspect.rs rename to lib/apps/fabro-cli/tests/it/cmd/inspect.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/install.rs b/lib/apps/fabro-cli/tests/it/cmd/install.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/install.rs rename to lib/apps/fabro-cli/tests/it/cmd/install.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/json_global.rs b/lib/apps/fabro-cli/tests/it/cmd/json_global.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/json_global.rs rename to lib/apps/fabro-cli/tests/it/cmd/json_global.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/logs.rs b/lib/apps/fabro-cli/tests/it/cmd/logs.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/logs.rs rename to lib/apps/fabro-cli/tests/it/cmd/logs.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/mcp.rs b/lib/apps/fabro-cli/tests/it/cmd/mcp.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/mcp.rs rename to lib/apps/fabro-cli/tests/it/cmd/mcp.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/mod.rs b/lib/apps/fabro-cli/tests/it/cmd/mod.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/mod.rs rename to lib/apps/fabro-cli/tests/it/cmd/mod.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/model.rs b/lib/apps/fabro-cli/tests/it/cmd/model.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/model.rs rename to lib/apps/fabro-cli/tests/it/cmd/model.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/model_list.rs b/lib/apps/fabro-cli/tests/it/cmd/model_list.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/model_list.rs rename to lib/apps/fabro-cli/tests/it/cmd/model_list.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/model_test.rs b/lib/apps/fabro-cli/tests/it/cmd/model_test.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/model_test.rs rename to lib/apps/fabro-cli/tests/it/cmd/model_test.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/parent.rs b/lib/apps/fabro-cli/tests/it/cmd/parent.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/parent.rs rename to lib/apps/fabro-cli/tests/it/cmd/parent.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/parse.rs b/lib/apps/fabro-cli/tests/it/cmd/parse.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/parse.rs rename to lib/apps/fabro-cli/tests/it/cmd/parse.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr.rs b/lib/apps/fabro-cli/tests/it/cmd/pr.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_close.rs b/lib/apps/fabro-cli/tests/it/cmd/pr_close.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr_close.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr_close.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_create.rs b/lib/apps/fabro-cli/tests/it/cmd/pr_create.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr_create.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr_create.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_link.rs b/lib/apps/fabro-cli/tests/it/cmd/pr_link.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr_link.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr_link.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_merge.rs b/lib/apps/fabro-cli/tests/it/cmd/pr_merge.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr_merge.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr_merge.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_unlink.rs b/lib/apps/fabro-cli/tests/it/cmd/pr_unlink.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr_unlink.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr_unlink.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_view.rs b/lib/apps/fabro-cli/tests/it/cmd/pr_view.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/pr_view.rs rename to lib/apps/fabro-cli/tests/it/cmd/pr_view.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/preflight.rs b/lib/apps/fabro-cli/tests/it/cmd/preflight.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/preflight.rs rename to lib/apps/fabro-cli/tests/it/cmd/preflight.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/provider.rs b/lib/apps/fabro-cli/tests/it/cmd/provider.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/provider.rs rename to lib/apps/fabro-cli/tests/it/cmd/provider.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/provider_login.rs b/lib/apps/fabro-cli/tests/it/cmd/provider_login.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/provider_login.rs rename to lib/apps/fabro-cli/tests/it/cmd/provider_login.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/ps.rs b/lib/apps/fabro-cli/tests/it/cmd/ps.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/ps.rs rename to lib/apps/fabro-cli/tests/it/cmd/ps.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/render_graph.rs b/lib/apps/fabro-cli/tests/it/cmd/render_graph.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/render_graph.rs rename to lib/apps/fabro-cli/tests/it/cmd/render_graph.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/repo.rs b/lib/apps/fabro-cli/tests/it/cmd/repo.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/repo.rs rename to lib/apps/fabro-cli/tests/it/cmd/repo.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/repo_deinit.rs b/lib/apps/fabro-cli/tests/it/cmd/repo_deinit.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/repo_deinit.rs rename to lib/apps/fabro-cli/tests/it/cmd/repo_deinit.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs b/lib/apps/fabro-cli/tests/it/cmd/repo_init.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/repo_init.rs rename to lib/apps/fabro-cli/tests/it/cmd/repo_init.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/resume.rs b/lib/apps/fabro-cli/tests/it/cmd/resume.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/resume.rs rename to lib/apps/fabro-cli/tests/it/cmd/resume.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/rewind.rs b/lib/apps/fabro-cli/tests/it/cmd/rewind.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/rewind.rs rename to lib/apps/fabro-cli/tests/it/cmd/rewind.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/rm.rs b/lib/apps/fabro-cli/tests/it/cmd/rm.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/rm.rs rename to lib/apps/fabro-cli/tests/it/cmd/rm.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/run.rs b/lib/apps/fabro-cli/tests/it/cmd/run.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/run.rs rename to lib/apps/fabro-cli/tests/it/cmd/run.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/runner.rs b/lib/apps/fabro-cli/tests/it/cmd/runner.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/runner.rs rename to lib/apps/fabro-cli/tests/it/cmd/runner.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/sandbox_cp.rs b/lib/apps/fabro-cli/tests/it/cmd/sandbox_cp.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/sandbox_cp.rs rename to lib/apps/fabro-cli/tests/it/cmd/sandbox_cp.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/sandbox_preview.rs b/lib/apps/fabro-cli/tests/it/cmd/sandbox_preview.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/sandbox_preview.rs rename to lib/apps/fabro-cli/tests/it/cmd/sandbox_preview.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/sandbox_ssh.rs b/lib/apps/fabro-cli/tests/it/cmd/sandbox_ssh.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/sandbox_ssh.rs rename to lib/apps/fabro-cli/tests/it/cmd/sandbox_ssh.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/secret.rs b/lib/apps/fabro-cli/tests/it/cmd/secret.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/secret.rs rename to lib/apps/fabro-cli/tests/it/cmd/secret.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/secret_list.rs b/lib/apps/fabro-cli/tests/it/cmd/secret_list.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/secret_list.rs rename to lib/apps/fabro-cli/tests/it/cmd/secret_list.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/secret_rm.rs b/lib/apps/fabro-cli/tests/it/cmd/secret_rm.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/secret_rm.rs rename to lib/apps/fabro-cli/tests/it/cmd/secret_rm.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/secret_set.rs b/lib/apps/fabro-cli/tests/it/cmd/secret_set.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/secret_set.rs rename to lib/apps/fabro-cli/tests/it/cmd/secret_set.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/send_analytics.rs b/lib/apps/fabro-cli/tests/it/cmd/send_analytics.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/send_analytics.rs rename to lib/apps/fabro-cli/tests/it/cmd/send_analytics.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/send_panic.rs b/lib/apps/fabro-cli/tests/it/cmd/send_panic.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/send_panic.rs rename to lib/apps/fabro-cli/tests/it/cmd/send_panic.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_start.rs b/lib/apps/fabro-cli/tests/it/cmd/server_start.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/server_start.rs rename to lib/apps/fabro-cli/tests/it/cmd/server_start.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_status.rs b/lib/apps/fabro-cli/tests/it/cmd/server_status.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/server_status.rs rename to lib/apps/fabro-cli/tests/it/cmd/server_status.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/server_stop.rs b/lib/apps/fabro-cli/tests/it/cmd/server_stop.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/server_stop.rs rename to lib/apps/fabro-cli/tests/it/cmd/server_stop.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/start.rs b/lib/apps/fabro-cli/tests/it/cmd/start.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/start.rs rename to lib/apps/fabro-cli/tests/it/cmd/start.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/support.rs b/lib/apps/fabro-cli/tests/it/cmd/support.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/support.rs rename to lib/apps/fabro-cli/tests/it/cmd/support.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/system.rs b/lib/apps/fabro-cli/tests/it/cmd/system.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/system.rs rename to lib/apps/fabro-cli/tests/it/cmd/system.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/system_df.rs b/lib/apps/fabro-cli/tests/it/cmd/system_df.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/system_df.rs rename to lib/apps/fabro-cli/tests/it/cmd/system_df.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/system_events.rs b/lib/apps/fabro-cli/tests/it/cmd/system_events.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/system_events.rs rename to lib/apps/fabro-cli/tests/it/cmd/system_events.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/system_info.rs b/lib/apps/fabro-cli/tests/it/cmd/system_info.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/system_info.rs rename to lib/apps/fabro-cli/tests/it/cmd/system_info.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/system_prune.rs b/lib/apps/fabro-cli/tests/it/cmd/system_prune.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/system_prune.rs rename to lib/apps/fabro-cli/tests/it/cmd/system_prune.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/system_repair.rs b/lib/apps/fabro-cli/tests/it/cmd/system_repair.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/system_repair.rs rename to lib/apps/fabro-cli/tests/it/cmd/system_repair.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/test_panic.rs b/lib/apps/fabro-cli/tests/it/cmd/test_panic.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/test_panic.rs rename to lib/apps/fabro-cli/tests/it/cmd/test_panic.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/top_level.rs b/lib/apps/fabro-cli/tests/it/cmd/top_level.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/top_level.rs rename to lib/apps/fabro-cli/tests/it/cmd/top_level.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/unarchive.rs b/lib/apps/fabro-cli/tests/it/cmd/unarchive.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/unarchive.rs rename to lib/apps/fabro-cli/tests/it/cmd/unarchive.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/uninstall.rs b/lib/apps/fabro-cli/tests/it/cmd/uninstall.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/uninstall.rs rename to lib/apps/fabro-cli/tests/it/cmd/uninstall.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/upgrade.rs b/lib/apps/fabro-cli/tests/it/cmd/upgrade.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/upgrade.rs rename to lib/apps/fabro-cli/tests/it/cmd/upgrade.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/validate.rs b/lib/apps/fabro-cli/tests/it/cmd/validate.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/validate.rs rename to lib/apps/fabro-cli/tests/it/cmd/validate.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/variable.rs b/lib/apps/fabro-cli/tests/it/cmd/variable.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/variable.rs rename to lib/apps/fabro-cli/tests/it/cmd/variable.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/variable_get.rs b/lib/apps/fabro-cli/tests/it/cmd/variable_get.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/variable_get.rs rename to lib/apps/fabro-cli/tests/it/cmd/variable_get.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/variable_list.rs b/lib/apps/fabro-cli/tests/it/cmd/variable_list.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/variable_list.rs rename to lib/apps/fabro-cli/tests/it/cmd/variable_list.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/variable_rm.rs b/lib/apps/fabro-cli/tests/it/cmd/variable_rm.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/variable_rm.rs rename to lib/apps/fabro-cli/tests/it/cmd/variable_rm.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/variable_set.rs b/lib/apps/fabro-cli/tests/it/cmd/variable_set.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/variable_set.rs rename to lib/apps/fabro-cli/tests/it/cmd/variable_set.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/version.rs b/lib/apps/fabro-cli/tests/it/cmd/version.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/version.rs rename to lib/apps/fabro-cli/tests/it/cmd/version.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/wait.rs b/lib/apps/fabro-cli/tests/it/cmd/wait.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/wait.rs rename to lib/apps/fabro-cli/tests/it/cmd/wait.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/worker_auth.rs b/lib/apps/fabro-cli/tests/it/cmd/worker_auth.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/worker_auth.rs rename to lib/apps/fabro-cli/tests/it/cmd/worker_auth.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/workflow.rs b/lib/apps/fabro-cli/tests/it/cmd/workflow.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/workflow.rs rename to lib/apps/fabro-cli/tests/it/cmd/workflow.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs b/lib/apps/fabro-cli/tests/it/cmd/workflow_create.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs rename to lib/apps/fabro-cli/tests/it/cmd/workflow_create.rs diff --git a/lib/crates/fabro-cli/tests/it/cmd/workflow_list.rs b/lib/apps/fabro-cli/tests/it/cmd/workflow_list.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/cmd/workflow_list.rs rename to lib/apps/fabro-cli/tests/it/cmd/workflow_list.rs diff --git a/lib/crates/fabro-cli/tests/it/main.rs b/lib/apps/fabro-cli/tests/it/main.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/main.rs rename to lib/apps/fabro-cli/tests/it/main.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/archive.rs b/lib/apps/fabro-cli/tests/it/scenario/archive.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/archive.rs rename to lib/apps/fabro-cli/tests/it/scenario/archive.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/artifacts.rs b/lib/apps/fabro-cli/tests/it/scenario/artifacts.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/artifacts.rs rename to lib/apps/fabro-cli/tests/it/scenario/artifacts.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/auth.rs b/lib/apps/fabro-cli/tests/it/scenario/auth.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/auth.rs rename to lib/apps/fabro-cli/tests/it/scenario/auth.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/exec.rs b/lib/apps/fabro-cli/tests/it/scenario/exec.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/exec.rs rename to lib/apps/fabro-cli/tests/it/scenario/exec.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs b/lib/apps/fabro-cli/tests/it/scenario/lifecycle.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs rename to lib/apps/fabro-cli/tests/it/scenario/lifecycle.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/mod.rs b/lib/apps/fabro-cli/tests/it/scenario/mod.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/mod.rs rename to lib/apps/fabro-cli/tests/it/scenario/mod.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/server_lifecycle.rs b/lib/apps/fabro-cli/tests/it/scenario/server_lifecycle.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/server_lifecycle.rs rename to lib/apps/fabro-cli/tests/it/scenario/server_lifecycle.rs diff --git a/lib/crates/fabro-cli/tests/it/scenario/smoke.rs b/lib/apps/fabro-cli/tests/it/scenario/smoke.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/scenario/smoke.rs rename to lib/apps/fabro-cli/tests/it/scenario/smoke.rs diff --git a/lib/crates/fabro-cli/tests/it/support/auth_harness.rs b/lib/apps/fabro-cli/tests/it/support/auth_harness.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/support/auth_harness.rs rename to lib/apps/fabro-cli/tests/it/support/auth_harness.rs diff --git a/lib/crates/fabro-cli/tests/it/support/auth_tokens.rs b/lib/apps/fabro-cli/tests/it/support/auth_tokens.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/support/auth_tokens.rs rename to lib/apps/fabro-cli/tests/it/support/auth_tokens.rs diff --git a/lib/crates/fabro-cli/tests/it/support/mod.rs b/lib/apps/fabro-cli/tests/it/support/mod.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/support/mod.rs rename to lib/apps/fabro-cli/tests/it/support/mod.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/acp.rs b/lib/apps/fabro-cli/tests/it/workflow/acp.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/acp.rs rename to lib/apps/fabro-cli/tests/it/workflow/acp.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/agent_linear.rs b/lib/apps/fabro-cli/tests/it/workflow/agent_linear.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/agent_linear.rs rename to lib/apps/fabro-cli/tests/it/workflow/agent_linear.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/artifacts.rs b/lib/apps/fabro-cli/tests/it/workflow/artifacts.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/artifacts.rs rename to lib/apps/fabro-cli/tests/it/workflow/artifacts.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs b/lib/apps/fabro-cli/tests/it/workflow/command_agent_mixed.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs rename to lib/apps/fabro-cli/tests/it/workflow/command_agent_mixed.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs b/lib/apps/fabro-cli/tests/it/workflow/command_pipeline.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs rename to lib/apps/fabro-cli/tests/it/workflow/command_pipeline.rs diff --git a/lib/apps/fabro-cli/tests/it/workflow/command_routing.rs b/lib/apps/fabro-cli/tests/it/workflow/command_routing.rs new file mode 100644 index 000000000..1052ae476 --- /dev/null +++ b/lib/apps/fabro-cli/tests/it/workflow/command_routing.rs @@ -0,0 +1,34 @@ +use fabro_test::test_context; + +use super::{completed_nodes, find_run_dir, fixture, read_conclusion, sandbox_tests, timeout_for}; + +sandbox_tests!(command_routing); + +fn scenario_command_routing(sandbox: &str) { + let context = test_context!(); + let workflow = fixture("command_routing.fabro"); + + context.validate().arg(&workflow).assert().success(); + + context + .run_cmd() + .args(["--auto-approve", "--environment", sandbox]) + .arg(workflow) + .timeout(timeout_for(sandbox)) + .assert() + .success(); + + let run_dir = find_run_dir(&context); + let conclusion = read_conclusion(&run_dir); + assert_eq!(conclusion["status"].as_str(), Some("succeeded")); + + let nodes = completed_nodes(&run_dir); + assert!( + nodes.contains(&"kept".to_string()), + "kept node should be in completed_nodes: {nodes:?}" + ); + assert!( + !nodes.contains(&"none".to_string()), + "none node should NOT be in completed_nodes: {nodes:?}" + ); +} diff --git a/lib/crates/fabro-cli/tests/it/workflow/conditional_branching.rs b/lib/apps/fabro-cli/tests/it/workflow/conditional_branching.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/conditional_branching.rs rename to lib/apps/fabro-cli/tests/it/workflow/conditional_branching.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/dry_run_examples.rs b/lib/apps/fabro-cli/tests/it/workflow/dry_run_examples.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/dry_run_examples.rs rename to lib/apps/fabro-cli/tests/it/workflow/dry_run_examples.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/fixtures/agent_linear.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/agent_linear.fabro similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/fixtures/agent_linear.fabro rename to lib/apps/fabro-cli/tests/it/workflow/fixtures/agent_linear.fabro diff --git a/lib/crates/fabro-cli/tests/it/workflow/fixtures/command_agent_mixed.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/command_agent_mixed.fabro similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/fixtures/command_agent_mixed.fabro rename to lib/apps/fabro-cli/tests/it/workflow/fixtures/command_agent_mixed.fabro diff --git a/lib/crates/fabro-cli/tests/it/workflow/fixtures/command_pipeline.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/command_pipeline.fabro similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/fixtures/command_pipeline.fabro rename to lib/apps/fabro-cli/tests/it/workflow/fixtures/command_pipeline.fabro diff --git a/lib/apps/fabro-cli/tests/it/workflow/fixtures/command_routing.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/command_routing.fabro new file mode 100644 index 000000000..0c47eccff --- /dev/null +++ b/lib/apps/fabro-cli/tests/it/workflow/fixtures/command_routing.fabro @@ -0,0 +1,23 @@ +digraph CommandRouting { + graph [goal="Route on structured output from a command"] + rankdir=LR + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + classify [ + label="Classify", + shape=parallelogram, + script="echo '{\"context_updates\":{\"kept_count\":2}}'", + output_schema="routing" + ] + gate [label="Check Count", shape=diamond] + kept [label="Kept", shape=parallelogram, script="echo kept"] + none [label="None", shape=parallelogram, script="echo none"] + + start -> classify -> gate + gate -> kept [label="Kept", condition="context.kept_count > 0"] + gate -> none [label="None"] + kept -> exit + none -> exit +} diff --git a/lib/crates/fabro-cli/tests/it/workflow/fixtures/conditional_branching.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/conditional_branching.fabro similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/fixtures/conditional_branching.fabro rename to lib/apps/fabro-cli/tests/it/workflow/fixtures/conditional_branching.fabro diff --git a/lib/crates/fabro-cli/tests/it/workflow/fixtures/full_stack.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/full_stack.fabro similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/fixtures/full_stack.fabro rename to lib/apps/fabro-cli/tests/it/workflow/fixtures/full_stack.fabro diff --git a/lib/crates/fabro-cli/tests/it/workflow/fixtures/human_gate.fabro b/lib/apps/fabro-cli/tests/it/workflow/fixtures/human_gate.fabro similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/fixtures/human_gate.fabro rename to lib/apps/fabro-cli/tests/it/workflow/fixtures/human_gate.fabro diff --git a/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs b/lib/apps/fabro-cli/tests/it/workflow/full_stack.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/full_stack.rs rename to lib/apps/fabro-cli/tests/it/workflow/full_stack.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs b/lib/apps/fabro-cli/tests/it/workflow/hooks.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/hooks.rs rename to lib/apps/fabro-cli/tests/it/workflow/hooks.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/human_gate.rs b/lib/apps/fabro-cli/tests/it/workflow/human_gate.rs similarity index 100% rename from lib/crates/fabro-cli/tests/it/workflow/human_gate.rs rename to lib/apps/fabro-cli/tests/it/workflow/human_gate.rs diff --git a/lib/crates/fabro-cli/tests/it/workflow/mod.rs b/lib/apps/fabro-cli/tests/it/workflow/mod.rs similarity index 99% rename from lib/crates/fabro-cli/tests/it/workflow/mod.rs rename to lib/apps/fabro-cli/tests/it/workflow/mod.rs index 365707d2c..5e0dd3738 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/mod.rs +++ b/lib/apps/fabro-cli/tests/it/workflow/mod.rs @@ -8,6 +8,7 @@ mod agent_linear; mod artifacts; mod command_agent_mixed; mod command_pipeline; +mod command_routing; mod conditional_branching; mod dry_run_examples; mod full_stack; diff --git a/lib/crates/fabro-cli/tests/manifest_path_round_trip.rs b/lib/apps/fabro-cli/tests/manifest_path_round_trip.rs similarity index 100% rename from lib/crates/fabro-cli/tests/manifest_path_round_trip.rs rename to lib/apps/fabro-cli/tests/manifest_path_round_trip.rs diff --git a/lib/crates/fabro-mcp-server/Cargo.toml b/lib/apps/fabro-mcp-server/Cargo.toml similarity index 59% rename from lib/crates/fabro-mcp-server/Cargo.toml rename to lib/apps/fabro-mcp-server/Cargo.toml index de3caa633..cdbf6b65f 100644 --- a/lib/crates/fabro-mcp-server/Cargo.toml +++ b/lib/apps/fabro-mcp-server/Cargo.toml @@ -15,15 +15,15 @@ workspace = true [dependencies] anyhow.workspace = true chrono = { workspace = true, features = ["serde"] } -fabro-api = { path = "../fabro-api" } -fabro-client = { path = "../fabro-client" } -fabro-manifest = { path = "../fabro-manifest" } -fabro-config = { path = "../fabro-config" } -fabro-model = { path = "../fabro-model" } +fabro-api = { path = "../../foundation/fabro-api" } +fabro-client = { path = "../../foundation/fabro-client" } +fabro-manifest = { path = "../../components/fabro-manifest" } +fabro-config = { path = "../../foundation/fabro-config" } +fabro-model = { path = "../../foundation/fabro-model" } fabro-server = { path = "../fabro-server" } -fabro-tool = { path = "../fabro-tool" } -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } +fabro-tool = { path = "../../components/fabro-tool" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } futures.workspace = true rmcp = { workspace = true, features = ["server", "macros", "schemars", "transport-io"] } schemars = "1.2.1" diff --git a/lib/crates/fabro-mcp-server/src/config.rs b/lib/apps/fabro-mcp-server/src/config.rs similarity index 100% rename from lib/crates/fabro-mcp-server/src/config.rs rename to lib/apps/fabro-mcp-server/src/config.rs diff --git a/lib/crates/fabro-mcp-server/src/lib.rs b/lib/apps/fabro-mcp-server/src/lib.rs similarity index 100% rename from lib/crates/fabro-mcp-server/src/lib.rs rename to lib/apps/fabro-mcp-server/src/lib.rs diff --git a/lib/crates/fabro-mcp-server/src/manifest_builder.rs b/lib/apps/fabro-mcp-server/src/manifest_builder.rs similarity index 100% rename from lib/crates/fabro-mcp-server/src/manifest_builder.rs rename to lib/apps/fabro-mcp-server/src/manifest_builder.rs diff --git a/lib/crates/fabro-mcp-server/src/server.rs b/lib/apps/fabro-mcp-server/src/server.rs similarity index 100% rename from lib/crates/fabro-mcp-server/src/server.rs rename to lib/apps/fabro-mcp-server/src/server.rs diff --git a/lib/crates/fabro-server/Cargo.toml b/lib/apps/fabro-server/Cargo.toml similarity index 56% rename from lib/crates/fabro-server/Cargo.toml rename to lib/apps/fabro-server/Cargo.toml index b951b339f..1f2c5bb61 100644 --- a/lib/crates/fabro-server/Cargo.toml +++ b/lib/apps/fabro-server/Cargo.toml @@ -21,35 +21,35 @@ required-features = ["test-support"] workspace = true [dependencies] -fabro-automation = { path = "../fabro-automation" } -fabro-auth = { path = "../fabro-auth" } -fabro-install = { path = "../fabro-install" } +fabro-automation = { path = "../../components/fabro-automation" } +fabro-auth = { path = "../../foundation/fabro-auth" } +fabro-install = { path = "../../components/fabro-install" } fabro-spa = { path = "../fabro-spa" } -fabro-config = { path = "../fabro-config" } +fabro-config = { path = "../../foundation/fabro-config" } fabro-environment.workspace = true -fabro-graphviz = { path = "../fabro-graphviz" } -fabro-hooks = { path = "../fabro-hooks" } -fabro-interview = { path = "../fabro-interview" } -fabro-slack = { path = "../fabro-slack" } -fabro-workflow = { path = "../fabro-workflow" } -fabro-validate = { path = "../fabro-validate" } -fabro-sandbox = { path = "../fabro-sandbox", features = ["daytona", "docker"] } -fabro-github = { path = "../fabro-github" } -fabro-agent = { path = "../fabro-agent" } -fabro-llm = { path = "../fabro-llm" } -fabro-manifest = { path = "../fabro-manifest" } -fabro-mcp-store = { path = "../fabro-mcp-store" } -fabro-model = { path = "../fabro-model" } -fabro-proc = { path = "../fabro-proc" } -fabro-tool = { path = "../fabro-tool" } -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } -fabro-api = { path = "../fabro-api" } -fabro-client = { path = "../fabro-client" } -fabro-db = { path = "../fabro-db" } -fabro-store = { path = "../fabro-store" } -fabro-vault = { path = "../fabro-vault" } -fabro-variable = { path = "../fabro-variable" } +fabro-graphviz = { path = "../../components/fabro-graphviz" } +fabro-hooks = { path = "../../components/fabro-hooks" } +fabro-interview = { path = "../../components/fabro-interview" } +fabro-slack = { path = "../../components/fabro-slack" } +fabro-workflow = { path = "../../components/fabro-workflow" } +fabro-validate = { path = "../../components/fabro-validate" } +fabro-sandbox = { path = "../../components/fabro-sandbox", features = ["daytona", "docker"] } +fabro-github = { path = "../../components/fabro-github" } +fabro-agent = { path = "../../components/fabro-agent" } +fabro-llm = { path = "../../components/fabro-llm" } +fabro-manifest = { path = "../../components/fabro-manifest" } +fabro-mcp-store = { path = "../../components/fabro-mcp-store" } +fabro-model = { path = "../../foundation/fabro-model" } +fabro-proc = { path = "../../foundation/fabro-proc" } +fabro-tool = { path = "../../components/fabro-tool" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } +fabro-api = { path = "../../foundation/fabro-api" } +fabro-client = { path = "../../foundation/fabro-client" } +fabro-db = { path = "../../foundation/fabro-db" } +fabro-store = { path = "../../components/fabro-store" } +fabro-vault = { path = "../../foundation/fabro-vault" } +fabro-variable = { path = "../../components/fabro-variable" } fabro-http.workspace = true fabro-redact.workspace = true fabro-static.workspace = true @@ -104,7 +104,7 @@ strum.workspace = true sysinfo = { version = "0.35.2", default-features = false, features = ["system", "disk"] } [build-dependencies] -fabro-build-support = { path = "../build-support" } +fabro-build-support = { path = "../../foundation/build-support" } chrono = { workspace = true } [dev-dependencies] @@ -117,7 +117,7 @@ sqlx.workspace = true tracing-subscriber.workspace = true tokio-util.workspace = true tokio-tungstenite.workspace = true -fabro-macros = { path = "../fabro-macros" } -fabro-sandbox = { path = "../fabro-sandbox", features = ["test-support"] } +fabro-macros = { path = "../../foundation/fabro-macros" } +fabro-sandbox = { path = "../../components/fabro-sandbox", features = ["test-support"] } fabro-test = { workspace = true } -fabro-types = { path = "../fabro-types", features = ["test-support"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["test-support"] } diff --git a/lib/crates/fabro-server/build.rs b/lib/apps/fabro-server/build.rs similarity index 100% rename from lib/crates/fabro-server/build.rs rename to lib/apps/fabro-server/build.rs diff --git a/lib/crates/fabro-server/migrations/2026051801_legacy_vault_entries.rs b/lib/apps/fabro-server/migrations/2026051801_legacy_vault_entries.rs similarity index 100% rename from lib/crates/fabro-server/migrations/2026051801_legacy_vault_entries.rs rename to lib/apps/fabro-server/migrations/2026051801_legacy_vault_entries.rs diff --git a/lib/crates/fabro-server/migrations/2026052501_optional_server_env_secrets_to_vault.rs b/lib/apps/fabro-server/migrations/2026052501_optional_server_env_secrets_to_vault.rs similarity index 100% rename from lib/crates/fabro-server/migrations/2026052501_optional_server_env_secrets_to_vault.rs rename to lib/apps/fabro-server/migrations/2026052501_optional_server_env_secrets_to_vault.rs diff --git a/lib/crates/fabro-server/src/auth/browser_shell.rs b/lib/apps/fabro-server/src/auth/browser_shell.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/browser_shell.rs rename to lib/apps/fabro-server/src/auth/browser_shell.rs diff --git a/lib/crates/fabro-server/src/auth/cli_flow.rs b/lib/apps/fabro-server/src/auth/cli_flow.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/cli_flow.rs rename to lib/apps/fabro-server/src/auth/cli_flow.rs diff --git a/lib/crates/fabro-server/src/auth/github_endpoints.rs b/lib/apps/fabro-server/src/auth/github_endpoints.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/github_endpoints.rs rename to lib/apps/fabro-server/src/auth/github_endpoints.rs diff --git a/lib/crates/fabro-server/src/auth/jwt.rs b/lib/apps/fabro-server/src/auth/jwt.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/jwt.rs rename to lib/apps/fabro-server/src/auth/jwt.rs diff --git a/lib/crates/fabro-server/src/auth/keys.rs b/lib/apps/fabro-server/src/auth/keys.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/keys.rs rename to lib/apps/fabro-server/src/auth/keys.rs diff --git a/lib/crates/fabro-server/src/auth/mod.rs b/lib/apps/fabro-server/src/auth/mod.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/mod.rs rename to lib/apps/fabro-server/src/auth/mod.rs diff --git a/lib/crates/fabro-server/src/auth/translate.rs b/lib/apps/fabro-server/src/auth/translate.rs similarity index 100% rename from lib/crates/fabro-server/src/auth/translate.rs rename to lib/apps/fabro-server/src/auth/translate.rs diff --git a/lib/crates/fabro-server/src/automation_materializer.rs b/lib/apps/fabro-server/src/automation_materializer.rs similarity index 100% rename from lib/crates/fabro-server/src/automation_materializer.rs rename to lib/apps/fabro-server/src/automation_materializer.rs diff --git a/lib/crates/fabro-server/src/canonical_host.rs b/lib/apps/fabro-server/src/canonical_host.rs similarity index 100% rename from lib/crates/fabro-server/src/canonical_host.rs rename to lib/apps/fabro-server/src/canonical_host.rs diff --git a/lib/crates/fabro-server/src/canonical_origin.rs b/lib/apps/fabro-server/src/canonical_origin.rs similarity index 100% rename from lib/crates/fabro-server/src/canonical_origin.rs rename to lib/apps/fabro-server/src/canonical_origin.rs diff --git a/lib/crates/fabro-server/src/csp.rs b/lib/apps/fabro-server/src/csp.rs similarity index 100% rename from lib/crates/fabro-server/src/csp.rs rename to lib/apps/fabro-server/src/csp.rs diff --git a/lib/crates/fabro-server/src/demo/mod.rs b/lib/apps/fabro-server/src/demo/mod.rs similarity index 100% rename from lib/crates/fabro-server/src/demo/mod.rs rename to lib/apps/fabro-server/src/demo/mod.rs diff --git a/lib/crates/fabro-server/src/diagnostics.rs b/lib/apps/fabro-server/src/diagnostics.rs similarity index 100% rename from lib/crates/fabro-server/src/diagnostics.rs rename to lib/apps/fabro-server/src/diagnostics.rs diff --git a/lib/crates/fabro-server/src/error.rs b/lib/apps/fabro-server/src/error.rs similarity index 100% rename from lib/crates/fabro-server/src/error.rs rename to lib/apps/fabro-server/src/error.rs diff --git a/lib/crates/fabro-server/src/git_checkout.rs b/lib/apps/fabro-server/src/git_checkout.rs similarity index 100% rename from lib/crates/fabro-server/src/git_checkout.rs rename to lib/apps/fabro-server/src/git_checkout.rs diff --git a/lib/crates/fabro-server/src/github_webhooks.rs b/lib/apps/fabro-server/src/github_webhooks.rs similarity index 100% rename from lib/crates/fabro-server/src/github_webhooks.rs rename to lib/apps/fabro-server/src/github_webhooks.rs diff --git a/lib/crates/fabro-server/src/install.rs b/lib/apps/fabro-server/src/install.rs similarity index 100% rename from lib/crates/fabro-server/src/install.rs rename to lib/apps/fabro-server/src/install.rs diff --git a/lib/crates/fabro-server/src/interp.rs b/lib/apps/fabro-server/src/interp.rs similarity index 100% rename from lib/crates/fabro-server/src/interp.rs rename to lib/apps/fabro-server/src/interp.rs diff --git a/lib/crates/fabro-server/src/jwt_auth.rs b/lib/apps/fabro-server/src/jwt_auth.rs similarity index 100% rename from lib/crates/fabro-server/src/jwt_auth.rs rename to lib/apps/fabro-server/src/jwt_auth.rs diff --git a/lib/crates/fabro-server/src/lib.rs b/lib/apps/fabro-server/src/lib.rs similarity index 100% rename from lib/crates/fabro-server/src/lib.rs rename to lib/apps/fabro-server/src/lib.rs diff --git a/lib/crates/fabro-server/src/manifest_validation.rs b/lib/apps/fabro-server/src/manifest_validation.rs similarity index 100% rename from lib/crates/fabro-server/src/manifest_validation.rs rename to lib/apps/fabro-server/src/manifest_validation.rs diff --git a/lib/crates/fabro-server/src/migrations.rs b/lib/apps/fabro-server/src/migrations.rs similarity index 100% rename from lib/crates/fabro-server/src/migrations.rs rename to lib/apps/fabro-server/src/migrations.rs diff --git a/lib/crates/fabro-server/src/principal_middleware.rs b/lib/apps/fabro-server/src/principal_middleware.rs similarity index 100% rename from lib/crates/fabro-server/src/principal_middleware.rs rename to lib/apps/fabro-server/src/principal_middleware.rs diff --git a/lib/crates/fabro-server/src/request_id.rs b/lib/apps/fabro-server/src/request_id.rs similarity index 100% rename from lib/crates/fabro-server/src/request_id.rs rename to lib/apps/fabro-server/src/request_id.rs diff --git a/lib/crates/fabro-server/src/run_files.rs b/lib/apps/fabro-server/src/run_files.rs similarity index 100% rename from lib/crates/fabro-server/src/run_files.rs rename to lib/apps/fabro-server/src/run_files.rs diff --git a/lib/crates/fabro-server/src/run_files_security.rs b/lib/apps/fabro-server/src/run_files_security.rs similarity index 100% rename from lib/crates/fabro-server/src/run_files_security.rs rename to lib/apps/fabro-server/src/run_files_security.rs diff --git a/lib/crates/fabro-server/src/run_manifest.rs b/lib/apps/fabro-server/src/run_manifest.rs similarity index 100% rename from lib/crates/fabro-server/src/run_manifest.rs rename to lib/apps/fabro-server/src/run_manifest.rs diff --git a/lib/crates/fabro-server/src/run_selector.rs b/lib/apps/fabro-server/src/run_selector.rs similarity index 100% rename from lib/crates/fabro-server/src/run_selector.rs rename to lib/apps/fabro-server/src/run_selector.rs diff --git a/lib/crates/fabro-server/src/run_title_generation.rs b/lib/apps/fabro-server/src/run_title_generation.rs similarity index 100% rename from lib/crates/fabro-server/src/run_title_generation.rs rename to lib/apps/fabro-server/src/run_title_generation.rs diff --git a/lib/crates/fabro-server/src/run_tool_manifest.rs b/lib/apps/fabro-server/src/run_tool_manifest.rs similarity index 100% rename from lib/crates/fabro-server/src/run_tool_manifest.rs rename to lib/apps/fabro-server/src/run_tool_manifest.rs diff --git a/lib/crates/fabro-server/src/security_headers.rs b/lib/apps/fabro-server/src/security_headers.rs similarity index 100% rename from lib/crates/fabro-server/src/security_headers.rs rename to lib/apps/fabro-server/src/security_headers.rs diff --git a/lib/crates/fabro-server/src/serve.rs b/lib/apps/fabro-server/src/serve.rs similarity index 100% rename from lib/crates/fabro-server/src/serve.rs rename to lib/apps/fabro-server/src/serve.rs diff --git a/lib/crates/fabro-server/src/server.rs b/lib/apps/fabro-server/src/server.rs similarity index 100% rename from lib/crates/fabro-server/src/server.rs rename to lib/apps/fabro-server/src/server.rs diff --git a/lib/crates/fabro-server/src/server/automation_scheduler.rs b/lib/apps/fabro-server/src/server/automation_scheduler.rs similarity index 100% rename from lib/crates/fabro-server/src/server/automation_scheduler.rs rename to lib/apps/fabro-server/src/server/automation_scheduler.rs diff --git a/lib/crates/fabro-server/src/server/handler/artifacts.rs b/lib/apps/fabro-server/src/server/handler/artifacts.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/artifacts.rs rename to lib/apps/fabro-server/src/server/handler/artifacts.rs diff --git a/lib/crates/fabro-server/src/server/handler/automations.rs b/lib/apps/fabro-server/src/server/handler/automations.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/automations.rs rename to lib/apps/fabro-server/src/server/handler/automations.rs diff --git a/lib/crates/fabro-server/src/server/handler/billing.rs b/lib/apps/fabro-server/src/server/handler/billing.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/billing.rs rename to lib/apps/fabro-server/src/server/handler/billing.rs diff --git a/lib/crates/fabro-server/src/server/handler/completions.rs b/lib/apps/fabro-server/src/server/handler/completions.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/completions.rs rename to lib/apps/fabro-server/src/server/handler/completions.rs diff --git a/lib/crates/fabro-server/src/server/handler/environments.rs b/lib/apps/fabro-server/src/server/handler/environments.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/environments.rs rename to lib/apps/fabro-server/src/server/handler/environments.rs diff --git a/lib/crates/fabro-server/src/server/handler/events.rs b/lib/apps/fabro-server/src/server/handler/events.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/events.rs rename to lib/apps/fabro-server/src/server/handler/events.rs diff --git a/lib/crates/fabro-server/src/server/handler/graph.rs b/lib/apps/fabro-server/src/server/handler/graph.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/graph.rs rename to lib/apps/fabro-server/src/server/handler/graph.rs diff --git a/lib/crates/fabro-server/src/server/handler/lifecycle.rs b/lib/apps/fabro-server/src/server/handler/lifecycle.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/lifecycle.rs rename to lib/apps/fabro-server/src/server/handler/lifecycle.rs diff --git a/lib/crates/fabro-server/src/server/handler/llm_sse.rs b/lib/apps/fabro-server/src/server/handler/llm_sse.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/llm_sse.rs rename to lib/apps/fabro-server/src/server/handler/llm_sse.rs diff --git a/lib/crates/fabro-server/src/server/handler/mcp_servers.rs b/lib/apps/fabro-server/src/server/handler/mcp_servers.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/mcp_servers.rs rename to lib/apps/fabro-server/src/server/handler/mcp_servers.rs diff --git a/lib/crates/fabro-server/src/server/handler/mod.rs b/lib/apps/fabro-server/src/server/handler/mod.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/mod.rs rename to lib/apps/fabro-server/src/server/handler/mod.rs diff --git a/lib/crates/fabro-server/src/server/handler/models.rs b/lib/apps/fabro-server/src/server/handler/models.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/models.rs rename to lib/apps/fabro-server/src/server/handler/models.rs diff --git a/lib/crates/fabro-server/src/server/handler/pair.rs b/lib/apps/fabro-server/src/server/handler/pair.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/pair.rs rename to lib/apps/fabro-server/src/server/handler/pair.rs diff --git a/lib/crates/fabro-server/src/server/handler/playground.rs b/lib/apps/fabro-server/src/server/handler/playground.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/playground.rs rename to lib/apps/fabro-server/src/server/handler/playground.rs diff --git a/lib/crates/fabro-server/src/server/handler/prompts/playground_system.md b/lib/apps/fabro-server/src/server/handler/prompts/playground_system.md similarity index 100% rename from lib/crates/fabro-server/src/server/handler/prompts/playground_system.md rename to lib/apps/fabro-server/src/server/handler/prompts/playground_system.md diff --git a/lib/crates/fabro-server/src/server/handler/pull_requests.rs b/lib/apps/fabro-server/src/server/handler/pull_requests.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/pull_requests.rs rename to lib/apps/fabro-server/src/server/handler/pull_requests.rs diff --git a/lib/crates/fabro-server/src/server/handler/runs.rs b/lib/apps/fabro-server/src/server/handler/runs.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/runs.rs rename to lib/apps/fabro-server/src/server/handler/runs.rs diff --git a/lib/crates/fabro-server/src/server/handler/sandbox.rs b/lib/apps/fabro-server/src/server/handler/sandbox.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/sandbox.rs rename to lib/apps/fabro-server/src/server/handler/sandbox.rs diff --git a/lib/crates/fabro-server/src/server/handler/sandboxes.rs b/lib/apps/fabro-server/src/server/handler/sandboxes.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/sandboxes.rs rename to lib/apps/fabro-server/src/server/handler/sandboxes.rs diff --git a/lib/crates/fabro-server/src/server/handler/secrets.rs b/lib/apps/fabro-server/src/server/handler/secrets.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/secrets.rs rename to lib/apps/fabro-server/src/server/handler/secrets.rs diff --git a/lib/crates/fabro-server/src/server/handler/sessions.rs b/lib/apps/fabro-server/src/server/handler/sessions.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/sessions.rs rename to lib/apps/fabro-server/src/server/handler/sessions.rs diff --git a/lib/crates/fabro-server/src/server/handler/steer.rs b/lib/apps/fabro-server/src/server/handler/steer.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/steer.rs rename to lib/apps/fabro-server/src/server/handler/steer.rs diff --git a/lib/crates/fabro-server/src/server/handler/system.rs b/lib/apps/fabro-server/src/server/handler/system.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/system.rs rename to lib/apps/fabro-server/src/server/handler/system.rs diff --git a/lib/crates/fabro-server/src/server/handler/variables.rs b/lib/apps/fabro-server/src/server/handler/variables.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/variables.rs rename to lib/apps/fabro-server/src/server/handler/variables.rs diff --git a/lib/crates/fabro-server/src/server/handler/worker_control.rs b/lib/apps/fabro-server/src/server/handler/worker_control.rs similarity index 100% rename from lib/crates/fabro-server/src/server/handler/worker_control.rs rename to lib/apps/fabro-server/src/server/handler/worker_control.rs diff --git a/lib/crates/fabro-server/src/server/resource_sampler.rs b/lib/apps/fabro-server/src/server/resource_sampler.rs similarity index 100% rename from lib/crates/fabro-server/src/server/resource_sampler.rs rename to lib/apps/fabro-server/src/server/resource_sampler.rs diff --git a/lib/crates/fabro-server/src/server/session_runtime.rs b/lib/apps/fabro-server/src/server/session_runtime.rs similarity index 100% rename from lib/crates/fabro-server/src/server/session_runtime.rs rename to lib/apps/fabro-server/src/server/session_runtime.rs diff --git a/lib/crates/fabro-server/src/server/tests.rs b/lib/apps/fabro-server/src/server/tests.rs similarity index 100% rename from lib/crates/fabro-server/src/server/tests.rs rename to lib/apps/fabro-server/src/server/tests.rs diff --git a/lib/crates/fabro-server/src/server_secrets.rs b/lib/apps/fabro-server/src/server_secrets.rs similarity index 100% rename from lib/crates/fabro-server/src/server_secrets.rs rename to lib/apps/fabro-server/src/server_secrets.rs diff --git a/lib/crates/fabro-server/src/spawn_env.rs b/lib/apps/fabro-server/src/spawn_env.rs similarity index 100% rename from lib/crates/fabro-server/src/spawn_env.rs rename to lib/apps/fabro-server/src/spawn_env.rs diff --git a/lib/crates/fabro-server/src/startup.rs b/lib/apps/fabro-server/src/startup.rs similarity index 100% rename from lib/crates/fabro-server/src/startup.rs rename to lib/apps/fabro-server/src/startup.rs diff --git a/lib/crates/fabro-server/src/static_files.rs b/lib/apps/fabro-server/src/static_files.rs similarity index 99% rename from lib/crates/fabro-server/src/static_files.rs rename to lib/apps/fabro-server/src/static_files.rs index cb5c500a2..b0a9e623e 100644 --- a/lib/crates/fabro-server/src/static_files.rs +++ b/lib/apps/fabro-server/src/static_files.rs @@ -632,7 +632,7 @@ mod tests { #[tokio::test] async fn dev_disk_only_does_not_fall_back_to_embedded() { // The embedded SPA snapshot has chunk-12xq903b.js (observed in - // lib/crates/fabro-spa/assets/index.html). In disk-only mode that + // lib/apps/fabro-spa/assets/index.html). In disk-only mode that // bytes-on-disk-or-503 contract must hold even for assets the // embedded snapshot would otherwise serve. let temp_dir = tempfile::tempdir().unwrap(); diff --git a/lib/crates/fabro-server/src/test_support.rs b/lib/apps/fabro-server/src/test_support.rs similarity index 100% rename from lib/crates/fabro-server/src/test_support.rs rename to lib/apps/fabro-server/src/test_support.rs diff --git a/lib/crates/fabro-server/src/web_auth.rs b/lib/apps/fabro-server/src/web_auth.rs similarity index 100% rename from lib/crates/fabro-server/src/web_auth.rs rename to lib/apps/fabro-server/src/web_auth.rs diff --git a/lib/crates/fabro-server/src/worker_control/bus.rs b/lib/apps/fabro-server/src/worker_control/bus.rs similarity index 100% rename from lib/crates/fabro-server/src/worker_control/bus.rs rename to lib/apps/fabro-server/src/worker_control/bus.rs diff --git a/lib/crates/fabro-server/src/worker_control/local.rs b/lib/apps/fabro-server/src/worker_control/local.rs similarity index 100% rename from lib/crates/fabro-server/src/worker_control/local.rs rename to lib/apps/fabro-server/src/worker_control/local.rs diff --git a/lib/crates/fabro-server/src/worker_control/mod.rs b/lib/apps/fabro-server/src/worker_control/mod.rs similarity index 100% rename from lib/crates/fabro-server/src/worker_control/mod.rs rename to lib/apps/fabro-server/src/worker_control/mod.rs diff --git a/lib/crates/fabro-server/src/worker_runtime.rs b/lib/apps/fabro-server/src/worker_runtime.rs similarity index 100% rename from lib/crates/fabro-server/src/worker_runtime.rs rename to lib/apps/fabro-server/src/worker_runtime.rs diff --git a/lib/crates/fabro-server/src/worker_token.rs b/lib/apps/fabro-server/src/worker_token.rs similarity index 100% rename from lib/crates/fabro-server/src/worker_token.rs rename to lib/apps/fabro-server/src/worker_token.rs diff --git a/lib/crates/fabro-server/tests/fixtures/spa/images/favicon.svg b/lib/apps/fabro-server/tests/fixtures/spa/images/favicon.svg similarity index 100% rename from lib/crates/fabro-server/tests/fixtures/spa/images/favicon.svg rename to lib/apps/fabro-server/tests/fixtures/spa/images/favicon.svg diff --git a/lib/crates/fabro-server/tests/fixtures/spa/index.html b/lib/apps/fabro-server/tests/fixtures/spa/index.html similarity index 100% rename from lib/crates/fabro-server/tests/fixtures/spa/index.html rename to lib/apps/fabro-server/tests/fixtures/spa/index.html diff --git a/lib/crates/fabro-server/tests/it/api/auth_sessions.rs b/lib/apps/fabro-server/tests/it/api/auth_sessions.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/auth_sessions.rs rename to lib/apps/fabro-server/tests/it/api/auth_sessions.rs diff --git a/lib/crates/fabro-server/tests/it/api/automations.rs b/lib/apps/fabro-server/tests/it/api/automations.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/automations.rs rename to lib/apps/fabro-server/tests/it/api/automations.rs diff --git a/lib/crates/fabro-server/tests/it/api/cli_auth_token.rs b/lib/apps/fabro-server/tests/it/api/cli_auth_token.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/cli_auth_token.rs rename to lib/apps/fabro-server/tests/it/api/cli_auth_token.rs diff --git a/lib/crates/fabro-server/tests/it/api/compression.rs b/lib/apps/fabro-server/tests/it/api/compression.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/compression.rs rename to lib/apps/fabro-server/tests/it/api/compression.rs diff --git a/lib/crates/fabro-server/tests/it/api/docs.rs b/lib/apps/fabro-server/tests/it/api/docs.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/docs.rs rename to lib/apps/fabro-server/tests/it/api/docs.rs diff --git a/lib/crates/fabro-server/tests/it/api/environments.rs b/lib/apps/fabro-server/tests/it/api/environments.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/environments.rs rename to lib/apps/fabro-server/tests/it/api/environments.rs diff --git a/lib/crates/fabro-server/tests/it/api/events.rs b/lib/apps/fabro-server/tests/it/api/events.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/events.rs rename to lib/apps/fabro-server/tests/it/api/events.rs diff --git a/lib/crates/fabro-server/tests/it/api/install.rs b/lib/apps/fabro-server/tests/it/api/install.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/install.rs rename to lib/apps/fabro-server/tests/it/api/install.rs diff --git a/lib/crates/fabro-server/tests/it/api/install_openai_compatible.rs b/lib/apps/fabro-server/tests/it/api/install_openai_compatible.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/install_openai_compatible.rs rename to lib/apps/fabro-server/tests/it/api/install_openai_compatible.rs diff --git a/lib/crates/fabro-server/tests/it/api/mcp_servers.rs b/lib/apps/fabro-server/tests/it/api/mcp_servers.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/mcp_servers.rs rename to lib/apps/fabro-server/tests/it/api/mcp_servers.rs diff --git a/lib/crates/fabro-server/tests/it/api/mod.rs b/lib/apps/fabro-server/tests/it/api/mod.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/mod.rs rename to lib/apps/fabro-server/tests/it/api/mod.rs diff --git a/lib/crates/fabro-server/tests/it/api/routing.rs b/lib/apps/fabro-server/tests/it/api/routing.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/routing.rs rename to lib/apps/fabro-server/tests/it/api/routing.rs diff --git a/lib/crates/fabro-server/tests/it/api/run_files.rs b/lib/apps/fabro-server/tests/it/api/run_files.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/run_files.rs rename to lib/apps/fabro-server/tests/it/api/run_files.rs diff --git a/lib/crates/fabro-server/tests/it/api/runs.rs b/lib/apps/fabro-server/tests/it/api/runs.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/runs.rs rename to lib/apps/fabro-server/tests/it/api/runs.rs diff --git a/lib/crates/fabro-server/tests/it/api/sandbox_vnc.rs b/lib/apps/fabro-server/tests/it/api/sandbox_vnc.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/sandbox_vnc.rs rename to lib/apps/fabro-server/tests/it/api/sandbox_vnc.rs diff --git a/lib/crates/fabro-server/tests/it/api/sessions.rs b/lib/apps/fabro-server/tests/it/api/sessions.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/sessions.rs rename to lib/apps/fabro-server/tests/it/api/sessions.rs diff --git a/lib/crates/fabro-server/tests/it/api/settings.rs b/lib/apps/fabro-server/tests/it/api/settings.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/settings.rs rename to lib/apps/fabro-server/tests/it/api/settings.rs diff --git a/lib/crates/fabro-server/tests/it/api/system.rs b/lib/apps/fabro-server/tests/it/api/system.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/system.rs rename to lib/apps/fabro-server/tests/it/api/system.rs diff --git a/lib/crates/fabro-server/tests/it/api/tcp.rs b/lib/apps/fabro-server/tests/it/api/tcp.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/tcp.rs rename to lib/apps/fabro-server/tests/it/api/tcp.rs diff --git a/lib/crates/fabro-server/tests/it/api/variables.rs b/lib/apps/fabro-server/tests/it/api/variables.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/api/variables.rs rename to lib/apps/fabro-server/tests/it/api/variables.rs diff --git a/lib/crates/fabro-server/tests/it/event_pagination.rs b/lib/apps/fabro-server/tests/it/event_pagination.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/event_pagination.rs rename to lib/apps/fabro-server/tests/it/event_pagination.rs diff --git a/lib/crates/fabro-server/tests/it/helpers.rs b/lib/apps/fabro-server/tests/it/helpers.rs similarity index 99% rename from lib/crates/fabro-server/tests/it/helpers.rs rename to lib/apps/fabro-server/tests/it/helpers.rs index 569f7e7d0..9ecf30bb8 100644 --- a/lib/crates/fabro-server/tests/it/helpers.rs +++ b/lib/apps/fabro-server/tests/it/helpers.rs @@ -142,7 +142,7 @@ pub(crate) fn repo_root() -> PathBuf { std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .ancestors() .nth(3) - .expect("fabro-server crate should be nested under lib/crates/fabro-server") + .expect("fabro-server crate should be nested under lib/apps/fabro-server") .to_path_buf() } diff --git a/lib/crates/fabro-server/tests/it/main.rs b/lib/apps/fabro-server/tests/it/main.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/main.rs rename to lib/apps/fabro-server/tests/it/main.rs diff --git a/lib/crates/fabro-server/tests/it/openapi_conformance.rs b/lib/apps/fabro-server/tests/it/openapi_conformance.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/openapi_conformance.rs rename to lib/apps/fabro-server/tests/it/openapi_conformance.rs diff --git a/lib/crates/fabro-server/tests/it/pagination.rs b/lib/apps/fabro-server/tests/it/pagination.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/pagination.rs rename to lib/apps/fabro-server/tests/it/pagination.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/archive.rs b/lib/apps/fabro-server/tests/it/scenario/archive.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/archive.rs rename to lib/apps/fabro-server/tests/it/scenario/archive.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/dry_run.rs b/lib/apps/fabro-server/tests/it/scenario/dry_run.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/dry_run.rs rename to lib/apps/fabro-server/tests/it/scenario/dry_run.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/lifecycle.rs b/lib/apps/fabro-server/tests/it/scenario/lifecycle.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/lifecycle.rs rename to lib/apps/fabro-server/tests/it/scenario/lifecycle.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/mod.rs b/lib/apps/fabro-server/tests/it/scenario/mod.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/mod.rs rename to lib/apps/fabro-server/tests/it/scenario/mod.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/run_completion.rs b/lib/apps/fabro-server/tests/it/scenario/run_completion.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/run_completion.rs rename to lib/apps/fabro-server/tests/it/scenario/run_completion.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/sse.rs b/lib/apps/fabro-server/tests/it/scenario/sse.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/sse.rs rename to lib/apps/fabro-server/tests/it/scenario/sse.rs diff --git a/lib/crates/fabro-server/tests/it/scenario/usage.rs b/lib/apps/fabro-server/tests/it/scenario/usage.rs similarity index 100% rename from lib/crates/fabro-server/tests/it/scenario/usage.rs rename to lib/apps/fabro-server/tests/it/scenario/usage.rs diff --git a/lib/crates/fabro-spa/Cargo.toml b/lib/apps/fabro-spa/Cargo.toml similarity index 100% rename from lib/crates/fabro-spa/Cargo.toml rename to lib/apps/fabro-spa/Cargo.toml diff --git a/lib/crates/fabro-spa/assets/.gitkeep b/lib/apps/fabro-spa/assets/.gitkeep similarity index 100% rename from lib/crates/fabro-spa/assets/.gitkeep rename to lib/apps/fabro-spa/assets/.gitkeep diff --git a/lib/crates/fabro-spa/src/lib.rs b/lib/apps/fabro-spa/src/lib.rs similarity index 100% rename from lib/crates/fabro-spa/src/lib.rs rename to lib/apps/fabro-spa/src/lib.rs diff --git a/lib/crates/fabro-acp/Cargo.toml b/lib/components/fabro-acp/Cargo.toml similarity index 88% rename from lib/crates/fabro-acp/Cargo.toml rename to lib/components/fabro-acp/Cargo.toml index 2fb62b2d9..3e1962dbe 100644 --- a/lib/crates/fabro-acp/Cargo.toml +++ b/lib/components/fabro-acp/Cargo.toml @@ -28,8 +28,8 @@ workspace = true agent-client-protocol.workspace = true agent-client-protocol-tokio.workspace = true fabro-sandbox = { path = "../fabro-sandbox", optional = true } -fabro-types = { path = "../fabro-types", optional = true } -fabro-util = { path = "../fabro-util" } +fabro-types = { path = "../../foundation/fabro-types", optional = true } +fabro-util = { path = "../../foundation/fabro-util" } serde_json.workspace = true shlex = "1" thiserror.workspace = true diff --git a/lib/crates/fabro-acp/src/command.rs b/lib/components/fabro-acp/src/command.rs similarity index 100% rename from lib/crates/fabro-acp/src/command.rs rename to lib/components/fabro-acp/src/command.rs diff --git a/lib/crates/fabro-acp/src/error.rs b/lib/components/fabro-acp/src/error.rs similarity index 100% rename from lib/crates/fabro-acp/src/error.rs rename to lib/components/fabro-acp/src/error.rs diff --git a/lib/crates/fabro-acp/src/lib.rs b/lib/components/fabro-acp/src/lib.rs similarity index 100% rename from lib/crates/fabro-acp/src/lib.rs rename to lib/components/fabro-acp/src/lib.rs diff --git a/lib/crates/fabro-acp/src/session.rs b/lib/components/fabro-acp/src/session.rs similarity index 100% rename from lib/crates/fabro-acp/src/session.rs rename to lib/components/fabro-acp/src/session.rs diff --git a/lib/crates/fabro-acp/src/test_support.rs b/lib/components/fabro-acp/src/test_support.rs similarity index 100% rename from lib/crates/fabro-acp/src/test_support.rs rename to lib/components/fabro-acp/src/test_support.rs diff --git a/lib/crates/fabro-acp/src/transport.rs b/lib/components/fabro-acp/src/transport.rs similarity index 100% rename from lib/crates/fabro-acp/src/transport.rs rename to lib/components/fabro-acp/src/transport.rs diff --git a/lib/crates/fabro-acp/tests/session.rs b/lib/components/fabro-acp/tests/session.rs similarity index 100% rename from lib/crates/fabro-acp/tests/session.rs rename to lib/components/fabro-acp/tests/session.rs diff --git a/lib/crates/fabro-agent/Cargo.toml b/lib/components/fabro-agent/Cargo.toml similarity index 76% rename from lib/crates/fabro-agent/Cargo.toml rename to lib/components/fabro-agent/Cargo.toml index 89f836440..4982a8667 100644 --- a/lib/crates/fabro-agent/Cargo.toml +++ b/lib/components/fabro-agent/Cargo.toml @@ -24,16 +24,16 @@ workspace = true [dependencies] clap.workspace = true anyhow.workspace = true -fabro-auth = { path = "../fabro-auth" } -fabro-config = { path = "../fabro-config", features = ["clap"] } -fabro-types = { path = "../fabro-types", features = ["clap"] } +fabro-auth = { path = "../../foundation/fabro-auth" } +fabro-config = { path = "../../foundation/fabro-config", features = ["clap"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["clap"] } fabro-llm = { path = "../fabro-llm" } -fabro-model = { path = "../fabro-model" } +fabro-model = { path = "../../foundation/fabro-model" } fabro-mcp = { path = "../fabro-mcp" } fabro-sandbox = { path = "../fabro-sandbox" } fabro-static.workspace = true -fabro-util = { path = "../fabro-util" } -fabro-vault = { path = "../fabro-vault" } +fabro-util = { path = "../../foundation/fabro-util" } +fabro-vault = { path = "../../foundation/fabro-vault" } fabro-http.workspace = true thiserror.workspace = true serde.workspace = true @@ -61,6 +61,6 @@ tokio = { workspace = true, features = ["test-util", "macros"] } tempfile = "3" paste = "1" fabro-sandbox = { path = "../fabro-sandbox", features = ["test-support"] } -fabro-macros = { path = "../fabro-macros" } +fabro-macros = { path = "../../foundation/fabro-macros" } fabro-test = { workspace = true } tracing-subscriber.workspace = true diff --git a/lib/crates/fabro-agent/README.md b/lib/components/fabro-agent/README.md similarity index 98% rename from lib/crates/fabro-agent/README.md rename to lib/components/fabro-agent/README.md index 5cac1be82..69764459e 100644 --- a/lib/crates/fabro-agent/README.md +++ b/lib/components/fabro-agent/README.md @@ -98,8 +98,6 @@ pub trait Sandbox: Send + Sync { ```rust pub struct SessionConfig { - pub max_turns: usize, // 0 = unlimited - pub max_tool_rounds_per_input: usize, // default: 200 pub default_command_timeout_ms: u64, // default: 10s pub max_command_timeout_ms: u64, // default: 600s pub enable_loop_detection: bool, // default: true @@ -134,7 +132,6 @@ let env = Arc::new(LocalSandbox::new( // 4. Configure the session let config = SessionConfig { - max_tool_rounds_per_input: 50, enable_loop_detection: true, user_instructions: Some("Always write tests first".into()), ..SessionConfig::default() diff --git a/lib/crates/fabro-agent/src/agent_profile.rs b/lib/components/fabro-agent/src/agent_profile.rs similarity index 100% rename from lib/crates/fabro-agent/src/agent_profile.rs rename to lib/components/fabro-agent/src/agent_profile.rs diff --git a/lib/crates/fabro-agent/src/apply_patch.lark b/lib/components/fabro-agent/src/apply_patch.lark similarity index 100% rename from lib/crates/fabro-agent/src/apply_patch.lark rename to lib/components/fabro-agent/src/apply_patch.lark diff --git a/lib/crates/fabro-agent/src/apply_patch.rs b/lib/components/fabro-agent/src/apply_patch.rs similarity index 100% rename from lib/crates/fabro-agent/src/apply_patch.rs rename to lib/components/fabro-agent/src/apply_patch.rs diff --git a/lib/crates/fabro-agent/src/cli.rs b/lib/components/fabro-agent/src/cli.rs similarity index 98% rename from lib/crates/fabro-agent/src/cli.rs rename to lib/components/fabro-agent/src/cli.rs index 7f34a3445..ddfdb3245 100644 --- a/lib/crates/fabro-agent/src/cli.rs +++ b/lib/components/fabro-agent/src/cli.rs @@ -1071,12 +1071,13 @@ mod tests { max_output: None, }), features: Some(SettingsModelFeatures { - tools: Some(true), - vision: Some(false), - reasoning: Some(false), - reasoning_effort: None, - prompt_cache: None, - sampling_params: None, + tools: Some(true), + vision: Some(false), + reasoning: Some(false), + reasoning_effort: None, + prompt_cache: None, + cache_control_breakpoints: None, + sampling_params: None, }), ..ModelCatalogSettings::default() }); @@ -1156,12 +1157,13 @@ mod tests { max_output: None, }), features: Some(SettingsModelFeatures { - tools: Some(true), - vision: Some(false), - reasoning: Some(false), - reasoning_effort: None, - prompt_cache: None, - sampling_params: None, + tools: Some(true), + vision: Some(false), + reasoning: Some(false), + reasoning_effort: None, + prompt_cache: None, + cache_control_breakpoints: None, + sampling_params: None, }), ..ModelCatalogSettings::default() }); diff --git a/lib/crates/fabro-agent/src/compaction.rs b/lib/components/fabro-agent/src/compaction.rs similarity index 100% rename from lib/crates/fabro-agent/src/compaction.rs rename to lib/components/fabro-agent/src/compaction.rs diff --git a/lib/crates/fabro-agent/src/config.rs b/lib/components/fabro-agent/src/config.rs similarity index 96% rename from lib/crates/fabro-agent/src/config.rs rename to lib/components/fabro-agent/src/config.rs index c54d49f01..bc22fd75c 100644 --- a/lib/crates/fabro-agent/src/config.rs +++ b/lib/components/fabro-agent/src/config.rs @@ -106,8 +106,6 @@ pub struct ToolSecrets { #[derive(Clone)] pub struct SessionOptions { - pub max_turns: usize, - pub max_tool_rounds_per_input: usize, pub default_command_timeout_ms: u64, pub max_command_timeout_ms: u64, pub reasoning_effort: Option, @@ -149,8 +147,6 @@ pub struct SessionOptions { impl std::fmt::Debug for SessionOptions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SessionOptions") - .field("max_turns", &self.max_turns) - .field("max_tool_rounds_per_input", &self.max_tool_rounds_per_input) .field( "default_command_timeout_ms", &self.default_command_timeout_ms, @@ -196,8 +192,6 @@ impl std::fmt::Debug for SessionOptions { impl Default for SessionOptions { fn default() -> Self { Self { - max_turns: 0, - max_tool_rounds_per_input: 0, default_command_timeout_ms: 10_000, max_command_timeout_ms: 600_000, max_tokens: None, @@ -280,8 +274,6 @@ mod tests { #[test] fn default_config_values() { let config = SessionOptions::default(); - assert_eq!(config.max_turns, 0); - assert_eq!(config.max_tool_rounds_per_input, 0); assert_eq!(config.default_command_timeout_ms, 10_000); assert_eq!(config.max_command_timeout_ms, 600_000); assert!(config.reasoning_effort.is_none()); @@ -328,13 +320,10 @@ mod tests { #[test] fn config_with_custom_values() { let config = SessionOptions { - max_turns: 50, reasoning_effort: Some(ReasoningEffort::High), ..Default::default() }; - assert_eq!(config.max_turns, 50); assert_eq!(config.reasoning_effort, Some(ReasoningEffort::High)); - assert_eq!(config.max_tool_rounds_per_input, 0); } #[test] diff --git a/lib/crates/fabro-agent/src/context_window.rs b/lib/components/fabro-agent/src/context_window.rs similarity index 100% rename from lib/crates/fabro-agent/src/context_window.rs rename to lib/components/fabro-agent/src/context_window.rs diff --git a/lib/crates/fabro-agent/src/docker_sandbox.rs b/lib/components/fabro-agent/src/docker_sandbox.rs similarity index 100% rename from lib/crates/fabro-agent/src/docker_sandbox.rs rename to lib/components/fabro-agent/src/docker_sandbox.rs diff --git a/lib/crates/fabro-agent/src/error.rs b/lib/components/fabro-agent/src/error.rs similarity index 100% rename from lib/crates/fabro-agent/src/error.rs rename to lib/components/fabro-agent/src/error.rs diff --git a/lib/crates/fabro-agent/src/event.rs b/lib/components/fabro-agent/src/event.rs similarity index 100% rename from lib/crates/fabro-agent/src/event.rs rename to lib/components/fabro-agent/src/event.rs diff --git a/lib/crates/fabro-agent/src/file_tracker.rs b/lib/components/fabro-agent/src/file_tracker.rs similarity index 100% rename from lib/crates/fabro-agent/src/file_tracker.rs rename to lib/components/fabro-agent/src/file_tracker.rs diff --git a/lib/crates/fabro-agent/src/history.rs b/lib/components/fabro-agent/src/history.rs similarity index 100% rename from lib/crates/fabro-agent/src/history.rs rename to lib/components/fabro-agent/src/history.rs diff --git a/lib/crates/fabro-agent/src/lib.rs b/lib/components/fabro-agent/src/lib.rs similarity index 100% rename from lib/crates/fabro-agent/src/lib.rs rename to lib/components/fabro-agent/src/lib.rs diff --git a/lib/crates/fabro-agent/src/local_sandbox.rs b/lib/components/fabro-agent/src/local_sandbox.rs similarity index 100% rename from lib/crates/fabro-agent/src/local_sandbox.rs rename to lib/components/fabro-agent/src/local_sandbox.rs diff --git a/lib/crates/fabro-agent/src/loop_detection.rs b/lib/components/fabro-agent/src/loop_detection.rs similarity index 100% rename from lib/crates/fabro-agent/src/loop_detection.rs rename to lib/components/fabro-agent/src/loop_detection.rs diff --git a/lib/crates/fabro-agent/src/mcp_integration.rs b/lib/components/fabro-agent/src/mcp_integration.rs similarity index 100% rename from lib/crates/fabro-agent/src/mcp_integration.rs rename to lib/components/fabro-agent/src/mcp_integration.rs diff --git a/lib/crates/fabro-agent/src/memory.rs b/lib/components/fabro-agent/src/memory.rs similarity index 100% rename from lib/crates/fabro-agent/src/memory.rs rename to lib/components/fabro-agent/src/memory.rs diff --git a/lib/crates/fabro-agent/src/profiles/anthropic.rs b/lib/components/fabro-agent/src/profiles/anthropic.rs similarity index 100% rename from lib/crates/fabro-agent/src/profiles/anthropic.rs rename to lib/components/fabro-agent/src/profiles/anthropic.rs diff --git a/lib/crates/fabro-agent/src/profiles/gemini.rs b/lib/components/fabro-agent/src/profiles/gemini.rs similarity index 100% rename from lib/crates/fabro-agent/src/profiles/gemini.rs rename to lib/components/fabro-agent/src/profiles/gemini.rs diff --git a/lib/crates/fabro-agent/src/profiles/mod.rs b/lib/components/fabro-agent/src/profiles/mod.rs similarity index 100% rename from lib/crates/fabro-agent/src/profiles/mod.rs rename to lib/components/fabro-agent/src/profiles/mod.rs diff --git a/lib/crates/fabro-agent/src/profiles/openai.rs b/lib/components/fabro-agent/src/profiles/openai.rs similarity index 96% rename from lib/crates/fabro-agent/src/profiles/openai.rs rename to lib/components/fabro-agent/src/profiles/openai.rs index ffc02d90c..868d2e2fe 100644 --- a/lib/crates/fabro-agent/src/profiles/openai.rs +++ b/lib/components/fabro-agent/src/profiles/openai.rs @@ -229,6 +229,11 @@ and focused on the task. {file_edit_failure_guidance} - Do not `git commit` your changes or create new git branches unless explicitly requested. +# Planning + +If you create a checklist or task list, you update item statuses incrementally as each item is \ +completed rather than marking every item done only at the end. + # Validating Your Work If the codebase has tests or the ability to build or run, consider using them to verify your \ @@ -336,6 +341,17 @@ mod tests { assert!(prompt.contains("existing code conventions")); } + #[test] + fn openai_system_prompt_matches_codex_incremental_plan_guidance() { + let profile = OpenAiProfile::new("gpt-5.5"); + let env = MockSandbox::linux(); + let prompt = profile.build_system_prompt(&env, &EnvContext::default(), &[], None, &[]); + assert!(prompt.contains( + "update item statuses incrementally as each item is completed rather than \ + marking every item done only at the end" + )); + } + #[test] fn openai_system_prompt_includes_memory() { let profile = OpenAiProfile::new("o3-mini"); diff --git a/lib/crates/fabro-agent/src/question_tools.rs b/lib/components/fabro-agent/src/question_tools.rs similarity index 100% rename from lib/crates/fabro-agent/src/question_tools.rs rename to lib/components/fabro-agent/src/question_tools.rs diff --git a/lib/crates/fabro-agent/src/read_before_write_sandbox.rs b/lib/components/fabro-agent/src/read_before_write_sandbox.rs similarity index 100% rename from lib/crates/fabro-agent/src/read_before_write_sandbox.rs rename to lib/components/fabro-agent/src/read_before_write_sandbox.rs diff --git a/lib/crates/fabro-agent/src/sandbox.rs b/lib/components/fabro-agent/src/sandbox.rs similarity index 100% rename from lib/crates/fabro-agent/src/sandbox.rs rename to lib/components/fabro-agent/src/sandbox.rs diff --git a/lib/crates/fabro-agent/src/session.rs b/lib/components/fabro-agent/src/session.rs similarity index 98% rename from lib/crates/fabro-agent/src/session.rs rename to lib/components/fabro-agent/src/session.rs index a169d3645..b36c8a639 100644 --- a/lib/crates/fabro-agent/src/session.rs +++ b/lib/components/fabro-agent/src/session.rs @@ -1224,10 +1224,6 @@ impl Session { self.config.speed = speed; } - pub const fn set_max_turns(&mut self, max_turns: usize) { - self.config.max_turns = max_turns; - } - #[must_use] pub const fn history(&self) -> &History { &self.history @@ -1239,7 +1235,14 @@ impl Session { } pub async fn process_input(&mut self, input: &str) -> Result<(), Error> { - self.process_input_with_runtime(input, AgentToolRuntime::default()) + self.process_input_with_output(input).await.map(drop) + } + + pub(crate) async fn process_input_with_output( + &mut self, + input: &str, + ) -> Result, Error> { + self.process_input_with_runtime_and_output(input, AgentToolRuntime::default()) .await } @@ -1266,6 +1269,16 @@ impl Session { input: &str, agent_tool_runtime: AgentToolRuntime, ) -> Result<(), Error> { + self.process_input_with_runtime_and_output(input, agent_tool_runtime) + .await + .map(drop) + } + + async fn process_input_with_runtime_and_output( + &mut self, + input: &str, + agent_tool_runtime: AgentToolRuntime, + ) -> Result, Error> { let mut timing = SessionInputTiming::default(); let mut usage = TokenCounts::default(); let mut cost = None; @@ -1357,7 +1370,7 @@ impl Session { timing: &mut SessionInputTiming, usage_accumulator: &mut TokenCounts, cost_accumulator: &mut Option, - ) -> Result<(), Error> { + ) -> Result, Error> { const STREAM_CONSUME_RETRIES: usize = 3; if self.state == SessionState::Closed { @@ -1395,8 +1408,6 @@ impl Session { text: expanded_input.clone(), }); - let mut round_count: usize = 0; - loop { // Top-of-loop: if the previous round's interrupt token fired, // swap in a fresh one before draining and rebuilding state. @@ -1451,26 +1462,6 @@ impl Session { self.wait_for_steer_if_needed().await?; self.drain_steering(); - // Check max_tool_rounds_per_input - if self.config.max_tool_rounds_per_input > 0 - && round_count >= self.config.max_tool_rounds_per_input - { - self.event_emitter - .emit(self.id.clone(), AgentEvent::TurnLimitReached { - max_turns: self.config.max_tool_rounds_per_input, - }); - break; - } - - // Check max_turns - if self.config.max_turns > 0 && self.history.turns().len() >= self.config.max_turns { - self.event_emitter - .emit(self.id.clone(), AgentEvent::TurnLimitReached { - max_turns: self.config.max_turns, - }); - break; - } - // Snapshot the per-round token; it stays stable for this iteration. let round_token = self .round_token @@ -1833,11 +1824,9 @@ impl Session { if should_continue { continue; } - break; + return Ok((!text.trim().is_empty()).then_some(text)); } - round_count += 1; - // Build a composite cancellation token covering both terminal // cancel and round (steer) interrupt. Tools observe it // cooperatively — they synthesize "Cancelled" results rather @@ -1919,8 +1908,6 @@ impl Session { .emit(self.id.clone(), AgentEvent::LoopDetected); } } - - Ok(()) } async fn compact_if_needed(&mut self) { @@ -2357,8 +2344,9 @@ mod tests { #[tokio::test] async fn text_only_response_natural_completion() { let mut session = make_session(vec![text_response("Hello there!")]).await; - session.process_input("Hi").await.unwrap(); + let output = session.process_input_with_output("Hi").await.unwrap(); + assert_eq!(output.as_deref(), Some("Hello there!")); assert_eq!(session.state(), SessionState::Idle); let turns = session.history().turns(); // UserTurn + AssistantTurn = 2 @@ -2543,55 +2531,12 @@ mod tests { } #[tokio::test] - async fn max_tool_rounds_enforced() { - let mut registry = ToolRegistry::new(); - registry.register(make_echo_tool()); + async fn empty_natural_completion_has_no_output() { + let mut session = make_session(vec![text_response(" ")]).await; - // Respond with tool calls indefinitely - let responses = vec![ - tool_call_response("echo", "call_1", serde_json::json!({"text": "a"})), - tool_call_response("echo", "call_2", serde_json::json!({"text": "b"})), - tool_call_response("echo", "call_3", serde_json::json!({"text": "c"})), - ]; + let output = session.process_input_with_output("Hi").await.unwrap(); - let config = SessionOptions { - max_tool_rounds_per_input: 2, - enable_loop_detection: false, - ..Default::default() - }; - - let mut session = make_session_with_tools_and_config(responses, registry, config).await; - session.process_input("Keep using tools").await.unwrap(); - - // Should stop after 2 rounds: User + (Asst+ToolResult) * 2 = 5 turns - assert_eq!(session.state(), SessionState::Idle); - let turns = session.history().turns(); - assert_eq!(turns.len(), 5); - } - - #[tokio::test] - async fn max_turns_enforced() { - let responses = vec![ - text_response("first"), - text_response("second"), - text_response("should not reach"), - ]; - - let config = SessionOptions { - max_turns: 3, - ..Default::default() - }; - - let mut session = make_session_with_config(responses, config).await; - - // First input: adds User + Assistant = 2 turns - session.process_input("one").await.unwrap(); - assert_eq!(session.history().turns().len(), 2); - - // Second input: adds User (now 3 turns), then max_turns check triggers - session.process_input("two").await.unwrap(); - // Should have 3 turns total (User + Asst + User), max_turns hit before LLM call - assert_eq!(session.history().turns().len(), 3); + assert_eq!(output, None); } #[tokio::test] diff --git a/lib/crates/fabro-agent/src/skills.rs b/lib/components/fabro-agent/src/skills.rs similarity index 100% rename from lib/crates/fabro-agent/src/skills.rs rename to lib/components/fabro-agent/src/skills.rs diff --git a/lib/crates/fabro-agent/src/subagent.rs b/lib/components/fabro-agent/src/subagent.rs similarity index 97% rename from lib/crates/fabro-agent/src/subagent.rs rename to lib/components/fabro-agent/src/subagent.rs index f0aa1c106..3cb2af0da 100644 --- a/lib/crates/fabro-agent/src/subagent.rs +++ b/lib/components/fabro-agent/src/subagent.rs @@ -13,7 +13,7 @@ use crate::error::{Error, InterruptReason}; use crate::session::{Session, SessionShutdownReason}; use crate::tool_registry::{RegisteredTool, ToolSource}; use crate::tools::required_str; -use crate::types::{AgentEvent, Message, SessionEvent}; +use crate::types::{AgentEvent, SessionEvent}; pub type SessionFactory = Arc Session + Send + Sync>; @@ -263,15 +263,18 @@ impl SubAgentSupervisor { let _ = start_rx.await; let result = async { session.initialize().await?; - session.process_input(&task_prompt_for_spawn).await?; + let output = session + .process_input_with_output(&task_prompt_for_spawn) + .await? + .ok_or_else(|| { + Error::InvalidState( + "Subagent completed without a non-empty final response".to_string(), + ) + })?; let turns = session.history().turns(); - let last_text = turns.iter().rev().find_map(|turn| match turn { - Message::Assistant { content, .. } => Some(content.clone()), - _ => None, - }); Ok(SubAgentResult { - output: last_text.unwrap_or_default(), - success: true, + output, + success: true, turns_used: turns.len(), }) } @@ -662,18 +665,6 @@ pub fn make_spawn_agent_tool( "task": { "type": "string", "description": "The task description for the subagent" - }, - "working_dir": { - "type": "string", - "description": "Working directory for the subagent" - }, - "model": { - "type": "string", - "description": "Model to use for the subagent" - }, - "max_turns": { - "type": "integer", - "description": "Maximum number of turns for the subagent" } }, "required": ["task"] @@ -685,13 +676,6 @@ pub fn make_spawn_agent_tool( Box::pin(async move { let task = required_str(&args, "task")?; - // Extract optional max_turns parameter - let max_turns = args - .get("max_turns") - .and_then(serde_json::Value::as_u64) - .map(|v| usize::try_from(v).unwrap_or(usize::MAX)); - - // Note: working_dir and model require session factory changes to wire through let mut session = session_factory(); // Inherit the parent agent's root session ID so todo tools // that scope by root (e.g. Anthropic tasks) share one list @@ -699,9 +683,6 @@ pub fn make_spawn_agent_tool( if let Some(root) = ctx.root_session_id.as_ref().or(ctx.session_id.as_ref()) { session.set_root_session_id(root.clone()); } - // Default subagent max_turns is 0 (unlimited) per spec (overridable via - // parameter) - session.set_max_turns(max_turns.unwrap_or(0)); supervisor .spawn(session, task.to_string(), current_depth) .map_err(|e| e.to_string()) @@ -1025,7 +1006,11 @@ mod tests { let spawn_tool = make_spawn_agent_tool(manager.clone(), factory, 0); assert_eq!(spawn_tool.definition.name, "spawn_agent"); - assert!(spawn_tool.definition.parameters["properties"]["task"].is_object()); + let spawn_properties = spawn_tool.definition.parameters["properties"] + .as_object() + .unwrap(); + assert_eq!(spawn_properties.len(), 1); + assert!(spawn_properties["task"].is_object()); let spawn_required = spawn_tool.definition.parameters["required"] .as_array() .unwrap(); @@ -1237,6 +1222,21 @@ mod tests { )); } + #[tokio::test] + async fn empty_final_response_is_not_reported_as_success() { + let manager = SubAgentSupervisor::new(3); + let session = make_session(vec![text_response("")]).await; + let agent_id = manager.spawn(session, "Do something".into(), 0).unwrap(); + + let result = manager.wait(&agent_id).await; + + assert!( + matches!(result, Err(Error::InvalidState(message)) if message.contains( + "without a non-empty final response" + )) + ); + } + #[tokio::test] async fn send_input_to_completed_agent_errors() { let manager = SubAgentSupervisor::new(3); diff --git a/lib/crates/fabro-agent/src/task_reminder.rs b/lib/components/fabro-agent/src/task_reminder.rs similarity index 100% rename from lib/crates/fabro-agent/src/task_reminder.rs rename to lib/components/fabro-agent/src/task_reminder.rs diff --git a/lib/crates/fabro-agent/src/test_support.rs b/lib/components/fabro-agent/src/test_support.rs similarity index 100% rename from lib/crates/fabro-agent/src/test_support.rs rename to lib/components/fabro-agent/src/test_support.rs diff --git a/lib/crates/fabro-agent/src/todo_runtime.rs b/lib/components/fabro-agent/src/todo_runtime.rs similarity index 100% rename from lib/crates/fabro-agent/src/todo_runtime.rs rename to lib/components/fabro-agent/src/todo_runtime.rs diff --git a/lib/crates/fabro-agent/src/todo_tools.rs b/lib/components/fabro-agent/src/todo_tools.rs similarity index 100% rename from lib/crates/fabro-agent/src/todo_tools.rs rename to lib/components/fabro-agent/src/todo_tools.rs diff --git a/lib/crates/fabro-agent/src/tool_execution.rs b/lib/components/fabro-agent/src/tool_execution.rs similarity index 100% rename from lib/crates/fabro-agent/src/tool_execution.rs rename to lib/components/fabro-agent/src/tool_execution.rs diff --git a/lib/crates/fabro-agent/src/tool_permissions.rs b/lib/components/fabro-agent/src/tool_permissions.rs similarity index 100% rename from lib/crates/fabro-agent/src/tool_permissions.rs rename to lib/components/fabro-agent/src/tool_permissions.rs diff --git a/lib/crates/fabro-agent/src/tool_registry.rs b/lib/components/fabro-agent/src/tool_registry.rs similarity index 100% rename from lib/crates/fabro-agent/src/tool_registry.rs rename to lib/components/fabro-agent/src/tool_registry.rs diff --git a/lib/crates/fabro-agent/src/tools.rs b/lib/components/fabro-agent/src/tools.rs similarity index 100% rename from lib/crates/fabro-agent/src/tools.rs rename to lib/components/fabro-agent/src/tools.rs diff --git a/lib/crates/fabro-agent/src/truncation.rs b/lib/components/fabro-agent/src/truncation.rs similarity index 100% rename from lib/crates/fabro-agent/src/truncation.rs rename to lib/components/fabro-agent/src/truncation.rs diff --git a/lib/crates/fabro-agent/src/types.rs b/lib/components/fabro-agent/src/types.rs similarity index 99% rename from lib/crates/fabro-agent/src/types.rs rename to lib/components/fabro-agent/src/types.rs index fc28c6f7a..a39600549 100644 --- a/lib/crates/fabro-agent/src/types.rs +++ b/lib/components/fabro-agent/src/types.rs @@ -284,9 +284,6 @@ pub enum AgentEvent { details: serde_json::Value, }, LoopDetected, - TurnLimitReached { - max_turns: usize, - }, SteeringInjected { text: String, /// Principal that authored the steer. Lifted to top-level @@ -469,9 +466,6 @@ impl AgentEvent { Self::LoopDetected => { warn!(session_id, "Loop detected"); } - Self::TurnLimitReached { max_turns } => { - warn!(session_id, max_turns, "Message limit reached"); - } Self::SteeringInjected { text, .. } => { debug!(session_id, text_len = text.len(), "Steering injected"); } diff --git a/lib/crates/fabro-agent/tests/it/compaction.rs b/lib/components/fabro-agent/tests/it/compaction.rs similarity index 99% rename from lib/crates/fabro-agent/tests/it/compaction.rs rename to lib/components/fabro-agent/tests/it/compaction.rs index 084deae6c..14cb88d5e 100644 --- a/lib/crates/fabro-agent/tests/it/compaction.rs +++ b/lib/components/fabro-agent/tests/it/compaction.rs @@ -53,7 +53,6 @@ fn make_openai_session(cwd: &Path, base_url: String, api_key: String) -> Session let profile: Arc = Arc::new(OpenAiProfile::new(MODEL)); let sandbox = Arc::new(LocalSandbox::new(cwd.to_path_buf())); let options = SessionOptions { - max_turns: 20, enable_context_compaction: true, compaction_threshold_percent: 80, compaction_preserve_turns: 6, diff --git a/lib/crates/fabro-agent/tests/it/guardrails.rs b/lib/components/fabro-agent/tests/it/guardrails.rs similarity index 100% rename from lib/crates/fabro-agent/tests/it/guardrails.rs rename to lib/components/fabro-agent/tests/it/guardrails.rs diff --git a/lib/crates/fabro-agent/tests/it/main.rs b/lib/components/fabro-agent/tests/it/main.rs similarity index 100% rename from lib/crates/fabro-agent/tests/it/main.rs rename to lib/components/fabro-agent/tests/it/main.rs diff --git a/lib/crates/fabro-agent/tests/it/parity_matrix.rs b/lib/components/fabro-agent/tests/it/parity_matrix.rs similarity index 99% rename from lib/crates/fabro-agent/tests/it/parity_matrix.rs rename to lib/components/fabro-agent/tests/it/parity_matrix.rs index fb0e92bdf..2bdf14a51 100644 --- a/lib/crates/fabro-agent/tests/it/parity_matrix.rs +++ b/lib/components/fabro-agent/tests/it/parity_matrix.rs @@ -125,11 +125,13 @@ async fn make_session( profile.register_subagent_tools(supervisor.clone(), factory, 0); let profile: Arc = Arc::from(profile); - let config = SessionOptions { - max_turns: 20, - ..SessionOptions::default() - }; - Session::new(client, profile, env, config, Some(supervisor)) + Session::new( + client, + profile, + env, + SessionOptions::default(), + Some(supervisor), + ) } async fn make_session_with_config( @@ -314,20 +316,17 @@ async fn openai_compatible_twin_uses_json_edit_file_tool() { "old_string": "old", "new_string": "new" }), - )), + )) + .text("Done."), ) .load(twin_openai().await) .await; - let config = SessionOptions { - max_turns: 2, - ..SessionOptions::default() - }; let mut session = make_openai_compatible_twin_session( ProviderId::new("litellm"), "gpt-5.4-mini", tmp.path(), - config, + SessionOptions::default(), &twin, ); session.initialize().await.unwrap(); @@ -766,7 +765,6 @@ macro_rules! reasoning_effort_tests { async fn $test_name() { let tmp = tempfile::tempdir().expect("failed to create tempdir"); let config = SessionOptions { - max_turns: 20, reasoning_effort: Some(fabro_llm::types::ReasoningEffort::Low), ..SessionOptions::default() }; @@ -845,7 +843,6 @@ macro_rules! loop_detection_tests { async fn $test_name() { let tmp = tempfile::tempdir().expect("failed to create tempdir"); let config = SessionOptions { - max_turns: 20, loop_detection_window: 3, ..SessionOptions::default() }; diff --git a/lib/crates/fabro-automation/Cargo.toml b/lib/components/fabro-automation/Cargo.toml similarity index 92% rename from lib/crates/fabro-automation/Cargo.toml rename to lib/components/fabro-automation/Cargo.toml index 6284862e7..9b1b75575 100644 --- a/lib/crates/fabro-automation/Cargo.toml +++ b/lib/components/fabro-automation/Cargo.toml @@ -15,7 +15,7 @@ workspace = true [dependencies] chrono.workspace = true croner.workspace = true -fabro-db = { path = "../fabro-db" } +fabro-db = { path = "../../foundation/fabro-db" } hex.workspace = true serde.workspace = true sha2.workspace = true diff --git a/lib/crates/fabro-automation/migrations/2026071101_file_definitions_to_sqlite.rs b/lib/components/fabro-automation/migrations/2026071101_file_definitions_to_sqlite.rs similarity index 100% rename from lib/crates/fabro-automation/migrations/2026071101_file_definitions_to_sqlite.rs rename to lib/components/fabro-automation/migrations/2026071101_file_definitions_to_sqlite.rs diff --git a/lib/crates/fabro-automation/src/error.rs b/lib/components/fabro-automation/src/error.rs similarity index 100% rename from lib/crates/fabro-automation/src/error.rs rename to lib/components/fabro-automation/src/error.rs diff --git a/lib/crates/fabro-automation/src/id.rs b/lib/components/fabro-automation/src/id.rs similarity index 100% rename from lib/crates/fabro-automation/src/id.rs rename to lib/components/fabro-automation/src/id.rs diff --git a/lib/crates/fabro-automation/src/lib.rs b/lib/components/fabro-automation/src/lib.rs similarity index 100% rename from lib/crates/fabro-automation/src/lib.rs rename to lib/components/fabro-automation/src/lib.rs diff --git a/lib/crates/fabro-automation/src/migrations.rs b/lib/components/fabro-automation/src/migrations.rs similarity index 100% rename from lib/crates/fabro-automation/src/migrations.rs rename to lib/components/fabro-automation/src/migrations.rs diff --git a/lib/crates/fabro-automation/src/model.rs b/lib/components/fabro-automation/src/model.rs similarity index 100% rename from lib/crates/fabro-automation/src/model.rs rename to lib/components/fabro-automation/src/model.rs diff --git a/lib/crates/fabro-automation/src/store.rs b/lib/components/fabro-automation/src/store.rs similarity index 100% rename from lib/crates/fabro-automation/src/store.rs rename to lib/components/fabro-automation/src/store.rs diff --git a/lib/crates/fabro-automation/tests/store.rs b/lib/components/fabro-automation/tests/store.rs similarity index 100% rename from lib/crates/fabro-automation/tests/store.rs rename to lib/components/fabro-automation/tests/store.rs diff --git a/lib/crates/fabro-checkpoint/Cargo.toml b/lib/components/fabro-checkpoint/Cargo.toml similarity index 82% rename from lib/crates/fabro-checkpoint/Cargo.toml rename to lib/components/fabro-checkpoint/Cargo.toml index 3734d01fb..4578f5345 100644 --- a/lib/crates/fabro-checkpoint/Cargo.toml +++ b/lib/components/fabro-checkpoint/Cargo.toml @@ -14,9 +14,9 @@ doctest = false workspace = true [dependencies] -fabro-config = { path = "../fabro-config" } +fabro-config = { path = "../../foundation/fabro-config" } fabro-store = { path = "../fabro-store" } -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } git2.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/lib/crates/fabro-checkpoint/src/author.rs b/lib/components/fabro-checkpoint/src/author.rs similarity index 100% rename from lib/crates/fabro-checkpoint/src/author.rs rename to lib/components/fabro-checkpoint/src/author.rs diff --git a/lib/crates/fabro-checkpoint/src/branch.rs b/lib/components/fabro-checkpoint/src/branch.rs similarity index 100% rename from lib/crates/fabro-checkpoint/src/branch.rs rename to lib/components/fabro-checkpoint/src/branch.rs diff --git a/lib/crates/fabro-checkpoint/src/error.rs b/lib/components/fabro-checkpoint/src/error.rs similarity index 100% rename from lib/crates/fabro-checkpoint/src/error.rs rename to lib/components/fabro-checkpoint/src/error.rs diff --git a/lib/crates/fabro-checkpoint/src/git.rs b/lib/components/fabro-checkpoint/src/git.rs similarity index 100% rename from lib/crates/fabro-checkpoint/src/git.rs rename to lib/components/fabro-checkpoint/src/git.rs diff --git a/lib/crates/fabro-checkpoint/src/lib.rs b/lib/components/fabro-checkpoint/src/lib.rs similarity index 100% rename from lib/crates/fabro-checkpoint/src/lib.rs rename to lib/components/fabro-checkpoint/src/lib.rs diff --git a/lib/crates/fabro-checkpoint/src/trailer.rs b/lib/components/fabro-checkpoint/src/trailer.rs similarity index 100% rename from lib/crates/fabro-checkpoint/src/trailer.rs rename to lib/components/fabro-checkpoint/src/trailer.rs diff --git a/lib/crates/fabro-dump/Cargo.toml b/lib/components/fabro-dump/Cargo.toml similarity index 75% rename from lib/crates/fabro-dump/Cargo.toml rename to lib/components/fabro-dump/Cargo.toml index d45a6beb9..a630d7cd9 100644 --- a/lib/crates/fabro-dump/Cargo.toml +++ b/lib/components/fabro-dump/Cargo.toml @@ -15,11 +15,11 @@ workspace = true anyhow.workspace = true bytes.workspace = true fabro-store = { path = "../fabro-store" } -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } futures.workspace = true serde.workspace = true serde_json.workspace = true [dev-dependencies] chrono = { workspace = true, features = ["serde"] } -fabro-types = { path = "../fabro-types", features = ["test-support"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["test-support"] } diff --git a/lib/crates/fabro-dump/src/lib.rs b/lib/components/fabro-dump/src/lib.rs similarity index 100% rename from lib/crates/fabro-dump/src/lib.rs rename to lib/components/fabro-dump/src/lib.rs diff --git a/lib/crates/fabro-environment/Cargo.toml b/lib/components/fabro-environment/Cargo.toml similarity index 80% rename from lib/crates/fabro-environment/Cargo.toml rename to lib/components/fabro-environment/Cargo.toml index ab06a442f..0c153ab04 100644 --- a/lib/crates/fabro-environment/Cargo.toml +++ b/lib/components/fabro-environment/Cargo.toml @@ -14,9 +14,9 @@ workspace = true [dependencies] chrono.workspace = true -fabro-db = { path = "../fabro-db" } -fabro-config = { path = "../fabro-config" } -fabro-types = { path = "../fabro-types" } +fabro-db = { path = "../../foundation/fabro-db" } +fabro-config = { path = "../../foundation/fabro-config" } +fabro-types = { path = "../../foundation/fabro-types" } hex.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/lib/crates/fabro-environment/src/error.rs b/lib/components/fabro-environment/src/error.rs similarity index 100% rename from lib/crates/fabro-environment/src/error.rs rename to lib/components/fabro-environment/src/error.rs diff --git a/lib/crates/fabro-environment/src/id.rs b/lib/components/fabro-environment/src/id.rs similarity index 100% rename from lib/crates/fabro-environment/src/id.rs rename to lib/components/fabro-environment/src/id.rs diff --git a/lib/crates/fabro-environment/src/lib.rs b/lib/components/fabro-environment/src/lib.rs similarity index 100% rename from lib/crates/fabro-environment/src/lib.rs rename to lib/components/fabro-environment/src/lib.rs diff --git a/lib/crates/fabro-environment/src/model.rs b/lib/components/fabro-environment/src/model.rs similarity index 100% rename from lib/crates/fabro-environment/src/model.rs rename to lib/components/fabro-environment/src/model.rs diff --git a/lib/crates/fabro-environment/src/store.rs b/lib/components/fabro-environment/src/store.rs similarity index 100% rename from lib/crates/fabro-environment/src/store.rs rename to lib/components/fabro-environment/src/store.rs diff --git a/lib/crates/fabro-environment/tests/store.rs b/lib/components/fabro-environment/tests/store.rs similarity index 100% rename from lib/crates/fabro-environment/tests/store.rs rename to lib/components/fabro-environment/tests/store.rs diff --git a/lib/crates/fabro-github/Cargo.toml b/lib/components/fabro-github/Cargo.toml similarity index 86% rename from lib/crates/fabro-github/Cargo.toml rename to lib/components/fabro-github/Cargo.toml index b953ee36c..9db73130e 100644 --- a/lib/crates/fabro-github/Cargo.toml +++ b/lib/components/fabro-github/Cargo.toml @@ -19,7 +19,7 @@ serde_json.workspace = true fabro-http.workspace = true fabro-redact.workspace = true fabro-static.workspace = true -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } jsonwebtoken.workspace = true chrono.workspace = true tracing.workspace = true @@ -28,7 +28,7 @@ base64.workspace = true thiserror.workspace = true [dev-dependencies] -fabro-macros = { path = "../fabro-macros" } +fabro-macros = { path = "../../foundation/fabro-macros" } fabro-test = { workspace = true } tokio = { workspace = true, features = ["test-util", "macros"] } tracing-subscriber.workspace = true diff --git a/lib/crates/fabro-github/src/lib.rs b/lib/components/fabro-github/src/lib.rs similarity index 100% rename from lib/crates/fabro-github/src/lib.rs rename to lib/components/fabro-github/src/lib.rs diff --git a/lib/crates/fabro-github/src/testdata/rsa_private.pem b/lib/components/fabro-github/src/testdata/rsa_private.pem similarity index 100% rename from lib/crates/fabro-github/src/testdata/rsa_private.pem rename to lib/components/fabro-github/src/testdata/rsa_private.pem diff --git a/lib/crates/fabro-github/tests/integration.rs b/lib/components/fabro-github/tests/integration.rs similarity index 100% rename from lib/crates/fabro-github/tests/integration.rs rename to lib/components/fabro-github/tests/integration.rs diff --git a/lib/crates/fabro-graphviz/Cargo.toml b/lib/components/fabro-graphviz/Cargo.toml similarity index 89% rename from lib/crates/fabro-graphviz/Cargo.toml rename to lib/components/fabro-graphviz/Cargo.toml index 3b33151d8..f78b3b9c2 100644 --- a/lib/crates/fabro-graphviz/Cargo.toml +++ b/lib/components/fabro-graphviz/Cargo.toml @@ -15,7 +15,7 @@ workspace = true [dependencies] anyhow.workspace = true graphviz-sys.workspace = true -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } nom = "7" regex = { workspace = true } serde = { workspace = true } diff --git a/lib/crates/fabro-graphviz/src/condition.rs b/lib/components/fabro-graphviz/src/condition.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/condition.rs rename to lib/components/fabro-graphviz/src/condition.rs diff --git a/lib/crates/fabro-graphviz/src/error.rs b/lib/components/fabro-graphviz/src/error.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/error.rs rename to lib/components/fabro-graphviz/src/error.rs diff --git a/lib/crates/fabro-graphviz/src/fidelity.rs b/lib/components/fabro-graphviz/src/fidelity.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/fidelity.rs rename to lib/components/fabro-graphviz/src/fidelity.rs diff --git a/lib/crates/fabro-graphviz/src/graph/mod.rs b/lib/components/fabro-graphviz/src/graph/mod.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/graph/mod.rs rename to lib/components/fabro-graphviz/src/graph/mod.rs diff --git a/lib/crates/fabro-graphviz/src/graph/types.rs b/lib/components/fabro-graphviz/src/graph/types.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/graph/types.rs rename to lib/components/fabro-graphviz/src/graph/types.rs diff --git a/lib/crates/fabro-graphviz/src/lib.rs b/lib/components/fabro-graphviz/src/lib.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/lib.rs rename to lib/components/fabro-graphviz/src/lib.rs diff --git a/lib/crates/fabro-graphviz/src/parser/ast.rs b/lib/components/fabro-graphviz/src/parser/ast.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/parser/ast.rs rename to lib/components/fabro-graphviz/src/parser/ast.rs diff --git a/lib/crates/fabro-graphviz/src/parser/grammar.rs b/lib/components/fabro-graphviz/src/parser/grammar.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/parser/grammar.rs rename to lib/components/fabro-graphviz/src/parser/grammar.rs diff --git a/lib/crates/fabro-graphviz/src/parser/lexer.rs b/lib/components/fabro-graphviz/src/parser/lexer.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/parser/lexer.rs rename to lib/components/fabro-graphviz/src/parser/lexer.rs diff --git a/lib/crates/fabro-graphviz/src/parser/mod.rs b/lib/components/fabro-graphviz/src/parser/mod.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/parser/mod.rs rename to lib/components/fabro-graphviz/src/parser/mod.rs diff --git a/lib/crates/fabro-graphviz/src/parser/semantic.rs b/lib/components/fabro-graphviz/src/parser/semantic.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/parser/semantic.rs rename to lib/components/fabro-graphviz/src/parser/semantic.rs diff --git a/lib/crates/fabro-graphviz/src/render.rs b/lib/components/fabro-graphviz/src/render.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/render.rs rename to lib/components/fabro-graphviz/src/render.rs diff --git a/lib/crates/fabro-graphviz/src/stylesheet.rs b/lib/components/fabro-graphviz/src/stylesheet.rs similarity index 100% rename from lib/crates/fabro-graphviz/src/stylesheet.rs rename to lib/components/fabro-graphviz/src/stylesheet.rs diff --git a/lib/crates/fabro-hooks/Cargo.toml b/lib/components/fabro-hooks/Cargo.toml similarity index 75% rename from lib/crates/fabro-hooks/Cargo.toml rename to lib/components/fabro-hooks/Cargo.toml index 559a7499a..02f25fdad 100644 --- a/lib/crates/fabro-hooks/Cargo.toml +++ b/lib/components/fabro-hooks/Cargo.toml @@ -14,12 +14,12 @@ workspace = true [dependencies] fabro-agent = { path = "../fabro-agent" } -fabro-auth = { path = "../fabro-auth" } +fabro-auth = { path = "../../foundation/fabro-auth" } fabro-llm = { path = "../fabro-llm" } -fabro-model = { path = "../fabro-model" } +fabro-model = { path = "../../foundation/fabro-model" } fabro-redact.workspace = true -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } fabro-http.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/lib/crates/fabro-hooks/src/bridge.rs b/lib/components/fabro-hooks/src/bridge.rs similarity index 100% rename from lib/crates/fabro-hooks/src/bridge.rs rename to lib/components/fabro-hooks/src/bridge.rs diff --git a/lib/crates/fabro-hooks/src/config.rs b/lib/components/fabro-hooks/src/config.rs similarity index 100% rename from lib/crates/fabro-hooks/src/config.rs rename to lib/components/fabro-hooks/src/config.rs diff --git a/lib/crates/fabro-hooks/src/executor.rs b/lib/components/fabro-hooks/src/executor.rs similarity index 100% rename from lib/crates/fabro-hooks/src/executor.rs rename to lib/components/fabro-hooks/src/executor.rs diff --git a/lib/crates/fabro-hooks/src/lib.rs b/lib/components/fabro-hooks/src/lib.rs similarity index 100% rename from lib/crates/fabro-hooks/src/lib.rs rename to lib/components/fabro-hooks/src/lib.rs diff --git a/lib/crates/fabro-hooks/src/runner.rs b/lib/components/fabro-hooks/src/runner.rs similarity index 100% rename from lib/crates/fabro-hooks/src/runner.rs rename to lib/components/fabro-hooks/src/runner.rs diff --git a/lib/crates/fabro-hooks/src/types.rs b/lib/components/fabro-hooks/src/types.rs similarity index 100% rename from lib/crates/fabro-hooks/src/types.rs rename to lib/components/fabro-hooks/src/types.rs diff --git a/lib/crates/fabro-hooks/tests/host_command_hooks.rs b/lib/components/fabro-hooks/tests/host_command_hooks.rs similarity index 100% rename from lib/crates/fabro-hooks/tests/host_command_hooks.rs rename to lib/components/fabro-hooks/tests/host_command_hooks.rs diff --git a/lib/crates/fabro-install/Cargo.toml b/lib/components/fabro-install/Cargo.toml similarity index 61% rename from lib/crates/fabro-install/Cargo.toml rename to lib/components/fabro-install/Cargo.toml index ccdd8b38c..d5e9ec416 100644 --- a/lib/crates/fabro-install/Cargo.toml +++ b/lib/components/fabro-install/Cargo.toml @@ -15,13 +15,13 @@ base64.workspace = true ring = "0.17" toml.workspace = true tokio.workspace = true -fabro-config = { path = "../fabro-config" } -fabro-db = { path = "../fabro-db" } +fabro-config = { path = "../../foundation/fabro-config" } +fabro-db = { path = "../../foundation/fabro-db" } fabro-environment.workspace = true fabro-static.workspace = true -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } -fabro-vault = { path = "../fabro-vault" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } +fabro-vault = { path = "../../foundation/fabro-vault" } [dev-dependencies] tempfile = "3" diff --git a/lib/crates/fabro-install/src/lib.rs b/lib/components/fabro-install/src/lib.rs similarity index 100% rename from lib/crates/fabro-install/src/lib.rs rename to lib/components/fabro-install/src/lib.rs diff --git a/lib/crates/fabro-interview/Cargo.toml b/lib/components/fabro-interview/Cargo.toml similarity index 82% rename from lib/crates/fabro-interview/Cargo.toml rename to lib/components/fabro-interview/Cargo.toml index ed18ab877..4c450f54a 100644 --- a/lib/crates/fabro-interview/Cargo.toml +++ b/lib/components/fabro-interview/Cargo.toml @@ -19,8 +19,8 @@ async-trait.workspace = true tokio.workspace = true tracing.workspace = true dialoguer.workspace = true -fabro-util = { path = "../fabro-util" } -fabro-types = { path = "../fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } +fabro-types = { path = "../../foundation/fabro-types" } [dev-dependencies] tokio = { workspace = true, features = ["test-util", "macros"] } diff --git a/lib/crates/fabro-interview/src/auto_approve.rs b/lib/components/fabro-interview/src/auto_approve.rs similarity index 100% rename from lib/crates/fabro-interview/src/auto_approve.rs rename to lib/components/fabro-interview/src/auto_approve.rs diff --git a/lib/crates/fabro-interview/src/callback.rs b/lib/components/fabro-interview/src/callback.rs similarity index 100% rename from lib/crates/fabro-interview/src/callback.rs rename to lib/components/fabro-interview/src/callback.rs diff --git a/lib/crates/fabro-interview/src/console.rs b/lib/components/fabro-interview/src/console.rs similarity index 100% rename from lib/crates/fabro-interview/src/console.rs rename to lib/components/fabro-interview/src/console.rs diff --git a/lib/crates/fabro-interview/src/control.rs b/lib/components/fabro-interview/src/control.rs similarity index 100% rename from lib/crates/fabro-interview/src/control.rs rename to lib/components/fabro-interview/src/control.rs diff --git a/lib/crates/fabro-interview/src/control_protocol.rs b/lib/components/fabro-interview/src/control_protocol.rs similarity index 100% rename from lib/crates/fabro-interview/src/control_protocol.rs rename to lib/components/fabro-interview/src/control_protocol.rs diff --git a/lib/crates/fabro-interview/src/lib.rs b/lib/components/fabro-interview/src/lib.rs similarity index 100% rename from lib/crates/fabro-interview/src/lib.rs rename to lib/components/fabro-interview/src/lib.rs diff --git a/lib/crates/fabro-interview/src/queue.rs b/lib/components/fabro-interview/src/queue.rs similarity index 100% rename from lib/crates/fabro-interview/src/queue.rs rename to lib/components/fabro-interview/src/queue.rs diff --git a/lib/crates/fabro-interview/src/recording.rs b/lib/components/fabro-interview/src/recording.rs similarity index 100% rename from lib/crates/fabro-interview/src/recording.rs rename to lib/components/fabro-interview/src/recording.rs diff --git a/lib/crates/fabro-interview/src/replay.rs b/lib/components/fabro-interview/src/replay.rs similarity index 100% rename from lib/crates/fabro-interview/src/replay.rs rename to lib/components/fabro-interview/src/replay.rs diff --git a/lib/crates/fabro-llm/Cargo.toml b/lib/components/fabro-llm/Cargo.toml similarity index 82% rename from lib/crates/fabro-llm/Cargo.toml rename to lib/components/fabro-llm/Cargo.toml index 9146976b6..b10321253 100644 --- a/lib/crates/fabro-llm/Cargo.toml +++ b/lib/components/fabro-llm/Cargo.toml @@ -39,12 +39,12 @@ aws-smithy-eventstream.workspace = true aws-smithy-runtime-api.workspace = true aws-smithy-types.workspace = true fabro-http.workspace = true -fabro-auth = { path = "../fabro-auth" } -fabro-model = { path = "../fabro-model" } +fabro-auth = { path = "../../foundation/fabro-auth" } +fabro-model = { path = "../../foundation/fabro-model" } fabro-redact.workspace = true fabro-static.workspace = true -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } [dev-dependencies] http = "1" @@ -53,6 +53,6 @@ tokio = { workspace = true, features = ["test-util", "macros"] } httpmock = "0.8" serde_json.workspace = true toml.workspace = true -fabro-macros = { path = "../fabro-macros" } +fabro-macros = { path = "../../foundation/fabro-macros" } fabro-test = { workspace = true } tracing-subscriber.workspace = true diff --git a/lib/crates/fabro-llm/README.md b/lib/components/fabro-llm/README.md similarity index 100% rename from lib/crates/fabro-llm/README.md rename to lib/components/fabro-llm/README.md diff --git a/lib/crates/fabro-llm/src/adapter_registry.rs b/lib/components/fabro-llm/src/adapter_registry.rs similarity index 100% rename from lib/crates/fabro-llm/src/adapter_registry.rs rename to lib/components/fabro-llm/src/adapter_registry.rs diff --git a/lib/crates/fabro-llm/src/attachments.rs b/lib/components/fabro-llm/src/attachments.rs similarity index 100% rename from lib/crates/fabro-llm/src/attachments.rs rename to lib/components/fabro-llm/src/attachments.rs diff --git a/lib/crates/fabro-llm/src/client.rs b/lib/components/fabro-llm/src/client.rs similarity index 100% rename from lib/crates/fabro-llm/src/client.rs rename to lib/components/fabro-llm/src/client.rs diff --git a/lib/crates/fabro-llm/src/codec/anthropic_messages/decode.rs b/lib/components/fabro-llm/src/codec/anthropic_messages/decode.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/anthropic_messages/decode.rs rename to lib/components/fabro-llm/src/codec/anthropic_messages/decode.rs diff --git a/lib/crates/fabro-llm/src/codec/anthropic_messages/encode.rs b/lib/components/fabro-llm/src/codec/anthropic_messages/encode.rs similarity index 97% rename from lib/crates/fabro-llm/src/codec/anthropic_messages/encode.rs rename to lib/components/fabro-llm/src/codec/anthropic_messages/encode.rs index 2803c221c..5ed999f87 100644 --- a/lib/crates/fabro-llm/src/codec/anthropic_messages/encode.rs +++ b/lib/components/fabro-llm/src/codec/anthropic_messages/encode.rs @@ -8,7 +8,8 @@ use base64::Engine; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use super::SYNTHETIC_TOOL_NAME; -use super::wire::{ApiMessage, ApiRequest, ApiToolDef, CacheControl, CountTokensRequest}; +use super::wire::{ApiMessage, ApiRequest, ApiToolDef, CountTokensRequest}; +use crate::codec::cache::{self, CacheControl}; use crate::codec::{AnthropicVersion, CodecCtx, EncodedRequest, extract_system_prompt}; use crate::types::{ ContentPart, Message, ReasoningEffort, ReasoningEffortFeature, Request, ResponseFormatType, @@ -48,7 +49,7 @@ pub(super) fn encode_count_tokens(ctx: &CodecCtx<'_>) -> EncodedRequest { /// hasn't opted out. fn auto_cache(ctx: &CodecCtx<'_>) -> bool { ctx.model.is_some_and(|m| m.features.prompt_cache) - && is_auto_cache_enabled(ctx.request.provider_options.as_ref()) + && cache::auto_cache_enabled(ctx.request.provider_options.as_ref(), "anthropic") } fn build_headers(ctx: &CodecCtx<'_>) -> Vec<(String, String)> { @@ -441,12 +442,6 @@ fn effort_to_budget_tokens(effort: ReasoningEffort, max_tokens: i64) -> i64 { budget.max(1024) } -fn is_auto_cache_enabled(provider_options: Option<&serde_json::Value>) -> bool { - anthropic_option(provider_options, "auto_cache") - .and_then(serde_json::Value::as_bool) - .unwrap_or(true) -} - fn system_with_cache_control(system: &str) -> serde_json::Value { serde_json::json!([{ "type": "text", @@ -462,18 +457,10 @@ fn apply_cache_control_to_last_tool(tools: &mut [ApiToolDef]) { } fn apply_cache_control_to_conversation_prefix(messages: &mut [ApiMessage]) { - let user_indices: Vec = messages - .iter() - .enumerate() - .filter(|(_, m)| m.role == "user") - .map(|(i, _)| i) - .collect(); - - if user_indices.len() < 2 { + let user_turns: Vec = messages.iter().map(|m| m.role == "user").collect(); + let Some(target_idx) = cache::conversation_breakpoint_index(&user_turns) else { return; - } - - let target_idx = user_indices[user_indices.len() - 2]; + }; if let Some(serde_json::Value::Object(map)) = messages[target_idx].content.last_mut() { map.insert( "cache_control".to_string(), @@ -651,37 +638,6 @@ reasoning = true .map(|(_, value)| value.as_str()) } - // --- auto_cache ---------------------------------------------------------- - - #[test] - fn auto_cache_enabled_by_default() { - assert!(is_auto_cache_enabled(None)); - } - - #[test] - fn auto_cache_enabled_when_true() { - let opts = serde_json::json!({"anthropic": {"auto_cache": true}}); - assert!(is_auto_cache_enabled(Some(&opts))); - } - - #[test] - fn auto_cache_disabled_when_false() { - let opts = serde_json::json!({"anthropic": {"auto_cache": false}}); - assert!(!is_auto_cache_enabled(Some(&opts))); - } - - #[test] - fn auto_cache_enabled_when_key_missing() { - let opts = serde_json::json!({"anthropic": {}}); - assert!(is_auto_cache_enabled(Some(&opts))); - } - - #[test] - fn auto_cache_enabled_when_anthropic_missing() { - let opts = serde_json::json!({"openai": {}}); - assert!(is_auto_cache_enabled(Some(&opts))); - } - // --- prompt-cache helpers ------------------------------------------------ #[test] diff --git a/lib/crates/fabro-llm/src/codec/anthropic_messages/mod.rs b/lib/components/fabro-llm/src/codec/anthropic_messages/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/anthropic_messages/mod.rs rename to lib/components/fabro-llm/src/codec/anthropic_messages/mod.rs diff --git a/lib/crates/fabro-llm/src/codec/anthropic_messages/stream.rs b/lib/components/fabro-llm/src/codec/anthropic_messages/stream.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/anthropic_messages/stream.rs rename to lib/components/fabro-llm/src/codec/anthropic_messages/stream.rs diff --git a/lib/crates/fabro-llm/src/codec/anthropic_messages/wire.rs b/lib/components/fabro-llm/src/codec/anthropic_messages/wire.rs similarity index 94% rename from lib/crates/fabro-llm/src/codec/anthropic_messages/wire.rs rename to lib/components/fabro-llm/src/codec/anthropic_messages/wire.rs index 80458dea4..70fb0ede6 100644 --- a/lib/crates/fabro-llm/src/codec/anthropic_messages/wire.rs +++ b/lib/components/fabro-llm/src/codec/anthropic_messages/wire.rs @@ -1,5 +1,7 @@ //! Serde types mirroring the Anthropic Messages wire shapes. +use crate::codec::cache::CacheControl; + #[derive(serde::Serialize)] pub(super) struct ApiRequest { pub model: String, @@ -77,21 +79,6 @@ pub(super) struct ApiToolDef { pub cache_control: Option, } -/// Anthropic `cache_control` annotation. -#[derive(serde::Serialize, Clone)] -pub(super) struct CacheControl { - #[serde(rename = "type")] - pub kind: String, -} - -impl CacheControl { - pub(super) fn ephemeral() -> Self { - Self { - kind: "ephemeral".to_string(), - } - } -} - // --- Response types --- #[derive(serde::Deserialize)] diff --git a/lib/crates/fabro-llm/src/codec/bedrock_converse/decode.rs b/lib/components/fabro-llm/src/codec/bedrock_converse/decode.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/bedrock_converse/decode.rs rename to lib/components/fabro-llm/src/codec/bedrock_converse/decode.rs diff --git a/lib/crates/fabro-llm/src/codec/bedrock_converse/encode.rs b/lib/components/fabro-llm/src/codec/bedrock_converse/encode.rs similarity index 99% rename from lib/crates/fabro-llm/src/codec/bedrock_converse/encode.rs rename to lib/components/fabro-llm/src/codec/bedrock_converse/encode.rs index 54a38314e..39c9c5c10 100644 --- a/lib/crates/fabro-llm/src/codec/bedrock_converse/encode.rs +++ b/lib/components/fabro-llm/src/codec/bedrock_converse/encode.rs @@ -342,7 +342,7 @@ fn apply_cache_point_to_conversation_prefix(messages: &mut [Value]) { /// codec). This is the passthrough for `additionalModelRequestFields`, /// `guardrailConfig`, `serviceTier`, and other Converse extensions. fn merge_provider_options(body: &mut Value, provider_options: Option<&Value>, provider_name: &str) { - merge_named_provider_options(body, provider_options, provider_name); + merge_named_provider_options(body, provider_options, provider_name, &[]); } #[cfg(test)] diff --git a/lib/crates/fabro-llm/src/codec/bedrock_converse/mod.rs b/lib/components/fabro-llm/src/codec/bedrock_converse/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/bedrock_converse/mod.rs rename to lib/components/fabro-llm/src/codec/bedrock_converse/mod.rs diff --git a/lib/crates/fabro-llm/src/codec/bedrock_converse/stream.rs b/lib/components/fabro-llm/src/codec/bedrock_converse/stream.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/bedrock_converse/stream.rs rename to lib/components/fabro-llm/src/codec/bedrock_converse/stream.rs diff --git a/lib/components/fabro-llm/src/codec/cache.rs b/lib/components/fabro-llm/src/codec/cache.rs new file mode 100644 index 000000000..ed1afe8ab --- /dev/null +++ b/lib/components/fabro-llm/src/codec/cache.rs @@ -0,0 +1,98 @@ +//! Shared prompt-cache policy: whether a request opts into explicit +//! Anthropic-style caching and where the conversation breakpoint lands. +//! Dialect codecs apply these decisions to their own wire shapes. + +/// Anthropic-style `cache_control` annotation. +#[derive(serde::Serialize, Clone)] +pub(crate) struct CacheControl { + #[serde(rename = "type")] + pub kind: String, +} + +impl CacheControl { + pub(crate) fn ephemeral() -> Self { + Self { + kind: "ephemeral".to_string(), + } + } +} + +/// Whether automatic prompt caching applies to this request: the +/// `provider_options..auto_cache` opt-out defaults to enabled. +pub(crate) fn auto_cache_enabled( + provider_options: Option<&serde_json::Value>, + namespace: &str, +) -> bool { + provider_options + .and_then(|opts| opts.get(namespace)) + .and_then(|ns| ns.get("auto_cache")) + .and_then(serde_json::Value::as_bool) + .unwrap_or(true) +} + +/// Index of the message carrying the conversation-prefix breakpoint: the +/// second-to-last user turn, so each iteration of an agent loop reuses the +/// prefix cached by the previous one. `user_turns[i]` is true when message +/// `i` advances the user side of the conversation (plain user messages, plus +/// tool results on dialects where they are separate messages). `None` until +/// the conversation has at least two user turns. +pub(crate) fn conversation_breakpoint_index(user_turns: &[bool]) -> Option { + let indices: Vec = user_turns + .iter() + .enumerate() + .filter_map(|(i, &is_user)| is_user.then_some(i)) + .collect(); + indices.len().checked_sub(2).map(|nth| indices[nth]) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn auto_cache_enabled_by_default() { + assert!(auto_cache_enabled(None, "anthropic")); + } + + #[test] + fn auto_cache_enabled_when_true() { + let opts = serde_json::json!({"anthropic": {"auto_cache": true}}); + assert!(auto_cache_enabled(Some(&opts), "anthropic")); + } + + #[test] + fn auto_cache_disabled_when_false() { + let opts = serde_json::json!({"openrouter": {"auto_cache": false}}); + assert!(!auto_cache_enabled(Some(&opts), "openrouter")); + } + + #[test] + fn auto_cache_enabled_when_key_missing() { + let opts = serde_json::json!({"anthropic": {}}); + assert!(auto_cache_enabled(Some(&opts), "anthropic")); + } + + #[test] + fn auto_cache_reads_only_its_own_namespace() { + let opts = serde_json::json!({"openrouter": {"auto_cache": false}}); + assert!(auto_cache_enabled(Some(&opts), "anthropic")); + } + + #[test] + fn conversation_breakpoint_none_below_two_user_turns() { + assert_eq!(conversation_breakpoint_index(&[]), None); + assert_eq!(conversation_breakpoint_index(&[true]), None); + assert_eq!(conversation_breakpoint_index(&[true, false, false]), None); + } + + #[test] + fn conversation_breakpoint_with_exactly_two_user_turns() { + assert_eq!(conversation_breakpoint_index(&[true, false, true]), Some(0)); + } + + #[test] + fn conversation_breakpoint_targets_second_to_last_user_turn() { + let turns = [true, false, true, false, true]; + assert_eq!(conversation_breakpoint_index(&turns), Some(2)); + } +} diff --git a/lib/crates/fabro-llm/src/codec/gemini_generate/decode.rs b/lib/components/fabro-llm/src/codec/gemini_generate/decode.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/gemini_generate/decode.rs rename to lib/components/fabro-llm/src/codec/gemini_generate/decode.rs diff --git a/lib/crates/fabro-llm/src/codec/gemini_generate/encode.rs b/lib/components/fabro-llm/src/codec/gemini_generate/encode.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/gemini_generate/encode.rs rename to lib/components/fabro-llm/src/codec/gemini_generate/encode.rs diff --git a/lib/crates/fabro-llm/src/codec/gemini_generate/mod.rs b/lib/components/fabro-llm/src/codec/gemini_generate/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/gemini_generate/mod.rs rename to lib/components/fabro-llm/src/codec/gemini_generate/mod.rs diff --git a/lib/crates/fabro-llm/src/codec/gemini_generate/stream.rs b/lib/components/fabro-llm/src/codec/gemini_generate/stream.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/gemini_generate/stream.rs rename to lib/components/fabro-llm/src/codec/gemini_generate/stream.rs diff --git a/lib/crates/fabro-llm/src/codec/gemini_generate/wire.rs b/lib/components/fabro-llm/src/codec/gemini_generate/wire.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/gemini_generate/wire.rs rename to lib/components/fabro-llm/src/codec/gemini_generate/wire.rs diff --git a/lib/crates/fabro-llm/src/codec/mod.rs b/lib/components/fabro-llm/src/codec/mod.rs similarity index 98% rename from lib/crates/fabro-llm/src/codec/mod.rs rename to lib/components/fabro-llm/src/codec/mod.rs index 9ddc1eb0a..5a7759b22 100644 --- a/lib/crates/fabro-llm/src/codec/mod.rs +++ b/lib/components/fabro-llm/src/codec/mod.rs @@ -12,6 +12,7 @@ pub(crate) mod anthropic_messages; pub(crate) mod bedrock_converse; +pub(crate) mod cache; pub(crate) mod gemini_generate; pub(crate) mod openai_compatible; pub(crate) mod openai_responses; @@ -38,11 +39,14 @@ pub(crate) fn split_inclusive_token_total(total: i64, detail: i64) -> (i64, i64) /// Merge `provider_options.` fields into an encoded request /// body. Used by codecs whose provider-options namespace is adapter-name keyed -/// rather than a single fixed provider. +/// rather than a single fixed provider. `known_keys` are control keys the +/// codec consumed itself (e.g. `auto_cache`); they are not re-merged into the +/// body. pub(crate) fn merge_named_provider_options( body: &mut serde_json::Value, provider_options: Option<&serde_json::Value>, provider_name: &str, + known_keys: &[&str], ) { let Some(opts) = provider_options.and_then(|opts| opts.get(provider_name)) else { return; @@ -55,6 +59,9 @@ pub(crate) fn merge_named_provider_options( }; for (key, value) in opts_map { + if known_keys.contains(&key.as_str()) { + continue; + } body_map.insert(key.clone(), value.clone()); } } diff --git a/lib/crates/fabro-llm/src/codec/openai_compatible/mod.rs b/lib/components/fabro-llm/src/codec/openai_compatible/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_compatible/mod.rs rename to lib/components/fabro-llm/src/codec/openai_compatible/mod.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_compatible/request.rs b/lib/components/fabro-llm/src/codec/openai_compatible/request.rs similarity index 70% rename from lib/crates/fabro-llm/src/codec/openai_compatible/request.rs rename to lib/components/fabro-llm/src/codec/openai_compatible/request.rs index 72920b34c..12be9e84b 100644 --- a/lib/crates/fabro-llm/src/codec/openai_compatible/request.rs +++ b/lib/components/fabro-llm/src/codec/openai_compatible/request.rs @@ -1,10 +1,14 @@ //! Request encoding: canonical `Request` → Chat Completions body. use super::translate; -use super::wire::ApiRequest; -use crate::codec::{CodecCtx, EncodedRequest, merge_named_provider_options}; +use super::wire::{ApiRequest, ChatMessage}; +use crate::codec::{CodecCtx, EncodedRequest, cache, merge_named_provider_options}; use crate::error::Error; +/// Known `provider_options.` keys the codec consumes itself; +/// not re-merged into the body. +const KNOWN_OPTION_KEYS: &[&str] = &["auto_cache"]; + /// Build the Chat Completions request for `ctx.request`. `stream` toggles the /// `stream` body field. The body is assembled as a `serde_json::Value` so /// `provider_options.` fields can be merged in before sending. @@ -13,7 +17,10 @@ use crate::error::Error; /// the Chat Completions tool envelope cannot represent. pub(super) fn encode(ctx: &CodecCtx<'_>, stream: bool) -> Result { let request = ctx.request; - let chat_messages = translate::translate_messages(&request.messages); + let mut chat_messages = translate::translate_messages(&request.messages); + if explicit_cache_breakpoints(ctx) { + apply_cache_breakpoints(&mut chat_messages); + } let tools = request .tools .as_ref() @@ -64,6 +71,37 @@ pub(super) fn encode(ctx: &CodecCtx<'_>, stream: bool) -> Result) -> bool { + ctx.model + .is_some_and(|m| m.features.prompt_cache && m.features.cache_control_breakpoints) + && cache::auto_cache_enabled(ctx.request.provider_options.as_ref(), ctx.provider_name) +} + +/// Mark the cacheable prefix: the last system message (upstream, tools and +/// system precede the conversation, so this breakpoint covers them too) and +/// the second-to-last user turn. Tool results count as user turns — they ride +/// in user messages on the upstream Anthropic wire. +fn apply_cache_breakpoints(messages: &mut [ChatMessage]) { + if let Some(system) = messages.iter_mut().rev().find(|m| m.role == "system") { + if let Some(content) = system.content.as_mut() { + content.mark_cache_breakpoint(); + } + } + + let user_turns: Vec = messages + .iter() + .map(|m| m.role == "user" || m.role == "tool") + .collect(); + if let Some(idx) = cache::conversation_breakpoint_index(&user_turns) { + if let Some(content) = messages[idx].content.as_mut() { + content.mark_cache_breakpoint(); + } + } +} + /// Merge `provider_options.` fields into the serialized API /// request body. /// @@ -74,7 +112,7 @@ pub(super) fn merge_provider_options( provider_options: Option<&serde_json::Value>, provider_name: &str, ) { - merge_named_provider_options(body, provider_options, provider_name); + merge_named_provider_options(body, provider_options, provider_name, KNOWN_OPTION_KEYS); } #[cfg(test)] @@ -315,4 +353,68 @@ mod tests { merge_provider_options(&mut body, Some(&opts), "groq"); assert_eq!(body["model"], "test"); } + + #[test] + fn merge_provider_options_consumes_auto_cache_control_key() { + let mut body = serde_json::json!({"model": "test"}); + let opts = serde_json::json!({"groq": {"auto_cache": false, "top_k": 5}}); + merge_provider_options(&mut body, Some(&opts), "groq"); + assert!(body.get("auto_cache").is_none()); + assert_eq!(body["top_k"], 5); + } + + // --- apply_cache_breakpoints --------------------------------------------- + + fn chat_message(role: &str, text: &str) -> ChatMessage { + ChatMessage { + role: role.to_string(), + content: Some(super::super::wire::ChatContent::Text(text.to_string())), + reasoning_content: None, + tool_call_id: None, + tool_calls: None, + } + } + + fn marked(message: &ChatMessage) -> bool { + let json = serde_json::to_value(message).unwrap(); + json["content"].is_array() && json["content"][0]["cache_control"]["type"] == "ephemeral" + } + + #[test] + fn cache_breakpoints_on_first_turn_mark_only_the_system_prompt() { + let mut messages = vec![chat_message("system", "sys"), chat_message("user", "task")]; + apply_cache_breakpoints(&mut messages); + assert!(marked(&messages[0])); + assert!(!marked(&messages[1])); + } + + #[test] + fn cache_breakpoints_count_tool_results_as_user_turns() { + let mut messages = vec![ + chat_message("system", "sys"), + chat_message("user", "task"), + chat_message("assistant", "calling a tool"), + chat_message("tool", "tool output"), + chat_message("assistant", "one more"), + chat_message("tool", "more output"), + ]; + apply_cache_breakpoints(&mut messages); + assert!(marked(&messages[0])); + // Second-to-last user turn: the first tool result, not the user task. + assert!(!marked(&messages[1])); + assert!(marked(&messages[3])); + assert!(!marked(&messages[5])); + } + + #[test] + fn cache_breakpoints_without_system_mark_only_the_conversation() { + let mut messages = vec![ + chat_message("user", "task"), + chat_message("assistant", "answer"), + chat_message("user", "follow-up"), + ]; + apply_cache_breakpoints(&mut messages); + assert!(marked(&messages[0])); + assert!(!marked(&messages[2])); + } } diff --git a/lib/crates/fabro-llm/src/codec/openai_compatible/response.rs b/lib/components/fabro-llm/src/codec/openai_compatible/response.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_compatible/response.rs rename to lib/components/fabro-llm/src/codec/openai_compatible/response.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_compatible/stream.rs b/lib/components/fabro-llm/src/codec/openai_compatible/stream.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_compatible/stream.rs rename to lib/components/fabro-llm/src/codec/openai_compatible/stream.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_compatible/translate.rs b/lib/components/fabro-llm/src/codec/openai_compatible/translate.rs similarity index 93% rename from lib/crates/fabro-llm/src/codec/openai_compatible/translate.rs rename to lib/components/fabro-llm/src/codec/openai_compatible/translate.rs index 127af26a2..dbef4831f 100644 --- a/lib/crates/fabro-llm/src/codec/openai_compatible/translate.rs +++ b/lib/components/fabro-llm/src/codec/openai_compatible/translate.rs @@ -1,6 +1,6 @@ //! Pure mapping between canonical types and the Chat Completions wire shapes. -use super::wire::{ChatFunction, ChatMessage, ChatToolCall}; +use super::wire::{ChatContent, ChatFunction, ChatMessage, ChatToolCall}; use crate::error::Error; use crate::types::{ ContentPart, CostSource, FinishReason, Message, Request, ResponseFormat, ResponseFormatType, @@ -67,7 +67,7 @@ pub(super) fn translate_messages(messages: &[Message]) -> Vec { .map_or_else(|| tr.content.to_string(), str::to_string); Some(ChatMessage { role: "tool".to_string(), - content: Some(output), + content: Some(ChatContent::Text(output)), reasoning_content: None, tool_call_id: Some(tr.tool_call_id.clone()), tool_calls: None, @@ -109,7 +109,11 @@ pub(super) fn translate_messages(messages: &[Message]) -> Vec { } let text = content_text_with_fallbacks(&msg.content); - let content = if text.is_empty() { None } else { Some(text) }; + let content = if text.is_empty() { + None + } else { + Some(ChatContent::Text(text)) + }; let tool_calls = if tool_calls.is_empty() { None } else { @@ -276,7 +280,10 @@ mod tests { }; let translated = translate_messages(&[msg]); assert_eq!( - translated[0].content.as_deref(), + translated[0] + .content + .as_ref() + .and_then(ChatContent::as_text), Some("Let me check the weather") ); let tool_calls = translated[0].tool_calls.as_ref().unwrap(); @@ -318,7 +325,13 @@ mod tests { let msg = Message::user("Hello"); let translated = translate_messages(&[msg]); assert_eq!(translated[0].role, "user"); - assert_eq!(translated[0].content.as_deref(), Some("Hello")); + assert_eq!( + translated[0] + .content + .as_ref() + .and_then(ChatContent::as_text), + Some("Hello") + ); assert!(translated[0].tool_calls.is_none()); } @@ -359,7 +372,10 @@ mod tests { }; let translated = translate_messages(&[msg]); assert_eq!( - translated[0].content.as_deref(), + translated[0] + .content + .as_ref() + .and_then(ChatContent::as_text), Some("[Audio content not supported by this provider]") ); } @@ -379,7 +395,10 @@ mod tests { }; let translated = translate_messages(&[msg]); assert_eq!( - translated[0].content.as_deref(), + translated[0] + .content + .as_ref() + .and_then(ChatContent::as_text), Some("[Document 'report.pdf': content type not supported by this provider]") ); } @@ -399,7 +418,10 @@ mod tests { }; let translated = translate_messages(&[msg]); assert_eq!( - translated[0].content.as_deref(), + translated[0] + .content + .as_ref() + .and_then(ChatContent::as_text), Some("[Document content not supported by this provider]") ); } @@ -421,7 +443,10 @@ mod tests { }; let translated = translate_messages(&[msg]); assert_eq!( - translated[0].content.as_deref(), + translated[0] + .content + .as_ref() + .and_then(ChatContent::as_text), Some("Check this: [Audio content not supported by this provider]") ); } diff --git a/lib/crates/fabro-llm/src/codec/openai_compatible/wire.rs b/lib/components/fabro-llm/src/codec/openai_compatible/wire.rs similarity index 73% rename from lib/crates/fabro-llm/src/codec/openai_compatible/wire.rs rename to lib/components/fabro-llm/src/codec/openai_compatible/wire.rs index 396b74a72..5bc0a3b93 100644 --- a/lib/crates/fabro-llm/src/codec/openai_compatible/wire.rs +++ b/lib/components/fabro-llm/src/codec/openai_compatible/wire.rs @@ -1,5 +1,6 @@ //! Serde types mirroring the OpenAI Chat Completions wire shapes. +use crate::codec::cache::CacheControl; use crate::codec::split_inclusive_token_total; use crate::types::{ReasoningEffort, TokenCounts}; @@ -31,7 +32,7 @@ pub(super) struct ApiRequest { pub(super) struct ChatMessage { pub role: String, #[serde(skip_serializing_if = "Option::is_none")] - pub content: Option, + pub content: Option, /// Reasoning/thinking content echoed back for providers that require it /// (Kimi). #[serde(skip_serializing_if = "Option::is_none")] @@ -42,6 +43,56 @@ pub(super) struct ChatMessage { pub tool_calls: Option>, } +/// Message content: plain text, or text parts when a part carries a +/// `cache_control` breakpoint (aggregators fronting Anthropic models forward +/// it upstream). Unmarked messages keep the plain-string form for maximum +/// compatibility with strict Chat Completions servers. +#[derive(serde::Serialize)] +#[serde(untagged)] +pub(super) enum ChatContent { + Text(String), + Parts(Vec), +} + +#[derive(serde::Serialize)] +pub(super) struct ChatTextPart { + #[serde(rename = "type")] + pub kind: String, + pub text: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub cache_control: Option, +} + +impl ChatContent { + /// Plain-text view for assertions. + #[cfg(test)] + pub(super) fn as_text(&self) -> Option<&str> { + match self { + Self::Text(text) => Some(text.as_str()), + Self::Parts(_) => None, + } + } + + /// Mark this content as a prompt-cache breakpoint, converting to parts + /// form so the annotation has somewhere to live. + pub(super) fn mark_cache_breakpoint(&mut self) { + match self { + Self::Text(text) => { + *self = Self::Parts(vec![ChatTextPart { + kind: "text".to_string(), + text: std::mem::take(text), + cache_control: Some(CacheControl::ephemeral()), + }]); + } + Self::Parts(parts) => { + if let Some(last) = parts.last_mut() { + last.cache_control = Some(CacheControl::ephemeral()); + } + } + } + } +} + #[derive(serde::Serialize)] pub(super) struct ChatToolCall { pub id: String, @@ -229,9 +280,53 @@ pub(super) struct AccumulatedToolCall { #[cfg(test)] mod tests { - use super::{ApiResponse, ApiUsage, StreamChunk}; + use super::{ApiResponse, ApiUsage, ChatContent, ChatTextPart, StreamChunk}; + use crate::codec::cache::CacheControl; use crate::types::TokenCounts; + #[test] + fn chat_content_text_serializes_as_plain_string() { + let content = ChatContent::Text("Hello".to_string()); + assert_eq!( + serde_json::to_value(&content).unwrap(), + serde_json::json!("Hello") + ); + } + + #[test] + fn mark_cache_breakpoint_converts_text_to_annotated_parts() { + let mut content = ChatContent::Text("Hello".to_string()); + content.mark_cache_breakpoint(); + assert_eq!( + serde_json::to_value(&content).unwrap(), + serde_json::json!([{ + "type": "text", + "text": "Hello", + "cache_control": {"type": "ephemeral"} + }]) + ); + } + + #[test] + fn mark_cache_breakpoint_annotates_last_existing_part() { + let mut content = ChatContent::Parts(vec![ + ChatTextPart { + kind: "text".to_string(), + text: "first".to_string(), + cache_control: None, + }, + ChatTextPart { + kind: "text".to_string(), + text: "second".to_string(), + cache_control: Some(CacheControl::ephemeral()), + }, + ]); + content.mark_cache_breakpoint(); + let json = serde_json::to_value(&content).unwrap(); + assert!(json[0].get("cache_control").is_none()); + assert_eq!(json[1]["cache_control"]["type"], "ephemeral"); + } + #[test] fn token_counts_bound_detail_to_parent_totals() { let usage: ApiUsage = serde_json::from_value(serde_json::json!({ diff --git a/lib/crates/fabro-llm/src/codec/openai_responses/decode.rs b/lib/components/fabro-llm/src/codec/openai_responses/decode.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_responses/decode.rs rename to lib/components/fabro-llm/src/codec/openai_responses/decode.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_responses/encode.rs b/lib/components/fabro-llm/src/codec/openai_responses/encode.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_responses/encode.rs rename to lib/components/fabro-llm/src/codec/openai_responses/encode.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_responses/mod.rs b/lib/components/fabro-llm/src/codec/openai_responses/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_responses/mod.rs rename to lib/components/fabro-llm/src/codec/openai_responses/mod.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_responses/stream.rs b/lib/components/fabro-llm/src/codec/openai_responses/stream.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_responses/stream.rs rename to lib/components/fabro-llm/src/codec/openai_responses/stream.rs diff --git a/lib/crates/fabro-llm/src/codec/openai_responses/wire.rs b/lib/components/fabro-llm/src/codec/openai_responses/wire.rs similarity index 100% rename from lib/crates/fabro-llm/src/codec/openai_responses/wire.rs rename to lib/components/fabro-llm/src/codec/openai_responses/wire.rs diff --git a/lib/crates/fabro-llm/src/cost.rs b/lib/components/fabro-llm/src/cost.rs similarity index 100% rename from lib/crates/fabro-llm/src/cost.rs rename to lib/components/fabro-llm/src/cost.rs diff --git a/lib/crates/fabro-llm/src/error.rs b/lib/components/fabro-llm/src/error.rs similarity index 100% rename from lib/crates/fabro-llm/src/error.rs rename to lib/components/fabro-llm/src/error.rs diff --git a/lib/crates/fabro-llm/src/generate.rs b/lib/components/fabro-llm/src/generate.rs similarity index 100% rename from lib/crates/fabro-llm/src/generate.rs rename to lib/components/fabro-llm/src/generate.rs diff --git a/lib/crates/fabro-llm/src/lib.rs b/lib/components/fabro-llm/src/lib.rs similarity index 100% rename from lib/crates/fabro-llm/src/lib.rs rename to lib/components/fabro-llm/src/lib.rs diff --git a/lib/crates/fabro-llm/src/middleware.rs b/lib/components/fabro-llm/src/middleware.rs similarity index 100% rename from lib/crates/fabro-llm/src/middleware.rs rename to lib/components/fabro-llm/src/middleware.rs diff --git a/lib/crates/fabro-llm/src/model_test.rs b/lib/components/fabro-llm/src/model_test.rs similarity index 89% rename from lib/crates/fabro-llm/src/model_test.rs rename to lib/components/fabro-llm/src/model_test.rs index 3d8ea8eb0..aec0a83a7 100644 --- a/lib/crates/fabro-llm/src/model_test.rs +++ b/lib/components/fabro-llm/src/model_test.rs @@ -223,12 +223,13 @@ mod tests { #[tokio::test] async fn run_model_test_deep_errors_when_model_lacks_tools() { let info = test_model_with(ModelFeatures { - tools: false, - vision: false, - reasoning: true, - reasoning_effort: ReasoningEffortFeature::Levels, - prompt_cache: false, - sampling_params: true, + tools: false, + vision: false, + reasoning: true, + reasoning_effort: ReasoningEffortFeature::Levels, + prompt_cache: false, + cache_control_breakpoints: false, + sampling_params: true, }); let outcome = run_model_test(&info, ModelTestMode::Deep, empty_test_client()).await; @@ -243,12 +244,13 @@ mod tests { #[test] fn deep_test_omits_effort_for_reasoning_without_effort_controls() { let info = test_model_with(ModelFeatures { - tools: true, - vision: false, - reasoning: true, - reasoning_effort: ReasoningEffortFeature::None, - prompt_cache: true, - sampling_params: true, + tools: true, + vision: false, + reasoning: true, + reasoning_effort: ReasoningEffortFeature::None, + prompt_cache: true, + cache_control_breakpoints: false, + sampling_params: true, }); let params = build_deep_test_params(&info, empty_test_client()) @@ -260,12 +262,13 @@ mod tests { #[test] fn deep_test_uses_high_effort_when_supported() { let info = test_model_with(ModelFeatures { - tools: true, - vision: false, - reasoning: true, - reasoning_effort: ReasoningEffortFeature::Levels, - prompt_cache: true, - sampling_params: true, + tools: true, + vision: false, + reasoning: true, + reasoning_effort: ReasoningEffortFeature::Levels, + prompt_cache: true, + cache_control_breakpoints: false, + sampling_params: true, }); let params = build_deep_test_params(&info, empty_test_client()) diff --git a/lib/crates/fabro-llm/src/provider.rs b/lib/components/fabro-llm/src/provider.rs similarity index 100% rename from lib/crates/fabro-llm/src/provider.rs rename to lib/components/fabro-llm/src/provider.rs diff --git a/lib/crates/fabro-llm/src/providers/anthropic.rs b/lib/components/fabro-llm/src/providers/anthropic.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/anthropic.rs rename to lib/components/fabro-llm/src/providers/anthropic.rs diff --git a/lib/crates/fabro-llm/src/providers/bedrock/eventstream.rs b/lib/components/fabro-llm/src/providers/bedrock/eventstream.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/bedrock/eventstream.rs rename to lib/components/fabro-llm/src/providers/bedrock/eventstream.rs diff --git a/lib/crates/fabro-llm/src/providers/bedrock/mod.rs b/lib/components/fabro-llm/src/providers/bedrock/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/bedrock/mod.rs rename to lib/components/fabro-llm/src/providers/bedrock/mod.rs diff --git a/lib/crates/fabro-llm/src/providers/bedrock/sigv4.rs b/lib/components/fabro-llm/src/providers/bedrock/sigv4.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/bedrock/sigv4.rs rename to lib/components/fabro-llm/src/providers/bedrock/sigv4.rs diff --git a/lib/crates/fabro-llm/src/providers/common.rs b/lib/components/fabro-llm/src/providers/common.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/common.rs rename to lib/components/fabro-llm/src/providers/common.rs diff --git a/lib/crates/fabro-llm/src/providers/fabro_server.rs b/lib/components/fabro-llm/src/providers/fabro_server.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/fabro_server.rs rename to lib/components/fabro-llm/src/providers/fabro_server.rs diff --git a/lib/crates/fabro-llm/src/providers/gemini.rs b/lib/components/fabro-llm/src/providers/gemini.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/gemini.rs rename to lib/components/fabro-llm/src/providers/gemini.rs diff --git a/lib/crates/fabro-llm/src/providers/mod.rs b/lib/components/fabro-llm/src/providers/mod.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/mod.rs rename to lib/components/fabro-llm/src/providers/mod.rs diff --git a/lib/crates/fabro-llm/src/providers/openai.rs b/lib/components/fabro-llm/src/providers/openai.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/openai.rs rename to lib/components/fabro-llm/src/providers/openai.rs diff --git a/lib/crates/fabro-llm/src/providers/openai_compatible.rs b/lib/components/fabro-llm/src/providers/openai_compatible.rs similarity index 100% rename from lib/crates/fabro-llm/src/providers/openai_compatible.rs rename to lib/components/fabro-llm/src/providers/openai_compatible.rs diff --git a/lib/crates/fabro-llm/src/retry.rs b/lib/components/fabro-llm/src/retry.rs similarity index 100% rename from lib/crates/fabro-llm/src/retry.rs rename to lib/components/fabro-llm/src/retry.rs diff --git a/lib/crates/fabro-llm/src/token_count.rs b/lib/components/fabro-llm/src/token_count.rs similarity index 100% rename from lib/crates/fabro-llm/src/token_count.rs rename to lib/components/fabro-llm/src/token_count.rs diff --git a/lib/crates/fabro-llm/src/tools.rs b/lib/components/fabro-llm/src/tools.rs similarity index 100% rename from lib/crates/fabro-llm/src/tools.rs rename to lib/components/fabro-llm/src/tools.rs diff --git a/lib/crates/fabro-llm/src/transport.rs b/lib/components/fabro-llm/src/transport.rs similarity index 100% rename from lib/crates/fabro-llm/src/transport.rs rename to lib/components/fabro-llm/src/transport.rs diff --git a/lib/crates/fabro-llm/src/types.rs b/lib/components/fabro-llm/src/types.rs similarity index 100% rename from lib/crates/fabro-llm/src/types.rs rename to lib/components/fabro-llm/src/types.rs diff --git a/lib/crates/fabro-llm/tests/integration.rs b/lib/components/fabro-llm/tests/integration.rs similarity index 96% rename from lib/crates/fabro-llm/tests/integration.rs rename to lib/components/fabro-llm/tests/integration.rs index 9eb558fd5..25d7e5f00 100644 --- a/lib/crates/fabro-llm/tests/integration.rs +++ b/lib/components/fabro-llm/tests/integration.rs @@ -683,3 +683,26 @@ async fn gemini_multi_turn_cache() { let adapter = GeminiAdapter::new(api_key); run_multi_turn_cache_test(&adapter, "gemini-2.5-flash", 0.5, Some(0.0)).await; } + +/// Prompt caching for Claude routed through OpenRouter: the catalog row opts +/// into explicit `cache_control` breakpoints, and OpenRouter must forward +/// them to Anthropic for cache reads to appear. Guards the end-to-end +/// passthrough the wire tests can't see. +#[fabro_macros::e2e_test(live("OPENROUTER_API_KEY"))] +async fn openrouter_claude_multi_turn_cache() { + let api_key = + std::env::var(EnvVars::OPENROUTER_API_KEY).expect("OPENROUTER_API_KEY must be set"); + let overrides: LlmCatalogSettings = toml::from_str( + r" +[providers.openrouter] +enabled = true +", + ) + .expect("OpenRouter catalog override should parse"); + let catalog = Catalog::from_builtin_with_overrides(&overrides) + .expect("enabled OpenRouter catalog should build"); + let adapter = OpenAiCompatibleAdapter::new(api_key, "https://openrouter.ai/api/v1") + .with_name("openrouter") + .with_catalog(Arc::new(catalog)); + run_multi_turn_cache_test(&adapter, "claude-haiku-4-5", 0.5, Some(0.0)).await; +} diff --git a/lib/crates/fabro-llm/tests/it/main.rs b/lib/components/fabro-llm/tests/it/main.rs similarity index 100% rename from lib/crates/fabro-llm/tests/it/main.rs rename to lib/components/fabro-llm/tests/it/main.rs diff --git a/lib/crates/fabro-llm/tests/it/support.rs b/lib/components/fabro-llm/tests/it/support.rs similarity index 100% rename from lib/crates/fabro-llm/tests/it/support.rs rename to lib/components/fabro-llm/tests/it/support.rs diff --git a/lib/crates/fabro-llm/tests/it/wire/anthropic.rs b/lib/components/fabro-llm/tests/it/wire/anthropic.rs similarity index 100% rename from lib/crates/fabro-llm/tests/it/wire/anthropic.rs rename to lib/components/fabro-llm/tests/it/wire/anthropic.rs diff --git a/lib/crates/fabro-llm/tests/it/wire/gemini.rs b/lib/components/fabro-llm/tests/it/wire/gemini.rs similarity index 100% rename from lib/crates/fabro-llm/tests/it/wire/gemini.rs rename to lib/components/fabro-llm/tests/it/wire/gemini.rs diff --git a/lib/crates/fabro-llm/tests/it/wire/mod.rs b/lib/components/fabro-llm/tests/it/wire/mod.rs similarity index 100% rename from lib/crates/fabro-llm/tests/it/wire/mod.rs rename to lib/components/fabro-llm/tests/it/wire/mod.rs diff --git a/lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs b/lib/components/fabro-llm/tests/it/wire/openai_compatible.rs similarity index 80% rename from lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs rename to lib/components/fabro-llm/tests/it/wire/openai_compatible.rs index b26416396..c8facd920 100644 --- a/lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +++ b/lib/components/fabro-llm/tests/it/wire/openai_compatible.rs @@ -10,7 +10,8 @@ use fabro_llm::types::{ Message, ReasoningEffort, Request, ResponseFormat, ResponseFormatType, ToolChoice, ToolDefinition, }; -use fabro_model::Catalog; +use fabro_model::catalog::LlmCatalogSettings; +use fabro_model::{Catalog, ProviderId}; use httpmock::prelude::*; use crate::support::{ @@ -256,6 +257,134 @@ async fn encode_kimi_k3_uses_catalog_reasoning_and_sampling_controls() { assert!(capture.body.get("top_p").is_none()); } +/// Counts JSON objects anywhere in `value` carrying a `cache_control` key. +fn count_cache_control_breakpoints(value: &serde_json::Value) -> usize { + match value { + serde_json::Value::Object(map) => { + usize::from(map.contains_key("cache_control")) + + map + .values() + .map(count_cache_control_breakpoints) + .sum::() + } + serde_json::Value::Array(items) => items.iter().map(count_cache_control_breakpoints).sum(), + _ => 0, + } +} + +/// Builtin catalog with the opt-in OpenRouter provider enabled. +fn openrouter_catalog() -> Arc { + let overrides: LlmCatalogSettings = toml::from_str("[providers.openrouter]\nenabled = true\n") + .expect("override TOML should parse"); + Arc::new( + Catalog::from_builtin_with_overrides(&overrides) + .expect("catalog with OpenRouter enabled should build"), + ) +} + +/// System + tools + two user turns against an OpenRouter model. +fn openrouter_multi_turn(model: &str) -> Request { + Request { + messages: vec![ + Message::system("You are a careful reviewer."), + Message::user("Review this."), + Message::assistant("Looking now."), + Message::user("Focus on the tests."), + ], + ..corpus_tools(model, None) + } +} + +/// OpenRouter serves Claude through this adapter, and Anthropic prompt +/// caching is opt-in per request: OpenRouter only forwards a cache write when +/// the body carries explicit ephemeral `cache_control` breakpoints (OpenAI +/// models cache implicitly; Anthropic models never do). The catalog row +/// declares `cache_control_breakpoints`, so the encoded request must mark the +/// cacheable prefix — otherwise every turn bills at the full uncached input +/// rate. +#[tokio::test] +async fn encode_openrouter_claude_marks_prompt_cache_breakpoints() { + let catalog = openrouter_catalog(); + let model = catalog + .get_on_provider(&ProviderId::new("openrouter"), "claude-fable-5") + .expect("OpenRouter Claude row should exist in the built-in catalog"); + assert!(model.features.prompt_cache); + assert!(model.features.cache_control_breakpoints); + + let request = openrouter_multi_turn("claude-fable-5"); + let capture = encode_capture_with(&request, move |adapter| { + adapter.with_name("openrouter").with_catalog(catalog) + }) + .await; + + assert_eq!(capture.body["model"], "anthropic/claude-fable-5"); + let messages = &capture.body["messages"]; + // The system prompt converts to parts form carrying a breakpoint; it + // covers the tool definitions too (tools precede system upstream). + assert_eq!(messages[0]["content"][0]["type"], "text"); + assert_eq!( + messages[0]["content"][0]["text"], + "You are a careful reviewer." + ); + assert_eq!( + messages[0]["content"][0]["cache_control"]["type"], + "ephemeral" + ); + // The second-to-last user turn carries the conversation breakpoint... + assert_eq!(messages[1]["content"][0]["text"], "Review this."); + assert_eq!( + messages[1]["content"][0]["cache_control"]["type"], + "ephemeral" + ); + // ...and the newest turn stays in plain-string form. + assert_eq!(messages[3]["content"], "Focus on the tests."); + assert_eq!(count_cache_control_breakpoints(&capture.body), 2); +} + +/// Models with implicit (server-side) caching must NOT get breakpoints even +/// though they support prompt caching — the annotation is an Anthropic-ism +/// the catalog row has to opt into. +#[tokio::test] +async fn encode_openrouter_implicit_cache_model_stays_plain() { + let catalog = openrouter_catalog(); + let model = catalog + .get_on_provider(&ProviderId::new("openrouter"), "gpt-5.6-luna") + .expect("OpenRouter GPT row should exist in the built-in catalog"); + assert!(model.features.prompt_cache); + assert!(!model.features.cache_control_breakpoints); + + let request = openrouter_multi_turn("gpt-5.6-luna"); + let capture = encode_capture_with(&request, move |adapter| { + adapter.with_name("openrouter").with_catalog(catalog) + }) + .await; + + assert_eq!(count_cache_control_breakpoints(&capture.body), 0); + assert_eq!( + capture.body["messages"][0]["content"], + "You are a careful reviewer." + ); +} + +/// `provider_options.openrouter.auto_cache = false` disables the breakpoints, +/// and the control key is consumed rather than merged into the body. +#[tokio::test] +async fn encode_openrouter_claude_auto_cache_opt_out() { + let request = Request { + provider_options: Some(serde_json::json!({"openrouter": {"auto_cache": false}})), + ..openrouter_multi_turn("claude-fable-5") + }; + let capture = encode_capture_with(&request, move |adapter| { + adapter + .with_name("openrouter") + .with_catalog(openrouter_catalog()) + }) + .await; + + assert_eq!(count_cache_control_breakpoints(&capture.body), 0); + assert!(capture.body.get("auto_cache").is_none()); +} + /// The provider_options namespace key is the runtime adapter NAME, not a /// static "openai_compatible" key (pinned in-module by /// `provider_options_uses_adapter_name`; this pins it from outside). diff --git a/lib/crates/fabro-llm/tests/it/wire/openai_responses.rs b/lib/components/fabro-llm/tests/it/wire/openai_responses.rs similarity index 100% rename from lib/crates/fabro-llm/tests/it/wire/openai_responses.rs rename to lib/components/fabro-llm/tests/it/wire/openai_responses.rs diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__count_tokens_wire_shape.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__count_tokens_wire_shape.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__count_tokens_wire_shape.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__count_tokens_wire_shape.snap index 566a6a703..c3b630136 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__count_tokens_wire_shape.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__count_tokens_wire_shape.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_error_identity.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_error_identity.snap similarity index 76% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_error_identity.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_error_identity.snap index a51d0f135..9fb304d73 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_error_identity.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_error_identity.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_identity.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_identity.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_identity.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_identity.snap index 187e2db49..6bb0e2544 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_identity.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_identity.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_route.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_route.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_route.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_route.snap index 425a60a06..4620d7897 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_route.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__custom_named_stream_route.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_max_tokens_stop_reason.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_max_tokens_stop_reason.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_max_tokens_stop_reason.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_max_tokens_stop_reason.snap index c03f511a7..b10bff658 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_max_tokens_stop_reason.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_max_tokens_stop_reason.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_thinking_and_redacted_thinking.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_thinking_and_redacted_thinking.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_thinking_and_redacted_thinking.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_thinking_and_redacted_thinking.snap index ca01b2a8d..bf89303f3 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_thinking_and_redacted_thinking.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_thinking_and_redacted_thinking.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_tool_use_stop_reason.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_tool_use_stop_reason.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_tool_use_stop_reason.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_tool_use_stop_reason.snap index b450339fc..2c1ec7d19 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_tool_use_stop_reason.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__decode_tool_use_stop_reason.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_audio_attachment.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_audio_attachment.snap similarity index 86% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_audio_attachment.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_audio_attachment.snap index 69a05af0e..54793a2b6 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_audio_attachment.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_audio_attachment.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_bad_file_path_attachments_dropped.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_bad_file_path_attachments_dropped.snap similarity index 82% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_bad_file_path_attachments_dropped.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_bad_file_path_attachments_dropped.snap index 498b4748b..c2c0acfb3 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_bad_file_path_attachments_dropped.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_bad_file_path_attachments_dropped.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_inline_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_inline_attachments.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_inline_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_inline_attachments.snap index 443e1e221..5fda9d667 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_inline_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_inline_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_multi_turn.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_multi_turn.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_multi_turn.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_multi_turn.snap index bc576585a..6042822b8 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_multi_turn.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_multi_turn.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_prompt_cache_with_catalog.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_prompt_cache_with_catalog.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_prompt_cache_with_catalog.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_prompt_cache_with_catalog.snap index f29eca145..5a6979bd6 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_prompt_cache_with_catalog.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_prompt_cache_with_catalog.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_provider_options_anthropic_namespace.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_provider_options_anthropic_namespace.snap similarity index 82% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_provider_options_anthropic_namespace.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_provider_options_anthropic_namespace.snap index 1475f5820..5c5107443 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_provider_options_anthropic_namespace.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_provider_options_anthropic_namespace.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_reasoning_effort_with_levels_catalog.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_reasoning_effort_with_levels_catalog.snap similarity index 85% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_reasoning_effort_with_levels_catalog.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_reasoning_effort_with_levels_catalog.snap index 59e4b54ef..45d4779ed 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_reasoning_effort_with_levels_catalog.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_reasoning_effort_with_levels_catalog.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_object.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_object.snap similarity index 84% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_object.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_object.snap index fe60412fb..f505a20d9 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_object.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_object.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_schema.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_schema.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_schema.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_schema.snap index e757af0a9..b8c0aa6a1 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_schema.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_response_format_json_schema.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_sampling_params.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_sampling_params.snap similarity index 86% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_sampling_params.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_sampling_params.snap index b50a93df6..c4abd0eda 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_sampling_params.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_sampling_params.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_thinking_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_thinking_round_trip.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_thinking_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_thinking_round_trip.snap index 5fedc6ff1..1619de37c 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_thinking_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_thinking_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_auto.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_auto.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_auto.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_auto.snap index d181d4796..552c709ce 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_auto.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_auto.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_named.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_named.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_named.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_named.snap index 5d7d5214b..868f115d1 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_named.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_named.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_none.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_none.snap similarity index 81% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_none.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_none.snap index 4fbbbaa5f..b9119a960 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_none.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_none.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_required.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_required.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_required.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_required.snap index b114a2048..35eb0a281 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_required.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_choice_required.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_round_trip.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_round_trip.snap index f744b1d64..50cce34a4 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_tool_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_url_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_url_attachments.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_url_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_url_attachments.snap index f4abc02be..438fbc222 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_url_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__encode_url_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_error_event_mid_stream.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_error_event_mid_stream.snap similarity index 76% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_error_event_mid_stream.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_error_event_mid_stream.snap index d2b78a3a2..6c8cc6f1a 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_error_event_mid_stream.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_error_event_mid_stream.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_events.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_events.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_events.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_events.snap index 5456021f0..04d15c0b8 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_events.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_events.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_request.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_request.snap similarity index 82% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_request.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_request.snap index 8b641b028..508c9c998 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_request.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_text_happy_path_request.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_thinking_with_signature_delta.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_thinking_with_signature_delta.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_thinking_with_signature_delta.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_thinking_with_signature_delta.snap index a7987f9e5..68e599c83 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_thinking_with_signature_delta.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_thinking_with_signature_delta.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_tool_call_deltas.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_tool_call_deltas.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_tool_call_deltas.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_tool_call_deltas.snap index 9eaa938d8..18c46f075 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_tool_call_deltas.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_tool_call_deltas.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_without_message_stop_emits_no_finish.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_without_message_stop_emits_no_finish.snap similarity index 81% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_without_message_stop_emits_no_finish.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_without_message_stop_emits_no_finish.snap index a12eec3d6..d2f4d0df0 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_without_message_stop_emits_no_finish.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__stream_without_message_stop_emits_no_finish.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_decode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_decode.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_decode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_decode.snap index 1d0a970e5..f4afb357d 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_decode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_decode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_encode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_encode.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_encode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_encode.snap index 63678178e..50f5a4167 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_encode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__anthropic__system_and_tools_encode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/anthropic.rs +source: lib/components/fabro-llm/tests/it/wire/anthropic.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__count_tokens_wire_shape.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__count_tokens_wire_shape.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__count_tokens_wire_shape.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__count_tokens_wire_shape.snap index 4056e3979..2d75a657c 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__count_tokens_wire_shape.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__count_tokens_wire_shape.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_http_error_identity.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_http_error_identity.snap similarity index 71% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_http_error_identity.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_http_error_identity.snap index 44df522ff..3f983d663 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_http_error_identity.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_http_error_identity.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_stream_identity.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_stream_identity.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_stream_identity.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_stream_identity.snap index 2f0106cc6..121057409 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_stream_identity.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__custom_named_stream_identity.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_function_call_with_thought_signature.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_function_call_with_thought_signature.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_function_call_with_thought_signature.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_function_call_with_thought_signature.snap index b0aa96702..751e5782b 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_function_call_with_thought_signature.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_function_call_with_thought_signature.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_max_tokens_finish_reason.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_max_tokens_finish_reason.snap new file mode 100644 index 000000000..a8a887691 --- /dev/null +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_max_tokens_finish_reason.snap @@ -0,0 +1,5 @@ +--- +source: lib/components/fabro-llm/tests/it/wire/gemini.rs +expression: rendered +--- +"length" diff --git a/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_safety_finish_reason.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_safety_finish_reason.snap new file mode 100644 index 000000000..627e69083 --- /dev/null +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_safety_finish_reason.snap @@ -0,0 +1,5 @@ +--- +source: lib/components/fabro-llm/tests/it/wire/gemini.rs +expression: rendered +--- +"content_filter" diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_thought_parts.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_thought_parts.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_thought_parts.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_thought_parts.snap index 4a45d6508..b20aec438 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_thought_parts.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_thought_parts.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_usage_arithmetic.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_usage_arithmetic.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_usage_arithmetic.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_usage_arithmetic.snap index eacc7a47f..d32b27d72 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_usage_arithmetic.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_usage_arithmetic.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_audio_attachment.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_audio_attachment.snap similarity index 89% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_audio_attachment.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_audio_attachment.snap index d3f6c4a7f..03246da8b 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_audio_attachment.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_audio_attachment.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_bad_file_path_attachments_dropped.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_bad_file_path_attachments_dropped.snap similarity index 86% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_bad_file_path_attachments_dropped.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_bad_file_path_attachments_dropped.snap index b4605fdbb..a27b54528 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_bad_file_path_attachments_dropped.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_bad_file_path_attachments_dropped.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_inline_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_inline_attachments.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_inline_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_inline_attachments.snap index 130c517ee..2db4f67e3 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_inline_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_inline_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_multi_turn.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_multi_turn.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_multi_turn.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_multi_turn.snap index af95759cb..a8a547cd6 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_multi_turn.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_multi_turn.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_can_override_safety_settings.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_can_override_safety_settings.snap similarity index 80% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_can_override_safety_settings.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_can_override_safety_settings.snap index 105e76111..6e2744c76 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_can_override_safety_settings.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_can_override_safety_settings.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_gemini_namespace.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_gemini_namespace.snap similarity index 87% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_gemini_namespace.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_gemini_namespace.snap index e6b616613..71baf9f7a 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_gemini_namespace.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_provider_options_gemini_namespace.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_reasoning_effort_with_levels_catalog.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_reasoning_effort_with_levels_catalog.snap similarity index 85% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_reasoning_effort_with_levels_catalog.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_reasoning_effort_with_levels_catalog.snap index 83f70e81f..2e71f5623 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_reasoning_effort_with_levels_catalog.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_reasoning_effort_with_levels_catalog.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_object.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_object.snap similarity index 87% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_object.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_object.snap index d16ceda4c..e04165dfd 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_object.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_object.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_schema.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_schema.snap similarity index 90% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_schema.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_schema.snap index 7a6a39742..e8fc16720 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_schema.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_response_format_json_schema.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_sampling_params.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_sampling_params.snap similarity index 88% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_sampling_params.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_sampling_params.snap index 1e9350fcd..87e6756df 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_sampling_params.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_sampling_params.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_thinking_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_thinking_round_trip.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_thinking_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_thinking_round_trip.snap index c0cdbd040..20f0d4724 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_thinking_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_thinking_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_auto.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_auto.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_auto.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_auto.snap index 186a9afce..9682d4bd0 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_auto.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_auto.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_named.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_named.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_named.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_named.snap index d31af7c7d..ebab86778 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_named.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_named.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_none.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_none.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_none.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_none.snap index 7bb3b0bfa..9e7fe12ce 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_none.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_none.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_required.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_required.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_required.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_required.snap index 7832bc141..0fee7b81d 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_required.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_choice_required.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_round_trip.snap similarity index 97% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_round_trip.snap index d0fdee6ce..245c3cd85 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_tool_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_url_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_url_attachments.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_url_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_url_attachments.snap index dcf7ef4e2..c3f9bdee7 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_url_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__encode_url_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_end_synthesizes_finish_without_finish_reason.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_end_synthesizes_finish_without_finish_reason.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_end_synthesizes_finish_without_finish_reason.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_end_synthesizes_finish_without_finish_reason.snap index de753baf2..5ceb7e1d0 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_end_synthesizes_finish_without_finish_reason.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_end_synthesizes_finish_without_finish_reason.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_function_call.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_function_call.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_function_call.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_function_call.snap index 7ff680eb5..22807bad4 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_function_call.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_function_call.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_events.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_events.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_events.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_events.snap index f3db67d70..f240b1c76 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_events.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_events.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_request.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_request.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_request.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_request.snap index cb853aaf9..90edf2a99 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_request.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_text_happy_path_request.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_thought_parts.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_thought_parts.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_thought_parts.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_thought_parts.snap index 88f094ab8..bf9e067fd 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_thought_parts.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__stream_thought_parts.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_decode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_decode.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_decode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_decode.snap index e6748c07e..f5e0bd096 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_decode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_decode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_encode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_encode.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_encode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_encode.snap index c6d3c6a48..2a445751f 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_encode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__system_and_tools_encode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs +source: lib/components/fabro-llm/tests/it/wire/gemini.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_reasoning_content_as_thinking.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_reasoning_content_as_thinking.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_reasoning_content_as_thinking.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_reasoning_content_as_thinking.snap index ff075964d..bd1327b8f 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_reasoning_content_as_thinking.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_reasoning_content_as_thinking.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_tool_calls_with_string_arguments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_tool_calls_with_string_arguments.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_tool_calls_with_string_arguments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_tool_calls_with_string_arguments.snap index 900b50942..0d52c2d73 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_tool_calls_with_string_arguments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_tool_calls_with_string_arguments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_openrouter_cost_and_cache_write.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_openrouter_cost_and_cache_write.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_openrouter_cost_and_cache_write.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_openrouter_cost_and_cache_write.snap index f473db75a..cff1eaf40 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_openrouter_cost_and_cache_write.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_openrouter_cost_and_cache_write.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_parses_token_details.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_parses_token_details.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_parses_token_details.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_parses_token_details.snap index c212ac7c2..8e2239f87 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_parses_token_details.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__decode_usage_parses_token_details.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_audio_attachment.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_audio_attachment.snap similarity index 75% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_audio_attachment.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_audio_attachment.snap index f647c24c0..101179520 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_audio_attachment.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_audio_attachment.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_bad_file_path_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_bad_file_path_attachments.snap similarity index 78% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_bad_file_path_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_bad_file_path_attachments.snap index 7b3833469..485ae8446 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_bad_file_path_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_bad_file_path_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_inline_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_inline_attachments.snap similarity index 78% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_inline_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_inline_attachments.snap index fcc58e5cf..10995e1e8 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_inline_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_inline_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_multi_turn.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_multi_turn.snap similarity index 85% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_multi_turn.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_multi_turn.snap index 6e4c35fa0..cbb42e14d 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_multi_turn.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_multi_turn.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_keyed_by_adapter_name.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_keyed_by_adapter_name.snap similarity index 73% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_keyed_by_adapter_name.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_keyed_by_adapter_name.snap index 2b0718398..75a4a2634 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_keyed_by_adapter_name.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_keyed_by_adapter_name.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_other_namespace_ignored.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_other_namespace_ignored.snap similarity index 69% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_other_namespace_ignored.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_other_namespace_ignored.snap index bb8c27f58..f3e189bfe 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_other_namespace_ignored.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_provider_options_other_namespace_ignored.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_object.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_object.snap similarity index 75% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_object.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_object.snap index 84f716422..24763c82f 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_object.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_object.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_schema.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_schema.snap similarity index 87% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_schema.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_schema.snap index 13961444e..7e35babc2 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_schema.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_response_format_json_schema.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_sampling_params.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_sampling_params.snap similarity index 76% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_sampling_params.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_sampling_params.snap index 6a345a9bf..74e26fcd1 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_sampling_params.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_sampling_params.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_thinking_round_trip_as_reasoning_content.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_thinking_round_trip_as_reasoning_content.snap similarity index 84% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_thinking_round_trip_as_reasoning_content.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_thinking_round_trip_as_reasoning_content.snap index 210eaa446..86c83a213 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_thinking_round_trip_as_reasoning_content.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_thinking_round_trip_as_reasoning_content.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_auto.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_auto.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_auto.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_auto.snap index 7b9bdab82..ea913ebfb 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_auto.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_auto.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_named.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_named.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_named.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_named.snap index 5da2b548a..442e8a42d 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_named.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_named.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_none.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_none.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_none.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_none.snap index a7ace0225..919c82db5 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_none.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_none.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_required.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_required.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_required.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_required.snap index d77d00bef..665fa3c5a 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_required.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_choice_required.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_round_trip.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_round_trip.snap index 43e81924f..d601c24dc 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_tool_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_url_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_url_attachments.snap similarity index 78% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_url_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_url_attachments.snap index fcc58e5cf..10995e1e8 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_url_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__encode_url_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_reasoning_deltas.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_reasoning_deltas.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_reasoning_deltas.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_reasoning_deltas.snap index 29f670850..ee27dbea7 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_reasoning_deltas.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_reasoning_deltas.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_events.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_events.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_events.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_events.snap index 0178193a9..0bb05a55b 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_events.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_events.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_request.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_request.snap similarity index 72% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_request.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_request.snap index 502647610..fa711fab9 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_request.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_text_happy_path_request.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_tool_call_deltas.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_tool_call_deltas.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_tool_call_deltas.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_tool_call_deltas.snap index fa79eefb0..fdce5d082 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_tool_call_deltas.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_tool_call_deltas.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_usage_openrouter_cost.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_usage_openrouter_cost.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_usage_openrouter_cost.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_usage_openrouter_cost.snap index da24b9b97..a560e0d83 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_usage_openrouter_cost.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_usage_openrouter_cost.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- [ diff --git a/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_or_content_synthesizes_nothing.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_or_content_synthesizes_nothing.snap new file mode 100644 index 000000000..082fdf19c --- /dev/null +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_or_content_synthesizes_nothing.snap @@ -0,0 +1,5 @@ +--- +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs +expression: rendered +--- +[] diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_synthesizes_finish_when_content_started.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_synthesizes_finish_when_content_started.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_synthesizes_finish_when_content_started.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_synthesizes_finish_when_content_started.snap index dc7dcfe6a..85127a104 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_synthesizes_finish_when_content_started.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_synthesizes_finish_when_content_started.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_decode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_decode.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_decode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_decode.snap index 078f9bb3b..eb24f04e8 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_decode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_decode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_encode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_encode.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_encode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_encode.snap index a732b5d44..675c550b4 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_encode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__system_and_tools_encode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs +source: lib/components/fabro-llm/tests/it/wire/openai_compatible.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__count_tokens_wire_shape.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__count_tokens_wire_shape.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__count_tokens_wire_shape.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__count_tokens_wire_shape.snap index 8524cc347..6491674c9 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__count_tokens_wire_shape.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__count_tokens_wire_shape.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__custom_named_stream_failed_event_identity.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__custom_named_stream_failed_event_identity.snap similarity index 74% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__custom_named_stream_failed_event_identity.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__custom_named_stream_failed_event_identity.snap index 5241566c3..3fb9cbd0b 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__custom_named_stream_failed_event_identity.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__custom_named_stream_failed_event_identity.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_incomplete_status_maps_to_length.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_incomplete_status_maps_to_length.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_incomplete_status_maps_to_length.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_incomplete_status_maps_to_length.snap index bb1e06744..fa27da4b8 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_incomplete_status_maps_to_length.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_incomplete_status_maps_to_length.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_reasoning_and_function_call_items.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_reasoning_and_function_call_items.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_reasoning_and_function_call_items.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_reasoning_and_function_call_items.snap index 48a5b2b8f..e4597626a 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_reasoning_and_function_call_items.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_reasoning_and_function_call_items.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_usage_subtracts_cached_and_reasoning.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_usage_subtracts_cached_and_reasoning.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_usage_subtracts_cached_and_reasoning.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_usage_subtracts_cached_and_reasoning.snap index 4c65c6906..98813798b 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_usage_subtracts_cached_and_reasoning.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__decode_usage_subtracts_cached_and_reasoning.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_audio_attachment.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_audio_attachment.snap similarity index 87% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_audio_attachment.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_audio_attachment.snap index 5e12b469e..52d5331bf 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_audio_attachment.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_audio_attachment.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_bad_file_path_attachments_dropped.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_bad_file_path_attachments_dropped.snap similarity index 88% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_bad_file_path_attachments_dropped.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_bad_file_path_attachments_dropped.snap index 93b5f16a3..5fd2a64fd 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_bad_file_path_attachments_dropped.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_bad_file_path_attachments_dropped.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_codex_mode_forces_streaming_and_omits_params.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_codex_mode_forces_streaming_and_omits_params.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_codex_mode_forces_streaming_and_omits_params.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_codex_mode_forces_streaming_and_omits_params.snap index 655b2e204..f57e05f8b 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_codex_mode_forces_streaming_and_omits_params.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_codex_mode_forces_streaming_and_omits_params.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_dual_id_tool_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_dual_id_tool_round_trip.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_dual_id_tool_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_dual_id_tool_round_trip.snap index 990bf91a8..5c98c82d4 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_dual_id_tool_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_dual_id_tool_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_inline_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_inline_attachments.snap similarity index 90% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_inline_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_inline_attachments.snap index 3d74e1cdd..172b57110 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_inline_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_inline_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_multi_turn.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_multi_turn.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_multi_turn.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_multi_turn.snap index 3e049c7fe..473ce2c5d 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_multi_turn.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_multi_turn.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_opaque_items_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_opaque_items_round_trip.snap similarity index 92% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_opaque_items_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_opaque_items_round_trip.snap index acfb705af..d672689b1 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_opaque_items_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_opaque_items_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_provider_options_openai_namespace.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_provider_options_openai_namespace.snap similarity index 83% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_provider_options_openai_namespace.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_provider_options_openai_namespace.snap index cd69aff6a..c2f03af2c 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_provider_options_openai_namespace.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_provider_options_openai_namespace.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_reasoning_effort_with_levels_catalog.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_reasoning_effort_with_levels_catalog.snap similarity index 85% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_reasoning_effort_with_levels_catalog.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_reasoning_effort_with_levels_catalog.snap index c9efcd60f..57d01b2f9 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_reasoning_effort_with_levels_catalog.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_reasoning_effort_with_levels_catalog.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_object.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_object.snap similarity index 85% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_object.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_object.snap index f4ccc269d..05ffc5a7d 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_object.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_object.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_schema.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_schema.snap similarity index 90% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_schema.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_schema.snap index 3fa0b83e2..1fe952e6c 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_schema.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_response_format_json_schema.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_sampling_params.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_sampling_params.snap similarity index 87% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_sampling_params.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_sampling_params.snap index 3634e5fb3..783c7ab18 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_sampling_params.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_sampling_params.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_thinking_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_thinking_round_trip.snap similarity index 91% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_thinking_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_thinking_round_trip.snap index e4b3593b0..a232e2e6a 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_thinking_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_thinking_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_auto.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_auto.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_auto.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_auto.snap index af2dfe6a9..b4b6aef6c 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_auto.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_auto.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_named.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_named.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_named.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_named.snap index cc00b7891..cb462f295 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_named.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_named.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_none.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_none.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_none.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_none.snap index fda379759..9c11e0609 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_none.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_none.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_required.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_required.snap similarity index 93% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_required.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_required.snap index b9e9d82e0..3b9ad1745 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_required.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_choice_required.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_round_trip.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_round_trip.snap similarity index 96% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_round_trip.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_round_trip.snap index 97c1654d9..f542622fb 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_round_trip.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_tool_round_trip.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_url_attachments.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_url_attachments.snap similarity index 90% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_url_attachments.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_url_attachments.snap index 9b67b8abc..b85c3da20 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_url_attachments.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__encode_url_attachments.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_failed_event_maps_to_error.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_failed_event_maps_to_error.snap similarity index 73% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_failed_event_maps_to_error.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_failed_event_maps_to_error.snap index 68057a6b7..191a90c06 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_failed_event_maps_to_error.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_failed_event_maps_to_error.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_incomplete_maps_to_length.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_incomplete_maps_to_length.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_incomplete_maps_to_length.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_incomplete_maps_to_length.snap index b09f7ef94..e274eb534 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_incomplete_maps_to_length.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_incomplete_maps_to_length.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_reasoning_summary_deltas.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_reasoning_summary_deltas.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_reasoning_summary_deltas.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_reasoning_summary_deltas.snap index 62cc38280..47f067f13 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_reasoning_summary_deltas.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_reasoning_summary_deltas.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_events.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_events.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_events.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_events.snap index 68b18e7c2..cde7ca175 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_events.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_events.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_request.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_request.snap similarity index 84% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_request.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_request.snap index c5f2d85eb..1dbf93c1a 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_request.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_text_happy_path_request.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_tool_call_deltas.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_tool_call_deltas.snap similarity index 97% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_tool_call_deltas.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_tool_call_deltas.snap index 66a9c3d89..a4cc2e686 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_tool_call_deltas.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__stream_tool_call_deltas.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- [ diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_decode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_decode.snap similarity index 95% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_decode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_decode.snap index d5d935100..01d4e4fd4 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_decode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_decode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_encode.snap b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_encode.snap similarity index 94% rename from lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_encode.snap rename to lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_encode.snap index 5c7aca225..165c53cc3 100644 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_encode.snap +++ b/lib/components/fabro-llm/tests/it/wire/snapshots/it__wire__openai_responses__system_and_tools_encode.snap @@ -1,5 +1,5 @@ --- -source: lib/crates/fabro-llm/tests/it/wire/openai_responses.rs +source: lib/components/fabro-llm/tests/it/wire/openai_responses.rs expression: rendered --- { diff --git a/lib/crates/fabro-manifest/Cargo.toml b/lib/components/fabro-manifest/Cargo.toml similarity index 68% rename from lib/crates/fabro-manifest/Cargo.toml rename to lib/components/fabro-manifest/Cargo.toml index 417dc9af0..0cc8a695a 100644 --- a/lib/crates/fabro-manifest/Cargo.toml +++ b/lib/components/fabro-manifest/Cargo.toml @@ -14,12 +14,12 @@ workspace = true [dependencies] anyhow.workspace = true -fabro-api = { path = "../fabro-api" } -fabro-config = { path = "../fabro-config" } +fabro-api = { path = "../../foundation/fabro-api" } +fabro-config = { path = "../../foundation/fabro-config" } fabro-github = { path = "../fabro-github" } fabro-graphviz = { path = "../fabro-graphviz" } -fabro-template = { path = "../fabro-template" } -fabro-types = { path = "../fabro-types" } +fabro-template = { path = "../../foundation/fabro-template" } +fabro-types = { path = "../../foundation/fabro-types" } fabro-workflow = { path = "../fabro-workflow" } git2.workspace = true toml.workspace = true diff --git a/lib/crates/fabro-manifest/src/lib.rs b/lib/components/fabro-manifest/src/lib.rs similarity index 95% rename from lib/crates/fabro-manifest/src/lib.rs rename to lib/components/fabro-manifest/src/lib.rs index 6acac3de9..f2f1b066b 100644 --- a/lib/crates/fabro-manifest/src/lib.rs +++ b/lib/components/fabro-manifest/src/lib.rs @@ -386,16 +386,44 @@ fn collect_workflow_files( for node in graph.nodes.values() { if let Some(prompt_ref) = node.attrs.get("prompt").and_then(AttrValue::as_str) { - if prompt_ref.starts_with('@') { - let bundled = collect_bundled_file( + if !prompt_ref.starts_with('@') { + collect_template_include_files( files, - workflow_base_dir, context.cwd, - prompt_ref.trim_start_matches('@'), - types::ManifestFileRefType::FileInline, - manifest_attr_reference_kind(AttributeScope::Node, "prompt", prompt_ref)?, - Some(workflow.dot_path.clone()), + TemplateSource::new( + workflow.dot_path.clone(), + workflow_template_root.clone(), + prompt_ref.to_owned(), + ), + Some(&workflow.dot_path), + &context.inputs, )?; + } + } + + for (name, value) in &node.attrs { + let Some(value) = value.as_str() else { + continue; + }; + let Some(ReferenceKind::FileInline) = + reference_kind_for_attribute(AttributeScope::Node, name, value) + else { + continue; + }; + let reference = value.strip_prefix('@').ok_or_else(|| { + anyhow!("file inline reference must start with '@': {name}={value}") + })?; + let bundled = collect_bundled_file( + files, + workflow_base_dir, + context.cwd, + reference, + types::ManifestFileRefType::FileInline, + ReferenceKind::FileInline, + Some(workflow.dot_path.clone()), + )?; + + if name == "prompt" { let source = std::fs::read_to_string(&bundled.absolute_path).with_context(|| { format!("Failed to read {}", bundled.absolute_path.display()) @@ -409,18 +437,6 @@ fn collect_workflow_files( Some(&bundled.path), &context.inputs, )?; - } else { - collect_template_include_files( - files, - context.cwd, - TemplateSource::new( - workflow.dot_path.clone(), - workflow_template_root.clone(), - prompt_ref.to_owned(), - ), - Some(&workflow.dot_path), - &context.inputs, - )?; } } @@ -897,6 +913,52 @@ mod tests { )])) } + fn assert_manifest_bundles_output_schema_file(node_attributes: &str) { + let temp = tempfile::tempdir().unwrap(); + let project = temp.path(); + let workflow_dir = project.join(".fabro/workflows/demo"); + let schema_source = r#"{"type":"object","required":["ok"]}"#; + std::fs::create_dir_all(workflow_dir.join("schemas")).unwrap(); + std::fs::write(project.join(".fabro/project.toml"), "_version = 1\n").unwrap(); + std::fs::write( + workflow_dir.join("workflow.toml"), + "_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n", + ) + .unwrap(); + std::fs::write( + workflow_dir.join("workflow.fabro"), + format!( + r#"digraph Demo {{ + start [shape=Mdiamond] + output [{node_attributes}, output_schema="@schemas/output.schema.json"] + exit [shape=Msquare] + start -> output -> exit + }}"# + ), + ) + .unwrap(); + std::fs::write( + workflow_dir.join("schemas/output.schema.json"), + schema_source, + ) + .unwrap(); + + let built = build_run_manifest(ManifestBuildInput { + workflow: PathBuf::from(".fabro/workflows/demo/workflow.toml"), + cwd: project.to_path_buf(), + environment_defaults: test_environment_defaults(), + ..Default::default() + }) + .unwrap(); + + let root = &built.manifest.workflows[".fabro/workflows/demo/workflow.fabro"]; + let schema = root + .files + .get(".fabro/workflows/demo/schemas/output.schema.json") + .expect("output_schema file should be bundled"); + assert_eq!(schema.content, schema_source); + } + #[test] fn build_run_overrides_sets_common_cli_and_mcp_layers() { let overrides = build_run_overrides(RunOverrideInput { @@ -966,6 +1028,17 @@ mod tests { ); } + // Regression coverage for https://github.com/fabro-sh/fabro/issues/476. + #[test] + fn build_manifest_bundles_agent_output_schema_file() { + assert_manifest_bundles_output_schema_file(r#"type="agent", prompt="Return JSON""#); + } + + #[test] + fn build_manifest_bundles_command_output_schema_file() { + assert_manifest_bundles_output_schema_file(r#"type="command", script="echo""#); + } + #[test] fn build_manifest_bundles_imports_prompts_and_children() { let temp = tempfile::tempdir().unwrap(); diff --git a/lib/crates/fabro-mcp-store/Cargo.toml b/lib/components/fabro-mcp-store/Cargo.toml similarity index 84% rename from lib/crates/fabro-mcp-store/Cargo.toml rename to lib/components/fabro-mcp-store/Cargo.toml index 0172d9bae..7fd46b3f0 100644 --- a/lib/crates/fabro-mcp-store/Cargo.toml +++ b/lib/components/fabro-mcp-store/Cargo.toml @@ -14,8 +14,8 @@ workspace = true [dependencies] chrono.workspace = true -fabro-db = { path = "../fabro-db" } -fabro-types = { path = "../fabro-types" } +fabro-db = { path = "../../foundation/fabro-db" } +fabro-types = { path = "../../foundation/fabro-types" } serde.workspace = true serde_json.workspace = true sqlx.workspace = true diff --git a/lib/crates/fabro-mcp-store/src/error.rs b/lib/components/fabro-mcp-store/src/error.rs similarity index 100% rename from lib/crates/fabro-mcp-store/src/error.rs rename to lib/components/fabro-mcp-store/src/error.rs diff --git a/lib/crates/fabro-mcp-store/src/lib.rs b/lib/components/fabro-mcp-store/src/lib.rs similarity index 100% rename from lib/crates/fabro-mcp-store/src/lib.rs rename to lib/components/fabro-mcp-store/src/lib.rs diff --git a/lib/crates/fabro-mcp-store/src/model.rs b/lib/components/fabro-mcp-store/src/model.rs similarity index 100% rename from lib/crates/fabro-mcp-store/src/model.rs rename to lib/components/fabro-mcp-store/src/model.rs diff --git a/lib/crates/fabro-mcp-store/src/store.rs b/lib/components/fabro-mcp-store/src/store.rs similarity index 100% rename from lib/crates/fabro-mcp-store/src/store.rs rename to lib/components/fabro-mcp-store/src/store.rs diff --git a/lib/crates/fabro-mcp-store/tests/store.rs b/lib/components/fabro-mcp-store/tests/store.rs similarity index 100% rename from lib/crates/fabro-mcp-store/tests/store.rs rename to lib/components/fabro-mcp-store/tests/store.rs diff --git a/lib/crates/fabro-mcp/Cargo.toml b/lib/components/fabro-mcp/Cargo.toml similarity index 87% rename from lib/crates/fabro-mcp/Cargo.toml rename to lib/components/fabro-mcp/Cargo.toml index 12f0b551c..78db96688 100644 --- a/lib/crates/fabro-mcp/Cargo.toml +++ b/lib/components/fabro-mcp/Cargo.toml @@ -14,9 +14,9 @@ workspace = true [dependencies] anyhow.workspace = true -fabro-config = { path = "../fabro-config" } +fabro-config = { path = "../../foundation/fabro-config" } fabro-http.workspace = true -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } serde.workspace = true serde_json.workspace = true tokio.workspace = true diff --git a/lib/crates/fabro-mcp/src/client.rs b/lib/components/fabro-mcp/src/client.rs similarity index 100% rename from lib/crates/fabro-mcp/src/client.rs rename to lib/components/fabro-mcp/src/client.rs diff --git a/lib/crates/fabro-mcp/src/client_handler.rs b/lib/components/fabro-mcp/src/client_handler.rs similarity index 100% rename from lib/crates/fabro-mcp/src/client_handler.rs rename to lib/components/fabro-mcp/src/client_handler.rs diff --git a/lib/crates/fabro-mcp/src/config.rs b/lib/components/fabro-mcp/src/config.rs similarity index 100% rename from lib/crates/fabro-mcp/src/config.rs rename to lib/components/fabro-mcp/src/config.rs diff --git a/lib/crates/fabro-mcp/src/connection_manager.rs b/lib/components/fabro-mcp/src/connection_manager.rs similarity index 100% rename from lib/crates/fabro-mcp/src/connection_manager.rs rename to lib/components/fabro-mcp/src/connection_manager.rs diff --git a/lib/crates/fabro-mcp/src/http_transport.rs b/lib/components/fabro-mcp/src/http_transport.rs similarity index 100% rename from lib/crates/fabro-mcp/src/http_transport.rs rename to lib/components/fabro-mcp/src/http_transport.rs diff --git a/lib/crates/fabro-mcp/src/lib.rs b/lib/components/fabro-mcp/src/lib.rs similarity index 100% rename from lib/crates/fabro-mcp/src/lib.rs rename to lib/components/fabro-mcp/src/lib.rs diff --git a/lib/crates/fabro-mcp/src/sse_client.rs b/lib/components/fabro-mcp/src/sse_client.rs similarity index 100% rename from lib/crates/fabro-mcp/src/sse_client.rs rename to lib/components/fabro-mcp/src/sse_client.rs diff --git a/lib/crates/fabro-mcp/tests/stdio_integration.rs b/lib/components/fabro-mcp/tests/stdio_integration.rs similarity index 100% rename from lib/crates/fabro-mcp/tests/stdio_integration.rs rename to lib/components/fabro-mcp/tests/stdio_integration.rs diff --git a/lib/crates/fabro-mcp/tests/test_mcp_server.py b/lib/components/fabro-mcp/tests/test_mcp_server.py similarity index 100% rename from lib/crates/fabro-mcp/tests/test_mcp_server.py rename to lib/components/fabro-mcp/tests/test_mcp_server.py diff --git a/lib/crates/fabro-sandbox/Cargo.toml b/lib/components/fabro-sandbox/Cargo.toml similarity index 89% rename from lib/crates/fabro-sandbox/Cargo.toml rename to lib/components/fabro-sandbox/Cargo.toml index dd90d2f0e..c2346d5ef 100644 --- a/lib/crates/fabro-sandbox/Cargo.toml +++ b/lib/components/fabro-sandbox/Cargo.toml @@ -34,9 +34,9 @@ hmac.workspace = true sha2.workspace = true hex.workspace = true uuid.workspace = true -fabro-proc = { path = "../fabro-proc" } +fabro-proc = { path = "../../foundation/fabro-proc" } fabro-static.workspace = true -fabro-util = { path = "../fabro-util" } +fabro-util = { path = "../../foundation/fabro-util" } fabro-redact.workspace = true # local @@ -49,9 +49,9 @@ bollard = { workspace = true, optional = true } tar = { workspace = true, optional = true } # daytona -fabro-config = { path = "../fabro-config", optional = true } +fabro-config = { path = "../../foundation/fabro-config", optional = true } fabro-github = { path = "../fabro-github", optional = true } -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } chrono = { workspace = true } diff --git a/lib/crates/fabro-sandbox/src/clone_source.rs b/lib/components/fabro-sandbox/src/clone_source.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/clone_source.rs rename to lib/components/fabro-sandbox/src/clone_source.rs diff --git a/lib/crates/fabro-sandbox/src/config.rs b/lib/components/fabro-sandbox/src/config.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/config.rs rename to lib/components/fabro-sandbox/src/config.rs diff --git a/lib/crates/fabro-sandbox/src/daytona/mod.rs b/lib/components/fabro-sandbox/src/daytona/mod.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/daytona/mod.rs rename to lib/components/fabro-sandbox/src/daytona/mod.rs diff --git a/lib/crates/fabro-sandbox/src/details.rs b/lib/components/fabro-sandbox/src/details.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/details.rs rename to lib/components/fabro-sandbox/src/details.rs diff --git a/lib/crates/fabro-sandbox/src/docker.rs b/lib/components/fabro-sandbox/src/docker.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/docker.rs rename to lib/components/fabro-sandbox/src/docker.rs diff --git a/lib/crates/fabro-sandbox/src/error.rs b/lib/components/fabro-sandbox/src/error.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/error.rs rename to lib/components/fabro-sandbox/src/error.rs diff --git a/lib/crates/fabro-sandbox/src/from_environment.rs b/lib/components/fabro-sandbox/src/from_environment.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/from_environment.rs rename to lib/components/fabro-sandbox/src/from_environment.rs diff --git a/lib/crates/fabro-sandbox/src/glob_match.rs b/lib/components/fabro-sandbox/src/glob_match.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/glob_match.rs rename to lib/components/fabro-sandbox/src/glob_match.rs diff --git a/lib/crates/fabro-sandbox/src/lib.rs b/lib/components/fabro-sandbox/src/lib.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/lib.rs rename to lib/components/fabro-sandbox/src/lib.rs diff --git a/lib/crates/fabro-sandbox/src/local.rs b/lib/components/fabro-sandbox/src/local.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/local.rs rename to lib/components/fabro-sandbox/src/local.rs diff --git a/lib/crates/fabro-sandbox/src/managed_labels.rs b/lib/components/fabro-sandbox/src/managed_labels.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/managed_labels.rs rename to lib/components/fabro-sandbox/src/managed_labels.rs diff --git a/lib/crates/fabro-sandbox/src/provider.rs b/lib/components/fabro-sandbox/src/provider.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/provider.rs rename to lib/components/fabro-sandbox/src/provider.rs diff --git a/lib/crates/fabro-sandbox/src/provider/daytona.rs b/lib/components/fabro-sandbox/src/provider/daytona.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/provider/daytona.rs rename to lib/components/fabro-sandbox/src/provider/daytona.rs diff --git a/lib/crates/fabro-sandbox/src/provider/docker.rs b/lib/components/fabro-sandbox/src/provider/docker.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/provider/docker.rs rename to lib/components/fabro-sandbox/src/provider/docker.rs diff --git a/lib/crates/fabro-sandbox/src/read_guard.rs b/lib/components/fabro-sandbox/src/read_guard.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/read_guard.rs rename to lib/components/fabro-sandbox/src/read_guard.rs diff --git a/lib/crates/fabro-sandbox/src/reconnect.rs b/lib/components/fabro-sandbox/src/reconnect.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/reconnect.rs rename to lib/components/fabro-sandbox/src/reconnect.rs diff --git a/lib/crates/fabro-sandbox/src/redact.rs b/lib/components/fabro-sandbox/src/redact.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/redact.rs rename to lib/components/fabro-sandbox/src/redact.rs diff --git a/lib/crates/fabro-sandbox/src/sandbox.rs b/lib/components/fabro-sandbox/src/sandbox.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/sandbox.rs rename to lib/components/fabro-sandbox/src/sandbox.rs diff --git a/lib/crates/fabro-sandbox/src/sandbox_spec.rs b/lib/components/fabro-sandbox/src/sandbox_spec.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/sandbox_spec.rs rename to lib/components/fabro-sandbox/src/sandbox_spec.rs diff --git a/lib/crates/fabro-sandbox/src/terminal.rs b/lib/components/fabro-sandbox/src/terminal.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/terminal.rs rename to lib/components/fabro-sandbox/src/terminal.rs diff --git a/lib/crates/fabro-sandbox/src/test_support.rs b/lib/components/fabro-sandbox/src/test_support.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/test_support.rs rename to lib/components/fabro-sandbox/src/test_support.rs diff --git a/lib/crates/fabro-sandbox/src/worktree.rs b/lib/components/fabro-sandbox/src/worktree.rs similarity index 100% rename from lib/crates/fabro-sandbox/src/worktree.rs rename to lib/components/fabro-sandbox/src/worktree.rs diff --git a/lib/crates/fabro-sandbox/tests/daytona_streaming_live.rs b/lib/components/fabro-sandbox/tests/daytona_streaming_live.rs similarity index 100% rename from lib/crates/fabro-sandbox/tests/daytona_streaming_live.rs rename to lib/components/fabro-sandbox/tests/daytona_streaming_live.rs diff --git a/lib/crates/fabro-sandbox/tests/docker_streaming.rs b/lib/components/fabro-sandbox/tests/docker_streaming.rs similarity index 100% rename from lib/crates/fabro-sandbox/tests/docker_streaming.rs rename to lib/components/fabro-sandbox/tests/docker_streaming.rs diff --git a/lib/crates/fabro-sandbox/tests/error.rs b/lib/components/fabro-sandbox/tests/error.rs similarity index 100% rename from lib/crates/fabro-sandbox/tests/error.rs rename to lib/components/fabro-sandbox/tests/error.rs diff --git a/lib/crates/fabro-slack/Cargo.toml b/lib/components/fabro-slack/Cargo.toml similarity index 93% rename from lib/crates/fabro-slack/Cargo.toml rename to lib/components/fabro-slack/Cargo.toml index 660f42dc4..14a5ad3af 100644 --- a/lib/crates/fabro-slack/Cargo.toml +++ b/lib/components/fabro-slack/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] fabro-interview = { path = "../fabro-interview" } -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } fabro-workflow = { path = "../fabro-workflow" } fabro-http.workspace = true fabro-static.workspace = true diff --git a/lib/crates/fabro-slack/src/blocks.rs b/lib/components/fabro-slack/src/blocks.rs similarity index 100% rename from lib/crates/fabro-slack/src/blocks.rs rename to lib/components/fabro-slack/src/blocks.rs diff --git a/lib/crates/fabro-slack/src/client.rs b/lib/components/fabro-slack/src/client.rs similarity index 100% rename from lib/crates/fabro-slack/src/client.rs rename to lib/components/fabro-slack/src/client.rs diff --git a/lib/crates/fabro-slack/src/config.rs b/lib/components/fabro-slack/src/config.rs similarity index 100% rename from lib/crates/fabro-slack/src/config.rs rename to lib/components/fabro-slack/src/config.rs diff --git a/lib/crates/fabro-slack/src/connection.rs b/lib/components/fabro-slack/src/connection.rs similarity index 100% rename from lib/crates/fabro-slack/src/connection.rs rename to lib/components/fabro-slack/src/connection.rs diff --git a/lib/crates/fabro-slack/src/dispatch.rs b/lib/components/fabro-slack/src/dispatch.rs similarity index 100% rename from lib/crates/fabro-slack/src/dispatch.rs rename to lib/components/fabro-slack/src/dispatch.rs diff --git a/lib/crates/fabro-slack/src/interaction.rs b/lib/components/fabro-slack/src/interaction.rs similarity index 100% rename from lib/crates/fabro-slack/src/interaction.rs rename to lib/components/fabro-slack/src/interaction.rs diff --git a/lib/crates/fabro-slack/src/lib.rs b/lib/components/fabro-slack/src/lib.rs similarity index 100% rename from lib/crates/fabro-slack/src/lib.rs rename to lib/components/fabro-slack/src/lib.rs diff --git a/lib/crates/fabro-slack/src/payload.rs b/lib/components/fabro-slack/src/payload.rs similarity index 100% rename from lib/crates/fabro-slack/src/payload.rs rename to lib/components/fabro-slack/src/payload.rs diff --git a/lib/crates/fabro-slack/src/socket.rs b/lib/components/fabro-slack/src/socket.rs similarity index 100% rename from lib/crates/fabro-slack/src/socket.rs rename to lib/components/fabro-slack/src/socket.rs diff --git a/lib/crates/fabro-slack/src/threads.rs b/lib/components/fabro-slack/src/threads.rs similarity index 100% rename from lib/crates/fabro-slack/src/threads.rs rename to lib/components/fabro-slack/src/threads.rs diff --git a/lib/crates/fabro-store/Cargo.toml b/lib/components/fabro-store/Cargo.toml similarity index 77% rename from lib/crates/fabro-store/Cargo.toml rename to lib/components/fabro-store/Cargo.toml index 234da7ad4..796119e18 100644 --- a/lib/crates/fabro-store/Cargo.toml +++ b/lib/components/fabro-store/Cargo.toml @@ -12,8 +12,8 @@ doctest = false workspace = true [dependencies] -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } hex.workspace = true slatedb.workspace = true object_store.workspace = true @@ -34,9 +34,9 @@ futures.workspace = true uuid.workspace = true [dev-dependencies] -fabro-types = { path = "../fabro-types", features = ["test-support"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["test-support"] } tokio = { workspace = true, features = ["test-util", "macros"] } tempfile = "3" ulid.workspace = true insta = { workspace = true } -fabro-db = { path = "../fabro-db" } +fabro-db = { path = "../../foundation/fabro-db" } diff --git a/lib/crates/fabro-store/src/artifact_store.rs b/lib/components/fabro-store/src/artifact_store.rs similarity index 100% rename from lib/crates/fabro-store/src/artifact_store.rs rename to lib/components/fabro-store/src/artifact_store.rs diff --git a/lib/crates/fabro-store/src/error.rs b/lib/components/fabro-store/src/error.rs similarity index 100% rename from lib/crates/fabro-store/src/error.rs rename to lib/components/fabro-store/src/error.rs diff --git a/lib/crates/fabro-store/src/keyed_mutex.rs b/lib/components/fabro-store/src/keyed_mutex.rs similarity index 100% rename from lib/crates/fabro-store/src/keyed_mutex.rs rename to lib/components/fabro-store/src/keyed_mutex.rs diff --git a/lib/crates/fabro-store/src/keys.rs b/lib/components/fabro-store/src/keys.rs similarity index 100% rename from lib/crates/fabro-store/src/keys.rs rename to lib/components/fabro-store/src/keys.rs diff --git a/lib/crates/fabro-store/src/lib.rs b/lib/components/fabro-store/src/lib.rs similarity index 100% rename from lib/crates/fabro-store/src/lib.rs rename to lib/components/fabro-store/src/lib.rs diff --git a/lib/crates/fabro-store/src/record/codec.rs b/lib/components/fabro-store/src/record/codec.rs similarity index 100% rename from lib/crates/fabro-store/src/record/codec.rs rename to lib/components/fabro-store/src/record/codec.rs diff --git a/lib/crates/fabro-store/src/record/mod.rs b/lib/components/fabro-store/src/record/mod.rs similarity index 100% rename from lib/crates/fabro-store/src/record/mod.rs rename to lib/components/fabro-store/src/record/mod.rs diff --git a/lib/crates/fabro-store/src/record/record_id.rs b/lib/components/fabro-store/src/record/record_id.rs similarity index 100% rename from lib/crates/fabro-store/src/record/record_id.rs rename to lib/components/fabro-store/src/record/record_id.rs diff --git a/lib/crates/fabro-store/src/record/repository.rs b/lib/components/fabro-store/src/record/repository.rs similarity index 100% rename from lib/crates/fabro-store/src/record/repository.rs rename to lib/components/fabro-store/src/record/repository.rs diff --git a/lib/crates/fabro-store/src/record/transaction.rs b/lib/components/fabro-store/src/record/transaction.rs similarity index 100% rename from lib/crates/fabro-store/src/record/transaction.rs rename to lib/components/fabro-store/src/record/transaction.rs diff --git a/lib/crates/fabro-store/src/run_sessions.rs b/lib/components/fabro-store/src/run_sessions.rs similarity index 100% rename from lib/crates/fabro-store/src/run_sessions.rs rename to lib/components/fabro-store/src/run_sessions.rs diff --git a/lib/crates/fabro-store/src/run_state.rs b/lib/components/fabro-store/src/run_state.rs similarity index 97% rename from lib/crates/fabro-store/src/run_state.rs rename to lib/components/fabro-store/src/run_state.rs index 6fcad91b8..b37903299 100644 --- a/lib/crates/fabro-store/src/run_state.rs +++ b/lib/components/fabro-store/src/run_state.rs @@ -571,18 +571,27 @@ impl RunProjectionReducer for RunProjection { stage.agent_control = AgentControlState::Running; } EventBody::TodoCreated(props) => { + if !should_project_root_agent_todo_event(stored, props.list_kind) { + return Ok(()); + } let Some(stage) = stage_at_stored_or_current_visit(self, stored, event.seq) else { return Ok(()); }; - apply_todo_created(stage, props, stored.parent_session_id.as_deref()); + apply_todo_created(stage, props); } EventBody::TodoUpdated(props) => { + if !should_project_root_agent_todo_event(stored, props.list_kind) { + return Ok(()); + } let Some(stage) = stage_at_stored_or_current_visit(self, stored, event.seq) else { return Ok(()); }; apply_todo_updated(stage, props); } EventBody::TodoDeleted(props) => { + if !should_project_root_agent_todo_event(stored, props.list_kind) { + return Ok(()); + } let Some(stage) = stage_at_stored_or_current_visit(self, stored, event.seq) else { return Ok(()); }; @@ -706,36 +715,37 @@ impl RunProjectionReducer for RunProjection { } } -fn apply_todo_created( - stage: &mut StageProjection, - props: &TodoCreatedProps, - parent_session_id: Option<&str>, -) { - // OpenAI plan lists are scoped per agent session (`openai_plan:`). - // A child subagent must not displace a different list already in the slot - // (which would clobber the root agent's plan); but it may create the slot - // when empty, or extend its own list. Anthropic task lists are root-scoped - // and pass through unchanged. - if matches!(props.list_kind, TodoListKind::OpenAiPlan) - && parent_session_id.is_some() - && stage - .todos - .as_ref() - .is_some_and(|list| list.list_id != props.list_id) - { - return; +/// Decide whether a TODO event should mutate +/// `StageProjection.root_agent_todos`. +/// +/// OpenAI plan lists are scoped per agent session (`openai_plan:`), +/// so a child/subagent session emits its own list events on the same stage. +/// The root-agent projection excludes those child plans, while the underlying +/// events remain in the run event log. Anthropic task lists are root-scoped +/// (`anthropic_tasks:`) and intentionally shared with +/// subagents, so they always project. +fn should_project_root_agent_todo_event(stored: &RunEvent, list_kind: TodoListKind) -> bool { + match list_kind { + TodoListKind::OpenAiPlan => stored.parent_session_id.is_none(), + TodoListKind::AnthropicTasks => true, } +} + +fn apply_todo_created(stage: &mut StageProjection, props: &TodoCreatedProps) { if stage - .todos + .root_agent_todos .as_ref() .is_none_or(|list| list.list_id != props.list_id || list.kind != props.list_kind) { - stage.todos = Some(TodoListProjection::new( + stage.root_agent_todos = Some(TodoListProjection::new( props.list_kind, props.list_id.clone(), )); } - let list = stage.todos.as_mut().expect("todo list was just inserted"); + let list = stage + .root_agent_todos + .as_mut() + .expect("todo list was just inserted"); list.upsert(TodoProjection { id: props.todo_id.clone(), status: props.status, @@ -752,7 +762,7 @@ fn apply_todo_created( fn apply_todo_updated(stage: &mut StageProjection, props: &TodoUpdatedProps) { if let Some(list) = stage - .todos + .root_agent_todos .as_mut() .filter(|list| list.list_id == props.list_id) { @@ -762,7 +772,7 @@ fn apply_todo_updated(stage: &mut StageProjection, props: &TodoUpdatedProps) { fn apply_todo_deleted(stage: &mut StageProjection, props: &TodoDeletedProps) { let Some(list) = stage - .todos + .root_agent_todos .as_mut() .filter(|list| list.list_id == props.list_id) else { @@ -770,7 +780,7 @@ fn apply_todo_deleted(stage: &mut StageProjection, props: &TodoDeletedProps) { }; list.remove(&props.todo_id); if list.items.is_empty() { - stage.todos = None; + stage.root_agent_todos = None; } } @@ -4472,11 +4482,14 @@ mod tests { StageId::new("code", 1) } - fn stage_todos<'a>(state: &'a RunProjection, stage_id: &StageId) -> &'a TodoListProjection { + fn root_agent_todos<'a>( + state: &'a RunProjection, + stage_id: &StageId, + ) -> &'a TodoListProjection { state .stage(stage_id) - .and_then(|stage| stage.todos.as_ref()) - .expect("stage todos present") + .and_then(|stage| stage.root_agent_todos.as_ref()) + .expect("root agent todos present") } fn child_stage_event(seq: u32, body: EventBody, stage_id: StageId) -> EventEnvelope { @@ -4566,7 +4579,7 @@ mod tests { )) .unwrap(); - let projection = stage_todos(&state, &stage_id); + let projection = root_agent_todos(&state, &stage_id); assert_eq!(projection.list_id, list); assert_eq!(projection.items.len(), 2); assert_eq!(projection.items[0].id, "a"); @@ -4601,7 +4614,7 @@ mod tests { )) .unwrap(); - let projection = stage_todos(&state, &stage_id); + let projection = root_agent_todos(&state, &stage_id); assert_eq!(projection.items.len(), 1); assert_eq!(projection.items[0].id, "b"); } @@ -4640,9 +4653,12 @@ mod tests { )) .unwrap(); - assert_eq!(stage_todos(&state, &plan_one).items[0].subject, "p1"); - assert_eq!(stage_todos(&state, &plan_two).items[0].subject, "p2"); - assert_eq!(stage_todos(&state, &claude).items[0].subject, "claude task"); + assert_eq!(root_agent_todos(&state, &plan_one).items[0].subject, "p1"); + assert_eq!(root_agent_todos(&state, &plan_two).items[0].subject, "p2"); + assert_eq!( + root_agent_todos(&state, &claude).items[0].subject, + "claude task" + ); } #[test] @@ -4728,7 +4744,7 @@ mod tests { )) .unwrap(); - let projection = stage_todos(&state, &stage_id); + let projection = root_agent_todos(&state, &stage_id); assert_eq!(projection.list_id, root_list); assert_eq!(projection.kind, TodoListKind::OpenAiPlan); assert_eq!(projection.items.len(), 2); @@ -4739,50 +4755,80 @@ mod tests { } #[test] - fn child_openai_plan_projects_when_no_root_plan_exists() { - // Regression: when a stage's root agent never calls update_plan but - // a subagent does, the subagent's plan must still surface in - // StageProjection.todos. Observed on run 01KSDXK5DJ61CFCK9YSDR8AETQ - // (implement@1), where a gpt-5.5 root delegated to a subagent that - // owned the only plan list — and the sidebar showed no todos. + fn child_openai_plan_does_not_project_when_root_has_no_plan() { let mut state = initialized_projection(); let stage_id = stage_id(); - let child_list = "openai_plan:child_session"; state - .apply_event(&child_stage_event( + .apply_event(&test_stage_event( 1, - created(child_list, TodoListKind::OpenAiPlan, "c-a", 0, "first"), + EventBody::StageStarted(started_props()), stage_id.clone(), )) .unwrap(); state .apply_event(&child_stage_event( 2, - created(child_list, TodoListKind::OpenAiPlan, "c-b", 1, "second"), - stage_id.clone(), - )) - .unwrap(); - state - .apply_event(&child_stage_event( - 3, - updated_status( - child_list, + created( + "openai_plan:child_session", TodoListKind::OpenAiPlan, "c-a", - TodoStatus::Completed, + 0, + "child work", ), stage_id.clone(), )) .unwrap(); - let projection = stage_todos(&state, &stage_id); - assert_eq!(projection.list_id, child_list); - assert_eq!(projection.kind, TodoListKind::OpenAiPlan); - assert_eq!(projection.items.len(), 2); - assert_eq!(projection.items[0].id, "c-a"); - assert_eq!(projection.items[0].status, TodoStatus::Completed); - assert_eq!(projection.items[1].id, "c-b"); - assert_eq!(projection.items[1].status, TodoStatus::Pending); + let stage = state.stage(&stage_id).expect("stage projection present"); + assert!( + stage.root_agent_todos.is_none(), + "a child session's plan must not become the stage's root plan" + ); + } + + #[test] + fn root_openai_plan_projects_after_earlier_child_plan_event() { + let mut state = initialized_projection(); + let stage_id = stage_id(); + let root_list = "openai_plan:root_session"; + state + .apply_event(&test_stage_event( + 1, + EventBody::StageStarted(started_props()), + stage_id.clone(), + )) + .unwrap(); + state + .apply_event(&child_stage_event( + 2, + created( + "openai_plan:child_session", + TodoListKind::OpenAiPlan, + "child-a", + 0, + "child work", + ), + stage_id.clone(), + )) + .unwrap(); + state + .apply_event(&test_stage_event( + 3, + created( + root_list, + TodoListKind::OpenAiPlan, + "root-a", + 0, + "root work", + ), + stage_id.clone(), + )) + .unwrap(); + + let projection = root_agent_todos(&state, &stage_id); + assert_eq!(projection.list_id, root_list); + assert_eq!(projection.items.len(), 1); + assert_eq!(projection.items[0].subject, "root work"); } #[test] @@ -4816,7 +4862,7 @@ mod tests { )) .unwrap(); - let projection = stage_todos(&state, &stage_id); + let projection = root_agent_todos(&state, &stage_id); assert_eq!(projection.list_id, list); assert_eq!(projection.kind, TodoListKind::AnthropicTasks); assert_eq!(projection.items.len(), 1); @@ -4882,7 +4928,7 @@ mod tests { )) .unwrap(); - let todo = &stage_todos(&state, &stage_id).items[0]; + let todo = &root_agent_todos(&state, &stage_id).items[0]; assert!(!todo.metadata.contains_key("k1")); assert_eq!(todo.metadata.get("k2"), Some(&serde_json::json!("v2"))); } diff --git a/lib/crates/fabro-store/src/run_summary_store.rs b/lib/components/fabro-store/src/run_summary_store.rs similarity index 100% rename from lib/crates/fabro-store/src/run_summary_store.rs rename to lib/components/fabro-store/src/run_summary_store.rs diff --git a/lib/crates/fabro-store/src/serializable_projection.rs b/lib/components/fabro-store/src/serializable_projection.rs similarity index 100% rename from lib/crates/fabro-store/src/serializable_projection.rs rename to lib/components/fabro-store/src/serializable_projection.rs diff --git a/lib/crates/fabro-store/src/slate/auth_codes.rs b/lib/components/fabro-store/src/slate/auth_codes.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/auth_codes.rs rename to lib/components/fabro-store/src/slate/auth_codes.rs diff --git a/lib/crates/fabro-store/src/slate/auth_tokens.rs b/lib/components/fabro-store/src/slate/auth_tokens.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/auth_tokens.rs rename to lib/components/fabro-store/src/slate/auth_tokens.rs diff --git a/lib/crates/fabro-store/src/slate/blob_store.rs b/lib/components/fabro-store/src/slate/blob_store.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/blob_store.rs rename to lib/components/fabro-store/src/slate/blob_store.rs diff --git a/lib/crates/fabro-store/src/slate/mod.rs b/lib/components/fabro-store/src/slate/mod.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/mod.rs rename to lib/components/fabro-store/src/slate/mod.rs diff --git a/lib/crates/fabro-store/src/slate/projection_cache.rs b/lib/components/fabro-store/src/slate/projection_cache.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/projection_cache.rs rename to lib/components/fabro-store/src/slate/projection_cache.rs diff --git a/lib/crates/fabro-store/src/slate/run_catalog_index.rs b/lib/components/fabro-store/src/slate/run_catalog_index.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/run_catalog_index.rs rename to lib/components/fabro-store/src/slate/run_catalog_index.rs diff --git a/lib/crates/fabro-store/src/slate/run_store.rs b/lib/components/fabro-store/src/slate/run_store.rs similarity index 100% rename from lib/crates/fabro-store/src/slate/run_store.rs rename to lib/components/fabro-store/src/slate/run_store.rs diff --git a/lib/crates/fabro-store/src/test_util.rs b/lib/components/fabro-store/src/test_util.rs similarity index 100% rename from lib/crates/fabro-store/src/test_util.rs rename to lib/components/fabro-store/src/test_util.rs diff --git a/lib/crates/fabro-store/src/types.rs b/lib/components/fabro-store/src/types.rs similarity index 100% rename from lib/crates/fabro-store/src/types.rs rename to lib/components/fabro-store/src/types.rs diff --git a/lib/crates/fabro-store/tests/serializable_projection.rs b/lib/components/fabro-store/tests/serializable_projection.rs similarity index 100% rename from lib/crates/fabro-store/tests/serializable_projection.rs rename to lib/components/fabro-store/tests/serializable_projection.rs diff --git a/lib/crates/fabro-tool/Cargo.toml b/lib/components/fabro-tool/Cargo.toml similarity index 64% rename from lib/crates/fabro-tool/Cargo.toml rename to lib/components/fabro-tool/Cargo.toml index c50ea6a38..0d3a2862d 100644 --- a/lib/crates/fabro-tool/Cargo.toml +++ b/lib/components/fabro-tool/Cargo.toml @@ -16,10 +16,10 @@ workspace = true anyhow.workspace = true async-trait.workspace = true chrono = { workspace = true, features = ["serde"] } -fabro-api = { path = "../fabro-api" } -fabro-client = { path = "../fabro-client" } -fabro-types = { path = "../fabro-types" } -fabro-util = { path = "../fabro-util" } +fabro-api = { path = "../../foundation/fabro-api" } +fabro-client = { path = "../../foundation/fabro-client" } +fabro-types = { path = "../../foundation/fabro-types" } +fabro-util = { path = "../../foundation/fabro-util" } futures.workspace = true schemars = "1.2.1" serde.workspace = true @@ -29,5 +29,5 @@ tokio.workspace = true toml.workspace = true [dev-dependencies] -fabro-types = { path = "../fabro-types", features = ["test-support"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["test-support"] } tempfile = "3" diff --git a/lib/crates/fabro-tool/src/common.rs b/lib/components/fabro-tool/src/common.rs similarity index 100% rename from lib/crates/fabro-tool/src/common.rs rename to lib/components/fabro-tool/src/common.rs diff --git a/lib/crates/fabro-tool/src/create.rs b/lib/components/fabro-tool/src/create.rs similarity index 100% rename from lib/crates/fabro-tool/src/create.rs rename to lib/components/fabro-tool/src/create.rs diff --git a/lib/crates/fabro-tool/src/events.rs b/lib/components/fabro-tool/src/events.rs similarity index 100% rename from lib/crates/fabro-tool/src/events.rs rename to lib/components/fabro-tool/src/events.rs diff --git a/lib/crates/fabro-tool/src/fabro_client.rs b/lib/components/fabro-tool/src/fabro_client.rs similarity index 100% rename from lib/crates/fabro-tool/src/fabro_client.rs rename to lib/components/fabro-tool/src/fabro_client.rs diff --git a/lib/crates/fabro-tool/src/gather.rs b/lib/components/fabro-tool/src/gather.rs similarity index 100% rename from lib/crates/fabro-tool/src/gather.rs rename to lib/components/fabro-tool/src/gather.rs diff --git a/lib/crates/fabro-tool/src/get.rs b/lib/components/fabro-tool/src/get.rs similarity index 100% rename from lib/crates/fabro-tool/src/get.rs rename to lib/components/fabro-tool/src/get.rs diff --git a/lib/crates/fabro-tool/src/interact.rs b/lib/components/fabro-tool/src/interact.rs similarity index 100% rename from lib/crates/fabro-tool/src/interact.rs rename to lib/components/fabro-tool/src/interact.rs diff --git a/lib/crates/fabro-tool/src/lib.rs b/lib/components/fabro-tool/src/lib.rs similarity index 100% rename from lib/crates/fabro-tool/src/lib.rs rename to lib/components/fabro-tool/src/lib.rs diff --git a/lib/crates/fabro-tool/src/manifest.rs b/lib/components/fabro-tool/src/manifest.rs similarity index 100% rename from lib/crates/fabro-tool/src/manifest.rs rename to lib/components/fabro-tool/src/manifest.rs diff --git a/lib/crates/fabro-tool/src/pair.rs b/lib/components/fabro-tool/src/pair.rs similarity index 100% rename from lib/crates/fabro-tool/src/pair.rs rename to lib/components/fabro-tool/src/pair.rs diff --git a/lib/crates/fabro-tool/src/search.rs b/lib/components/fabro-tool/src/search.rs similarity index 100% rename from lib/crates/fabro-tool/src/search.rs rename to lib/components/fabro-tool/src/search.rs diff --git a/lib/crates/fabro-tracker/Cargo.toml b/lib/components/fabro-tracker/Cargo.toml similarity index 100% rename from lib/crates/fabro-tracker/Cargo.toml rename to lib/components/fabro-tracker/Cargo.toml diff --git a/lib/crates/fabro-tracker/src/fixtures/github-app-test-key.pem b/lib/components/fabro-tracker/src/fixtures/github-app-test-key.pem similarity index 100% rename from lib/crates/fabro-tracker/src/fixtures/github-app-test-key.pem rename to lib/components/fabro-tracker/src/fixtures/github-app-test-key.pem diff --git a/lib/crates/fabro-tracker/src/github.rs b/lib/components/fabro-tracker/src/github.rs similarity index 100% rename from lib/crates/fabro-tracker/src/github.rs rename to lib/components/fabro-tracker/src/github.rs diff --git a/lib/crates/fabro-tracker/src/lib.rs b/lib/components/fabro-tracker/src/lib.rs similarity index 100% rename from lib/crates/fabro-tracker/src/lib.rs rename to lib/components/fabro-tracker/src/lib.rs diff --git a/lib/crates/fabro-tracker/src/linear.rs b/lib/components/fabro-tracker/src/linear.rs similarity index 100% rename from lib/crates/fabro-tracker/src/linear.rs rename to lib/components/fabro-tracker/src/linear.rs diff --git a/lib/crates/fabro-validate/Cargo.toml b/lib/components/fabro-validate/Cargo.toml similarity index 81% rename from lib/crates/fabro-validate/Cargo.toml rename to lib/components/fabro-validate/Cargo.toml index a6c3ef3f9..4287fd1f1 100644 --- a/lib/crates/fabro-validate/Cargo.toml +++ b/lib/components/fabro-validate/Cargo.toml @@ -15,8 +15,8 @@ workspace = true [dependencies] fabro-acp = { path = "../fabro-acp", default-features = false } fabro-graphviz = { path = "../fabro-graphviz" } -fabro-model = { path = "../fabro-model" } -fabro-types = { path = "../fabro-types" } +fabro-model = { path = "../../foundation/fabro-model" } +fabro-types = { path = "../../foundation/fabro-types" } serde = { workspace = true } thiserror = { workspace = true } diff --git a/lib/crates/fabro-validate/src/lib.rs b/lib/components/fabro-validate/src/lib.rs similarity index 100% rename from lib/crates/fabro-validate/src/lib.rs rename to lib/components/fabro-validate/src/lib.rs diff --git a/lib/crates/fabro-validate/src/rules/all_conditional_edges.rs b/lib/components/fabro-validate/src/rules/all_conditional_edges.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/all_conditional_edges.rs rename to lib/components/fabro-validate/src/rules/all_conditional_edges.rs diff --git a/lib/crates/fabro-validate/src/rules/backend_valid.rs b/lib/components/fabro-validate/src/rules/backend_valid.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/backend_valid.rs rename to lib/components/fabro-validate/src/rules/backend_valid.rs diff --git a/lib/crates/fabro-validate/src/rules/condition_syntax.rs b/lib/components/fabro-validate/src/rules/condition_syntax.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/condition_syntax.rs rename to lib/components/fabro-validate/src/rules/condition_syntax.rs diff --git a/lib/crates/fabro-validate/src/rules/direction_valid.rs b/lib/components/fabro-validate/src/rules/direction_valid.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/direction_valid.rs rename to lib/components/fabro-validate/src/rules/direction_valid.rs diff --git a/lib/crates/fabro-validate/src/rules/edge_target_exists.rs b/lib/components/fabro-validate/src/rules/edge_target_exists.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/edge_target_exists.rs rename to lib/components/fabro-validate/src/rules/edge_target_exists.rs diff --git a/lib/crates/fabro-validate/src/rules/exit_no_outgoing.rs b/lib/components/fabro-validate/src/rules/exit_no_outgoing.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/exit_no_outgoing.rs rename to lib/components/fabro-validate/src/rules/exit_no_outgoing.rs diff --git a/lib/crates/fabro-validate/src/rules/fidelity_valid.rs b/lib/components/fabro-validate/src/rules/fidelity_valid.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/fidelity_valid.rs rename to lib/components/fabro-validate/src/rules/fidelity_valid.rs diff --git a/lib/crates/fabro-validate/src/rules/freeform_edge_count.rs b/lib/components/fabro-validate/src/rules/freeform_edge_count.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/freeform_edge_count.rs rename to lib/components/fabro-validate/src/rules/freeform_edge_count.rs diff --git a/lib/crates/fabro-validate/src/rules/goal_gate_has_retry.rs b/lib/components/fabro-validate/src/rules/goal_gate_has_retry.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/goal_gate_has_retry.rs rename to lib/components/fabro-validate/src/rules/goal_gate_has_retry.rs diff --git a/lib/crates/fabro-validate/src/rules/import_error.rs b/lib/components/fabro-validate/src/rules/import_error.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/import_error.rs rename to lib/components/fabro-validate/src/rules/import_error.rs diff --git a/lib/crates/fabro-validate/src/rules/inert_attribute.rs b/lib/components/fabro-validate/src/rules/inert_attribute.rs similarity index 90% rename from lib/crates/fabro-validate/src/rules/inert_attribute.rs rename to lib/components/fabro-validate/src/rules/inert_attribute.rs index fef9c5454..6c547d26f 100644 --- a/lib/crates/fabro-validate/src/rules/inert_attribute.rs +++ b/lib/components/fabro-validate/src/rules/inert_attribute.rs @@ -20,7 +20,8 @@ const HANDLER_SPECIFIC_ATTRS: &[(&str, &[&str])] = &[ ("duration", &["wait"]), ("join_policy", &["parallel"]), ("max_parallel", &["parallel"]), - ("output_schema", &["agent", "prompt"]), + ("output_retries", &["agent", "prompt"]), + ("output_schema", &["agent", "prompt", "command"]), ("prompt", &["agent", "prompt", "parallel.fan_in"]), ]; @@ -152,15 +153,18 @@ mod tests { } #[test] - fn warns_on_output_schema_on_command_node() { + fn warns_on_output_retries_on_command_node() { let mut g = minimal_graph(); g.nodes.insert( "run".to_string(), - node_with_attr("run", "parallelogram", "output_schema", "routing"), + node_with_attr("run", "parallelogram", "output_retries", "2"), ); + let d = Rule.apply(&g); + assert_eq!(d.len(), 1); - assert!(d[0].message.contains("'output_schema'")); + assert!(d[0].message.contains("'output_retries'")); + assert!(d[0].message.contains("agent, prompt")); } #[test] @@ -170,6 +174,10 @@ mod tests { "run".to_string(), node_with_attr("run", "parallelogram", "script", "echo hi"), ); + g.nodes.insert( + "audit".to_string(), + node_with_attr("audit", "parallelogram", "output_schema", "routing"), + ); g.nodes.insert( "pause".to_string(), node_with_attr("pause", "insulator", "duration", "30s"), @@ -178,6 +186,10 @@ mod tests { "work".to_string(), node_with_attr("work", "box", "prompt", "do things"), ); + g.nodes.insert( + "review".to_string(), + node_with_attr("review", "box", "output_retries", "2"), + ); g.nodes.insert( "fork".to_string(), node_with_attr("fork", "component", "join_policy", "wait_all"), @@ -186,6 +198,10 @@ mod tests { "spec".to_string(), node_with_attr("spec", "tab", "output_schema", "routing"), ); + g.nodes.insert( + "prompt".to_string(), + node_with_attr("prompt", "tab", "output_retries", "2"), + ); assert!(Rule.apply(&g).is_empty()); } diff --git a/lib/crates/fabro-validate/src/rules/mod.rs b/lib/components/fabro-validate/src/rules/mod.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/mod.rs rename to lib/components/fabro-validate/src/rules/mod.rs diff --git a/lib/crates/fabro-validate/src/rules/model_support.rs b/lib/components/fabro-validate/src/rules/model_support.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/model_support.rs rename to lib/components/fabro-validate/src/rules/model_support.rs diff --git a/lib/crates/fabro-validate/src/rules/node_model_known.rs b/lib/components/fabro-validate/src/rules/node_model_known.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/node_model_known.rs rename to lib/components/fabro-validate/src/rules/node_model_known.rs diff --git a/lib/crates/fabro-validate/src/rules/orphan_custom_outcome.rs b/lib/components/fabro-validate/src/rules/orphan_custom_outcome.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/orphan_custom_outcome.rs rename to lib/components/fabro-validate/src/rules/orphan_custom_outcome.rs diff --git a/lib/crates/fabro-validate/src/rules/parallel_branch.rs b/lib/components/fabro-validate/src/rules/parallel_branch.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/parallel_branch.rs rename to lib/components/fabro-validate/src/rules/parallel_branch.rs diff --git a/lib/crates/fabro-validate/src/rules/parallel_branch_inert_attribute.rs b/lib/components/fabro-validate/src/rules/parallel_branch_inert_attribute.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/parallel_branch_inert_attribute.rs rename to lib/components/fabro-validate/src/rules/parallel_branch_inert_attribute.rs diff --git a/lib/crates/fabro-validate/src/rules/prompt_on_llm_nodes.rs b/lib/components/fabro-validate/src/rules/prompt_on_llm_nodes.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/prompt_on_llm_nodes.rs rename to lib/components/fabro-validate/src/rules/prompt_on_llm_nodes.rs diff --git a/lib/crates/fabro-validate/src/rules/random_selection_no_conditions.rs b/lib/components/fabro-validate/src/rules/random_selection_no_conditions.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/random_selection_no_conditions.rs rename to lib/components/fabro-validate/src/rules/random_selection_no_conditions.rs diff --git a/lib/crates/fabro-validate/src/rules/reachability.rs b/lib/components/fabro-validate/src/rules/reachability.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/reachability.rs rename to lib/components/fabro-validate/src/rules/reachability.rs diff --git a/lib/crates/fabro-validate/src/rules/reserved_keyword_node_id.rs b/lib/components/fabro-validate/src/rules/reserved_keyword_node_id.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/reserved_keyword_node_id.rs rename to lib/components/fabro-validate/src/rules/reserved_keyword_node_id.rs diff --git a/lib/crates/fabro-validate/src/rules/retry_target_exists.rs b/lib/components/fabro-validate/src/rules/retry_target_exists.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/retry_target_exists.rs rename to lib/components/fabro-validate/src/rules/retry_target_exists.rs diff --git a/lib/crates/fabro-validate/src/rules/script_absolute_cd.rs b/lib/components/fabro-validate/src/rules/script_absolute_cd.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/script_absolute_cd.rs rename to lib/components/fabro-validate/src/rules/script_absolute_cd.rs diff --git a/lib/crates/fabro-validate/src/rules/selection_valid.rs b/lib/components/fabro-validate/src/rules/selection_valid.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/selection_valid.rs rename to lib/components/fabro-validate/src/rules/selection_valid.rs diff --git a/lib/crates/fabro-validate/src/rules/start_no_incoming.rs b/lib/components/fabro-validate/src/rules/start_no_incoming.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/start_no_incoming.rs rename to lib/components/fabro-validate/src/rules/start_no_incoming.rs diff --git a/lib/crates/fabro-validate/src/rules/start_node.rs b/lib/components/fabro-validate/src/rules/start_node.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/start_node.rs rename to lib/components/fabro-validate/src/rules/start_node.rs diff --git a/lib/crates/fabro-validate/src/rules/stylesheet_model_known.rs b/lib/components/fabro-validate/src/rules/stylesheet_model_known.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/stylesheet_model_known.rs rename to lib/components/fabro-validate/src/rules/stylesheet_model_known.rs diff --git a/lib/crates/fabro-validate/src/rules/stylesheet_syntax.rs b/lib/components/fabro-validate/src/rules/stylesheet_syntax.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/stylesheet_syntax.rs rename to lib/components/fabro-validate/src/rules/stylesheet_syntax.rs diff --git a/lib/crates/fabro-validate/src/rules/terminal_node.rs b/lib/components/fabro-validate/src/rules/terminal_node.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/terminal_node.rs rename to lib/components/fabro-validate/src/rules/terminal_node.rs diff --git a/lib/crates/fabro-validate/src/rules/test_support.rs b/lib/components/fabro-validate/src/rules/test_support.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/test_support.rs rename to lib/components/fabro-validate/src/rules/test_support.rs diff --git a/lib/crates/fabro-validate/src/rules/thread_id_requires_fidelity_full.rs b/lib/components/fabro-validate/src/rules/thread_id_requires_fidelity_full.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/thread_id_requires_fidelity_full.rs rename to lib/components/fabro-validate/src/rules/thread_id_requires_fidelity_full.rs diff --git a/lib/crates/fabro-validate/src/rules/type_known.rs b/lib/components/fabro-validate/src/rules/type_known.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/type_known.rs rename to lib/components/fabro-validate/src/rules/type_known.rs diff --git a/lib/crates/fabro-validate/src/rules/unresolved_file_ref.rs b/lib/components/fabro-validate/src/rules/unresolved_file_ref.rs similarity index 100% rename from lib/crates/fabro-validate/src/rules/unresolved_file_ref.rs rename to lib/components/fabro-validate/src/rules/unresolved_file_ref.rs diff --git a/lib/crates/fabro-variable/Cargo.toml b/lib/components/fabro-variable/Cargo.toml similarity index 82% rename from lib/crates/fabro-variable/Cargo.toml rename to lib/components/fabro-variable/Cargo.toml index f709d06a0..7de3fe2a6 100644 --- a/lib/crates/fabro-variable/Cargo.toml +++ b/lib/components/fabro-variable/Cargo.toml @@ -14,8 +14,8 @@ workspace = true [dependencies] chrono.workspace = true -fabro-db = { path = "../fabro-db" } -fabro-types = { path = "../fabro-types" } +fabro-db = { path = "../../foundation/fabro-db" } +fabro-types = { path = "../../foundation/fabro-types" } serde.workspace = true serde_json.workspace = true sqlx.workspace = true diff --git a/lib/crates/fabro-variable/src/lib.rs b/lib/components/fabro-variable/src/lib.rs similarity index 100% rename from lib/crates/fabro-variable/src/lib.rs rename to lib/components/fabro-variable/src/lib.rs diff --git a/lib/crates/fabro-variable/tests/store.rs b/lib/components/fabro-variable/tests/store.rs similarity index 100% rename from lib/crates/fabro-variable/tests/store.rs rename to lib/components/fabro-variable/tests/store.rs diff --git a/lib/crates/fabro-workflow/Cargo.toml b/lib/components/fabro-workflow/Cargo.toml similarity index 78% rename from lib/crates/fabro-workflow/Cargo.toml rename to lib/components/fabro-workflow/Cargo.toml index e719de2fd..932bb0071 100644 --- a/lib/crates/fabro-workflow/Cargo.toml +++ b/lib/components/fabro-workflow/Cargo.toml @@ -21,10 +21,10 @@ workspace = true [dependencies] anyhow.workspace = true -fabro-auth = { path = "../fabro-auth" } +fabro-auth = { path = "../../foundation/fabro-auth" } fabro-acp = { path = "../fabro-acp" } fabro-agent = { path = "../fabro-agent" } -fabro-config = { path = "../fabro-config" } +fabro-config = { path = "../../foundation/fabro-config" } fabro-graphviz = { path = "../fabro-graphviz" } fabro-hooks = { path = "../fabro-hooks" } fabro-validate = { path = "../fabro-validate" } @@ -33,17 +33,17 @@ fabro-sandbox = { path = "../fabro-sandbox", features = ["daytona"] } fabro-mcp = { path = "../fabro-mcp" } fabro-github = { path = "../fabro-github" } fabro-interview = { path = "../fabro-interview" } -fabro-template = { path = "../fabro-template" } +fabro-template = { path = "../../foundation/fabro-template" } fabro-tool = { path = "../fabro-tool" } -fabro-util = { path = "../fabro-util" } +fabro-util = { path = "../../foundation/fabro-util" } fabro-redact.workspace = true fabro-checkpoint = { path = "../fabro-checkpoint" } fabro-llm = { path = "../fabro-llm" } -fabro-model = { path = "../fabro-model" } -fabro-core = { path = "../fabro-core" } +fabro-model = { path = "../../foundation/fabro-model" } +fabro-core = { path = "../../foundation/fabro-core" } fabro-store = { path = "../fabro-store" } fabro-static.workspace = true -fabro-types = { path = "../fabro-types" } +fabro-types = { path = "../../foundation/fabro-types" } fabro-http.workspace = true thiserror.workspace = true serde.workspace = true @@ -72,12 +72,12 @@ tracing.workspace = true walkdir.workspace = true tempfile = "3" toml.workspace = true -fabro-vault = { path = "../fabro-vault" } +fabro-vault = { path = "../../foundation/fabro-vault" } [dev-dependencies] base64.workspace = true fabro-acp = { path = "../fabro-acp", features = ["test-support"] } fabro-workflow = { path = ".", features = ["test-support"] } -fabro-api = { path = "../fabro-api" } +fabro-api = { path = "../../foundation/fabro-api" } fabro-environment = { path = "../fabro-environment" } fabro-sandbox = { path = "../fabro-sandbox", features = ["daytona", "docker", "test-support"] } fabro-mcp = { path = "../fabro-mcp" } @@ -86,7 +86,7 @@ object_store.workspace = true assert_cmd = "2" predicates = "3" httpmock = "0.8" -fabro-macros = { path = "../fabro-macros" } +fabro-macros = { path = "../../foundation/fabro-macros" } fabro-test = { workspace = true } -fabro-types = { path = "../fabro-types", features = ["test-support"] } +fabro-types = { path = "../../foundation/fabro-types", features = ["test-support"] } shlex = "1" diff --git a/lib/crates/fabro-workflow/README.md b/lib/components/fabro-workflow/README.md similarity index 100% rename from lib/crates/fabro-workflow/README.md rename to lib/components/fabro-workflow/README.md diff --git a/lib/crates/fabro-workflow/src/artifact.rs b/lib/components/fabro-workflow/src/artifact.rs similarity index 100% rename from lib/crates/fabro-workflow/src/artifact.rs rename to lib/components/fabro-workflow/src/artifact.rs diff --git a/lib/crates/fabro-workflow/src/artifact_snapshot.rs b/lib/components/fabro-workflow/src/artifact_snapshot.rs similarity index 100% rename from lib/crates/fabro-workflow/src/artifact_snapshot.rs rename to lib/components/fabro-workflow/src/artifact_snapshot.rs diff --git a/lib/crates/fabro-workflow/src/artifact_upload.rs b/lib/components/fabro-workflow/src/artifact_upload.rs similarity index 100% rename from lib/crates/fabro-workflow/src/artifact_upload.rs rename to lib/components/fabro-workflow/src/artifact_upload.rs diff --git a/lib/crates/fabro-workflow/src/billing_rollup.rs b/lib/components/fabro-workflow/src/billing_rollup.rs similarity index 100% rename from lib/crates/fabro-workflow/src/billing_rollup.rs rename to lib/components/fabro-workflow/src/billing_rollup.rs diff --git a/lib/crates/fabro-workflow/src/command_log.rs b/lib/components/fabro-workflow/src/command_log.rs similarity index 100% rename from lib/crates/fabro-workflow/src/command_log.rs rename to lib/components/fabro-workflow/src/command_log.rs diff --git a/lib/crates/fabro-workflow/src/condition.rs b/lib/components/fabro-workflow/src/condition.rs similarity index 100% rename from lib/crates/fabro-workflow/src/condition.rs rename to lib/components/fabro-workflow/src/condition.rs diff --git a/lib/crates/fabro-workflow/src/context.rs b/lib/components/fabro-workflow/src/context.rs similarity index 100% rename from lib/crates/fabro-workflow/src/context.rs rename to lib/components/fabro-workflow/src/context.rs diff --git a/lib/crates/fabro-workflow/src/error.rs b/lib/components/fabro-workflow/src/error.rs similarity index 99% rename from lib/crates/fabro-workflow/src/error.rs rename to lib/components/fabro-workflow/src/error.rs index 71ded36ac..75b6d317f 100644 --- a/lib/crates/fabro-workflow/src/error.rs +++ b/lib/components/fabro-workflow/src/error.rs @@ -90,8 +90,6 @@ const BUDGET_EXHAUSTED_HINTS: &[&str] = &[ "context length", "budget", "quota exceeded", - "max_turns", - "max turns", "max_tokens", "max tokens", "context window exceeded", @@ -1233,7 +1231,7 @@ mod tests { #[test] fn budget_exhausted_hints_count() { - assert_eq!(BUDGET_EXHAUSTED_HINTS.len(), 12); + assert_eq!(BUDGET_EXHAUSTED_HINTS.len(), 10); } #[test] @@ -1313,22 +1311,6 @@ mod tests { ); } - #[test] - fn classify_reason_max_turns() { - assert_eq!( - classify_failure_reason("hit max_turns limit"), - FailureCategory::BudgetExhausted - ); - } - - #[test] - fn classify_reason_max_turns_space() { - assert_eq!( - classify_failure_reason("max turns reached"), - FailureCategory::BudgetExhausted - ); - } - #[test] fn classify_reason_max_tokens() { assert_eq!( diff --git a/lib/crates/fabro-workflow/src/event.rs b/lib/components/fabro-workflow/src/event.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event.rs rename to lib/components/fabro-workflow/src/event.rs diff --git a/lib/crates/fabro-workflow/src/event/convert.rs b/lib/components/fabro-workflow/src/event/convert.rs similarity index 99% rename from lib/crates/fabro-workflow/src/event/convert.rs rename to lib/components/fabro-workflow/src/event/convert.rs index 423833f3a..2ba7e4661 100644 --- a/lib/crates/fabro-workflow/src/event/convert.rs +++ b/lib/components/fabro-workflow/src/event/convert.rs @@ -674,12 +674,6 @@ fn event_body_from_event(event: &Event) -> EventBody { AgentEvent::LoopDetected => { EventBody::AgentLoopDetected(fabro_types::AgentLoopDetectedProps { visit: *visit }) } - AgentEvent::TurnLimitReached { max_turns } => { - EventBody::AgentTurnLimitReached(fabro_types::AgentTurnLimitReachedProps { - max_turns: *max_turns, - visit: *visit, - }) - } AgentEvent::SteeringInjected { text, .. } => { EventBody::AgentSteeringInjected(fabro_types::AgentSteeringInjectedProps { text: text.clone(), diff --git a/lib/crates/fabro-workflow/src/event/emitter.rs b/lib/components/fabro-workflow/src/event/emitter.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event/emitter.rs rename to lib/components/fabro-workflow/src/event/emitter.rs diff --git a/lib/crates/fabro-workflow/src/event/events.rs b/lib/components/fabro-workflow/src/event/events.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event/events.rs rename to lib/components/fabro-workflow/src/event/events.rs diff --git a/lib/crates/fabro-workflow/src/event/names.rs b/lib/components/fabro-workflow/src/event/names.rs similarity index 99% rename from lib/crates/fabro-workflow/src/event/names.rs rename to lib/components/fabro-workflow/src/event/names.rs index 68aa373b0..39a2bf5e3 100644 --- a/lib/crates/fabro-workflow/src/event/names.rs +++ b/lib/components/fabro-workflow/src/event/names.rs @@ -81,7 +81,6 @@ pub fn event_name(event: &Event) -> &'static str { AgentEvent::Error { .. } => "agent.error", AgentEvent::Warning { .. } => "agent.warning", AgentEvent::LoopDetected => "agent.loop.detected", - AgentEvent::TurnLimitReached { .. } => "agent.turn.limit", AgentEvent::SteeringInjected { .. } => "agent.steering.injected", AgentEvent::RoundInterrupted { .. } => "agent.round.interrupted", AgentEvent::CompactionStarted { .. } => "agent.compaction.started", diff --git a/lib/crates/fabro-workflow/src/event/redaction.rs b/lib/components/fabro-workflow/src/event/redaction.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event/redaction.rs rename to lib/components/fabro-workflow/src/event/redaction.rs diff --git a/lib/crates/fabro-workflow/src/event/sink.rs b/lib/components/fabro-workflow/src/event/sink.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event/sink.rs rename to lib/components/fabro-workflow/src/event/sink.rs diff --git a/lib/crates/fabro-workflow/src/event/stored_fields.rs b/lib/components/fabro-workflow/src/event/stored_fields.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event/stored_fields.rs rename to lib/components/fabro-workflow/src/event/stored_fields.rs diff --git a/lib/crates/fabro-workflow/src/event/test_support.rs b/lib/components/fabro-workflow/src/event/test_support.rs similarity index 100% rename from lib/crates/fabro-workflow/src/event/test_support.rs rename to lib/components/fabro-workflow/src/event/test_support.rs diff --git a/lib/crates/fabro-workflow/src/file_resolver.rs b/lib/components/fabro-workflow/src/file_resolver.rs similarity index 100% rename from lib/crates/fabro-workflow/src/file_resolver.rs rename to lib/components/fabro-workflow/src/file_resolver.rs diff --git a/lib/crates/fabro-workflow/src/git.rs b/lib/components/fabro-workflow/src/git.rs similarity index 100% rename from lib/crates/fabro-workflow/src/git.rs rename to lib/components/fabro-workflow/src/git.rs diff --git a/lib/crates/fabro-workflow/src/github_token_source.rs b/lib/components/fabro-workflow/src/github_token_source.rs similarity index 100% rename from lib/crates/fabro-workflow/src/github_token_source.rs rename to lib/components/fabro-workflow/src/github_token_source.rs diff --git a/lib/crates/fabro-workflow/src/graph.rs b/lib/components/fabro-workflow/src/graph.rs similarity index 100% rename from lib/crates/fabro-workflow/src/graph.rs rename to lib/components/fabro-workflow/src/graph.rs diff --git a/lib/crates/fabro-workflow/src/graph/routing.rs b/lib/components/fabro-workflow/src/graph/routing.rs similarity index 100% rename from lib/crates/fabro-workflow/src/graph/routing.rs rename to lib/components/fabro-workflow/src/graph/routing.rs diff --git a/lib/crates/fabro-workflow/src/handler/agent.rs b/lib/components/fabro-workflow/src/handler/agent.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/agent.rs rename to lib/components/fabro-workflow/src/handler/agent.rs diff --git a/lib/crates/fabro-workflow/src/handler/command.rs b/lib/components/fabro-workflow/src/handler/command.rs similarity index 76% rename from lib/crates/fabro-workflow/src/handler/command.rs rename to lib/components/fabro-workflow/src/handler/command.rs index 146324013..54fe0ff20 100644 --- a/lib/crates/fabro-workflow/src/handler/command.rs +++ b/lib/components/fabro-workflow/src/handler/command.rs @@ -6,6 +6,7 @@ use fabro_graphviz::graph::{Graph, Node}; use fabro_types::{CommandTermination, StageTiming}; use fabro_util::shell::shell_quote; +use super::structured_output::{self, StructuredOutputError}; use super::{EngineServices, Handler, NodeTimeoutPolicy}; use crate::command_log::CommandLogRecorder; use crate::context::{Context, keys}; @@ -76,6 +77,8 @@ impl Handler for CommandHandler { ))); } + let output_schema = structured_output::parse_node_output_schema(node)?; + let command = if language == "python" { format!("python3 -c {}", shell_quote(script)) } else { @@ -165,13 +168,31 @@ impl Handler for CommandHandler { } if result.exit_code == Some(0) { - let mut outcome = Outcome::success(); + let validation = output_schema.as_ref().map(|schema| { + ( + schema, + structured_output::validate_response_text(schema, &finalized.output_text), + ) + }); + let mut outcome = if let Some((_, Err(error))) = &validation { + Outcome::fail_deterministic(schema_validation_failure_reason( + script, + error, + &finalized.output_text, + )) + } else { + let mut outcome = Outcome::success(); + outcome.notes = Some(format!("Script completed: {script}")); + outcome + }; outcome.context_updates.insert( keys::COMMAND_OUTPUT.to_string(), serde_json::json!(finalized.output_ref), ); - outcome.notes = Some(format!("Script completed: {script}")); outcome.timing = Some(StageTiming::active_only(0, result.duration_ms)); + if let Some((schema, Ok(validated))) = validation { + structured_output::apply_validated_output(node, schema, &validated, &mut outcome); + } Ok(outcome) } else { let mut reason = format!( @@ -194,6 +215,20 @@ impl Handler for CommandHandler { } } +fn schema_validation_failure_reason( + script: &str, + error: &StructuredOutputError, + output_text: &str, +) -> String { + let mut reason = format!("Script output failed output_schema validation: {script}"); + for message in error.messages() { + reason.push_str("\n- "); + reason.push_str(message); + } + append_output_tail(&mut reason, output_text); + reason +} + fn append_output_tail(reason: &mut String, output: &str) { let output_tail = tail_bytes(output, 4096); if !output_tail.trim().is_empty() { @@ -227,9 +262,12 @@ mod tests { use super::*; use crate::command_log::command_log_path; - use crate::outcome::StageOutcome; + use crate::outcome::{FailureCategory, StageOutcome}; use crate::runtime_store::{RunStoreBackend, RunStoreHandle}; + const PASSED_OUTPUT_SCHEMA: &str = + r#"{"type":"object","required":["passed"],"properties":{"passed":{"type":"boolean"}}}"#; + #[derive(Default)] struct MemoryRunStoreBackend { blobs: Mutex>, @@ -465,6 +503,385 @@ mod tests { assert!(!outcome.context_updates.contains_key("command.stderr")); } + #[tokio::test] + async fn command_custom_output_schema_stores_output_context_key() { + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String(r#"echo '{"passed": true}'"#.to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String(PASSED_OUTPUT_SCHEMA.to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + let services = make_services(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &services) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Succeeded); + assert_eq!( + outcome.context_updates.get("output.audit"), + Some(&serde_json::json!({"passed": true})), + ); + let command_output = outcome + .context_updates + .get(keys::COMMAND_OUTPUT) + .expect("command.output should still be set"); + assert!( + command_text(&services, command_output) + .await + .contains(r#"{"passed": true}"#) + ); + } + + #[tokio::test] + async fn command_custom_output_schema_validates_last_json_object() { + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String( + r#"printf '%s\n' 'starting audit' '{"passed": false}' 'final result:' '{"passed": true}'"# + .to_string(), + ), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String(PASSED_OUTPUT_SCHEMA.to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Succeeded); + assert_eq!( + outcome.context_updates.get("output.audit"), + Some(&serde_json::json!({"passed": true})), + ); + } + + #[tokio::test] + async fn command_custom_output_schema_failure_is_deterministic() { + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String(r#"echo '{"passed":"yes"}'"#.to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String(PASSED_OUTPUT_SCHEMA.to_string()), + ); + node.attrs + .insert("output_retries".to_string(), AttrValue::Integer(7)); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + let services = make_services(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &services) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Failed { + retry_requested: false, + }); + assert_eq!( + outcome.failure_category(), + Some(FailureCategory::Deterministic) + ); + let reason = outcome + .failure_reason() + .expect("schema validation failure should have a reason"); + assert!( + reason.contains("Script output failed output_schema validation: echo"), + "unexpected failure reason: {reason}" + ); + assert!( + reason.contains("boolean"), + "validator message should be included: {reason}" + ); + assert!( + reason.contains("## output"), + "output heading missing: {reason}" + ); + assert!( + reason.contains(r#"{"passed":"yes"}"#), + "output tail missing: {reason}" + ); + assert!( + !reason.contains("repair attempt"), + "commands must not claim repair attempts: {reason}" + ); + assert!( + outcome.context_updates.contains_key(keys::COMMAND_OUTPUT), + "command.output should be set on validation failure" + ); + assert!(outcome.timing.is_some()); + assert_eq!(outcome.notes, None); + } + + #[tokio::test] + async fn command_routing_output_schema_no_json_object_fails() { + let handler = CommandHandler; + let mut node = Node::new("route"); + node.attrs.insert( + "script".to_string(), + AttrValue::String("echo not-json".to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String("routing".to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Failed { + retry_requested: false, + }); + assert_eq!( + outcome.failure_category(), + Some(FailureCategory::Deterministic) + ); + let reason = outcome.failure_reason().unwrap(); + assert!(reason.contains("no JSON object found"), "got: {reason}"); + assert!(reason.contains("## output\nnot-json"), "got: {reason}"); + } + + #[tokio::test] + async fn command_routing_output_schema_applies_routing_fields() { + let handler = CommandHandler; + let mut node = Node::new("route"); + node.attrs.insert( + "script".to_string(), + AttrValue::String( + r#"echo '{"preferred_next_label":"fix","context_updates":{"kept_count":2}}'"# + .to_string(), + ), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String("routing".to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Succeeded); + assert_eq!(outcome.preferred_label.as_deref(), Some("fix")); + assert_eq!( + outcome.context_updates.get("kept_count"), + Some(&serde_json::json!(2)) + ); + assert!(outcome.context_updates.contains_key(keys::COMMAND_OUTPUT)); + } + + #[tokio::test] + async fn command_routing_output_schema_outcome_failed_override() { + let handler = CommandHandler; + let mut node = Node::new("route"); + node.attrs.insert( + "script".to_string(), + AttrValue::String( + r#"echo '{"outcome":"failed","failure_reason":"tests failed"}'"#.to_string(), + ), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String("routing".to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Failed { + retry_requested: false, + }); + assert_eq!(outcome.failure_reason(), Some("tests failed")); + assert_eq!( + outcome.failure_category(), + Some(FailureCategory::Deterministic) + ); + } + + #[tokio::test] + async fn command_invalid_output_schema_fails_before_execution() { + let spy = std::sync::Arc::new(SpySandbox::new(fabro_agent::sandbox::ExecResult { + stdout: String::new(), + stderr: String::new(), + exit_code: Some(0), + termination: CommandTermination::Exited, + duration_ms: 1, + })); + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String("echo should-not-run".to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String("{".to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + let mut services = make_spy_services(spy.clone()); + let event_names = Arc::new(std::sync::Mutex::new(Vec::new())); + let captured_event_names = Arc::clone(&event_names); + let emitter = Arc::new(crate::event::Emitter::new(fixtures::RUN_1)); + emitter.on_event(move |event| { + captured_event_names + .lock() + .unwrap() + .push(event.event_name().to_string()); + }); + services.run = services.run.with_emitter(emitter); + + let error = handler + .execute(&node, &context, &graph, run_dir.path(), &services) + .await + .unwrap_err(); + + assert!( + error.to_string().contains("Invalid output_schema"), + "unexpected error: {error}" + ); + assert_eq!( + spy.captured_command(), + None, + "invalid schema must fail before sandbox execution" + ); + assert!( + event_names.lock().unwrap().is_empty(), + "invalid schema must fail before event emission" + ); + } + + #[tokio::test] + async fn command_nonzero_exit_skips_schema_validation() { + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String(r#"echo '{"passed":"bad"}'; exit 1"#.to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String(PASSED_OUTPUT_SCHEMA.to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Failed { + retry_requested: false, + }); + let reason = outcome.failure_reason().unwrap(); + assert!(reason.contains("exit code: 1"), "got: {reason}"); + assert!( + !reason.contains("output_schema validation"), + "nonzero exits must skip schema validation: {reason}" + ); + } + + #[tokio::test] + async fn command_simulate_ignores_output_schema() { + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String("echo should-not-run".to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String("{not a valid schema".to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .simulate(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Succeeded); + assert!(outcome.notes.as_deref().unwrap().contains("[Simulated]")); + assert_eq!( + outcome.context_updates.get(keys::COMMAND_OUTPUT), + Some(&serde_json::json!("")) + ); + assert!(!outcome.context_updates.contains_key("output.audit")); + } + + #[tokio::test] + async fn command_python_custom_output_schema() { + let handler = CommandHandler; + let mut node = Node::new("audit"); + node.attrs.insert( + "script".to_string(), + AttrValue::String(r#"import json; print(json.dumps({"passed": True}))"#.to_string()), + ); + node.attrs.insert( + "language".to_string(), + AttrValue::String("python".to_string()), + ); + node.attrs.insert( + "output_schema".to_string(), + AttrValue::String(PASSED_OUTPUT_SCHEMA.to_string()), + ); + let context = Context::new(); + let graph = Graph::new("test"); + let run_dir = tempfile::tempdir().unwrap(); + + let outcome = handler + .execute(&node, &context, &graph, run_dir.path(), &make_services()) + .await + .unwrap(); + + assert_eq!(outcome.status, StageOutcome::Succeeded); + assert_eq!( + outcome.context_updates.get("output.audit"), + Some(&serde_json::json!({"passed": true})), + ); + assert!(outcome.context_updates.contains_key(keys::COMMAND_OUTPUT)); + } + #[tokio::test] async fn script_handler_reports_command_duration_as_tool_timing() { let handler = CommandHandler; diff --git a/lib/crates/fabro-workflow/src/handler/conditional.rs b/lib/components/fabro-workflow/src/handler/conditional.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/conditional.rs rename to lib/components/fabro-workflow/src/handler/conditional.rs diff --git a/lib/crates/fabro-workflow/src/handler/exit.rs b/lib/components/fabro-workflow/src/handler/exit.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/exit.rs rename to lib/components/fabro-workflow/src/handler/exit.rs diff --git a/lib/crates/fabro-workflow/src/handler/fan_in.rs b/lib/components/fabro-workflow/src/handler/fan_in.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/fan_in.rs rename to lib/components/fabro-workflow/src/handler/fan_in.rs diff --git a/lib/crates/fabro-workflow/src/handler/human.rs b/lib/components/fabro-workflow/src/handler/human.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/human.rs rename to lib/components/fabro-workflow/src/handler/human.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/acp.rs b/lib/components/fabro-workflow/src/handler/llm/acp.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/acp.rs rename to lib/components/fabro-workflow/src/handler/llm/acp.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/activation_lease.rs b/lib/components/fabro-workflow/src/handler/llm/activation_lease.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/activation_lease.rs rename to lib/components/fabro-workflow/src/handler/llm/activation_lease.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/api.rs b/lib/components/fabro-workflow/src/handler/llm/api.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/api.rs rename to lib/components/fabro-workflow/src/handler/llm/api.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/changed_files.rs b/lib/components/fabro-workflow/src/handler/llm/changed_files.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/changed_files.rs rename to lib/components/fabro-workflow/src/handler/llm/changed_files.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/mod.rs b/lib/components/fabro-workflow/src/handler/llm/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/mod.rs rename to lib/components/fabro-workflow/src/handler/llm/mod.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/preamble.rs b/lib/components/fabro-workflow/src/handler/llm/preamble.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/preamble.rs rename to lib/components/fabro-workflow/src/handler/llm/preamble.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/router.rs b/lib/components/fabro-workflow/src/handler/llm/router.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/router.rs rename to lib/components/fabro-workflow/src/handler/llm/router.rs diff --git a/lib/crates/fabro-workflow/src/handler/llm/routing.rs b/lib/components/fabro-workflow/src/handler/llm/routing.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/llm/routing.rs rename to lib/components/fabro-workflow/src/handler/llm/routing.rs diff --git a/lib/crates/fabro-workflow/src/handler/manager_loop.rs b/lib/components/fabro-workflow/src/handler/manager_loop.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/manager_loop.rs rename to lib/components/fabro-workflow/src/handler/manager_loop.rs diff --git a/lib/crates/fabro-workflow/src/handler/mod.rs b/lib/components/fabro-workflow/src/handler/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/mod.rs rename to lib/components/fabro-workflow/src/handler/mod.rs diff --git a/lib/crates/fabro-workflow/src/handler/parallel.rs b/lib/components/fabro-workflow/src/handler/parallel.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/parallel.rs rename to lib/components/fabro-workflow/src/handler/parallel.rs diff --git a/lib/crates/fabro-workflow/src/handler/prompt.rs b/lib/components/fabro-workflow/src/handler/prompt.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/prompt.rs rename to lib/components/fabro-workflow/src/handler/prompt.rs diff --git a/lib/crates/fabro-workflow/src/handler/start.rs b/lib/components/fabro-workflow/src/handler/start.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/start.rs rename to lib/components/fabro-workflow/src/handler/start.rs diff --git a/lib/crates/fabro-workflow/src/handler/structured_output.rs b/lib/components/fabro-workflow/src/handler/structured_output.rs similarity index 99% rename from lib/crates/fabro-workflow/src/handler/structured_output.rs rename to lib/components/fabro-workflow/src/handler/structured_output.rs index ea2c2aad1..d36435bf3 100644 --- a/lib/crates/fabro-workflow/src/handler/structured_output.rs +++ b/lib/components/fabro-workflow/src/handler/structured_output.rs @@ -72,7 +72,6 @@ impl StructuredOutputError { self.kind } - #[cfg(test)] #[must_use] pub(crate) fn messages(&self) -> &[String] { &self.messages diff --git a/lib/crates/fabro-workflow/src/handler/wait.rs b/lib/components/fabro-workflow/src/handler/wait.rs similarity index 100% rename from lib/crates/fabro-workflow/src/handler/wait.rs rename to lib/components/fabro-workflow/src/handler/wait.rs diff --git a/lib/crates/fabro-workflow/src/hook_context.rs b/lib/components/fabro-workflow/src/hook_context.rs similarity index 100% rename from lib/crates/fabro-workflow/src/hook_context.rs rename to lib/components/fabro-workflow/src/hook_context.rs diff --git a/lib/crates/fabro-workflow/src/interview_runtime.rs b/lib/components/fabro-workflow/src/interview_runtime.rs similarity index 100% rename from lib/crates/fabro-workflow/src/interview_runtime.rs rename to lib/components/fabro-workflow/src/interview_runtime.rs diff --git a/lib/crates/fabro-workflow/src/lib.rs b/lib/components/fabro-workflow/src/lib.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lib.rs rename to lib/components/fabro-workflow/src/lib.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/artifact.rs b/lib/components/fabro-workflow/src/lifecycle/artifact.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/artifact.rs rename to lib/components/fabro-workflow/src/lifecycle/artifact.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/auto_status.rs b/lib/components/fabro-workflow/src/lifecycle/auto_status.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/auto_status.rs rename to lib/components/fabro-workflow/src/lifecycle/auto_status.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/circuit_breaker.rs b/lib/components/fabro-workflow/src/lifecycle/circuit_breaker.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/circuit_breaker.rs rename to lib/components/fabro-workflow/src/lifecycle/circuit_breaker.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/event.rs b/lib/components/fabro-workflow/src/lifecycle/event.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/event.rs rename to lib/components/fabro-workflow/src/lifecycle/event.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/fidelity.rs b/lib/components/fabro-workflow/src/lifecycle/fidelity.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/fidelity.rs rename to lib/components/fabro-workflow/src/lifecycle/fidelity.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/git.rs b/lib/components/fabro-workflow/src/lifecycle/git.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/git.rs rename to lib/components/fabro-workflow/src/lifecycle/git.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/hook.rs b/lib/components/fabro-workflow/src/lifecycle/hook.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/hook.rs rename to lib/components/fabro-workflow/src/lifecycle/hook.rs diff --git a/lib/crates/fabro-workflow/src/lifecycle/mod.rs b/lib/components/fabro-workflow/src/lifecycle/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/lifecycle/mod.rs rename to lib/components/fabro-workflow/src/lifecycle/mod.rs diff --git a/lib/crates/fabro-workflow/src/node_handler.rs b/lib/components/fabro-workflow/src/node_handler.rs similarity index 100% rename from lib/crates/fabro-workflow/src/node_handler.rs rename to lib/components/fabro-workflow/src/node_handler.rs diff --git a/lib/crates/fabro-workflow/src/operations/archive.rs b/lib/components/fabro-workflow/src/operations/archive.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/archive.rs rename to lib/components/fabro-workflow/src/operations/archive.rs diff --git a/lib/crates/fabro-workflow/src/operations/create.rs b/lib/components/fabro-workflow/src/operations/create.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/create.rs rename to lib/components/fabro-workflow/src/operations/create.rs diff --git a/lib/crates/fabro-workflow/src/operations/fork.rs b/lib/components/fabro-workflow/src/operations/fork.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/fork.rs rename to lib/components/fabro-workflow/src/operations/fork.rs diff --git a/lib/crates/fabro-workflow/src/operations/mod.rs b/lib/components/fabro-workflow/src/operations/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/mod.rs rename to lib/components/fabro-workflow/src/operations/mod.rs diff --git a/lib/crates/fabro-workflow/src/operations/resume.rs b/lib/components/fabro-workflow/src/operations/resume.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/resume.rs rename to lib/components/fabro-workflow/src/operations/resume.rs diff --git a/lib/crates/fabro-workflow/src/operations/retry.rs b/lib/components/fabro-workflow/src/operations/retry.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/retry.rs rename to lib/components/fabro-workflow/src/operations/retry.rs diff --git a/lib/crates/fabro-workflow/src/operations/rewind.rs b/lib/components/fabro-workflow/src/operations/rewind.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/rewind.rs rename to lib/components/fabro-workflow/src/operations/rewind.rs diff --git a/lib/crates/fabro-workflow/src/operations/run_store.rs b/lib/components/fabro-workflow/src/operations/run_store.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/run_store.rs rename to lib/components/fabro-workflow/src/operations/run_store.rs diff --git a/lib/crates/fabro-workflow/src/operations/source.rs b/lib/components/fabro-workflow/src/operations/source.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/source.rs rename to lib/components/fabro-workflow/src/operations/source.rs diff --git a/lib/crates/fabro-workflow/src/operations/start.rs b/lib/components/fabro-workflow/src/operations/start.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/start.rs rename to lib/components/fabro-workflow/src/operations/start.rs diff --git a/lib/crates/fabro-workflow/src/operations/timeline.rs b/lib/components/fabro-workflow/src/operations/timeline.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/timeline.rs rename to lib/components/fabro-workflow/src/operations/timeline.rs diff --git a/lib/crates/fabro-workflow/src/operations/validate.rs b/lib/components/fabro-workflow/src/operations/validate.rs similarity index 100% rename from lib/crates/fabro-workflow/src/operations/validate.rs rename to lib/components/fabro-workflow/src/operations/validate.rs diff --git a/lib/crates/fabro-workflow/src/outcome.rs b/lib/components/fabro-workflow/src/outcome.rs similarity index 100% rename from lib/crates/fabro-workflow/src/outcome.rs rename to lib/components/fabro-workflow/src/outcome.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/execute.rs b/lib/components/fabro-workflow/src/pipeline/execute.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/execute.rs rename to lib/components/fabro-workflow/src/pipeline/execute.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs b/lib/components/fabro-workflow/src/pipeline/execute/tests.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/execute/tests.rs rename to lib/components/fabro-workflow/src/pipeline/execute/tests.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/finalize.rs b/lib/components/fabro-workflow/src/pipeline/finalize.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/finalize.rs rename to lib/components/fabro-workflow/src/pipeline/finalize.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/initialize.rs b/lib/components/fabro-workflow/src/pipeline/initialize.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/initialize.rs rename to lib/components/fabro-workflow/src/pipeline/initialize.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/mod.rs b/lib/components/fabro-workflow/src/pipeline/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/mod.rs rename to lib/components/fabro-workflow/src/pipeline/mod.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/parse.rs b/lib/components/fabro-workflow/src/pipeline/parse.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/parse.rs rename to lib/components/fabro-workflow/src/pipeline/parse.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/persist.rs b/lib/components/fabro-workflow/src/pipeline/persist.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/persist.rs rename to lib/components/fabro-workflow/src/pipeline/persist.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/prompts/pr_body.md b/lib/components/fabro-workflow/src/pipeline/prompts/pr_body.md similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/prompts/pr_body.md rename to lib/components/fabro-workflow/src/pipeline/prompts/pr_body.md diff --git a/lib/crates/fabro-workflow/src/pipeline/pull_request.rs b/lib/components/fabro-workflow/src/pipeline/pull_request.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/pull_request.rs rename to lib/components/fabro-workflow/src/pipeline/pull_request.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/transform.rs b/lib/components/fabro-workflow/src/pipeline/transform.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/transform.rs rename to lib/components/fabro-workflow/src/pipeline/transform.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/types.rs b/lib/components/fabro-workflow/src/pipeline/types.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/types.rs rename to lib/components/fabro-workflow/src/pipeline/types.rs diff --git a/lib/crates/fabro-workflow/src/pipeline/validate.rs b/lib/components/fabro-workflow/src/pipeline/validate.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pipeline/validate.rs rename to lib/components/fabro-workflow/src/pipeline/validate.rs diff --git a/lib/crates/fabro-workflow/src/pull_request.rs b/lib/components/fabro-workflow/src/pull_request.rs similarity index 100% rename from lib/crates/fabro-workflow/src/pull_request.rs rename to lib/components/fabro-workflow/src/pull_request.rs diff --git a/lib/crates/fabro-workflow/src/records/checkpoint.rs b/lib/components/fabro-workflow/src/records/checkpoint.rs similarity index 100% rename from lib/crates/fabro-workflow/src/records/checkpoint.rs rename to lib/components/fabro-workflow/src/records/checkpoint.rs diff --git a/lib/crates/fabro-workflow/src/records/conclusion.rs b/lib/components/fabro-workflow/src/records/conclusion.rs similarity index 100% rename from lib/crates/fabro-workflow/src/records/conclusion.rs rename to lib/components/fabro-workflow/src/records/conclusion.rs diff --git a/lib/crates/fabro-workflow/src/records/mod.rs b/lib/components/fabro-workflow/src/records/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/records/mod.rs rename to lib/components/fabro-workflow/src/records/mod.rs diff --git a/lib/crates/fabro-workflow/src/records/run.rs b/lib/components/fabro-workflow/src/records/run.rs similarity index 100% rename from lib/crates/fabro-workflow/src/records/run.rs rename to lib/components/fabro-workflow/src/records/run.rs diff --git a/lib/crates/fabro-workflow/src/records/start.rs b/lib/components/fabro-workflow/src/records/start.rs similarity index 100% rename from lib/crates/fabro-workflow/src/records/start.rs rename to lib/components/fabro-workflow/src/records/start.rs diff --git a/lib/crates/fabro-workflow/src/retry.rs b/lib/components/fabro-workflow/src/retry.rs similarity index 100% rename from lib/crates/fabro-workflow/src/retry.rs rename to lib/components/fabro-workflow/src/retry.rs diff --git a/lib/crates/fabro-workflow/src/run_control.rs b/lib/components/fabro-workflow/src/run_control.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_control.rs rename to lib/components/fabro-workflow/src/run_control.rs diff --git a/lib/crates/fabro-workflow/src/run_dir.rs b/lib/components/fabro-workflow/src/run_dir.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_dir.rs rename to lib/components/fabro-workflow/src/run_dir.rs diff --git a/lib/crates/fabro-workflow/src/run_lookup.rs b/lib/components/fabro-workflow/src/run_lookup.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_lookup.rs rename to lib/components/fabro-workflow/src/run_lookup.rs diff --git a/lib/crates/fabro-workflow/src/run_materialization.rs b/lib/components/fabro-workflow/src/run_materialization.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_materialization.rs rename to lib/components/fabro-workflow/src/run_materialization.rs diff --git a/lib/crates/fabro-workflow/src/run_metadata.rs b/lib/components/fabro-workflow/src/run_metadata.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_metadata.rs rename to lib/components/fabro-workflow/src/run_metadata.rs diff --git a/lib/crates/fabro-workflow/src/run_options.rs b/lib/components/fabro-workflow/src/run_options.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_options.rs rename to lib/components/fabro-workflow/src/run_options.rs diff --git a/lib/crates/fabro-workflow/src/run_status.rs b/lib/components/fabro-workflow/src/run_status.rs similarity index 100% rename from lib/crates/fabro-workflow/src/run_status.rs rename to lib/components/fabro-workflow/src/run_status.rs diff --git a/lib/crates/fabro-workflow/src/runtime_store.rs b/lib/components/fabro-workflow/src/runtime_store.rs similarity index 100% rename from lib/crates/fabro-workflow/src/runtime_store.rs rename to lib/components/fabro-workflow/src/runtime_store.rs diff --git a/lib/crates/fabro-workflow/src/sandbox_git.rs b/lib/components/fabro-workflow/src/sandbox_git.rs similarity index 100% rename from lib/crates/fabro-workflow/src/sandbox_git.rs rename to lib/components/fabro-workflow/src/sandbox_git.rs diff --git a/lib/crates/fabro-workflow/src/sandbox_git_runtime.rs b/lib/components/fabro-workflow/src/sandbox_git_runtime.rs similarity index 100% rename from lib/crates/fabro-workflow/src/sandbox_git_runtime.rs rename to lib/components/fabro-workflow/src/sandbox_git_runtime.rs diff --git a/lib/crates/fabro-workflow/src/services.rs b/lib/components/fabro-workflow/src/services.rs similarity index 100% rename from lib/crates/fabro-workflow/src/services.rs rename to lib/components/fabro-workflow/src/services.rs diff --git a/lib/crates/fabro-workflow/src/stage_scope.rs b/lib/components/fabro-workflow/src/stage_scope.rs similarity index 100% rename from lib/crates/fabro-workflow/src/stage_scope.rs rename to lib/components/fabro-workflow/src/stage_scope.rs diff --git a/lib/crates/fabro-workflow/src/static_reference.rs b/lib/components/fabro-workflow/src/static_reference.rs similarity index 100% rename from lib/crates/fabro-workflow/src/static_reference.rs rename to lib/components/fabro-workflow/src/static_reference.rs diff --git a/lib/crates/fabro-workflow/src/steering_hub.rs b/lib/components/fabro-workflow/src/steering_hub.rs similarity index 100% rename from lib/crates/fabro-workflow/src/steering_hub.rs rename to lib/components/fabro-workflow/src/steering_hub.rs diff --git a/lib/crates/fabro-workflow/src/test_support.rs b/lib/components/fabro-workflow/src/test_support.rs similarity index 100% rename from lib/crates/fabro-workflow/src/test_support.rs rename to lib/components/fabro-workflow/src/test_support.rs diff --git a/lib/crates/fabro-workflow/src/transforms/file_inlining.rs b/lib/components/fabro-workflow/src/transforms/file_inlining.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/file_inlining.rs rename to lib/components/fabro-workflow/src/transforms/file_inlining.rs diff --git a/lib/crates/fabro-workflow/src/transforms/import.rs b/lib/components/fabro-workflow/src/transforms/import.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/import.rs rename to lib/components/fabro-workflow/src/transforms/import.rs diff --git a/lib/crates/fabro-workflow/src/transforms/importable_field.rs b/lib/components/fabro-workflow/src/transforms/importable_field.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/importable_field.rs rename to lib/components/fabro-workflow/src/transforms/importable_field.rs diff --git a/lib/crates/fabro-workflow/src/transforms/mod.rs b/lib/components/fabro-workflow/src/transforms/mod.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/mod.rs rename to lib/components/fabro-workflow/src/transforms/mod.rs diff --git a/lib/crates/fabro-workflow/src/transforms/model_resolution.rs b/lib/components/fabro-workflow/src/transforms/model_resolution.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/model_resolution.rs rename to lib/components/fabro-workflow/src/transforms/model_resolution.rs diff --git a/lib/crates/fabro-workflow/src/transforms/preamble.rs b/lib/components/fabro-workflow/src/transforms/preamble.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/preamble.rs rename to lib/components/fabro-workflow/src/transforms/preamble.rs diff --git a/lib/crates/fabro-workflow/src/transforms/stylesheet.rs b/lib/components/fabro-workflow/src/transforms/stylesheet.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/stylesheet.rs rename to lib/components/fabro-workflow/src/transforms/stylesheet.rs diff --git a/lib/crates/fabro-workflow/src/transforms/stylesheet_application.rs b/lib/components/fabro-workflow/src/transforms/stylesheet_application.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/stylesheet_application.rs rename to lib/components/fabro-workflow/src/transforms/stylesheet_application.rs diff --git a/lib/crates/fabro-workflow/src/transforms/variable_expansion.rs b/lib/components/fabro-workflow/src/transforms/variable_expansion.rs similarity index 100% rename from lib/crates/fabro-workflow/src/transforms/variable_expansion.rs rename to lib/components/fabro-workflow/src/transforms/variable_expansion.rs diff --git a/lib/crates/fabro-workflow/src/workflow_bundle.rs b/lib/components/fabro-workflow/src/workflow_bundle.rs similarity index 100% rename from lib/crates/fabro-workflow/src/workflow_bundle.rs rename to lib/components/fabro-workflow/src/workflow_bundle.rs diff --git a/lib/crates/fabro-workflow/tests/it/attractor_compat.rs b/lib/components/fabro-workflow/tests/it/attractor_compat.rs similarity index 100% rename from lib/crates/fabro-workflow/tests/it/attractor_compat.rs rename to lib/components/fabro-workflow/tests/it/attractor_compat.rs diff --git a/lib/crates/fabro-workflow/tests/it/cp_integration.rs b/lib/components/fabro-workflow/tests/it/cp_integration.rs similarity index 100% rename from lib/crates/fabro-workflow/tests/it/cp_integration.rs rename to lib/components/fabro-workflow/tests/it/cp_integration.rs diff --git a/lib/crates/fabro-workflow/tests/it/daytona_integration.rs b/lib/components/fabro-workflow/tests/it/daytona_integration.rs similarity index 100% rename from lib/crates/fabro-workflow/tests/it/daytona_integration.rs rename to lib/components/fabro-workflow/tests/it/daytona_integration.rs diff --git a/lib/crates/fabro-workflow/tests/it/git_integration.rs b/lib/components/fabro-workflow/tests/it/git_integration.rs similarity index 100% rename from lib/crates/fabro-workflow/tests/it/git_integration.rs rename to lib/components/fabro-workflow/tests/it/git_integration.rs diff --git a/lib/crates/fabro-workflow/tests/it/integration.rs b/lib/components/fabro-workflow/tests/it/integration.rs similarity index 99% rename from lib/crates/fabro-workflow/tests/it/integration.rs rename to lib/components/fabro-workflow/tests/it/integration.rs index 590739b72..5f3f75a55 100644 --- a/lib/crates/fabro-workflow/tests/it/integration.rs +++ b/lib/components/fabro-workflow/tests/it/integration.rs @@ -33,7 +33,7 @@ use fabro_interview::{ use fabro_model::catalog::{LlmCatalogSettings, ProviderCatalogSettings}; use fabro_model::{Catalog, ProviderId}; use fabro_store::{ArtifactKey, ArtifactStore, Database}; -use fabro_types::{RunEvent, RunId, StageId, WorkflowSettings, parse_blob_ref}; +use fabro_types::{EventBody, RunEvent, RunId, StageId, WorkflowSettings, parse_blob_ref}; use fabro_validate::{Severity, validate, validate_or_raise}; use fabro_workflow::context::Context; use fabro_workflow::error::{Error, FailureSignatureExt}; @@ -1931,6 +1931,82 @@ fn make_graph_with_start_exit(name: &str) -> Graph { graph } +#[tokio::test] +async fn command_schema_validation_failure_does_not_consume_retries() { + let mut graph = make_graph_with_start_exit("CommandSchemaNoRetry"); + let mut audit = Node::new("audit"); + audit.attrs.insert( + "shape".to_string(), + AttrValue::String("parallelogram".to_string()), + ); + audit.attrs.insert( + "script".to_string(), + AttrValue::String(r#"echo '{"passed":"yes"}'"#.to_string()), + ); + audit.attrs.insert( + "output_schema".to_string(), + AttrValue::String( + r#"{"type":"object","required":["passed"],"properties":{"passed":{"type":"boolean"}}}"# + .to_string(), + ), + ); + audit + .attrs + .insert("max_retries".to_string(), AttrValue::Integer(2)); + graph.nodes.insert("audit".to_string(), audit); + graph.edges.push(Edge::new("start", "audit")); + + let emitter = Emitter::default(); + let events = collect_events(&emitter); + let dir = tempfile::tempdir().unwrap(); + let mut registry = HandlerRegistry::new(Box::new(StartHandler)); + registry.register("start", Box::new(StartHandler)); + registry.register("exit", Box::new(ExitHandler)); + registry.register("command", Box::new(CommandHandler)); + let engine = WorkflowRunner::new(registry, Arc::new(emitter), local_env()); + let mut run_options = make_run_options(dir.path()); + run_options.run_id = test_run_id("command-schema-no-retry"); + + let (outcome, state) = engine + .run_with_state(&graph, &run_options) + .await + .expect("deterministic command failure should remain a workflow outcome"); + + assert_eq!(outcome.status, StageOutcome::Failed { + retry_requested: false, + }); + assert_eq!( + outcome.failure_category(), + Some(fabro_workflow::outcome::FailureCategory::Deterministic) + ); + let checkpoint = state + .current_checkpoint() + .expect("checkpoint should be captured"); + let audit_outcome = checkpoint + .node_outcomes + .get("audit") + .expect("audit outcome should be checkpointed"); + assert_eq!(audit_outcome.status, StageOutcome::Failed { + retry_requested: false, + }); + assert_eq!( + audit_outcome.failure_category(), + Some(fabro_workflow::outcome::FailureCategory::Deterministic) + ); + assert_eq!( + checkpoint.node_retries.get("audit").copied().unwrap_or(0), + 0, + "schema validation should not consume node retries" + ); + let command_starts = events + .lock() + .unwrap() + .iter() + .filter(|event| matches!(event.body, EventBody::CommandStarted(_))) + .count(); + assert_eq!(command_starts, 1, "command should execute exactly once"); +} + #[tokio::test] async fn smoke_test_with_mock_codergen_backend() { // Pipeline: diff --git a/lib/crates/fabro-workflow/tests/it/main.rs b/lib/components/fabro-workflow/tests/it/main.rs similarity index 100% rename from lib/crates/fabro-workflow/tests/it/main.rs rename to lib/components/fabro-workflow/tests/it/main.rs diff --git a/lib/crates/fabro-workflow/tests/materialize_run.rs b/lib/components/fabro-workflow/tests/materialize_run.rs similarity index 100% rename from lib/crates/fabro-workflow/tests/materialize_run.rs rename to lib/components/fabro-workflow/tests/materialize_run.rs diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_max_tokens_finish_reason.snap b/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_max_tokens_finish_reason.snap deleted file mode 100644 index f80ff0a94..000000000 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_max_tokens_finish_reason.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs -expression: rendered ---- -"length" diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_safety_finish_reason.snap b/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_safety_finish_reason.snap deleted file mode 100644 index aedfd4696..000000000 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__gemini__decode_safety_finish_reason.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: lib/crates/fabro-llm/tests/it/wire/gemini.rs -expression: rendered ---- -"content_filter" diff --git a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_or_content_synthesizes_nothing.snap b/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_or_content_synthesizes_nothing.snap deleted file mode 100644 index 4c107d93b..000000000 --- a/lib/crates/fabro-llm/tests/it/wire/snapshots/it__wire__openai_compatible__stream_without_done_or_content_synthesizes_nothing.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: lib/crates/fabro-llm/tests/it/wire/openai_compatible.rs -expression: rendered ---- -[] diff --git a/lib/crates/build-support/Cargo.toml b/lib/foundation/build-support/Cargo.toml similarity index 100% rename from lib/crates/build-support/Cargo.toml rename to lib/foundation/build-support/Cargo.toml diff --git a/lib/crates/build-support/git_metadata.rs b/lib/foundation/build-support/git_metadata.rs similarity index 100% rename from lib/crates/build-support/git_metadata.rs rename to lib/foundation/build-support/git_metadata.rs diff --git a/lib/crates/fabro-api/Cargo.toml b/lib/foundation/fabro-api/Cargo.toml similarity index 93% rename from lib/crates/fabro-api/Cargo.toml rename to lib/foundation/fabro-api/Cargo.toml index 298dad17c..bc28670af 100644 --- a/lib/crates/fabro-api/Cargo.toml +++ b/lib/foundation/fabro-api/Cargo.toml @@ -15,7 +15,7 @@ wildcard_imports = "warn" [dependencies] chrono = { workspace = true, features = ["serde"] } -fabro-automation = { path = "../fabro-automation" } +fabro-automation = { path = "../../components/fabro-automation" } fabro-config = { path = "../fabro-config" } fabro-environment.workspace = true fabro-model = { path = "../fabro-model" } diff --git a/lib/crates/fabro-api/build.rs b/lib/foundation/fabro-api/build.rs similarity index 99% rename from lib/crates/fabro-api/build.rs rename to lib/foundation/fabro-api/build.rs index 635ef1aab..cdec7f07a 100644 --- a/lib/crates/fabro-api/build.rs +++ b/lib/foundation/fabro-api/build.rs @@ -155,7 +155,7 @@ fn spec_path_from_manifest_dir(manifest_dir: &Path) -> PathBuf { manifest_dir .ancestors() .nth(3) - .expect("fabro-api manifest dir should be nested under /lib/crates/fabro-api") + .expect("fabro-api manifest dir should be nested under /lib/foundation/fabro-api") .join("docs/public/api-reference/fabro-api.yaml") } diff --git a/lib/crates/fabro-api/src/lib.rs b/lib/foundation/fabro-api/src/lib.rs similarity index 100% rename from lib/crates/fabro-api/src/lib.rs rename to lib/foundation/fabro-api/src/lib.rs diff --git a/lib/crates/fabro-api/tests/agent_session_activated_props_round_trip.rs b/lib/foundation/fabro-api/tests/agent_session_activated_props_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/agent_session_activated_props_round_trip.rs rename to lib/foundation/fabro-api/tests/agent_session_activated_props_round_trip.rs diff --git a/lib/crates/fabro-api/tests/automation_round_trip.rs b/lib/foundation/fabro-api/tests/automation_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/automation_round_trip.rs rename to lib/foundation/fabro-api/tests/automation_round_trip.rs diff --git a/lib/crates/fabro-api/tests/billed_token_counts_round_trip.rs b/lib/foundation/fabro-api/tests/billed_token_counts_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/billed_token_counts_round_trip.rs rename to lib/foundation/fabro-api/tests/billed_token_counts_round_trip.rs diff --git a/lib/crates/fabro-api/tests/command_termination_round_trip.rs b/lib/foundation/fabro-api/tests/command_termination_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/command_termination_round_trip.rs rename to lib/foundation/fabro-api/tests/command_termination_round_trip.rs diff --git a/lib/crates/fabro-api/tests/completion_message_round_trip.rs b/lib/foundation/fabro-api/tests/completion_message_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/completion_message_round_trip.rs rename to lib/foundation/fabro-api/tests/completion_message_round_trip.rs diff --git a/lib/crates/fabro-api/tests/cost_source_round_trip.rs b/lib/foundation/fabro-api/tests/cost_source_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/cost_source_round_trip.rs rename to lib/foundation/fabro-api/tests/cost_source_round_trip.rs diff --git a/lib/crates/fabro-api/tests/diff_stats_round_trip.rs b/lib/foundation/fabro-api/tests/diff_stats_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/diff_stats_round_trip.rs rename to lib/foundation/fabro-api/tests/diff_stats_round_trip.rs diff --git a/lib/crates/fabro-api/tests/diff_summary_round_trip.rs b/lib/foundation/fabro-api/tests/diff_summary_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/diff_summary_round_trip.rs rename to lib/foundation/fabro-api/tests/diff_summary_round_trip.rs diff --git a/lib/crates/fabro-api/tests/environment_round_trip.rs b/lib/foundation/fabro-api/tests/environment_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/environment_round_trip.rs rename to lib/foundation/fabro-api/tests/environment_round_trip.rs diff --git a/lib/crates/fabro-api/tests/event_envelope_round_trip.rs b/lib/foundation/fabro-api/tests/event_envelope_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/event_envelope_round_trip.rs rename to lib/foundation/fabro-api/tests/event_envelope_round_trip.rs diff --git a/lib/crates/fabro-api/tests/git_context_round_trip.rs b/lib/foundation/fabro-api/tests/git_context_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/git_context_round_trip.rs rename to lib/foundation/fabro-api/tests/git_context_round_trip.rs diff --git a/lib/crates/fabro-api/tests/interview_option_round_trip.rs b/lib/foundation/fabro-api/tests/interview_option_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/interview_option_round_trip.rs rename to lib/foundation/fabro-api/tests/interview_option_round_trip.rs diff --git a/lib/crates/fabro-api/tests/interview_question_record_round_trip.rs b/lib/foundation/fabro-api/tests/interview_question_record_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/interview_question_record_round_trip.rs rename to lib/foundation/fabro-api/tests/interview_question_record_round_trip.rs diff --git a/lib/crates/fabro-api/tests/mcp_server_round_trip.rs b/lib/foundation/fabro-api/tests/mcp_server_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/mcp_server_round_trip.rs rename to lib/foundation/fabro-api/tests/mcp_server_round_trip.rs diff --git a/lib/crates/fabro-api/tests/mcp_transport_round_trip.rs b/lib/foundation/fabro-api/tests/mcp_transport_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/mcp_transport_round_trip.rs rename to lib/foundation/fabro-api/tests/mcp_transport_round_trip.rs diff --git a/lib/crates/fabro-api/tests/model_costs_round_trip.rs b/lib/foundation/fabro-api/tests/model_costs_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/model_costs_round_trip.rs rename to lib/foundation/fabro-api/tests/model_costs_round_trip.rs diff --git a/lib/crates/fabro-api/tests/model_features_round_trip.rs b/lib/foundation/fabro-api/tests/model_features_round_trip.rs similarity index 73% rename from lib/crates/fabro-api/tests/model_features_round_trip.rs rename to lib/foundation/fabro-api/tests/model_features_round_trip.rs index 8e6f60aef..42f596665 100644 --- a/lib/crates/fabro-api/tests/model_features_round_trip.rs +++ b/lib/foundation/fabro-api/tests/model_features_round_trip.rs @@ -11,12 +11,13 @@ fn model_features_reuses_canonical_type() { #[test] fn model_features_json_matches_openapi_shape() { let features = ModelFeatures { - tools: true, - vision: true, - reasoning: true, - reasoning_effort: ReasoningEffortFeature::Levels, - prompt_cache: false, - sampling_params: true, + tools: true, + vision: true, + reasoning: true, + reasoning_effort: ReasoningEffortFeature::Levels, + prompt_cache: false, + cache_control_breakpoints: false, + sampling_params: true, }; let json = serde_json::to_value(&features).unwrap(); @@ -25,6 +26,7 @@ fn model_features_json_matches_openapi_shape() { assert_eq!(json["reasoning"], true); assert_eq!(json["reasoning_effort"], "levels"); assert_eq!(json["prompt_cache"], false); + assert_eq!(json["cache_control_breakpoints"], false); assert_eq!(json["sampling_params"], true); let round_trip: ApiModelFeatures = serde_json::from_value(json).unwrap(); diff --git a/lib/crates/fabro-api/tests/model_limits_round_trip.rs b/lib/foundation/fabro-api/tests/model_limits_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/model_limits_round_trip.rs rename to lib/foundation/fabro-api/tests/model_limits_round_trip.rs diff --git a/lib/crates/fabro-api/tests/model_round_trip.rs b/lib/foundation/fabro-api/tests/model_round_trip.rs similarity index 86% rename from lib/crates/fabro-api/tests/model_round_trip.rs rename to lib/foundation/fabro-api/tests/model_round_trip.rs index 93d4e7692..57d3db557 100644 --- a/lib/crates/fabro-api/tests/model_round_trip.rs +++ b/lib/foundation/fabro-api/tests/model_round_trip.rs @@ -24,12 +24,13 @@ fn model_json_matches_openapi_shape() { training: Some("2025-08-01".to_string()), knowledge_cutoff: Some("May 2025".to_string()), features: ModelFeatures { - tools: true, - vision: true, - reasoning: true, - reasoning_effort: ReasoningEffortFeature::Levels, - prompt_cache: true, - sampling_params: true, + tools: true, + vision: true, + reasoning: true, + reasoning_effort: ReasoningEffortFeature::Levels, + prompt_cache: true, + cache_control_breakpoints: false, + sampling_params: true, }, costs: ModelCosts { input_cost_per_mtok: Some(5.0), diff --git a/lib/crates/fabro-api/tests/model_test_mode_round_trip.rs b/lib/foundation/fabro-api/tests/model_test_mode_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/model_test_mode_round_trip.rs rename to lib/foundation/fabro-api/tests/model_test_mode_round_trip.rs diff --git a/lib/crates/fabro-api/tests/pair_round_trip.rs b/lib/foundation/fabro-api/tests/pair_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/pair_round_trip.rs rename to lib/foundation/fabro-api/tests/pair_round_trip.rs diff --git a/lib/crates/fabro-api/tests/pending_interview_record_round_trip.rs b/lib/foundation/fabro-api/tests/pending_interview_record_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/pending_interview_record_round_trip.rs rename to lib/foundation/fabro-api/tests/pending_interview_record_round_trip.rs diff --git a/lib/crates/fabro-api/tests/pre_run_push_outcome_round_trip.rs b/lib/foundation/fabro-api/tests/pre_run_push_outcome_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/pre_run_push_outcome_round_trip.rs rename to lib/foundation/fabro-api/tests/pre_run_push_outcome_round_trip.rs diff --git a/lib/crates/fabro-api/tests/principal_round_trip.rs b/lib/foundation/fabro-api/tests/principal_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/principal_round_trip.rs rename to lib/foundation/fabro-api/tests/principal_round_trip.rs diff --git a/lib/crates/fabro-api/tests/provider_id_round_trip.rs b/lib/foundation/fabro-api/tests/provider_id_round_trip.rs similarity index 85% rename from lib/crates/fabro-api/tests/provider_id_round_trip.rs rename to lib/foundation/fabro-api/tests/provider_id_round_trip.rs index e648b008f..970c7158f 100644 --- a/lib/crates/fabro-api/tests/provider_id_round_trip.rs +++ b/lib/foundation/fabro-api/tests/provider_id_round_trip.rs @@ -34,12 +34,13 @@ fn provider_id_json_matches_openapi_shape_through_model() { training: None, knowledge_cutoff: None, features: ModelFeatures { - tools: false, - vision: false, - reasoning: false, - reasoning_effort: ReasoningEffortFeature::None, - prompt_cache: false, - sampling_params: true, + tools: false, + vision: false, + reasoning: false, + reasoning_effort: ReasoningEffortFeature::None, + prompt_cache: false, + cache_control_breakpoints: false, + sampling_params: true, }, costs: ModelCosts { input_cost_per_mtok: None, diff --git a/lib/crates/fabro-api/tests/provider_round_trip.rs b/lib/foundation/fabro-api/tests/provider_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/provider_round_trip.rs rename to lib/foundation/fabro-api/tests/provider_round_trip.rs diff --git a/lib/crates/fabro-api/tests/pull_request_round_trip.rs b/lib/foundation/fabro-api/tests/pull_request_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/pull_request_round_trip.rs rename to lib/foundation/fabro-api/tests/pull_request_round_trip.rs diff --git a/lib/crates/fabro-api/tests/question_type_round_trip.rs b/lib/foundation/fabro-api/tests/question_type_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/question_type_round_trip.rs rename to lib/foundation/fabro-api/tests/question_type_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_billing_stage_round_trip.rs b/lib/foundation/fabro-api/tests/run_billing_stage_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_billing_stage_round_trip.rs rename to lib/foundation/fabro-api/tests/run_billing_stage_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_event_round_trip.rs b/lib/foundation/fabro-api/tests/run_event_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_event_round_trip.rs rename to lib/foundation/fabro-api/tests/run_event_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_failure_round_trip.rs b/lib/foundation/fabro-api/tests/run_failure_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_failure_round_trip.rs rename to lib/foundation/fabro-api/tests/run_failure_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_integrations_round_trip.rs b/lib/foundation/fabro-api/tests/run_integrations_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_integrations_round_trip.rs rename to lib/foundation/fabro-api/tests/run_integrations_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_projection_round_trip.rs b/lib/foundation/fabro-api/tests/run_projection_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_projection_round_trip.rs rename to lib/foundation/fabro-api/tests/run_projection_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_sandbox_round_trip.rs b/lib/foundation/fabro-api/tests/run_sandbox_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_sandbox_round_trip.rs rename to lib/foundation/fabro-api/tests/run_sandbox_round_trip.rs diff --git a/lib/crates/fabro-api/tests/run_summary_round_trip.rs b/lib/foundation/fabro-api/tests/run_summary_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/run_summary_round_trip.rs rename to lib/foundation/fabro-api/tests/run_summary_round_trip.rs diff --git a/lib/crates/fabro-api/tests/sandbox_details_round_trip.rs b/lib/foundation/fabro-api/tests/sandbox_details_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/sandbox_details_round_trip.rs rename to lib/foundation/fabro-api/tests/sandbox_details_round_trip.rs diff --git a/lib/crates/fabro-api/tests/sandbox_inventory_round_trip.rs b/lib/foundation/fabro-api/tests/sandbox_inventory_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/sandbox_inventory_round_trip.rs rename to lib/foundation/fabro-api/tests/sandbox_inventory_round_trip.rs diff --git a/lib/crates/fabro-api/tests/sandbox_services_round_trip.rs b/lib/foundation/fabro-api/tests/sandbox_services_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/sandbox_services_round_trip.rs rename to lib/foundation/fabro-api/tests/sandbox_services_round_trip.rs diff --git a/lib/crates/fabro-api/tests/secret_metadata_round_trip.rs b/lib/foundation/fabro-api/tests/secret_metadata_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/secret_metadata_round_trip.rs rename to lib/foundation/fabro-api/tests/secret_metadata_round_trip.rs diff --git a/lib/crates/fabro-api/tests/secret_type_round_trip.rs b/lib/foundation/fabro-api/tests/secret_type_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/secret_type_round_trip.rs rename to lib/foundation/fabro-api/tests/secret_type_round_trip.rs diff --git a/lib/crates/fabro-api/tests/server_settings_round_trip.rs b/lib/foundation/fabro-api/tests/server_settings_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/server_settings_round_trip.rs rename to lib/foundation/fabro-api/tests/server_settings_round_trip.rs diff --git a/lib/crates/fabro-api/tests/session_contract_round_trip.rs b/lib/foundation/fabro-api/tests/session_contract_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/session_contract_round_trip.rs rename to lib/foundation/fabro-api/tests/session_contract_round_trip.rs diff --git a/lib/crates/fabro-api/tests/stage_completion_round_trip.rs b/lib/foundation/fabro-api/tests/stage_completion_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/stage_completion_round_trip.rs rename to lib/foundation/fabro-api/tests/stage_completion_round_trip.rs diff --git a/lib/crates/fabro-api/tests/stage_handler_round_trip.rs b/lib/foundation/fabro-api/tests/stage_handler_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/stage_handler_round_trip.rs rename to lib/foundation/fabro-api/tests/stage_handler_round_trip.rs diff --git a/lib/crates/fabro-api/tests/stage_model_usage_round_trip.rs b/lib/foundation/fabro-api/tests/stage_model_usage_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/stage_model_usage_round_trip.rs rename to lib/foundation/fabro-api/tests/stage_model_usage_round_trip.rs diff --git a/lib/crates/fabro-api/tests/stage_outcome_round_trip.rs b/lib/foundation/fabro-api/tests/stage_outcome_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/stage_outcome_round_trip.rs rename to lib/foundation/fabro-api/tests/stage_outcome_round_trip.rs diff --git a/lib/crates/fabro-api/tests/stage_projection_round_trip.rs b/lib/foundation/fabro-api/tests/stage_projection_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/stage_projection_round_trip.rs rename to lib/foundation/fabro-api/tests/stage_projection_round_trip.rs diff --git a/lib/crates/fabro-api/tests/stage_state_round_trip.rs b/lib/foundation/fabro-api/tests/stage_state_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/stage_state_round_trip.rs rename to lib/foundation/fabro-api/tests/stage_state_round_trip.rs diff --git a/lib/crates/fabro-api/tests/status_round_trip.rs b/lib/foundation/fabro-api/tests/status_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/status_round_trip.rs rename to lib/foundation/fabro-api/tests/status_round_trip.rs diff --git a/lib/crates/fabro-api/tests/submit_answer_request_round_trip.rs b/lib/foundation/fabro-api/tests/submit_answer_request_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/submit_answer_request_round_trip.rs rename to lib/foundation/fabro-api/tests/submit_answer_request_round_trip.rs diff --git a/lib/crates/fabro-api/tests/system_integrations_round_trip.rs b/lib/foundation/fabro-api/tests/system_integrations_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/system_integrations_round_trip.rs rename to lib/foundation/fabro-api/tests/system_integrations_round_trip.rs diff --git a/lib/crates/fabro-api/tests/variable_round_trip.rs b/lib/foundation/fabro-api/tests/variable_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/variable_round_trip.rs rename to lib/foundation/fabro-api/tests/variable_round_trip.rs diff --git a/lib/crates/fabro-api/tests/workflow_settings_round_trip.rs b/lib/foundation/fabro-api/tests/workflow_settings_round_trip.rs similarity index 100% rename from lib/crates/fabro-api/tests/workflow_settings_round_trip.rs rename to lib/foundation/fabro-api/tests/workflow_settings_round_trip.rs diff --git a/lib/crates/fabro-auth/Cargo.toml b/lib/foundation/fabro-auth/Cargo.toml similarity index 100% rename from lib/crates/fabro-auth/Cargo.toml rename to lib/foundation/fabro-auth/Cargo.toml diff --git a/lib/crates/fabro-auth/src/context.rs b/lib/foundation/fabro-auth/src/context.rs similarity index 100% rename from lib/crates/fabro-auth/src/context.rs rename to lib/foundation/fabro-auth/src/context.rs diff --git a/lib/crates/fabro-auth/src/credential.rs b/lib/foundation/fabro-auth/src/credential.rs similarity index 100% rename from lib/crates/fabro-auth/src/credential.rs rename to lib/foundation/fabro-auth/src/credential.rs diff --git a/lib/crates/fabro-auth/src/credential_source.rs b/lib/foundation/fabro-auth/src/credential_source.rs similarity index 100% rename from lib/crates/fabro-auth/src/credential_source.rs rename to lib/foundation/fabro-auth/src/credential_source.rs diff --git a/lib/crates/fabro-auth/src/env_source.rs b/lib/foundation/fabro-auth/src/env_source.rs similarity index 100% rename from lib/crates/fabro-auth/src/env_source.rs rename to lib/foundation/fabro-auth/src/env_source.rs diff --git a/lib/crates/fabro-auth/src/extra_headers_source.rs b/lib/foundation/fabro-auth/src/extra_headers_source.rs similarity index 100% rename from lib/crates/fabro-auth/src/extra_headers_source.rs rename to lib/foundation/fabro-auth/src/extra_headers_source.rs diff --git a/lib/crates/fabro-auth/src/lib.rs b/lib/foundation/fabro-auth/src/lib.rs similarity index 100% rename from lib/crates/fabro-auth/src/lib.rs rename to lib/foundation/fabro-auth/src/lib.rs diff --git a/lib/crates/fabro-auth/src/refresh.rs b/lib/foundation/fabro-auth/src/refresh.rs similarity index 100% rename from lib/crates/fabro-auth/src/refresh.rs rename to lib/foundation/fabro-auth/src/refresh.rs diff --git a/lib/crates/fabro-auth/src/resolve.rs b/lib/foundation/fabro-auth/src/resolve.rs similarity index 100% rename from lib/crates/fabro-auth/src/resolve.rs rename to lib/foundation/fabro-auth/src/resolve.rs diff --git a/lib/crates/fabro-auth/src/sql_vault_source.rs b/lib/foundation/fabro-auth/src/sql_vault_source.rs similarity index 100% rename from lib/crates/fabro-auth/src/sql_vault_source.rs rename to lib/foundation/fabro-auth/src/sql_vault_source.rs diff --git a/lib/crates/fabro-auth/src/strategies/api_key.rs b/lib/foundation/fabro-auth/src/strategies/api_key.rs similarity index 100% rename from lib/crates/fabro-auth/src/strategies/api_key.rs rename to lib/foundation/fabro-auth/src/strategies/api_key.rs diff --git a/lib/crates/fabro-auth/src/strategies/codex_device.rs b/lib/foundation/fabro-auth/src/strategies/codex_device.rs similarity index 100% rename from lib/crates/fabro-auth/src/strategies/codex_device.rs rename to lib/foundation/fabro-auth/src/strategies/codex_device.rs diff --git a/lib/crates/fabro-auth/src/strategies/mod.rs b/lib/foundation/fabro-auth/src/strategies/mod.rs similarity index 100% rename from lib/crates/fabro-auth/src/strategies/mod.rs rename to lib/foundation/fabro-auth/src/strategies/mod.rs diff --git a/lib/crates/fabro-auth/src/strategy.rs b/lib/foundation/fabro-auth/src/strategy.rs similarity index 100% rename from lib/crates/fabro-auth/src/strategy.rs rename to lib/foundation/fabro-auth/src/strategy.rs diff --git a/lib/crates/fabro-auth/src/vault_ext.rs b/lib/foundation/fabro-auth/src/vault_ext.rs similarity index 100% rename from lib/crates/fabro-auth/src/vault_ext.rs rename to lib/foundation/fabro-auth/src/vault_ext.rs diff --git a/lib/crates/fabro-auth/src/vault_source.rs b/lib/foundation/fabro-auth/src/vault_source.rs similarity index 100% rename from lib/crates/fabro-auth/src/vault_source.rs rename to lib/foundation/fabro-auth/src/vault_source.rs diff --git a/lib/crates/fabro-client/Cargo.toml b/lib/foundation/fabro-client/Cargo.toml similarity index 100% rename from lib/crates/fabro-client/Cargo.toml rename to lib/foundation/fabro-client/Cargo.toml diff --git a/lib/crates/fabro-client/src/auth_store.rs b/lib/foundation/fabro-client/src/auth_store.rs similarity index 100% rename from lib/crates/fabro-client/src/auth_store.rs rename to lib/foundation/fabro-client/src/auth_store.rs diff --git a/lib/crates/fabro-client/src/client.rs b/lib/foundation/fabro-client/src/client.rs similarity index 100% rename from lib/crates/fabro-client/src/client.rs rename to lib/foundation/fabro-client/src/client.rs diff --git a/lib/crates/fabro-client/src/credential.rs b/lib/foundation/fabro-client/src/credential.rs similarity index 100% rename from lib/crates/fabro-client/src/credential.rs rename to lib/foundation/fabro-client/src/credential.rs diff --git a/lib/crates/fabro-client/src/error.rs b/lib/foundation/fabro-client/src/error.rs similarity index 100% rename from lib/crates/fabro-client/src/error.rs rename to lib/foundation/fabro-client/src/error.rs diff --git a/lib/crates/fabro-client/src/lib.rs b/lib/foundation/fabro-client/src/lib.rs similarity index 100% rename from lib/crates/fabro-client/src/lib.rs rename to lib/foundation/fabro-client/src/lib.rs diff --git a/lib/crates/fabro-client/src/session.rs b/lib/foundation/fabro-client/src/session.rs similarity index 100% rename from lib/crates/fabro-client/src/session.rs rename to lib/foundation/fabro-client/src/session.rs diff --git a/lib/crates/fabro-client/src/sse.rs b/lib/foundation/fabro-client/src/sse.rs similarity index 100% rename from lib/crates/fabro-client/src/sse.rs rename to lib/foundation/fabro-client/src/sse.rs diff --git a/lib/crates/fabro-client/src/target.rs b/lib/foundation/fabro-client/src/target.rs similarity index 100% rename from lib/crates/fabro-client/src/target.rs rename to lib/foundation/fabro-client/src/target.rs diff --git a/lib/crates/fabro-config/Cargo.toml b/lib/foundation/fabro-config/Cargo.toml similarity index 100% rename from lib/crates/fabro-config/Cargo.toml rename to lib/foundation/fabro-config/Cargo.toml diff --git a/lib/crates/fabro-config/migrations/2026050101_legacy_sandbox_to_environments.rs b/lib/foundation/fabro-config/migrations/2026050101_legacy_sandbox_to_environments.rs similarity index 100% rename from lib/crates/fabro-config/migrations/2026050101_legacy_sandbox_to_environments.rs rename to lib/foundation/fabro-config/migrations/2026050101_legacy_sandbox_to_environments.rs diff --git a/lib/crates/fabro-config/migrations/2026052801_settings_environments_to_server_files.rs b/lib/foundation/fabro-config/migrations/2026052801_settings_environments_to_server_files.rs similarity index 100% rename from lib/crates/fabro-config/migrations/2026052801_settings_environments_to_server_files.rs rename to lib/foundation/fabro-config/migrations/2026052801_settings_environments_to_server_files.rs diff --git a/lib/crates/fabro-config/src/bind.rs b/lib/foundation/fabro-config/src/bind.rs similarity index 100% rename from lib/crates/fabro-config/src/bind.rs rename to lib/foundation/fabro-config/src/bind.rs diff --git a/lib/crates/fabro-config/src/builders.rs b/lib/foundation/fabro-config/src/builders.rs similarity index 98% rename from lib/crates/fabro-config/src/builders.rs rename to lib/foundation/fabro-config/src/builders.rs index 1ffdc650d..a1e00907c 100644 --- a/lib/crates/fabro-config/src/builders.rs +++ b/lib/foundation/fabro-config/src/builders.rs @@ -417,12 +417,13 @@ fn model_limits_to_catalog(limits: &LlmModelLimits) -> model_catalog::SettingsMo fn model_features_to_catalog(features: &LlmModelFeatures) -> model_catalog::SettingsModelFeatures { model_catalog::SettingsModelFeatures { - tools: features.tools, - vision: features.vision, - reasoning: features.reasoning, - reasoning_effort: features.reasoning_effort, - prompt_cache: features.prompt_cache, - sampling_params: features.sampling_params, + tools: features.tools, + vision: features.vision, + reasoning: features.reasoning, + reasoning_effort: features.reasoning_effort, + prompt_cache: features.prompt_cache, + cache_control_breakpoints: features.cache_control_breakpoints, + sampling_params: features.sampling_params, } } diff --git a/lib/crates/fabro-config/src/daemon.rs b/lib/foundation/fabro-config/src/daemon.rs similarity index 100% rename from lib/crates/fabro-config/src/daemon.rs rename to lib/foundation/fabro-config/src/daemon.rs diff --git a/lib/crates/fabro-config/src/defaults.rs b/lib/foundation/fabro-config/src/defaults.rs similarity index 100% rename from lib/crates/fabro-config/src/defaults.rs rename to lib/foundation/fabro-config/src/defaults.rs diff --git a/lib/crates/fabro-config/src/defaults.toml b/lib/foundation/fabro-config/src/defaults.toml similarity index 100% rename from lib/crates/fabro-config/src/defaults.toml rename to lib/foundation/fabro-config/src/defaults.toml diff --git a/lib/crates/fabro-config/src/envfile.rs b/lib/foundation/fabro-config/src/envfile.rs similarity index 100% rename from lib/crates/fabro-config/src/envfile.rs rename to lib/foundation/fabro-config/src/envfile.rs diff --git a/lib/crates/fabro-config/src/error.rs b/lib/foundation/fabro-config/src/error.rs similarity index 100% rename from lib/crates/fabro-config/src/error.rs rename to lib/foundation/fabro-config/src/error.rs diff --git a/lib/crates/fabro-config/src/home.rs b/lib/foundation/fabro-config/src/home.rs similarity index 100% rename from lib/crates/fabro-config/src/home.rs rename to lib/foundation/fabro-config/src/home.rs diff --git a/lib/crates/fabro-config/src/input_overrides.rs b/lib/foundation/fabro-config/src/input_overrides.rs similarity index 100% rename from lib/crates/fabro-config/src/input_overrides.rs rename to lib/foundation/fabro-config/src/input_overrides.rs diff --git a/lib/crates/fabro-config/src/layers/cli.rs b/lib/foundation/fabro-config/src/layers/cli.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/cli.rs rename to lib/foundation/fabro-config/src/layers/cli.rs diff --git a/lib/crates/fabro-config/src/layers/combine.rs b/lib/foundation/fabro-config/src/layers/combine.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/combine.rs rename to lib/foundation/fabro-config/src/layers/combine.rs diff --git a/lib/crates/fabro-config/src/layers/environment.rs b/lib/foundation/fabro-config/src/layers/environment.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/environment.rs rename to lib/foundation/fabro-config/src/layers/environment.rs diff --git a/lib/crates/fabro-config/src/layers/llm.rs b/lib/foundation/fabro-config/src/layers/llm.rs similarity index 98% rename from lib/crates/fabro-config/src/layers/llm.rs rename to lib/foundation/fabro-config/src/layers/llm.rs index ca29d2bbd..4f89c55fc 100644 --- a/lib/crates/fabro-config/src/layers/llm.rs +++ b/lib/foundation/fabro-config/src/layers/llm.rs @@ -238,17 +238,19 @@ pub struct ModelLimits { #[serde(deny_unknown_fields)] pub struct ModelFeatures { #[serde(default, skip_serializing_if = "Option::is_none")] - pub tools: Option, + pub tools: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub vision: Option, + pub vision: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub reasoning: Option, + pub reasoning: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub reasoning_effort: Option, + pub reasoning_effort: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub prompt_cache: Option, + pub prompt_cache: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub sampling_params: Option, + pub cache_control_breakpoints: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sampling_params: Option, } /// User-facing allow-list for native control values Fabro accepts on this diff --git a/lib/crates/fabro-config/src/layers/log_filter.rs b/lib/foundation/fabro-config/src/layers/log_filter.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/log_filter.rs rename to lib/foundation/fabro-config/src/layers/log_filter.rs diff --git a/lib/crates/fabro-config/src/layers/maps.rs b/lib/foundation/fabro-config/src/layers/maps.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/maps.rs rename to lib/foundation/fabro-config/src/layers/maps.rs diff --git a/lib/crates/fabro-config/src/layers/mod.rs b/lib/foundation/fabro-config/src/layers/mod.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/mod.rs rename to lib/foundation/fabro-config/src/layers/mod.rs diff --git a/lib/crates/fabro-config/src/layers/project.rs b/lib/foundation/fabro-config/src/layers/project.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/project.rs rename to lib/foundation/fabro-config/src/layers/project.rs diff --git a/lib/crates/fabro-config/src/layers/run.rs b/lib/foundation/fabro-config/src/layers/run.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/run.rs rename to lib/foundation/fabro-config/src/layers/run.rs diff --git a/lib/crates/fabro-config/src/layers/server.rs b/lib/foundation/fabro-config/src/layers/server.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/server.rs rename to lib/foundation/fabro-config/src/layers/server.rs diff --git a/lib/crates/fabro-config/src/layers/settings.rs b/lib/foundation/fabro-config/src/layers/settings.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/settings.rs rename to lib/foundation/fabro-config/src/layers/settings.rs diff --git a/lib/crates/fabro-config/src/layers/splice_array.rs b/lib/foundation/fabro-config/src/layers/splice_array.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/splice_array.rs rename to lib/foundation/fabro-config/src/layers/splice_array.rs diff --git a/lib/crates/fabro-config/src/layers/workflow.rs b/lib/foundation/fabro-config/src/layers/workflow.rs similarity index 100% rename from lib/crates/fabro-config/src/layers/workflow.rs rename to lib/foundation/fabro-config/src/layers/workflow.rs diff --git a/lib/crates/fabro-config/src/lib.rs b/lib/foundation/fabro-config/src/lib.rs similarity index 100% rename from lib/crates/fabro-config/src/lib.rs rename to lib/foundation/fabro-config/src/lib.rs diff --git a/lib/crates/fabro-config/src/load.rs b/lib/foundation/fabro-config/src/load.rs similarity index 100% rename from lib/crates/fabro-config/src/load.rs rename to lib/foundation/fabro-config/src/load.rs diff --git a/lib/crates/fabro-config/src/logging.rs b/lib/foundation/fabro-config/src/logging.rs similarity index 100% rename from lib/crates/fabro-config/src/logging.rs rename to lib/foundation/fabro-config/src/logging.rs diff --git a/lib/crates/fabro-config/src/migrations.rs b/lib/foundation/fabro-config/src/migrations.rs similarity index 100% rename from lib/crates/fabro-config/src/migrations.rs rename to lib/foundation/fabro-config/src/migrations.rs diff --git a/lib/crates/fabro-config/src/parse.rs b/lib/foundation/fabro-config/src/parse.rs similarity index 100% rename from lib/crates/fabro-config/src/parse.rs rename to lib/foundation/fabro-config/src/parse.rs diff --git a/lib/crates/fabro-config/src/project.rs b/lib/foundation/fabro-config/src/project.rs similarity index 100% rename from lib/crates/fabro-config/src/project.rs rename to lib/foundation/fabro-config/src/project.rs diff --git a/lib/crates/fabro-config/src/resolve/cli.rs b/lib/foundation/fabro-config/src/resolve/cli.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/cli.rs rename to lib/foundation/fabro-config/src/resolve/cli.rs diff --git a/lib/crates/fabro-config/src/resolve/environment.rs b/lib/foundation/fabro-config/src/resolve/environment.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/environment.rs rename to lib/foundation/fabro-config/src/resolve/environment.rs diff --git a/lib/crates/fabro-config/src/resolve/error.rs b/lib/foundation/fabro-config/src/resolve/error.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/error.rs rename to lib/foundation/fabro-config/src/resolve/error.rs diff --git a/lib/crates/fabro-config/src/resolve/mod.rs b/lib/foundation/fabro-config/src/resolve/mod.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/mod.rs rename to lib/foundation/fabro-config/src/resolve/mod.rs diff --git a/lib/crates/fabro-config/src/resolve/project.rs b/lib/foundation/fabro-config/src/resolve/project.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/project.rs rename to lib/foundation/fabro-config/src/resolve/project.rs diff --git a/lib/crates/fabro-config/src/resolve/run.rs b/lib/foundation/fabro-config/src/resolve/run.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/run.rs rename to lib/foundation/fabro-config/src/resolve/run.rs diff --git a/lib/crates/fabro-config/src/resolve/server.rs b/lib/foundation/fabro-config/src/resolve/server.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/server.rs rename to lib/foundation/fabro-config/src/resolve/server.rs diff --git a/lib/crates/fabro-config/src/resolve/workflow.rs b/lib/foundation/fabro-config/src/resolve/workflow.rs similarity index 100% rename from lib/crates/fabro-config/src/resolve/workflow.rs rename to lib/foundation/fabro-config/src/resolve/workflow.rs diff --git a/lib/crates/fabro-config/src/run.rs b/lib/foundation/fabro-config/src/run.rs similarity index 100% rename from lib/crates/fabro-config/src/run.rs rename to lib/foundation/fabro-config/src/run.rs diff --git a/lib/crates/fabro-config/src/storage.rs b/lib/foundation/fabro-config/src/storage.rs similarity index 100% rename from lib/crates/fabro-config/src/storage.rs rename to lib/foundation/fabro-config/src/storage.rs diff --git a/lib/crates/fabro-config/src/tests/combine.rs b/lib/foundation/fabro-config/src/tests/combine.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/combine.rs rename to lib/foundation/fabro-config/src/tests/combine.rs diff --git a/lib/crates/fabro-config/src/tests/defaults.rs b/lib/foundation/fabro-config/src/tests/defaults.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/defaults.rs rename to lib/foundation/fabro-config/src/tests/defaults.rs diff --git a/lib/crates/fabro-config/src/tests/log_filter.rs b/lib/foundation/fabro-config/src/tests/log_filter.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/log_filter.rs rename to lib/foundation/fabro-config/src/tests/log_filter.rs diff --git a/lib/crates/fabro-config/src/tests/mod.rs b/lib/foundation/fabro-config/src/tests/mod.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/mod.rs rename to lib/foundation/fabro-config/src/tests/mod.rs diff --git a/lib/crates/fabro-config/src/tests/resolve_cli.rs b/lib/foundation/fabro-config/src/tests/resolve_cli.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/resolve_cli.rs rename to lib/foundation/fabro-config/src/tests/resolve_cli.rs diff --git a/lib/crates/fabro-config/src/tests/resolve_project.rs b/lib/foundation/fabro-config/src/tests/resolve_project.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/resolve_project.rs rename to lib/foundation/fabro-config/src/tests/resolve_project.rs diff --git a/lib/crates/fabro-config/src/tests/resolve_root.rs b/lib/foundation/fabro-config/src/tests/resolve_root.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/resolve_root.rs rename to lib/foundation/fabro-config/src/tests/resolve_root.rs diff --git a/lib/crates/fabro-config/src/tests/resolve_run.rs b/lib/foundation/fabro-config/src/tests/resolve_run.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/resolve_run.rs rename to lib/foundation/fabro-config/src/tests/resolve_run.rs diff --git a/lib/crates/fabro-config/src/tests/resolve_server.rs b/lib/foundation/fabro-config/src/tests/resolve_server.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/resolve_server.rs rename to lib/foundation/fabro-config/src/tests/resolve_server.rs diff --git a/lib/crates/fabro-config/src/tests/resolve_workflow.rs b/lib/foundation/fabro-config/src/tests/resolve_workflow.rs similarity index 100% rename from lib/crates/fabro-config/src/tests/resolve_workflow.rs rename to lib/foundation/fabro-config/src/tests/resolve_workflow.rs diff --git a/lib/crates/fabro-config/src/user.rs b/lib/foundation/fabro-config/src/user.rs similarity index 100% rename from lib/crates/fabro-config/src/user.rs rename to lib/foundation/fabro-config/src/user.rs diff --git a/lib/crates/fabro-core/Cargo.toml b/lib/foundation/fabro-core/Cargo.toml similarity index 100% rename from lib/crates/fabro-core/Cargo.toml rename to lib/foundation/fabro-core/Cargo.toml diff --git a/lib/crates/fabro-core/src/context.rs b/lib/foundation/fabro-core/src/context.rs similarity index 100% rename from lib/crates/fabro-core/src/context.rs rename to lib/foundation/fabro-core/src/context.rs diff --git a/lib/crates/fabro-core/src/error.rs b/lib/foundation/fabro-core/src/error.rs similarity index 100% rename from lib/crates/fabro-core/src/error.rs rename to lib/foundation/fabro-core/src/error.rs diff --git a/lib/crates/fabro-core/src/executor.rs b/lib/foundation/fabro-core/src/executor.rs similarity index 100% rename from lib/crates/fabro-core/src/executor.rs rename to lib/foundation/fabro-core/src/executor.rs diff --git a/lib/crates/fabro-core/src/graph.rs b/lib/foundation/fabro-core/src/graph.rs similarity index 100% rename from lib/crates/fabro-core/src/graph.rs rename to lib/foundation/fabro-core/src/graph.rs diff --git a/lib/crates/fabro-core/src/handler.rs b/lib/foundation/fabro-core/src/handler.rs similarity index 100% rename from lib/crates/fabro-core/src/handler.rs rename to lib/foundation/fabro-core/src/handler.rs diff --git a/lib/crates/fabro-core/src/lib.rs b/lib/foundation/fabro-core/src/lib.rs similarity index 100% rename from lib/crates/fabro-core/src/lib.rs rename to lib/foundation/fabro-core/src/lib.rs diff --git a/lib/crates/fabro-core/src/lifecycle.rs b/lib/foundation/fabro-core/src/lifecycle.rs similarity index 100% rename from lib/crates/fabro-core/src/lifecycle.rs rename to lib/foundation/fabro-core/src/lifecycle.rs diff --git a/lib/crates/fabro-core/src/outcome.rs b/lib/foundation/fabro-core/src/outcome.rs similarity index 100% rename from lib/crates/fabro-core/src/outcome.rs rename to lib/foundation/fabro-core/src/outcome.rs diff --git a/lib/crates/fabro-core/src/retry.rs b/lib/foundation/fabro-core/src/retry.rs similarity index 100% rename from lib/crates/fabro-core/src/retry.rs rename to lib/foundation/fabro-core/src/retry.rs diff --git a/lib/crates/fabro-core/src/stall.rs b/lib/foundation/fabro-core/src/stall.rs similarity index 100% rename from lib/crates/fabro-core/src/stall.rs rename to lib/foundation/fabro-core/src/stall.rs diff --git a/lib/crates/fabro-core/src/state.rs b/lib/foundation/fabro-core/src/state.rs similarity index 100% rename from lib/crates/fabro-core/src/state.rs rename to lib/foundation/fabro-core/src/state.rs diff --git a/lib/crates/fabro-core/src/test_fixtures.rs b/lib/foundation/fabro-core/src/test_fixtures.rs similarity index 100% rename from lib/crates/fabro-core/src/test_fixtures.rs rename to lib/foundation/fabro-core/src/test_fixtures.rs diff --git a/lib/crates/fabro-db/Cargo.toml b/lib/foundation/fabro-db/Cargo.toml similarity index 100% rename from lib/crates/fabro-db/Cargo.toml rename to lib/foundation/fabro-db/Cargo.toml diff --git a/lib/crates/fabro-db/migrations/2026063001_variables.sql b/lib/foundation/fabro-db/migrations/2026063001_variables.sql similarity index 100% rename from lib/crates/fabro-db/migrations/2026063001_variables.sql rename to lib/foundation/fabro-db/migrations/2026063001_variables.sql diff --git a/lib/crates/fabro-db/migrations/2026063002_environments.sql b/lib/foundation/fabro-db/migrations/2026063002_environments.sql similarity index 100% rename from lib/crates/fabro-db/migrations/2026063002_environments.sql rename to lib/foundation/fabro-db/migrations/2026063002_environments.sql diff --git a/lib/crates/fabro-db/migrations/2026071101_secrets.sql b/lib/foundation/fabro-db/migrations/2026071101_secrets.sql similarity index 100% rename from lib/crates/fabro-db/migrations/2026071101_secrets.sql rename to lib/foundation/fabro-db/migrations/2026071101_secrets.sql diff --git a/lib/crates/fabro-db/migrations/2026071102_mcp_servers.sql b/lib/foundation/fabro-db/migrations/2026071102_mcp_servers.sql similarity index 100% rename from lib/crates/fabro-db/migrations/2026071102_mcp_servers.sql rename to lib/foundation/fabro-db/migrations/2026071102_mcp_servers.sql diff --git a/lib/crates/fabro-db/migrations/2026071103_automations.sql b/lib/foundation/fabro-db/migrations/2026071103_automations.sql similarity index 100% rename from lib/crates/fabro-db/migrations/2026071103_automations.sql rename to lib/foundation/fabro-db/migrations/2026071103_automations.sql diff --git a/lib/crates/fabro-db/migrations/2026071104_runs.sql b/lib/foundation/fabro-db/migrations/2026071104_runs.sql similarity index 100% rename from lib/crates/fabro-db/migrations/2026071104_runs.sql rename to lib/foundation/fabro-db/migrations/2026071104_runs.sql diff --git a/lib/crates/fabro-db/src/lib.rs b/lib/foundation/fabro-db/src/lib.rs similarity index 100% rename from lib/crates/fabro-db/src/lib.rs rename to lib/foundation/fabro-db/src/lib.rs diff --git a/lib/crates/fabro-db/tests/sqlite.rs b/lib/foundation/fabro-db/tests/sqlite.rs similarity index 100% rename from lib/crates/fabro-db/tests/sqlite.rs rename to lib/foundation/fabro-db/tests/sqlite.rs diff --git a/lib/crates/fabro-dev/Cargo.toml b/lib/foundation/fabro-dev/Cargo.toml similarity index 100% rename from lib/crates/fabro-dev/Cargo.toml rename to lib/foundation/fabro-dev/Cargo.toml diff --git a/lib/crates/fabro-dev/src/commands/bench_tests.rs b/lib/foundation/fabro-dev/src/commands/bench_tests.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/bench_tests.rs rename to lib/foundation/fabro-dev/src/commands/bench_tests.rs diff --git a/lib/crates/fabro-dev/src/commands/build.rs b/lib/foundation/fabro-dev/src/commands/build.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/build.rs rename to lib/foundation/fabro-dev/src/commands/build.rs diff --git a/lib/crates/fabro-dev/src/commands/docker_build.rs b/lib/foundation/fabro-dev/src/commands/docker_build.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/docker_build.rs rename to lib/foundation/fabro-dev/src/commands/docker_build.rs diff --git a/lib/crates/fabro-dev/src/commands/docs.rs b/lib/foundation/fabro-dev/src/commands/docs.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/docs.rs rename to lib/foundation/fabro-dev/src/commands/docs.rs diff --git a/lib/crates/fabro-dev/src/commands/docs_cli_reference.rs b/lib/foundation/fabro-dev/src/commands/docs_cli_reference.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/docs_cli_reference.rs rename to lib/foundation/fabro-dev/src/commands/docs_cli_reference.rs diff --git a/lib/crates/fabro-dev/src/commands/docs_options_reference.rs b/lib/foundation/fabro-dev/src/commands/docs_options_reference.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/docs_options_reference.rs rename to lib/foundation/fabro-dev/src/commands/docs_options_reference.rs diff --git a/lib/crates/fabro-dev/src/commands/mod.rs b/lib/foundation/fabro-dev/src/commands/mod.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/mod.rs rename to lib/foundation/fabro-dev/src/commands/mod.rs diff --git a/lib/crates/fabro-dev/src/commands/release.rs b/lib/foundation/fabro-dev/src/commands/release.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/release.rs rename to lib/foundation/fabro-dev/src/commands/release.rs diff --git a/lib/crates/fabro-dev/src/commands/spa.rs b/lib/foundation/fabro-dev/src/commands/spa.rs similarity index 100% rename from lib/crates/fabro-dev/src/commands/spa.rs rename to lib/foundation/fabro-dev/src/commands/spa.rs diff --git a/lib/crates/fabro-dev/src/commands/spa_check.rs b/lib/foundation/fabro-dev/src/commands/spa_check.rs similarity index 97% rename from lib/crates/fabro-dev/src/commands/spa_check.rs rename to lib/foundation/fabro-dev/src/commands/spa_check.rs index 1fa93ea14..6ac315a79 100644 --- a/lib/crates/fabro-dev/src/commands/spa_check.rs +++ b/lib/foundation/fabro-dev/src/commands/spa_check.rs @@ -14,7 +14,7 @@ const DEFAULT_PAYLOAD_BUDGET_BYTES: u64 = 5 * 1024 * 1024; #[derive(Debug, Args)] pub(crate) struct SpaCheckArgs { - /// Repository root containing lib/crates/fabro-spa/assets. + /// Repository root containing lib/apps/fabro-spa/assets. #[arg(long, hide = true)] root: Option, /// Override the raw asset budget. @@ -36,7 +36,7 @@ pub(crate) fn spa_check(args: SpaCheckArgs) -> Result<()> { let root = args.root.unwrap_or_else(workspace_root); let web_dir = root.join("apps/fabro-web"); let dist_dir = web_dir.join("dist"); - let asset_dir = root.join("lib/crates/fabro-spa/assets"); + let asset_dir = root.join("lib/apps/fabro-spa/assets"); check_spa_asset_budgets( &asset_dir, args.asset_budget_bytes, diff --git a/lib/crates/fabro-dev/src/commands/spa_refresh.rs b/lib/foundation/fabro-dev/src/commands/spa_refresh.rs similarity index 97% rename from lib/crates/fabro-dev/src/commands/spa_refresh.rs rename to lib/foundation/fabro-dev/src/commands/spa_refresh.rs index 2174392ae..a8709ff65 100644 --- a/lib/crates/fabro-dev/src/commands/spa_refresh.rs +++ b/lib/foundation/fabro-dev/src/commands/spa_refresh.rs @@ -14,7 +14,7 @@ const DEFAULT_PAYLOAD_BUDGET_BYTES: u64 = 5 * 1024 * 1024; #[derive(Debug, Args)] pub(crate) struct SpaRefreshArgs { - /// Repository root containing apps/fabro-web and lib/crates/fabro-spa. + /// Repository root containing apps/fabro-web and lib/apps/fabro-spa. #[arg(long, hide = true)] root: Option, /// Override the raw asset budget. @@ -49,7 +49,7 @@ fn spa_refresh_root_with_budgets( ) -> Result<()> { let web_dir = root.join("apps/fabro-web"); let dist_dir = web_dir.join("dist"); - let asset_dir = root.join("lib/crates/fabro-spa/assets"); + let asset_dir = root.join("lib/apps/fabro-spa/assets"); println!("Running bun run build in apps/fabro-web..."); run_bun_build(&web_dir)?; @@ -61,7 +61,7 @@ fn spa_refresh_root_with_budgets( asset_budget_bytes, payload_budget_bytes, )?; - println!("Refreshed lib/crates/fabro-spa/assets"); + println!("Refreshed lib/apps/fabro-spa/assets"); Ok(()) } @@ -411,14 +411,14 @@ mod tests { write_file(fixture.path(), "apps/fabro-web/dist/index.html", b"hello"); write_file( fixture.path(), - "lib/crates/fabro-spa/assets/index.html", + "lib/apps/fabro-spa/assets/index.html", b"embedded", ); let error = refresh_from_dist( fixture.path(), &fixture.path().join("apps/fabro-web/dist"), - &fixture.path().join("lib/crates/fabro-spa/assets"), + &fixture.path().join("lib/apps/fabro-spa/assets"), 4, 100, ) @@ -431,7 +431,7 @@ mod tests { "budget failure should report raw byte overage: {error:#}" ); assert_eq!( - read_bytes(fixture.path(), "lib/crates/fabro-spa/assets/index.html"), + read_bytes(fixture.path(), "lib/apps/fabro-spa/assets/index.html"), b"embedded" ); } diff --git a/lib/crates/fabro-dev/src/lib.rs b/lib/foundation/fabro-dev/src/lib.rs similarity index 100% rename from lib/crates/fabro-dev/src/lib.rs rename to lib/foundation/fabro-dev/src/lib.rs diff --git a/lib/crates/fabro-dev/src/main.rs b/lib/foundation/fabro-dev/src/main.rs similarity index 100% rename from lib/crates/fabro-dev/src/main.rs rename to lib/foundation/fabro-dev/src/main.rs diff --git a/lib/crates/fabro-dev/tests/it/docker_build.rs b/lib/foundation/fabro-dev/tests/it/docker_build.rs similarity index 100% rename from lib/crates/fabro-dev/tests/it/docker_build.rs rename to lib/foundation/fabro-dev/tests/it/docker_build.rs diff --git a/lib/crates/fabro-dev/tests/it/docker_entrypoint.rs b/lib/foundation/fabro-dev/tests/it/docker_entrypoint.rs similarity index 100% rename from lib/crates/fabro-dev/tests/it/docker_entrypoint.rs rename to lib/foundation/fabro-dev/tests/it/docker_entrypoint.rs diff --git a/lib/crates/fabro-dev/tests/it/docs.rs b/lib/foundation/fabro-dev/tests/it/docs.rs similarity index 100% rename from lib/crates/fabro-dev/tests/it/docs.rs rename to lib/foundation/fabro-dev/tests/it/docs.rs diff --git a/lib/crates/fabro-dev/tests/it/main.rs b/lib/foundation/fabro-dev/tests/it/main.rs similarity index 98% rename from lib/crates/fabro-dev/tests/it/main.rs rename to lib/foundation/fabro-dev/tests/it/main.rs index cbf952b4a..ee503d466 100644 --- a/lib/crates/fabro-dev/tests/it/main.rs +++ b/lib/foundation/fabro-dev/tests/it/main.rs @@ -144,7 +144,7 @@ fn cargo_dev_alias_points_at_fabro_dev() { #[test] fn fabro_dev_does_not_depend_on_fabro_cli() { - let manifest = read_file(&workspace_root(), "lib/crates/fabro-dev/Cargo.toml"); + let manifest = read_file(&workspace_root(), "lib/foundation/fabro-dev/Cargo.toml"); assert!( !manifest.contains("fabro-cli"), "fabro-dev should shell out to fabro-cli instead of depending on it:\n{manifest}" diff --git a/lib/crates/fabro-dev/tests/it/policy.rs b/lib/foundation/fabro-dev/tests/it/policy.rs similarity index 93% rename from lib/crates/fabro-dev/tests/it/policy.rs rename to lib/foundation/fabro-dev/tests/it/policy.rs index 3043c43b2..2f2da24b6 100644 --- a/lib/crates/fabro-dev/tests/it/policy.rs +++ b/lib/foundation/fabro-dev/tests/it/policy.rs @@ -21,15 +21,15 @@ use crate::workspace_root; /// markdown) are not part of the allowlist. const BOOTSTRAP_CATALOG_ALLOWED_PATH_FRAGMENTS: &[&str] = &[ // The bootstrap module itself. - "lib/crates/fabro-model/src/bootstrap_catalog", + "lib/foundation/fabro-model/src/bootstrap_catalog", // Public module declaration for the bootstrap hatch. - "lib/crates/fabro-model/src/lib.rs", + "lib/foundation/fabro-model/src/lib.rs", // Install / first-run / API-key validation flows that legitimately need // a built-in catalog before any project settings have been loaded. - "lib/crates/fabro-install/", - "lib/crates/fabro-cli/src/commands/install/", - "lib/crates/fabro-cli/src/shared/install_", - "lib/crates/fabro-cli/src/shared/api_key_validation", + "lib/components/fabro-install/", + "lib/apps/fabro-cli/src/commands/install/", + "lib/apps/fabro-cli/src/shared/install_", + "lib/apps/fabro-cli/src/shared/api_key_validation", // Test support modules. "tests/", "test_support", @@ -42,7 +42,7 @@ const BOOTSTRAP_CATALOG_ALLOWED_PATH_FRAGMENTS: &[&str] = &[ /// `Catalog::builtin()` is reserved for `fabro-model` internals and tests. const CATALOG_BUILTIN_ALLOWED_PATH_FRAGMENTS: &[&str] = &[ // The catalog owner may define and test the built-in/default catalog. - "lib/crates/fabro-model/", + "lib/foundation/fabro-model/", // Tests and test support may use built-ins as fixtures. "/tests/", "/tests/it/", @@ -52,11 +52,11 @@ const CATALOG_BUILTIN_ALLOWED_PATH_FRAGMENTS: &[&str] = &[ const TEMPLATE_RENDER_ALLOWED_PATH_FRAGMENTS: &[&str] = &[ // The template crate owns the rendering API and its tests. - "lib/crates/fabro-template/src/lib.rs", + "lib/foundation/fabro-template/src/lib.rs", // Workflow-definition rendering must stay centralized here. - "lib/crates/fabro-workflow/src/transforms/variable_expansion.rs", + "lib/components/fabro-workflow/src/transforms/variable_expansion.rs", // Hook header/env interpolation is a separate system. - "lib/crates/fabro-hooks/src/executor.rs", + "lib/components/fabro-hooks/src/executor.rs", // This policy test names the forbidden patterns. "/tests/it/policy.rs", ]; @@ -79,7 +79,7 @@ fn bootstrap_catalog_references_stay_in_allowlist() { assert!( violations.is_empty(), - "bootstrap_catalog (install-only) referenced from non-allowlisted source files:\n{}\n\nIf this is intentional, add the path fragment to BOOTSTRAP_CATALOG_ALLOWED_PATH_FRAGMENTS in lib/crates/fabro-dev/tests/it/policy.rs.", + "bootstrap_catalog (install-only) referenced from non-allowlisted source files:\n{}\n\nIf this is intentional, add the path fragment to BOOTSTRAP_CATALOG_ALLOWED_PATH_FRAGMENTS in lib/foundation/fabro-dev/tests/it/policy.rs.", format_violations(violations), ); } @@ -91,7 +91,7 @@ fn catalog_builtin_references_stay_in_allowlist() { assert!( violations.is_empty(), - "Catalog::builtin() referenced from non-allowlisted production source files:\n{}\n\nRuntime code should use a resolved settings catalog via `Catalog::from_builtin_with_overrides(...)` or an injected `Arc`. If this is intentional test/bootstrap code, add the path fragment to CATALOG_BUILTIN_ALLOWED_PATH_FRAGMENTS in lib/crates/fabro-dev/tests/it/policy.rs.", + "Catalog::builtin() referenced from non-allowlisted production source files:\n{}\n\nRuntime code should use a resolved settings catalog via `Catalog::from_builtin_with_overrides(...)` or an injected `Arc`. If this is intentional test/bootstrap code, add the path fragment to CATALOG_BUILTIN_ALLOWED_PATH_FRAGMENTS in lib/foundation/fabro-dev/tests/it/policy.rs.", format_violations(violations), ); } diff --git a/lib/crates/fabro-dev/tests/it/release.rs b/lib/foundation/fabro-dev/tests/it/release.rs similarity index 98% rename from lib/crates/fabro-dev/tests/it/release.rs rename to lib/foundation/fabro-dev/tests/it/release.rs index f6949725e..28fb4cf94 100644 --- a/lib/crates/fabro-dev/tests/it/release.rs +++ b/lib/foundation/fabro-dev/tests/it/release.rs @@ -100,7 +100,7 @@ fn dry_run_computes_stable_version_from_date() { "dry-run should print one SPA refresh command:\n{stdout}" ); assert!( - !stdout.contains("git diff --exit-code -- lib/crates/fabro-spa/assets"), + !stdout.contains("git diff --exit-code -- lib/apps/fabro-spa/assets"), "dry-run should not print a separate SPA asset diff command:\n{stdout}" ); assert!( diff --git a/lib/crates/fabro-dev/tests/it/spa.rs b/lib/foundation/fabro-dev/tests/it/spa.rs similarity index 93% rename from lib/crates/fabro-dev/tests/it/spa.rs rename to lib/foundation/fabro-dev/tests/it/spa.rs index 6eeba4194..dd3749c3f 100644 --- a/lib/crates/fabro-dev/tests/it/spa.rs +++ b/lib/foundation/fabro-dev/tests/it/spa.rs @@ -23,10 +23,10 @@ fn check_passes_when_dist_matches_assets_and_budgets_pass() { write_file(fixture.path(), "apps/fabro-web/dist/index.html", b"hello"); write_file( fixture.path(), - "lib/crates/fabro-spa/assets/index.html", + "lib/apps/fabro-spa/assets/index.html", b"hello", ); - write_file(fixture.path(), "lib/crates/fabro-spa/assets/.gitkeep", b""); + write_file(fixture.path(), "lib/apps/fabro-spa/assets/.gitkeep", b""); let output = fabro_dev() .args([ @@ -65,7 +65,7 @@ fn check_fails_when_assets_exceed_budget() { write_file(fixture.path(), "apps/fabro-web/dist/index.html", b"hello"); write_file( fixture.path(), - "lib/crates/fabro-spa/assets/index.html", + "lib/apps/fabro-spa/assets/index.html", b"hello", ); @@ -102,7 +102,7 @@ fn check_fails_when_assets_do_not_match_dist() { write_file(fixture.path(), "apps/fabro-web/dist/index.html", b"current"); write_file( fixture.path(), - "lib/crates/fabro-spa/assets/index.html", + "lib/apps/fabro-spa/assets/index.html", b"embedded", ); @@ -129,7 +129,7 @@ fn check_fails_when_assets_do_not_match_dist() { "stale assets should fail the check:\n{stderr}" ); assert_eq!( - read_bytes(fixture.path(), "lib/crates/fabro-spa/assets/index.html"), + read_bytes(fixture.path(), "lib/apps/fabro-spa/assets/index.html"), b"embedded" ); } @@ -140,7 +140,7 @@ fn check_fails_when_source_map_is_present_in_assets() { write_file(fixture.path(), "apps/fabro-web/dist/index.html", b"hello"); write_file( fixture.path(), - "lib/crates/fabro-spa/assets/assets/app.js.map", + "lib/apps/fabro-spa/assets/assets/app.js.map", b"map", ); diff --git a/lib/crates/fabro-http/Cargo.toml b/lib/foundation/fabro-http/Cargo.toml similarity index 100% rename from lib/crates/fabro-http/Cargo.toml rename to lib/foundation/fabro-http/Cargo.toml diff --git a/lib/crates/fabro-http/src/lib.rs b/lib/foundation/fabro-http/src/lib.rs similarity index 100% rename from lib/crates/fabro-http/src/lib.rs rename to lib/foundation/fabro-http/src/lib.rs diff --git a/lib/crates/fabro-macros/Cargo.toml b/lib/foundation/fabro-macros/Cargo.toml similarity index 100% rename from lib/crates/fabro-macros/Cargo.toml rename to lib/foundation/fabro-macros/Cargo.toml diff --git a/lib/crates/fabro-macros/src/lib.rs b/lib/foundation/fabro-macros/src/lib.rs similarity index 100% rename from lib/crates/fabro-macros/src/lib.rs rename to lib/foundation/fabro-macros/src/lib.rs diff --git a/lib/crates/fabro-macros/src/options_metadata.rs b/lib/foundation/fabro-macros/src/options_metadata.rs similarity index 100% rename from lib/crates/fabro-macros/src/options_metadata.rs rename to lib/foundation/fabro-macros/src/options_metadata.rs diff --git a/lib/crates/fabro-macros/tests/options_metadata.rs b/lib/foundation/fabro-macros/tests/options_metadata.rs similarity index 100% rename from lib/crates/fabro-macros/tests/options_metadata.rs rename to lib/foundation/fabro-macros/tests/options_metadata.rs diff --git a/lib/crates/fabro-model/Cargo.toml b/lib/foundation/fabro-model/Cargo.toml similarity index 100% rename from lib/crates/fabro-model/Cargo.toml rename to lib/foundation/fabro-model/Cargo.toml diff --git a/lib/crates/fabro-model/src/adapter.rs b/lib/foundation/fabro-model/src/adapter.rs similarity index 100% rename from lib/crates/fabro-model/src/adapter.rs rename to lib/foundation/fabro-model/src/adapter.rs diff --git a/lib/crates/fabro-model/src/billing.rs b/lib/foundation/fabro-model/src/billing.rs similarity index 100% rename from lib/crates/fabro-model/src/billing.rs rename to lib/foundation/fabro-model/src/billing.rs diff --git a/lib/crates/fabro-model/src/bootstrap_catalog.rs b/lib/foundation/fabro-model/src/bootstrap_catalog.rs similarity index 100% rename from lib/crates/fabro-model/src/bootstrap_catalog.rs rename to lib/foundation/fabro-model/src/bootstrap_catalog.rs diff --git a/lib/crates/fabro-model/src/catalog.rs b/lib/foundation/fabro-model/src/catalog.rs similarity index 98% rename from lib/crates/fabro-model/src/catalog.rs rename to lib/foundation/fabro-model/src/catalog.rs index 69790f96d..cac9f878f 100644 --- a/lib/crates/fabro-model/src/catalog.rs +++ b/lib/foundation/fabro-model/src/catalog.rs @@ -139,17 +139,19 @@ pub struct SettingsModelLimits { #[serde(deny_unknown_fields)] pub struct SettingsModelFeatures { #[serde(default)] - pub tools: Option, + pub tools: Option, #[serde(default)] - pub vision: Option, + pub vision: Option, #[serde(default)] - pub reasoning: Option, + pub reasoning: Option, #[serde(default)] - pub reasoning_effort: Option, + pub reasoning_effort: Option, #[serde(default)] - pub prompt_cache: Option, + pub prompt_cache: Option, #[serde(default)] - pub sampling_params: Option, + pub cache_control_breakpoints: Option, + #[serde(default)] + pub sampling_params: Option, } #[derive(Debug, Clone, Default, PartialEq, Deserialize)] @@ -574,6 +576,10 @@ pub enum CatalogBuildError { ReasoningEffortControlsWithoutReasoning { model: String }, #[error("model '{model}' declares reasoning_effort feature but features.reasoning is false")] ReasoningEffortWithoutReasoning { model: String }, + #[error( + "model '{model}' declares cache_control_breakpoints but features.prompt_cache is false" + )] + CacheControlBreakpointsWithoutPromptCache { model: String }, #[error( "model '{model}' must declare at least one reasoning_effort when features.reasoning_effort is levels or always_adaptive" )] @@ -1947,12 +1953,15 @@ fn merge_model_features_settings( fallback: &SettingsModelFeatures, ) -> SettingsModelFeatures { SettingsModelFeatures { - tools: higher.tools.or(fallback.tools), - vision: higher.vision.or(fallback.vision), - reasoning: higher.reasoning.or(fallback.reasoning), - reasoning_effort: higher.reasoning_effort.or(fallback.reasoning_effort), - prompt_cache: higher.prompt_cache.or(fallback.prompt_cache), - sampling_params: higher.sampling_params.or(fallback.sampling_params), + tools: higher.tools.or(fallback.tools), + vision: higher.vision.or(fallback.vision), + reasoning: higher.reasoning.or(fallback.reasoning), + reasoning_effort: higher.reasoning_effort.or(fallback.reasoning_effort), + prompt_cache: higher.prompt_cache.or(fallback.prompt_cache), + cache_control_breakpoints: higher + .cache_control_breakpoints + .or(fallback.cache_control_breakpoints), + sampling_params: higher.sampling_params.or(fallback.sampling_params), } } @@ -2261,6 +2270,15 @@ fn build_model_features( model: model_id.to_string(), }); } + let prompt_cache = features.prompt_cache.unwrap_or_default(); + let cache_control_breakpoints = features.cache_control_breakpoints.unwrap_or_default(); + if cache_control_breakpoints && !prompt_cache { + return Err( + CatalogBuildError::CacheControlBreakpointsWithoutPromptCache { + model: model_id.to_string(), + }, + ); + } Ok(ModelFeatures { tools: features @@ -2277,7 +2295,8 @@ fn build_model_features( })?, reasoning, reasoning_effort, - prompt_cache: features.prompt_cache.unwrap_or_default(), + prompt_cache, + cache_control_breakpoints, sampling_params: features.sampling_params.unwrap_or(true), }) } @@ -2977,6 +2996,7 @@ enabled = true 0.5, ReasoningEffortFeature::Levels, false, + false, BillingPolicy::OpenAi, ), ( @@ -2989,6 +3009,7 @@ enabled = true 0.25, ReasoningEffortFeature::Levels, false, + false, BillingPolicy::OpenAi, ), ( @@ -3001,6 +3022,7 @@ enabled = true 0.1, ReasoningEffortFeature::Levels, false, + false, BillingPolicy::OpenAi, ), ( @@ -3013,6 +3035,7 @@ enabled = true 0.5, ReasoningEffortFeature::Levels, false, + true, BillingPolicy::Anthropic, ), ( @@ -3025,6 +3048,7 @@ enabled = true 1.0, ReasoningEffortFeature::AlwaysAdaptive, false, + true, BillingPolicy::Anthropic, ), ]; @@ -3039,6 +3063,7 @@ enabled = true cache_input_cost, reasoning_effort, sampling_params, + cache_control_breakpoints, billing_policy, ) in expected { @@ -3055,6 +3080,10 @@ enabled = true assert!(model.features.prompt_cache, "{id}"); assert_eq!(model.features.reasoning_effort, reasoning_effort, "{id}"); assert_eq!(model.features.sampling_params, sampling_params, "{id}"); + assert_eq!( + model.features.cache_control_breakpoints, cache_control_breakpoints, + "{id}" + ); assert_eq!(model.costs.input_cost_per_mtok, Some(input_cost), "{id}"); assert_eq!(model.costs.output_cost_per_mtok, Some(output_cost), "{id}"); assert_eq!( @@ -3213,6 +3242,7 @@ enabled = true reasoning: true, reasoning_effort: Levels, prompt_cache: true, + cache_control_breakpoints: false, sampling_params: true, }, costs: ModelCosts { @@ -3277,6 +3307,7 @@ enabled = true reasoning: true, reasoning_effort: AlwaysAdaptive, prompt_cache: true, + cache_control_breakpoints: false, sampling_params: false, }, costs: ModelCosts { @@ -5683,6 +5714,39 @@ reasoning_effort = "levels" )); } + #[test] + fn catalog_from_settings_rejects_cache_control_breakpoints_without_prompt_cache() { + let settings = minimal_settings( + r#" +[providers.test] +display_name = "Test" +adapter = "openai_compatible" +agent_profile = "openai" +base_url = "https://example.test/v1" + +[models.model] +provider = "test" +display_name = "Model" +family = "test" + +[models.model.limits] +context_window = 1000 + +[models.model.features] +tools = true +vision = false +reasoning = false +cache_control_breakpoints = true +"#, + ); + + assert!(matches!( + Catalog::from_settings(&settings).unwrap_err(), + CatalogBuildError::CacheControlBreakpointsWithoutPromptCache { model } + if model == "model" + )); + } + #[test] fn catalog_from_settings_rejects_always_adaptive_effort_without_reasoning() { let settings = minimal_settings( @@ -5843,6 +5907,7 @@ sampling_params = false reasoning: true, reasoning_effort: Levels, prompt_cache: true, + cache_control_breakpoints: false, sampling_params: true, }, costs: ModelCosts { @@ -5899,6 +5964,7 @@ sampling_params = false reasoning: true, reasoning_effort: None, prompt_cache: true, + cache_control_breakpoints: false, sampling_params: false, }, costs: ModelCosts { @@ -5947,6 +6013,7 @@ sampling_params = false reasoning: true, reasoning_effort: AlwaysAdaptive, prompt_cache: true, + cache_control_breakpoints: false, sampling_params: false, }, costs: ModelCosts { @@ -6019,6 +6086,7 @@ sampling_params = false reasoning: true, reasoning_effort: Levels, prompt_cache: true, + cache_control_breakpoints: false, sampling_params: true, }, costs: ModelCosts { @@ -6084,6 +6152,7 @@ sampling_params = false reasoning: true, reasoning_effort: Levels, prompt_cache: false, + cache_control_breakpoints: false, sampling_params: true, }, costs: ModelCosts { @@ -6140,6 +6209,7 @@ sampling_params = false reasoning: true, reasoning_effort: Levels, prompt_cache: false, + cache_control_breakpoints: false, sampling_params: true, }, costs: ModelCosts { diff --git a/lib/crates/fabro-model/src/catalog/providers/anthropic.toml b/lib/foundation/fabro-model/src/catalog/providers/anthropic.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/anthropic.toml rename to lib/foundation/fabro-model/src/catalog/providers/anthropic.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/bedrock-openai.toml b/lib/foundation/fabro-model/src/catalog/providers/bedrock-openai.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/bedrock-openai.toml rename to lib/foundation/fabro-model/src/catalog/providers/bedrock-openai.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/bedrock.toml b/lib/foundation/fabro-model/src/catalog/providers/bedrock.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/bedrock.toml rename to lib/foundation/fabro-model/src/catalog/providers/bedrock.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/gemini.toml b/lib/foundation/fabro-model/src/catalog/providers/gemini.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/gemini.toml rename to lib/foundation/fabro-model/src/catalog/providers/gemini.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/inception.toml b/lib/foundation/fabro-model/src/catalog/providers/inception.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/inception.toml rename to lib/foundation/fabro-model/src/catalog/providers/inception.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/kimi.toml b/lib/foundation/fabro-model/src/catalog/providers/kimi.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/kimi.toml rename to lib/foundation/fabro-model/src/catalog/providers/kimi.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/litellm.toml b/lib/foundation/fabro-model/src/catalog/providers/litellm.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/litellm.toml rename to lib/foundation/fabro-model/src/catalog/providers/litellm.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/minimax.toml b/lib/foundation/fabro-model/src/catalog/providers/minimax.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/minimax.toml rename to lib/foundation/fabro-model/src/catalog/providers/minimax.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/ollama.toml b/lib/foundation/fabro-model/src/catalog/providers/ollama.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/ollama.toml rename to lib/foundation/fabro-model/src/catalog/providers/ollama.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/openai.toml b/lib/foundation/fabro-model/src/catalog/providers/openai.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/openai.toml rename to lib/foundation/fabro-model/src/catalog/providers/openai.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/openrouter.toml b/lib/foundation/fabro-model/src/catalog/providers/openrouter.toml similarity index 98% rename from lib/crates/fabro-model/src/catalog/providers/openrouter.toml rename to lib/foundation/fabro-model/src/catalog/providers/openrouter.toml index 4ef7d7c63..b83aa7662 100644 --- a/lib/crates/fabro-model/src/catalog/providers/openrouter.toml +++ b/lib/foundation/fabro-model/src/catalog/providers/openrouter.toml @@ -50,6 +50,7 @@ vision = true reasoning = true reasoning_effort = "always_adaptive" prompt_cache = true +cache_control_breakpoints = true sampling_params = false [providers.openrouter.models."claude-fable-5".costs] @@ -76,6 +77,7 @@ vision = true reasoning = true reasoning_effort = "levels" prompt_cache = true +cache_control_breakpoints = true sampling_params = false [providers.openrouter.models."claude-opus-4-8".costs] @@ -98,6 +100,7 @@ tools = true vision = true reasoning = true prompt_cache = true +cache_control_breakpoints = true [providers.openrouter.models."claude-opus-4-7".costs] input_cost_per_mtok = 5.0 @@ -121,6 +124,7 @@ tools = true vision = true reasoning = true prompt_cache = true +cache_control_breakpoints = true [providers.openrouter.models."claude-sonnet-4-6".costs] input_cost_per_mtok = 3.0 @@ -144,6 +148,7 @@ tools = true vision = true reasoning = false prompt_cache = true +cache_control_breakpoints = true [providers.openrouter.models."claude-haiku-4-5".costs] input_cost_per_mtok = 1.0 diff --git a/lib/crates/fabro-model/src/catalog/providers/poolside.toml b/lib/foundation/fabro-model/src/catalog/providers/poolside.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/poolside.toml rename to lib/foundation/fabro-model/src/catalog/providers/poolside.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/venice.toml b/lib/foundation/fabro-model/src/catalog/providers/venice.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/venice.toml rename to lib/foundation/fabro-model/src/catalog/providers/venice.toml diff --git a/lib/crates/fabro-model/src/catalog/providers/zai.toml b/lib/foundation/fabro-model/src/catalog/providers/zai.toml similarity index 100% rename from lib/crates/fabro-model/src/catalog/providers/zai.toml rename to lib/foundation/fabro-model/src/catalog/providers/zai.toml diff --git a/lib/crates/fabro-model/src/codec.rs b/lib/foundation/fabro-model/src/codec.rs similarity index 100% rename from lib/crates/fabro-model/src/codec.rs rename to lib/foundation/fabro-model/src/codec.rs diff --git a/lib/crates/fabro-model/src/ids.rs b/lib/foundation/fabro-model/src/ids.rs similarity index 100% rename from lib/crates/fabro-model/src/ids.rs rename to lib/foundation/fabro-model/src/ids.rs diff --git a/lib/crates/fabro-model/src/lib.rs b/lib/foundation/fabro-model/src/lib.rs similarity index 100% rename from lib/crates/fabro-model/src/lib.rs rename to lib/foundation/fabro-model/src/lib.rs diff --git a/lib/crates/fabro-model/src/model_ref.rs b/lib/foundation/fabro-model/src/model_ref.rs similarity index 100% rename from lib/crates/fabro-model/src/model_ref.rs rename to lib/foundation/fabro-model/src/model_ref.rs diff --git a/lib/crates/fabro-model/src/model_test.rs b/lib/foundation/fabro-model/src/model_test.rs similarity index 100% rename from lib/crates/fabro-model/src/model_test.rs rename to lib/foundation/fabro-model/src/model_test.rs diff --git a/lib/crates/fabro-model/src/provider.rs b/lib/foundation/fabro-model/src/provider.rs similarity index 100% rename from lib/crates/fabro-model/src/provider.rs rename to lib/foundation/fabro-model/src/provider.rs diff --git a/lib/crates/fabro-model/src/reasoning.rs b/lib/foundation/fabro-model/src/reasoning.rs similarity index 100% rename from lib/crates/fabro-model/src/reasoning.rs rename to lib/foundation/fabro-model/src/reasoning.rs diff --git a/lib/crates/fabro-model/src/types.rs b/lib/foundation/fabro-model/src/types.rs similarity index 88% rename from lib/crates/fabro-model/src/types.rs rename to lib/foundation/fabro-model/src/types.rs index fb0164804..ae3804784 100644 --- a/lib/crates/fabro-model/src/types.rs +++ b/lib/foundation/fabro-model/src/types.rs @@ -41,21 +41,28 @@ fn default_true() -> bool { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ModelFeatures { - pub tools: bool, - pub vision: bool, - pub reasoning: bool, + pub tools: bool, + pub vision: bool, + pub reasoning: bool, /// Whether this model endpoint supports a native reasoning-effort /// parameter. User-facing allowed effort values live in catalog controls. #[serde(default)] - pub reasoning_effort: ReasoningEffortFeature, + pub reasoning_effort: ReasoningEffortFeature, /// Whether this model endpoint supports prompt caching annotations. #[serde(default)] - pub prompt_cache: bool, + pub prompt_cache: bool, + /// Whether the endpoint only caches when the request marks the cacheable + /// prefix with Anthropic-style `cache_control` breakpoints. Set on + /// OpenAI-compatible routes fronting Anthropic models (e.g. Claude via + /// OpenRouter); dialects whose caching mechanism is implied (native + /// Anthropic, Bedrock) ignore it. + #[serde(default)] + pub cache_control_breakpoints: bool, /// Whether the model endpoint accepts classic sampling parameters /// (`temperature`, `top_p`). Models with always-on adaptive behavior /// reject them. #[serde(default = "default_true")] - pub sampling_params: bool, + pub sampling_params: bool, } impl ModelFeatures { @@ -221,12 +228,13 @@ mod tests { training: Some("training".to_string()), knowledge_cutoff: Some("knowledge-cutoff".to_string()), features: ModelFeatures { - tools: true, - vision: true, - reasoning: true, - reasoning_effort: ReasoningEffortFeature::Levels, - prompt_cache: true, - sampling_params: true, + tools: true, + vision: true, + reasoning: true, + reasoning_effort: ReasoningEffortFeature::Levels, + prompt_cache: true, + cache_control_breakpoints: false, + sampling_params: true, }, costs: ModelCosts { input_cost_per_mtok: Some(1.0), diff --git a/lib/crates/fabro-oauth/Cargo.toml b/lib/foundation/fabro-oauth/Cargo.toml similarity index 100% rename from lib/crates/fabro-oauth/Cargo.toml rename to lib/foundation/fabro-oauth/Cargo.toml diff --git a/lib/crates/fabro-oauth/examples/login.rs b/lib/foundation/fabro-oauth/examples/login.rs similarity index 100% rename from lib/crates/fabro-oauth/examples/login.rs rename to lib/foundation/fabro-oauth/examples/login.rs diff --git a/lib/crates/fabro-oauth/src/lib.rs b/lib/foundation/fabro-oauth/src/lib.rs similarity index 100% rename from lib/crates/fabro-oauth/src/lib.rs rename to lib/foundation/fabro-oauth/src/lib.rs diff --git a/lib/crates/fabro-options-metadata/Cargo.toml b/lib/foundation/fabro-options-metadata/Cargo.toml similarity index 100% rename from lib/crates/fabro-options-metadata/Cargo.toml rename to lib/foundation/fabro-options-metadata/Cargo.toml diff --git a/lib/crates/fabro-options-metadata/src/lib.rs b/lib/foundation/fabro-options-metadata/src/lib.rs similarity index 100% rename from lib/crates/fabro-options-metadata/src/lib.rs rename to lib/foundation/fabro-options-metadata/src/lib.rs diff --git a/lib/crates/fabro-proc/Cargo.toml b/lib/foundation/fabro-proc/Cargo.toml similarity index 100% rename from lib/crates/fabro-proc/Cargo.toml rename to lib/foundation/fabro-proc/Cargo.toml diff --git a/lib/crates/fabro-proc/build.rs b/lib/foundation/fabro-proc/build.rs similarity index 100% rename from lib/crates/fabro-proc/build.rs rename to lib/foundation/fabro-proc/build.rs diff --git a/lib/crates/fabro-proc/c/capture_argv.c b/lib/foundation/fabro-proc/c/capture_argv.c similarity index 100% rename from lib/crates/fabro-proc/c/capture_argv.c rename to lib/foundation/fabro-proc/c/capture_argv.c diff --git a/lib/crates/fabro-proc/src/flock.rs b/lib/foundation/fabro-proc/src/flock.rs similarity index 100% rename from lib/crates/fabro-proc/src/flock.rs rename to lib/foundation/fabro-proc/src/flock.rs diff --git a/lib/crates/fabro-proc/src/lib.rs b/lib/foundation/fabro-proc/src/lib.rs similarity index 100% rename from lib/crates/fabro-proc/src/lib.rs rename to lib/foundation/fabro-proc/src/lib.rs diff --git a/lib/crates/fabro-proc/src/pre_exec.rs b/lib/foundation/fabro-proc/src/pre_exec.rs similarity index 100% rename from lib/crates/fabro-proc/src/pre_exec.rs rename to lib/foundation/fabro-proc/src/pre_exec.rs diff --git a/lib/crates/fabro-proc/src/signal.rs b/lib/foundation/fabro-proc/src/signal.rs similarity index 100% rename from lib/crates/fabro-proc/src/signal.rs rename to lib/foundation/fabro-proc/src/signal.rs diff --git a/lib/crates/fabro-proc/src/title.rs b/lib/foundation/fabro-proc/src/title.rs similarity index 100% rename from lib/crates/fabro-proc/src/title.rs rename to lib/foundation/fabro-proc/src/title.rs diff --git a/lib/crates/fabro-redact/Cargo.toml b/lib/foundation/fabro-redact/Cargo.toml similarity index 100% rename from lib/crates/fabro-redact/Cargo.toml rename to lib/foundation/fabro-redact/Cargo.toml diff --git a/lib/crates/fabro-redact/build.rs b/lib/foundation/fabro-redact/build.rs similarity index 100% rename from lib/crates/fabro-redact/build.rs rename to lib/foundation/fabro-redact/build.rs diff --git a/lib/crates/fabro-redact/data/gitleaks.toml b/lib/foundation/fabro-redact/data/gitleaks.toml similarity index 100% rename from lib/crates/fabro-redact/data/gitleaks.toml rename to lib/foundation/fabro-redact/data/gitleaks.toml diff --git a/lib/crates/fabro-redact/src/entropy.rs b/lib/foundation/fabro-redact/src/entropy.rs similarity index 100% rename from lib/crates/fabro-redact/src/entropy.rs rename to lib/foundation/fabro-redact/src/entropy.rs diff --git a/lib/crates/fabro-redact/src/gitleaks.rs b/lib/foundation/fabro-redact/src/gitleaks.rs similarity index 100% rename from lib/crates/fabro-redact/src/gitleaks.rs rename to lib/foundation/fabro-redact/src/gitleaks.rs diff --git a/lib/crates/fabro-redact/src/jsonl.rs b/lib/foundation/fabro-redact/src/jsonl.rs similarity index 100% rename from lib/crates/fabro-redact/src/jsonl.rs rename to lib/foundation/fabro-redact/src/jsonl.rs diff --git a/lib/crates/fabro-redact/src/lib.rs b/lib/foundation/fabro-redact/src/lib.rs similarity index 100% rename from lib/crates/fabro-redact/src/lib.rs rename to lib/foundation/fabro-redact/src/lib.rs diff --git a/lib/crates/fabro-redact/src/safe_url.rs b/lib/foundation/fabro-redact/src/safe_url.rs similarity index 100% rename from lib/crates/fabro-redact/src/safe_url.rs rename to lib/foundation/fabro-redact/src/safe_url.rs diff --git a/lib/crates/fabro-static/Cargo.toml b/lib/foundation/fabro-static/Cargo.toml similarity index 100% rename from lib/crates/fabro-static/Cargo.toml rename to lib/foundation/fabro-static/Cargo.toml diff --git a/lib/crates/fabro-static/src/env_vars.rs b/lib/foundation/fabro-static/src/env_vars.rs similarity index 100% rename from lib/crates/fabro-static/src/env_vars.rs rename to lib/foundation/fabro-static/src/env_vars.rs diff --git a/lib/crates/fabro-static/src/lib.rs b/lib/foundation/fabro-static/src/lib.rs similarity index 100% rename from lib/crates/fabro-static/src/lib.rs rename to lib/foundation/fabro-static/src/lib.rs diff --git a/lib/crates/fabro-static/src/secret_registry.rs b/lib/foundation/fabro-static/src/secret_registry.rs similarity index 100% rename from lib/crates/fabro-static/src/secret_registry.rs rename to lib/foundation/fabro-static/src/secret_registry.rs diff --git a/lib/crates/fabro-telemetry/Cargo.toml b/lib/foundation/fabro-telemetry/Cargo.toml similarity index 100% rename from lib/crates/fabro-telemetry/Cargo.toml rename to lib/foundation/fabro-telemetry/Cargo.toml diff --git a/lib/crates/fabro-telemetry/src/anonymous_id.rs b/lib/foundation/fabro-telemetry/src/anonymous_id.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/anonymous_id.rs rename to lib/foundation/fabro-telemetry/src/anonymous_id.rs diff --git a/lib/crates/fabro-telemetry/src/buffer.rs b/lib/foundation/fabro-telemetry/src/buffer.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/buffer.rs rename to lib/foundation/fabro-telemetry/src/buffer.rs diff --git a/lib/crates/fabro-telemetry/src/context.rs b/lib/foundation/fabro-telemetry/src/context.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/context.rs rename to lib/foundation/fabro-telemetry/src/context.rs diff --git a/lib/crates/fabro-telemetry/src/event.rs b/lib/foundation/fabro-telemetry/src/event.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/event.rs rename to lib/foundation/fabro-telemetry/src/event.rs diff --git a/lib/crates/fabro-telemetry/src/git.rs b/lib/foundation/fabro-telemetry/src/git.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/git.rs rename to lib/foundation/fabro-telemetry/src/git.rs diff --git a/lib/crates/fabro-telemetry/src/lib.rs b/lib/foundation/fabro-telemetry/src/lib.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/lib.rs rename to lib/foundation/fabro-telemetry/src/lib.rs diff --git a/lib/crates/fabro-telemetry/src/panic.rs b/lib/foundation/fabro-telemetry/src/panic.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/panic.rs rename to lib/foundation/fabro-telemetry/src/panic.rs diff --git a/lib/crates/fabro-telemetry/src/sanitize.rs b/lib/foundation/fabro-telemetry/src/sanitize.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/sanitize.rs rename to lib/foundation/fabro-telemetry/src/sanitize.rs diff --git a/lib/crates/fabro-telemetry/src/sender.rs b/lib/foundation/fabro-telemetry/src/sender.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/sender.rs rename to lib/foundation/fabro-telemetry/src/sender.rs diff --git a/lib/crates/fabro-telemetry/src/spawn.rs b/lib/foundation/fabro-telemetry/src/spawn.rs similarity index 100% rename from lib/crates/fabro-telemetry/src/spawn.rs rename to lib/foundation/fabro-telemetry/src/spawn.rs diff --git a/lib/crates/fabro-template/Cargo.toml b/lib/foundation/fabro-template/Cargo.toml similarity index 100% rename from lib/crates/fabro-template/Cargo.toml rename to lib/foundation/fabro-template/Cargo.toml diff --git a/lib/crates/fabro-template/src/dependency.rs b/lib/foundation/fabro-template/src/dependency.rs similarity index 100% rename from lib/crates/fabro-template/src/dependency.rs rename to lib/foundation/fabro-template/src/dependency.rs diff --git a/lib/crates/fabro-template/src/lib.rs b/lib/foundation/fabro-template/src/lib.rs similarity index 100% rename from lib/crates/fabro-template/src/lib.rs rename to lib/foundation/fabro-template/src/lib.rs diff --git a/lib/crates/fabro-template/src/store.rs b/lib/foundation/fabro-template/src/store.rs similarity index 100% rename from lib/crates/fabro-template/src/store.rs rename to lib/foundation/fabro-template/src/store.rs diff --git a/lib/crates/fabro-test/Cargo.toml b/lib/foundation/fabro-test/Cargo.toml similarity index 93% rename from lib/crates/fabro-test/Cargo.toml rename to lib/foundation/fabro-test/Cargo.toml index eebfcf09c..be07f0968 100644 --- a/lib/crates/fabro-test/Cargo.toml +++ b/lib/foundation/fabro-test/Cargo.toml @@ -17,7 +17,7 @@ anyhow.workspace = true assert_cmd = "2" axum = { workspace = true } fabro-config = { path = "../fabro-config" } -fabro-install = { path = "../fabro-install" } +fabro-install = { path = "../../components/fabro-install" } fabro-proc = { path = "../fabro-proc" } fabro-static.workspace = true fabro-types = { path = "../fabro-types" } diff --git a/lib/crates/fabro-test/src/http_assert.rs b/lib/foundation/fabro-test/src/http_assert.rs similarity index 100% rename from lib/crates/fabro-test/src/http_assert.rs rename to lib/foundation/fabro-test/src/http_assert.rs diff --git a/lib/crates/fabro-test/src/lib.rs b/lib/foundation/fabro-test/src/lib.rs similarity index 100% rename from lib/crates/fabro-test/src/lib.rs rename to lib/foundation/fabro-test/src/lib.rs diff --git a/lib/crates/fabro-types/Cargo.toml b/lib/foundation/fabro-types/Cargo.toml similarity index 100% rename from lib/crates/fabro-types/Cargo.toml rename to lib/foundation/fabro-types/Cargo.toml diff --git a/lib/crates/fabro-types/src/artifact.rs b/lib/foundation/fabro-types/src/artifact.rs similarity index 100% rename from lib/crates/fabro-types/src/artifact.rs rename to lib/foundation/fabro-types/src/artifact.rs diff --git a/lib/crates/fabro-types/src/auth.rs b/lib/foundation/fabro-types/src/auth.rs similarity index 100% rename from lib/crates/fabro-types/src/auth.rs rename to lib/foundation/fabro-types/src/auth.rs diff --git a/lib/crates/fabro-types/src/billing.rs b/lib/foundation/fabro-types/src/billing.rs similarity index 100% rename from lib/crates/fabro-types/src/billing.rs rename to lib/foundation/fabro-types/src/billing.rs diff --git a/lib/crates/fabro-types/src/blob_ref.rs b/lib/foundation/fabro-types/src/blob_ref.rs similarity index 100% rename from lib/crates/fabro-types/src/blob_ref.rs rename to lib/foundation/fabro-types/src/blob_ref.rs diff --git a/lib/crates/fabro-types/src/checkpoint.rs b/lib/foundation/fabro-types/src/checkpoint.rs similarity index 100% rename from lib/crates/fabro-types/src/checkpoint.rs rename to lib/foundation/fabro-types/src/checkpoint.rs diff --git a/lib/crates/fabro-types/src/command_output.rs b/lib/foundation/fabro-types/src/command_output.rs similarity index 100% rename from lib/crates/fabro-types/src/command_output.rs rename to lib/foundation/fabro-types/src/command_output.rs diff --git a/lib/crates/fabro-types/src/conclusion.rs b/lib/foundation/fabro-types/src/conclusion.rs similarity index 100% rename from lib/crates/fabro-types/src/conclusion.rs rename to lib/foundation/fabro-types/src/conclusion.rs diff --git a/lib/crates/fabro-types/src/dense.rs b/lib/foundation/fabro-types/src/dense.rs similarity index 100% rename from lib/crates/fabro-types/src/dense.rs rename to lib/foundation/fabro-types/src/dense.rs diff --git a/lib/crates/fabro-types/src/diff.rs b/lib/foundation/fabro-types/src/diff.rs similarity index 100% rename from lib/crates/fabro-types/src/diff.rs rename to lib/foundation/fabro-types/src/diff.rs diff --git a/lib/crates/fabro-types/src/event_envelope.rs b/lib/foundation/fabro-types/src/event_envelope.rs similarity index 100% rename from lib/crates/fabro-types/src/event_envelope.rs rename to lib/foundation/fabro-types/src/event_envelope.rs diff --git a/lib/crates/fabro-types/src/failure_signature.rs b/lib/foundation/fabro-types/src/failure_signature.rs similarity index 100% rename from lib/crates/fabro-types/src/failure_signature.rs rename to lib/foundation/fabro-types/src/failure_signature.rs diff --git a/lib/crates/fabro-types/src/graph.rs b/lib/foundation/fabro-types/src/graph.rs similarity index 100% rename from lib/crates/fabro-types/src/graph.rs rename to lib/foundation/fabro-types/src/graph.rs diff --git a/lib/crates/fabro-types/src/id.rs b/lib/foundation/fabro-types/src/id.rs similarity index 100% rename from lib/crates/fabro-types/src/id.rs rename to lib/foundation/fabro-types/src/id.rs diff --git a/lib/crates/fabro-types/src/interview.rs b/lib/foundation/fabro-types/src/interview.rs similarity index 100% rename from lib/crates/fabro-types/src/interview.rs rename to lib/foundation/fabro-types/src/interview.rs diff --git a/lib/crates/fabro-types/src/lib.rs b/lib/foundation/fabro-types/src/lib.rs similarity index 100% rename from lib/crates/fabro-types/src/lib.rs rename to lib/foundation/fabro-types/src/lib.rs diff --git a/lib/crates/fabro-types/src/llm_backend.rs b/lib/foundation/fabro-types/src/llm_backend.rs similarity index 100% rename from lib/crates/fabro-types/src/llm_backend.rs rename to lib/foundation/fabro-types/src/llm_backend.rs diff --git a/lib/crates/fabro-types/src/manifest_path.rs b/lib/foundation/fabro-types/src/manifest_path.rs similarity index 100% rename from lib/crates/fabro-types/src/manifest_path.rs rename to lib/foundation/fabro-types/src/manifest_path.rs diff --git a/lib/crates/fabro-types/src/mcp_store.rs b/lib/foundation/fabro-types/src/mcp_store.rs similarity index 100% rename from lib/crates/fabro-types/src/mcp_store.rs rename to lib/foundation/fabro-types/src/mcp_store.rs diff --git a/lib/crates/fabro-types/src/outcome.rs b/lib/foundation/fabro-types/src/outcome.rs similarity index 100% rename from lib/crates/fabro-types/src/outcome.rs rename to lib/foundation/fabro-types/src/outcome.rs diff --git a/lib/crates/fabro-types/src/pair.rs b/lib/foundation/fabro-types/src/pair.rs similarity index 100% rename from lib/crates/fabro-types/src/pair.rs rename to lib/foundation/fabro-types/src/pair.rs diff --git a/lib/crates/fabro-types/src/principal.rs b/lib/foundation/fabro-types/src/principal.rs similarity index 100% rename from lib/crates/fabro-types/src/principal.rs rename to lib/foundation/fabro-types/src/principal.rs diff --git a/lib/crates/fabro-types/src/pull_request.rs b/lib/foundation/fabro-types/src/pull_request.rs similarity index 100% rename from lib/crates/fabro-types/src/pull_request.rs rename to lib/foundation/fabro-types/src/pull_request.rs diff --git a/lib/crates/fabro-types/src/repository.rs b/lib/foundation/fabro-types/src/repository.rs similarity index 100% rename from lib/crates/fabro-types/src/repository.rs rename to lib/foundation/fabro-types/src/repository.rs diff --git a/lib/crates/fabro-types/src/run.rs b/lib/foundation/fabro-types/src/run.rs similarity index 100% rename from lib/crates/fabro-types/src/run.rs rename to lib/foundation/fabro-types/src/run.rs diff --git a/lib/crates/fabro-types/src/run_blob_id.rs b/lib/foundation/fabro-types/src/run_blob_id.rs similarity index 100% rename from lib/crates/fabro-types/src/run_blob_id.rs rename to lib/foundation/fabro-types/src/run_blob_id.rs diff --git a/lib/crates/fabro-types/src/run_event/agent.rs b/lib/foundation/fabro-types/src/run_event/agent.rs similarity index 99% rename from lib/crates/fabro-types/src/run_event/agent.rs rename to lib/foundation/fabro-types/src/run_event/agent.rs index 2c128826c..ea7476a3c 100644 --- a/lib/crates/fabro-types/src/run_event/agent.rs +++ b/lib/foundation/fabro-types/src/run_event/agent.rs @@ -195,12 +195,6 @@ pub struct AgentLoopDetectedProps { pub visit: u32, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct AgentTurnLimitReachedProps { - pub max_turns: usize, - pub visit: u32, -} - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AgentSteeringInjectedProps { pub text: String, diff --git a/lib/crates/fabro-types/src/run_event/infra.rs b/lib/foundation/fabro-types/src/run_event/infra.rs similarity index 100% rename from lib/crates/fabro-types/src/run_event/infra.rs rename to lib/foundation/fabro-types/src/run_event/infra.rs diff --git a/lib/crates/fabro-types/src/run_event/misc.rs b/lib/foundation/fabro-types/src/run_event/misc.rs similarity index 100% rename from lib/crates/fabro-types/src/run_event/misc.rs rename to lib/foundation/fabro-types/src/run_event/misc.rs diff --git a/lib/crates/fabro-types/src/run_event/mod.rs b/lib/foundation/fabro-types/src/run_event/mod.rs similarity index 99% rename from lib/crates/fabro-types/src/run_event/mod.rs rename to lib/foundation/fabro-types/src/run_event/mod.rs index 04d6575d9..924738b3e 100644 --- a/lib/crates/fabro-types/src/run_event/mod.rs +++ b/lib/foundation/fabro-types/src/run_event/mod.rs @@ -220,8 +220,6 @@ pub enum EventBody { AgentWarning(AgentWarningProps), #[serde(rename = "agent.loop.detected")] AgentLoopDetected(AgentLoopDetectedProps), - #[serde(rename = "agent.turn.limit")] - AgentTurnLimitReached(AgentTurnLimitReachedProps), #[serde(rename = "agent.steering.injected")] AgentSteeringInjected(AgentSteeringInjectedProps), #[serde(rename = "agent.round.interrupted")] @@ -501,7 +499,6 @@ impl EventBody { Self::AgentError(_) => "agent.error", Self::AgentWarning(_) => "agent.warning", Self::AgentLoopDetected(_) => "agent.loop.detected", - Self::AgentTurnLimitReached(_) => "agent.turn.limit", Self::AgentSteeringInjected(_) => "agent.steering.injected", Self::AgentRoundInterrupted(_) => "agent.round.interrupted", Self::AgentPairUserMessage(_) => "agent.pair.user_message", @@ -674,7 +671,6 @@ fn is_known_event_name(event: &str) -> bool { | "agent.error" | "agent.warning" | "agent.loop.detected" - | "agent.turn.limit" | "agent.steering.injected" | "agent.round.interrupted" | "agent.pair.user_message" diff --git a/lib/crates/fabro-types/src/run_event/run.rs b/lib/foundation/fabro-types/src/run_event/run.rs similarity index 100% rename from lib/crates/fabro-types/src/run_event/run.rs rename to lib/foundation/fabro-types/src/run_event/run.rs diff --git a/lib/crates/fabro-types/src/run_event/session.rs b/lib/foundation/fabro-types/src/run_event/session.rs similarity index 100% rename from lib/crates/fabro-types/src/run_event/session.rs rename to lib/foundation/fabro-types/src/run_event/session.rs diff --git a/lib/crates/fabro-types/src/run_event/stage.rs b/lib/foundation/fabro-types/src/run_event/stage.rs similarity index 100% rename from lib/crates/fabro-types/src/run_event/stage.rs rename to lib/foundation/fabro-types/src/run_event/stage.rs diff --git a/lib/crates/fabro-types/src/run_event/todo.rs b/lib/foundation/fabro-types/src/run_event/todo.rs similarity index 100% rename from lib/crates/fabro-types/src/run_event/todo.rs rename to lib/foundation/fabro-types/src/run_event/todo.rs diff --git a/lib/crates/fabro-types/src/run_failure.rs b/lib/foundation/fabro-types/src/run_failure.rs similarity index 100% rename from lib/crates/fabro-types/src/run_failure.rs rename to lib/foundation/fabro-types/src/run_failure.rs diff --git a/lib/crates/fabro-types/src/run_id.rs b/lib/foundation/fabro-types/src/run_id.rs similarity index 100% rename from lib/crates/fabro-types/src/run_id.rs rename to lib/foundation/fabro-types/src/run_id.rs diff --git a/lib/crates/fabro-types/src/run_projection.rs b/lib/foundation/fabro-types/src/run_projection.rs similarity index 98% rename from lib/crates/fabro-types/src/run_projection.rs rename to lib/foundation/fabro-types/src/run_projection.rs index 07d3a5bf8..2300927b7 100644 --- a/lib/crates/fabro-types/src/run_projection.rs +++ b/lib/foundation/fabro-types/src/run_projection.rs @@ -351,8 +351,13 @@ pub struct StageProjection { pub usage: BilledTokenCounts, #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub todos: Option, + /// Todo/task list owned by the stage's root agent session. + /// + /// OpenAI child sessions own separate per-session plans and do not appear + /// here. Anthropic task lists are root-scoped and shared with child + /// sessions, so child mutations of that shared list do appear here. + #[serde(default, rename = "todos", skip_serializing_if = "Option::is_none")] + pub root_agent_todos: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub subagents: Vec, #[serde(default, skip_serializing_if = "SkillsProjection::is_empty")] @@ -462,7 +467,7 @@ impl StageProjection { timing: None, usage: BilledTokenCounts::default(), model: None, - todos: None, + root_agent_todos: None, subagents: Vec::new(), skills: SkillsProjection::default(), permission_level: None, diff --git a/lib/crates/fabro-types/src/run_sandbox.rs b/lib/foundation/fabro-types/src/run_sandbox.rs similarity index 100% rename from lib/crates/fabro-types/src/run_sandbox.rs rename to lib/foundation/fabro-types/src/run_sandbox.rs diff --git a/lib/crates/fabro-types/src/run_summary.rs b/lib/foundation/fabro-types/src/run_summary.rs similarity index 100% rename from lib/crates/fabro-types/src/run_summary.rs rename to lib/foundation/fabro-types/src/run_summary.rs diff --git a/lib/crates/fabro-types/src/run_title.rs b/lib/foundation/fabro-types/src/run_title.rs similarity index 100% rename from lib/crates/fabro-types/src/run_title.rs rename to lib/foundation/fabro-types/src/run_title.rs diff --git a/lib/crates/fabro-types/src/sandbox_details.rs b/lib/foundation/fabro-types/src/sandbox_details.rs similarity index 100% rename from lib/crates/fabro-types/src/sandbox_details.rs rename to lib/foundation/fabro-types/src/sandbox_details.rs diff --git a/lib/crates/fabro-types/src/sandbox_inventory.rs b/lib/foundation/fabro-types/src/sandbox_inventory.rs similarity index 100% rename from lib/crates/fabro-types/src/sandbox_inventory.rs rename to lib/foundation/fabro-types/src/sandbox_inventory.rs diff --git a/lib/crates/fabro-types/src/sandbox_provider.rs b/lib/foundation/fabro-types/src/sandbox_provider.rs similarity index 100% rename from lib/crates/fabro-types/src/sandbox_provider.rs rename to lib/foundation/fabro-types/src/sandbox_provider.rs diff --git a/lib/crates/fabro-types/src/sandbox_services.rs b/lib/foundation/fabro-types/src/sandbox_services.rs similarity index 100% rename from lib/crates/fabro-types/src/sandbox_services.rs rename to lib/foundation/fabro-types/src/sandbox_services.rs diff --git a/lib/crates/fabro-types/src/secret.rs b/lib/foundation/fabro-types/src/secret.rs similarity index 100% rename from lib/crates/fabro-types/src/secret.rs rename to lib/foundation/fabro-types/src/secret.rs diff --git a/lib/crates/fabro-types/src/session.rs b/lib/foundation/fabro-types/src/session.rs similarity index 100% rename from lib/crates/fabro-types/src/session.rs rename to lib/foundation/fabro-types/src/session.rs diff --git a/lib/crates/fabro-types/src/settings/cli.rs b/lib/foundation/fabro-types/src/settings/cli.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/cli.rs rename to lib/foundation/fabro-types/src/settings/cli.rs diff --git a/lib/crates/fabro-types/src/settings/duration.rs b/lib/foundation/fabro-types/src/settings/duration.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/duration.rs rename to lib/foundation/fabro-types/src/settings/duration.rs diff --git a/lib/crates/fabro-types/src/settings/interp.rs b/lib/foundation/fabro-types/src/settings/interp.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/interp.rs rename to lib/foundation/fabro-types/src/settings/interp.rs diff --git a/lib/crates/fabro-types/src/settings/mod.rs b/lib/foundation/fabro-types/src/settings/mod.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/mod.rs rename to lib/foundation/fabro-types/src/settings/mod.rs diff --git a/lib/crates/fabro-types/src/settings/model_ref.rs b/lib/foundation/fabro-types/src/settings/model_ref.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/model_ref.rs rename to lib/foundation/fabro-types/src/settings/model_ref.rs diff --git a/lib/crates/fabro-types/src/settings/project.rs b/lib/foundation/fabro-types/src/settings/project.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/project.rs rename to lib/foundation/fabro-types/src/settings/project.rs diff --git a/lib/crates/fabro-types/src/settings/public_url.rs b/lib/foundation/fabro-types/src/settings/public_url.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/public_url.rs rename to lib/foundation/fabro-types/src/settings/public_url.rs diff --git a/lib/crates/fabro-types/src/settings/run.rs b/lib/foundation/fabro-types/src/settings/run.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/run.rs rename to lib/foundation/fabro-types/src/settings/run.rs diff --git a/lib/crates/fabro-types/src/settings/server.rs b/lib/foundation/fabro-types/src/settings/server.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/server.rs rename to lib/foundation/fabro-types/src/settings/server.rs diff --git a/lib/crates/fabro-types/src/settings/size.rs b/lib/foundation/fabro-types/src/settings/size.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/size.rs rename to lib/foundation/fabro-types/src/settings/size.rs diff --git a/lib/crates/fabro-types/src/settings/workflow.rs b/lib/foundation/fabro-types/src/settings/workflow.rs similarity index 100% rename from lib/crates/fabro-types/src/settings/workflow.rs rename to lib/foundation/fabro-types/src/settings/workflow.rs diff --git a/lib/crates/fabro-types/src/stage_completion.rs b/lib/foundation/fabro-types/src/stage_completion.rs similarity index 100% rename from lib/crates/fabro-types/src/stage_completion.rs rename to lib/foundation/fabro-types/src/stage_completion.rs diff --git a/lib/crates/fabro-types/src/stage_handler.rs b/lib/foundation/fabro-types/src/stage_handler.rs similarity index 100% rename from lib/crates/fabro-types/src/stage_handler.rs rename to lib/foundation/fabro-types/src/stage_handler.rs diff --git a/lib/crates/fabro-types/src/stage_id.rs b/lib/foundation/fabro-types/src/stage_id.rs similarity index 100% rename from lib/crates/fabro-types/src/stage_id.rs rename to lib/foundation/fabro-types/src/stage_id.rs diff --git a/lib/crates/fabro-types/src/start.rs b/lib/foundation/fabro-types/src/start.rs similarity index 100% rename from lib/crates/fabro-types/src/start.rs rename to lib/foundation/fabro-types/src/start.rs diff --git a/lib/crates/fabro-types/src/status.rs b/lib/foundation/fabro-types/src/status.rs similarity index 100% rename from lib/crates/fabro-types/src/status.rs rename to lib/foundation/fabro-types/src/status.rs diff --git a/lib/crates/fabro-types/src/steering.rs b/lib/foundation/fabro-types/src/steering.rs similarity index 100% rename from lib/crates/fabro-types/src/steering.rs rename to lib/foundation/fabro-types/src/steering.rs diff --git a/lib/crates/fabro-types/src/system_integrations.rs b/lib/foundation/fabro-types/src/system_integrations.rs similarity index 100% rename from lib/crates/fabro-types/src/system_integrations.rs rename to lib/foundation/fabro-types/src/system_integrations.rs diff --git a/lib/crates/fabro-types/src/test_support.rs b/lib/foundation/fabro-types/src/test_support.rs similarity index 100% rename from lib/crates/fabro-types/src/test_support.rs rename to lib/foundation/fabro-types/src/test_support.rs diff --git a/lib/crates/fabro-types/src/timing.rs b/lib/foundation/fabro-types/src/timing.rs similarity index 100% rename from lib/crates/fabro-types/src/timing.rs rename to lib/foundation/fabro-types/src/timing.rs diff --git a/lib/crates/fabro-types/src/todo.rs b/lib/foundation/fabro-types/src/todo.rs similarity index 100% rename from lib/crates/fabro-types/src/todo.rs rename to lib/foundation/fabro-types/src/todo.rs diff --git a/lib/crates/fabro-types/src/transcript.rs b/lib/foundation/fabro-types/src/transcript.rs similarity index 100% rename from lib/crates/fabro-types/src/transcript.rs rename to lib/foundation/fabro-types/src/transcript.rs diff --git a/lib/crates/fabro-types/src/variable.rs b/lib/foundation/fabro-types/src/variable.rs similarity index 100% rename from lib/crates/fabro-types/src/variable.rs rename to lib/foundation/fabro-types/src/variable.rs diff --git a/lib/crates/fabro-types/tests/run_event_serde.rs b/lib/foundation/fabro-types/tests/run_event_serde.rs similarity index 100% rename from lib/crates/fabro-types/tests/run_event_serde.rs rename to lib/foundation/fabro-types/tests/run_event_serde.rs diff --git a/lib/crates/fabro-types/tests/run_failure_serde.rs b/lib/foundation/fabro-types/tests/run_failure_serde.rs similarity index 100% rename from lib/crates/fabro-types/tests/run_failure_serde.rs rename to lib/foundation/fabro-types/tests/run_failure_serde.rs diff --git a/lib/crates/fabro-types/tests/run_spec_methods.rs b/lib/foundation/fabro-types/tests/run_spec_methods.rs similarity index 100% rename from lib/crates/fabro-types/tests/run_spec_methods.rs rename to lib/foundation/fabro-types/tests/run_spec_methods.rs diff --git a/lib/crates/fabro-types/tests/run_spec_serde.rs b/lib/foundation/fabro-types/tests/run_spec_serde.rs similarity index 100% rename from lib/crates/fabro-types/tests/run_spec_serde.rs rename to lib/foundation/fabro-types/tests/run_spec_serde.rs diff --git a/lib/crates/fabro-types/tests/sandbox_inventory_serde.rs b/lib/foundation/fabro-types/tests/sandbox_inventory_serde.rs similarity index 100% rename from lib/crates/fabro-types/tests/sandbox_inventory_serde.rs rename to lib/foundation/fabro-types/tests/sandbox_inventory_serde.rs diff --git a/lib/crates/fabro-types/tests/sandbox_model_serde.rs b/lib/foundation/fabro-types/tests/sandbox_model_serde.rs similarity index 100% rename from lib/crates/fabro-types/tests/sandbox_model_serde.rs rename to lib/foundation/fabro-types/tests/sandbox_model_serde.rs diff --git a/lib/crates/fabro-types/tests/stage_handler.rs b/lib/foundation/fabro-types/tests/stage_handler.rs similarity index 100% rename from lib/crates/fabro-types/tests/stage_handler.rs rename to lib/foundation/fabro-types/tests/stage_handler.rs diff --git a/lib/crates/fabro-util/Cargo.toml b/lib/foundation/fabro-util/Cargo.toml similarity index 100% rename from lib/crates/fabro-util/Cargo.toml rename to lib/foundation/fabro-util/Cargo.toml diff --git a/lib/crates/fabro-util/src/backoff.rs b/lib/foundation/fabro-util/src/backoff.rs similarity index 100% rename from lib/crates/fabro-util/src/backoff.rs rename to lib/foundation/fabro-util/src/backoff.rs diff --git a/lib/crates/fabro-util/src/browser.rs b/lib/foundation/fabro-util/src/browser.rs similarity index 100% rename from lib/crates/fabro-util/src/browser.rs rename to lib/foundation/fabro-util/src/browser.rs diff --git a/lib/crates/fabro-util/src/check_report.rs b/lib/foundation/fabro-util/src/check_report.rs similarity index 100% rename from lib/crates/fabro-util/src/check_report.rs rename to lib/foundation/fabro-util/src/check_report.rs diff --git a/lib/crates/fabro-util/src/dev_token.rs b/lib/foundation/fabro-util/src/dev_token.rs similarity index 100% rename from lib/crates/fabro-util/src/dev_token.rs rename to lib/foundation/fabro-util/src/dev_token.rs diff --git a/lib/crates/fabro-util/src/env.rs b/lib/foundation/fabro-util/src/env.rs similarity index 100% rename from lib/crates/fabro-util/src/env.rs rename to lib/foundation/fabro-util/src/env.rs diff --git a/lib/crates/fabro-util/src/error.rs b/lib/foundation/fabro-util/src/error.rs similarity index 100% rename from lib/crates/fabro-util/src/error.rs rename to lib/foundation/fabro-util/src/error.rs diff --git a/lib/crates/fabro-util/src/exit.rs b/lib/foundation/fabro-util/src/exit.rs similarity index 100% rename from lib/crates/fabro-util/src/exit.rs rename to lib/foundation/fabro-util/src/exit.rs diff --git a/lib/crates/fabro-util/src/home.rs b/lib/foundation/fabro-util/src/home.rs similarity index 100% rename from lib/crates/fabro-util/src/home.rs rename to lib/foundation/fabro-util/src/home.rs diff --git a/lib/crates/fabro-util/src/json.rs b/lib/foundation/fabro-util/src/json.rs similarity index 100% rename from lib/crates/fabro-util/src/json.rs rename to lib/foundation/fabro-util/src/json.rs diff --git a/lib/crates/fabro-util/src/lib.rs b/lib/foundation/fabro-util/src/lib.rs similarity index 100% rename from lib/crates/fabro-util/src/lib.rs rename to lib/foundation/fabro-util/src/lib.rs diff --git a/lib/crates/fabro-util/src/path.rs b/lib/foundation/fabro-util/src/path.rs similarity index 100% rename from lib/crates/fabro-util/src/path.rs rename to lib/foundation/fabro-util/src/path.rs diff --git a/lib/crates/fabro-util/src/printer.rs b/lib/foundation/fabro-util/src/printer.rs similarity index 100% rename from lib/crates/fabro-util/src/printer.rs rename to lib/foundation/fabro-util/src/printer.rs diff --git a/lib/crates/fabro-util/src/run_log.rs b/lib/foundation/fabro-util/src/run_log.rs similarity index 100% rename from lib/crates/fabro-util/src/run_log.rs rename to lib/foundation/fabro-util/src/run_log.rs diff --git a/lib/crates/fabro-util/src/session_secret.rs b/lib/foundation/fabro-util/src/session_secret.rs similarity index 100% rename from lib/crates/fabro-util/src/session_secret.rs rename to lib/foundation/fabro-util/src/session_secret.rs diff --git a/lib/crates/fabro-util/src/shell.rs b/lib/foundation/fabro-util/src/shell.rs similarity index 100% rename from lib/crates/fabro-util/src/shell.rs rename to lib/foundation/fabro-util/src/shell.rs diff --git a/lib/crates/fabro-util/src/terminal.rs b/lib/foundation/fabro-util/src/terminal.rs similarity index 100% rename from lib/crates/fabro-util/src/terminal.rs rename to lib/foundation/fabro-util/src/terminal.rs diff --git a/lib/crates/fabro-util/src/text.rs b/lib/foundation/fabro-util/src/text.rs similarity index 100% rename from lib/crates/fabro-util/src/text.rs rename to lib/foundation/fabro-util/src/text.rs diff --git a/lib/crates/fabro-util/src/time.rs b/lib/foundation/fabro-util/src/time.rs similarity index 100% rename from lib/crates/fabro-util/src/time.rs rename to lib/foundation/fabro-util/src/time.rs diff --git a/lib/crates/fabro-util/src/version.rs b/lib/foundation/fabro-util/src/version.rs similarity index 100% rename from lib/crates/fabro-util/src/version.rs rename to lib/foundation/fabro-util/src/version.rs diff --git a/lib/crates/fabro-util/src/warnings.rs b/lib/foundation/fabro-util/src/warnings.rs similarity index 100% rename from lib/crates/fabro-util/src/warnings.rs rename to lib/foundation/fabro-util/src/warnings.rs diff --git a/lib/crates/fabro-util/tests/dev_token.rs b/lib/foundation/fabro-util/tests/dev_token.rs similarity index 100% rename from lib/crates/fabro-util/tests/dev_token.rs rename to lib/foundation/fabro-util/tests/dev_token.rs diff --git a/lib/crates/fabro-util/tests/error_chain.rs b/lib/foundation/fabro-util/tests/error_chain.rs similarity index 100% rename from lib/crates/fabro-util/tests/error_chain.rs rename to lib/foundation/fabro-util/tests/error_chain.rs diff --git a/lib/crates/fabro-vault/Cargo.toml b/lib/foundation/fabro-vault/Cargo.toml similarity index 100% rename from lib/crates/fabro-vault/Cargo.toml rename to lib/foundation/fabro-vault/Cargo.toml diff --git a/lib/crates/fabro-vault/src/lib.rs b/lib/foundation/fabro-vault/src/lib.rs similarity index 100% rename from lib/crates/fabro-vault/src/lib.rs rename to lib/foundation/fabro-vault/src/lib.rs diff --git a/lib/crates/fabro-vault/src/store.rs b/lib/foundation/fabro-vault/src/store.rs similarity index 100% rename from lib/crates/fabro-vault/src/store.rs rename to lib/foundation/fabro-vault/src/store.rs diff --git a/lib/crates/fabro-vault/tests/store.rs b/lib/foundation/fabro-vault/tests/store.rs similarity index 100% rename from lib/crates/fabro-vault/tests/store.rs rename to lib/foundation/fabro-vault/tests/store.rs diff --git a/lib/packages/fabro-api-client/src/models/model-features.ts b/lib/packages/fabro-api-client/src/models/model-features.ts index 7eb314d8c..5f23381b3 100644 --- a/lib/packages/fabro-api-client/src/models/model-features.ts +++ b/lib/packages/fabro-api-client/src/models/model-features.ts @@ -38,6 +38,10 @@ export interface ModelFeatures { * Whether the model endpoint supports prompt caching. */ 'prompt_cache': boolean; + /** + * Whether the endpoint only caches when the request marks the cacheable prefix with Anthropic-style cache_control breakpoints (e.g. Claude via OpenRouter). + */ + 'cache_control_breakpoints': boolean; /** * Whether the model accepts classic sampling parameters (temperature, top_p). */ diff --git a/test/twin/openai/src/openai/models.rs b/test/twin/openai/src/openai/models.rs index abd2e47e7..c6b33bc31 100644 --- a/test/twin/openai/src/openai/models.rs +++ b/test/twin/openai/src/openai/models.rs @@ -625,15 +625,34 @@ impl ChatCompletionsRequest { #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct ChatMessage { - pub role: String, - pub content: Value, + pub role: String, + pub content: Option, + pub reasoning_content: Option, + pub tool_call_id: Option, + pub tool_calls: Option>, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ChatMessageToolCall { + pub id: String, + #[serde(rename = "type")] + pub kind: String, + pub function: ChatMessageToolCallFunction, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ChatMessageToolCallFunction { + pub name: String, + pub arguments: String, } impl ChatMessage { fn extract_texts(&self) -> Vec { - match &self.content { - Value::String(text) => vec![normalize_whitespace(text)], - Value::Array(parts) => parts + match self.content.as_ref() { + Some(Value::String(text)) => vec![normalize_whitespace(text)], + Some(Value::Array(parts)) => parts .iter() .filter_map(|part| { part.get("text") @@ -647,13 +666,21 @@ impl ChatMessage { fn contains_reasoning_content(&self) -> bool { self.role == "assistant" - && self.content.as_array().is_some_and(|parts| { - parts.iter().any(|part| { - part.get("type") - .and_then(Value::as_str) - .is_some_and(|kind| kind == "reasoning") - }) - }) + && (self + .reasoning_content + .as_deref() + .is_some_and(|reasoning| !reasoning.trim().is_empty()) + || self + .content + .as_ref() + .and_then(Value::as_array) + .is_some_and(|parts| { + parts.iter().any(|part| { + part.get("type") + .and_then(Value::as_str) + .is_some_and(|kind| kind == "reasoning") + }) + })) } } @@ -680,14 +707,24 @@ fn validate_chat_message(message: &ChatMessage) -> Result<(), OpenAiError> { )); } - match &message.content { - Value::String(_) => Ok(()), - Value::Array(parts) if !parts.is_empty() => { + validate_chat_tool_fields(message)?; + + match message.content.as_ref() { + Some(Value::String(_)) => Ok(()), + Some(Value::Array(parts)) if !parts.is_empty() => { for part in parts { validate_chat_message_part(part, &message.role)?; } Ok(()) } + None if message.role == "assistant" + && message + .tool_calls + .as_ref() + .is_some_and(|tool_calls| !tool_calls.is_empty()) => + { + Ok(()) + } _ => Err(OpenAiError::invalid_request( "messages", "unsupported message content shape", @@ -695,6 +732,56 @@ fn validate_chat_message(message: &ChatMessage) -> Result<(), OpenAiError> { } } +fn validate_chat_tool_fields(message: &ChatMessage) -> Result<(), OpenAiError> { + if message.role == "tool" { + if message + .tool_call_id + .as_deref() + .is_none_or(|tool_call_id| tool_call_id.trim().is_empty()) + { + return Err(OpenAiError::invalid_request( + "messages", + "tool messages require a tool_call_id", + )); + } + } else if message.tool_call_id.is_some() { + return Err(OpenAiError::invalid_request( + "messages", + "tool_call_id is only supported on tool messages", + )); + } + + let Some(tool_calls) = &message.tool_calls else { + return Ok(()); + }; + if message.role != "assistant" { + return Err(OpenAiError::invalid_request( + "messages", + "tool_calls are only supported on assistant messages", + )); + } + if tool_calls.is_empty() { + return Err(OpenAiError::invalid_request( + "messages", + "tool_calls must not be empty", + )); + } + for tool_call in tool_calls { + if tool_call.id.trim().is_empty() + || tool_call.kind != "function" + || tool_call.function.name.trim().is_empty() + || tool_call.function.arguments.trim().is_empty() + { + return Err(OpenAiError::invalid_request( + "messages", + "invalid assistant tool call", + )); + } + } + + Ok(()) +} + fn validate_chat_message_part(part: &Value, role: &str) -> Result<(), OpenAiError> { let Some(object) = part.as_object() else { return Err(OpenAiError::invalid_request( diff --git a/test/twin/openai/tests/chat_completions_contract.rs b/test/twin/openai/tests/chat_completions_contract.rs index db1ace170..6fe4c27fe 100644 --- a/test/twin/openai/tests/chat_completions_contract.rs +++ b/test/twin/openai/tests/chat_completions_contract.rs @@ -90,6 +90,44 @@ async fn chat_completions_accepts_supported_openai_compatible_fields() { ); } +#[tokio::test] +async fn chat_completions_accepts_tool_call_history() { + let server = common::spawn_server().await.expect("server should start"); + + let response = server + .post_chat(json!({ + "model": "gpt-test", + "messages": [ + { "role": "user", "content": "replace old with new" }, + { + "role": "assistant", + "tool_calls": [{ + "id": "call_edit", + "type": "function", + "function": { + "name": "edit_file", + "arguments": "{\"old\":\"old\",\"new\":\"new\"}" + } + }] + }, + { + "role": "tool", + "content": "Updated data.txt", + "tool_call_id": "call_edit" + } + ], + "stream": false + })) + .await; + + assert_eq!(response.status(), 200); + let body = response.json::().await.expect("json"); + assert_eq!( + body["choices"][0]["message"]["content"], + "deterministic: replace old with new" + ); +} + #[tokio::test] async fn chat_completions_supports_scripted_tool_call_and_json_schema() { let server = common::spawn_server().await.expect("server should start");