diff --git a/.env.example b/.env.example index 2a8ad144..ddfbef9c 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,6 @@ RHO_DAEMON_API_KEY= # The following are NOT read by rho — setting them has no effect: # RHO_DAEMON_PORT / RHO_DAEMON_HOST — daemon binds 127.0.0.1:4590; # override with the --host / --port flags (see `rho daemon start --help`) -# EYRIE_API_KEY / EYRIE_BASE_URL, HARRIER_API_KEY / HARRIER_ADDR — +# FLUX_API_KEY / FLUX_BASE_URL, HARRIER_API_KEY / HARRIER_ADDR — # consumed by sibling services, not by this binary # See docs/user-guide/05-configuration.md for the full precedence chain. diff --git a/.github/actions/checkout-eyrie/action.yml b/.github/actions/checkout-flux/action.yml similarity index 100% rename from .github/actions/checkout-eyrie/action.yml rename to .github/actions/checkout-flux/action.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 513ee192..6044aca5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ concurrency: env: GO_VERSION: "1.26.6" - # Module resolution: eyrie is the only GrayCodeAI module dependency and it is + # Module resolution: flux is the only GrayCodeAI module dependency and it is # published on the public proxy. Direct-VCS fallback is retained for anything # else. GOPROXY: "https://proxy.golang.org,direct" @@ -29,7 +29,7 @@ env: # Go's reference is explicit that a committed go.work "may cause a continuous # integration (CI) system to select and thus test the wrong versions of a # module's dependencies" and that "CI systems should generally not be allowed to - # use the go.work file". Because ./.github/actions/checkout-eyrie clones the + # use the go.work file". Because ./.github/actions/checkout-flux clones the # siblings at branch HEAD into the workspace parent, workspace mode would silently # hide version skew (e.g. a shared pin that no longer matches). # The `module` job opts back in (GOWORK: "auto") since `go work sync` and the @@ -73,10 +73,10 @@ jobs: run: bash ./scripts/check-ecosystem-boundaries.sh - name: internal layer boundary guard run: bash ./scripts/check-internal-layer-imports.sh - - name: eyrie client boundary guard - run: bash ./scripts/check-eyrie-client-imports.sh - - name: eyrie engine facade boundary guard - run: bash ./scripts/check-eyrie-engine-boundary.sh + - name: flux client boundary guard + run: bash ./scripts/check-flux-client-imports.sh + - name: flux engine facade boundary guard + run: bash ./scripts/check-flux-engine-boundary.sh # ------------------------------------------------------------------------- # 2. Module hygiene — tidy, verify Rho plus the sibling Go modules via go.work. @@ -94,7 +94,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref == 'feat/ecosystem-wiring-pr' && 'feat/ecosystem-wiring' || github.head_ref || github.ref_name }} - name: Generate ecosystem workspace @@ -145,7 +145,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -168,7 +168,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -187,7 +187,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -212,7 +212,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -241,7 +241,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -311,7 +311,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -419,7 +419,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c @@ -477,7 +477,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -522,7 +522,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -552,7 +552,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/.github/workflows/compatibility-matrix.yml b/.github/workflows/compatibility-matrix.yml index e52499ae..ac80831d 100644 --- a/.github/workflows/compatibility-matrix.yml +++ b/.github/workflows/compatibility-matrix.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7848ced2..1ca97e97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 # goreleaser needs full history for changelog # Releases build against the sibling repos resolved via the workspace go.work. - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-flux with: ref: ${{ github.ref_name }} diff --git a/.shared-templates/docs/coverage-matrix.md b/.shared-templates/docs/coverage-matrix.md index 0b8e5c6c..ed283de1 100644 --- a/.shared-templates/docs/coverage-matrix.md +++ b/.shared-templates/docs/coverage-matrix.md @@ -9,7 +9,7 @@ repo's threshold, update both the CI file and this table in the same PR.** | Repo | Threshold | Mechanism | |---|---|---| | `rho` | 65% | inline `bc` check in `ci.yml` | -| `eyrie` | 60% | inline `bc` check in `ci.yml` | +| `flux` | 60% | inline `bc` check in `ci.yml` | | `graycode-skills` | n/a | no Go/Python test suite (skill/content registry) | ## Why thresholds differ per repo diff --git a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl index 890991e5..2dc77a71 100644 --- a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl +++ b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl @@ -12,7 +12,7 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" # ============================================================================= -# VARIANT 1 — Support engine (eyrie, harrier, shrike, swift, kestrel, merlin). +# VARIANT 1 — Support engine (flux, harrier, shrike, swift, kestrel, merlin). # Engines are peers: they may depend on falcon, # but never on rho/internal/* or another engine. # ============================================================================= @@ -81,7 +81,7 @@ echo "ecosystem boundary guard passed" # only — never on a support engine directly, and never on rho/internal. # ============================================================================= # -# FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(eyrie|harrier|shrike|swift|kestrel|merlin)(/|")' +# FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(flux|harrier|shrike|swift|kestrel|merlin)(/|")' # FORBIDDEN_INTERNAL='github\.com/GrayCodeAI/rho/internal' # # (same rg/grep + report pattern as Variant 1, naming "SDKs/skills must go diff --git a/.shared-templates/workflows/go-release.yml.tmpl b/.shared-templates/workflows/go-release.yml.tmpl index 3a5d4d5c..a8ac6ea0 100644 --- a/.shared-templates/workflows/go-release.yml.tmpl +++ b/.shared-templates/workflows/go-release.yml.tmpl @@ -23,8 +23,8 @@ jobs: fetch-depth: 0 # goreleaser needs full history for changelog # Only needed if this repo has local workspace dependencies on other - # graycode-eco repos at build time (rho itself clones eyrie this way via - # ./.github/actions/checkout-eyrie). Omit for repos with none. + # graycode-eco repos at build time (rho itself clones flux this way via + # ./.github/actions/checkout-flux). Omit for repos with none. - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/AGENTS.md b/AGENTS.md index b8ad7f19..ead53ba9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ alwaysApply: false # Extending rho rho is an open-source code intelligence platform. It lives in the `graycode-eco` -workspace alongside the ecosystem repos that power it (`eyrie`, +workspace alongside the ecosystem repos that power it (`flux`, `shrike`, `harrier`, `swift`, `kestrel`, `merlin`). This document describes how to extend rho with custom tools, skills, hooks, and integrations. @@ -192,8 +192,8 @@ Cross-repo severity and finding contracts now live in rho's `internal/contracts` ### Architecture note: provider ownership Implement provider protocols, adapters, catalog metadata, credential mappings, and -provider contract tests in `../eyrie` (the eyrie engine's repo) first. Rho consumes providers only -through Eyrie's stable engine facade; Rho changes should be limited to host UX +provider contract tests in `../flux` (the flux engine's repo) first. Rho consumes providers only +through Flux's stable engine facade; Rho changes should be limited to host UX and facade integration. Concentrate AI is a pay-as-you-go gateway implemented with its native Responses API (`/v1/responses`) under the `concentrate-payg` deployment. @@ -245,7 +245,7 @@ This project is indexed by GitNexus as **rho** (97743 symbols, 322940 relationsh ### Workspace workflow (sibling repos) -rho depends on ecosystem repos (`eyrie`, etc.) as independent sibling repos in the `graycode-eco` workspace. Rho's `go.work` lists them as `../`, so local changes in any sibling are automatically picked up by rho. Each sibling is its own git repo, versioned and released independently. +rho depends on ecosystem repos (`flux`, etc.) as independent sibling repos in the `graycode-eco` workspace. Rho's `go.work` lists them as `../`, so local changes in any sibling are automatically picked up by rho. Each sibling is its own git repo, versioned and released independently. 1. Edit + test in `../` — run its tests, run `make test` in rho 2. Push from the sibling: `git push origin ` diff --git a/CHANGELOG.md b/CHANGELOG.md index 12cb6cf9..9421dc23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- **Dependency renamed eyrie → flux**: rho now depends on + `github.com/GrayCodeAI/flux v0.0.1` (the provider runtime was renamed). + All imports, env vars (`EYRIE_CONFIG_DIR` → `FLUX_CONFIG_DIR`, + `EYRIE_MODEL_CATALOG_PATH` → `FLUX_MODEL_CATALOG_PATH`), boundary-guard + scripts, ecosystem manifest entries, docs, and internal identifiers were + renamed to match. User state paths move from `~/.../eyrie/provider.json` + to `~/.../flux/provider.json`. + ### Security - **Session lock TOCTOU eliminated**: `AcquireLock`'s stat → stale-if->5min → remove → O_EXCL dance could delete a live lock on misjudged staleness and let two instances open the same session. Mutual exclusion now uses an OS advisory lock (`gofrs/flock`, promoted to a direct dependency); a crashed holder's lock is reclaimed instantly because the kernel drops the flock at process death. The lock file keeps PID/timestamps purely as diagnostics. - **Hardened atomic writes for state files**: global settings, checkpoint file contents and restores, handovers, and named checkpoints now go through `internal/safewrite` (same 0600 mode as before, plus fsync+rename atomicity and symlink refusal at the destination). @@ -22,20 +31,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - **BREAKING — `rho credentials migrate` removed**: the subcommand, its man page entry, and the `MigrateEnvFileCredentials` wrappers are gone, and `rho path` no longer reports legacy `~/.rho/env` / `~/.rho/.env` files. Save keys through `/config`. -- **BREAKING — settings model/provider migration removed**: `LoadSettings` no longer moves `model`/`provider` from `settings.json` into Eyrie's `provider.json`, and `SetActiveSelection` is deleted. Stale `model`/`provider` values left in `settings.json` are now ignored on load (Eyrie's selection wins; `--settings` overrides still apply); select the model in `/config`. +- **BREAKING — settings model/provider migration removed**: `LoadSettings` no longer moves `model`/`provider` from `settings.json` into Flux's `provider.json`, and `SetActiveSelection` is deleted. Stale `model`/`provider` values left in `settings.json` are now ignored on load (Flux's selection wins; `--settings` overrides still apply); select the model in `/config`. - **BREAKING — startup provider-secrets migration removed**: print/REPL/watch/TUI startup no longer calls `MigrateProviderSecrets`, and the method is dropped from the gateway `CatalogMaintenance` interface. `rho path` still fails when `provider.json` holds secrets; remove those fields manually. - **BREAKING — `shared/types` guards removed**: `scripts/check-shared-types-imports.sh`, the `contracts-guard` make target, its lefthook and CI steps, and the matching testaudit checks are deleted because the package no longer exists. The ecosystem boundary guards still block `rho/internal` imports. ## [0.2.0] — 2026-07-13 ### Changed -- **Rho/Eyrie production boundary completed**: Rho owns the product face, - sessions, tools, permissions, and public schemas while Eyrie v0.2.1 owns +- **Rho/Flux production boundary completed**: Rho owns the product face, + sessions, tools, permissions, and public schemas while Flux v0.2.1 owns credentials, catalog resolution, provider transport, resilience, and usage - telemetry behind the stable `eyrie/engine` facade. + telemetry behind the stable `flux/engine` facade. - **Provider routing and usage attribution hardened**: resolved route changes, continuation segments, and terminal usage are propagated without duplicate - accounting, and production Eyrie calls use exactly one resilience layer. + accounting, and production Flux calls use exactly one resilience layer. - **Daemon conversations are durable**: JSON and SSE chat requests create or resume persisted sessions, expose stable session IDs, preserve metadata, and distinguish invalid, missing, and corrupt state. @@ -48,8 +57,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **`--permission-mode` CLI flag removed**; `--dangerously-skip-permissions` unchanged (now maps to the Autonomous tier). New `--dry-run` flag added as an unconditional kill switch (deny every tool call, regardless of tier or spec stage) — replaces `dontAsk`'s hard-lockout role. - **Version re-baselined to `0.1.0`** across `cmd/rho/main.go`, `cmd/daemon.go`, `flake.nix`, `.github/workflows/release.yml`, and the `update`/daemon test suites, aligning rho - with the rest of the GrayCodeAI ecosystem (`eyrie`, `shrike`, `harrier`, `kestrel`, `merlin`). -- **Architecture boundary hardening**: Rho now owns runtime request/response DTOs, transport config/provider seams, and review/verification product-boundary contracts, with `eyrie/client` usage restricted to internal adapters and guarded in CI. + with the rest of the GrayCodeAI ecosystem (`flux`, `shrike`, `harrier`, `kestrel`, `merlin`). +- **Architecture boundary hardening**: Rho now owns runtime request/response DTOs, transport config/provider seams, and review/verification product-boundary contracts, with `flux/client` usage restricted to internal adapters and guarded in CI. - **`shared/types` removed**: Rho no longer ships the old shared type path, and local boundary checks now block any attempt to reintroduce it. ### Added @@ -237,5 +246,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Project scaffold with cobra CLI and Bubbletea TUI - Interactive chat REPL with textarea input, spinner, lipgloss styling -- eyrie wired as LLM provider dependency +- flux wired as LLM provider dependency - GitHub Actions CI diff --git a/Makefile b/Makefile index 3d0553de..90e0de64 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ GORELEASER := $(GOBIN_DIR)/goreleaser # --------------------------------------------------------------------------- # Phony declarations (alphabetical). # --------------------------------------------------------------------------- -.PHONY: all bench boundaries build check-replace ci clean ecosystem-guard eyrie-client-guard eyrie-engine-guard manifest-guard peer-guard internal-layers-guard package-boundaries-guard release-parity cover cover-new fmt help install lint lint-fix \ +.PHONY: all bench boundaries build check-replace ci clean ecosystem-guard flux-client-guard flux-engine-guard manifest-guard peer-guard internal-layers-guard package-boundaries-guard release-parity cover cover-new fmt help install lint lint-fix \ release security setup smoke path sync test test-10x test-live test-new test-race tidy version vet api-docs api-validate workspace check-replace: ## Fail if go.mod has local replace directives (run before tagging) @@ -121,11 +121,11 @@ vet: ## Run go vet. ecosystem-guard: ## Fail if external ecosystem repos import rho/internal. bash ./scripts/check-ecosystem-boundaries.sh -eyrie-client-guard: ## Fail on any production eyrie/client import. - bash ./scripts/check-eyrie-client-imports.sh +flux-client-guard: ## Fail on any production flux/client import. + bash ./scripts/check-flux-client-imports.sh -eyrie-engine-guard: ## Require all production Eyrie imports to use the stable engine facade. - bash ./scripts/check-eyrie-engine-boundary.sh +flux-engine-guard: ## Require all production Flux imports to use the stable engine facade. + bash ./scripts/check-flux-engine-boundary.sh peer-guard: ## Fail if support engines import each other instead of depending only on Rho contracts. bash ./scripts/check-support-repo-coupling.sh @@ -136,7 +136,7 @@ internal-layers-guard: ## Enforce one-way dependencies across stable Rho interna package-boundaries-guard: ## Enforce AST/package-graph boundaries with file/line diagnostics. bash ./scripts/check-package-boundaries.sh -boundaries: manifest-guard check-replace ecosystem-guard eyrie-client-guard eyrie-engine-guard peer-guard internal-layers-guard package-boundaries-guard ## Alias for all boundary guards (matches `make boundaries` in engine repos). +boundaries: manifest-guard check-replace ecosystem-guard flux-client-guard flux-engine-guard peer-guard internal-layers-guard package-boundaries-guard ## Alias for all boundary guards (matches `make boundaries` in engine repos). release-parity: ## Verify every go.mod ecosystem version resolves to a reachable remote commit. bash ./scripts/check-module-release-parity.sh diff --git a/README.md b/README.md index dab384b4..d36c692f 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ rho is an AI-powered coding agent that lives in your terminal. It reads your cod **Developer path:** one machine, keychain credentials, local memory. Run `rho path` to check readiness. -- **Model-agnostic** — supports many first-class providers through [eyrie](https://github.com/GrayCodeAI/eyrie) (the exact count is dynamic — see `rho --help`), including Anthropic, OpenAI, Gemini, Fireworks AI, Concentrate AI (pay-as-you-go), DeepSeek, and Ollama +- **Model-agnostic** — supports many first-class providers through [flux](https://github.com/GrayCodeAI/flux) (the exact count is dynamic — see `rho --help`), including Anthropic, OpenAI, Gemini, Fireworks AI, Concentrate AI (pay-as-you-go), DeepSeek, and Ollama - **Zero CGO** — single static binary, cross-compiled for linux/darwin/windows on amd64/arm64 - **Privacy-first** — your code never leaves your machine except to the LLM API you choose - **Runs anywhere** — host execution on any machine with a shell, including over SSH @@ -267,8 +267,8 @@ Features adopted from open-source agent projects. All are off by default unless | Structural code match | `CodeMatch` tool | Tree-sitter query search over Go/Python/TS/TSX | | Composable toolsets | `rho toolset [name]` + `Toolset` tool | Named tool groups (research, dev, ops, full_stack) | | App verification | `AppVerify` tool | Boot-smoke check with readiness polling and evidence artifacts | -| Media generation | `GenerateMedia` tool | Image/video generation with local persistence. Backend via `tool.SetMediaEngine`; an OpenAI-compatible client ships in `eyrie/client` (`ImageClient`), wired by the host (boundary-guarded — rho routes through the eyrie facade) | -| Voice transcription | Telegram voice notes + `stt` package | Transcribe Telegram voice/audio into the prompt. Backend via `stt.SetTranscriber`; an OpenAI-compatible client ships in `eyrie/client` (`AudioClient`), wired by the host | +| Media generation | `GenerateMedia` tool | Image/video generation with local persistence. Backend via `tool.SetMediaEngine`; an OpenAI-compatible client ships in `flux/client` (`ImageClient`), wired by the host (boundary-guarded — rho routes through the flux facade) | +| Voice transcription | Telegram voice notes + `stt` package | Transcribe Telegram voice/audio into the prompt. Backend via `stt.SetTranscriber`; an OpenAI-compatible client ships in `flux/client` (`AudioClient`), wired by the host | | Bounded autonomous budgets | `internal/engine` (`AutonomousBudget`) | Track turns/tokens/time/continuations; report why a run stopped (budget vs gate-passed vs error) | | Agent family messaging | `internal/multiagent` (`FamilyMessenger`) | Direct parent/sibling/child messages with pending caps + rate limits | | Path reservations | `internal/multiagent` ledger | Detect overlapping-file changes between parallel branches | @@ -327,7 +327,7 @@ rho exec --agent reviewer "review last commit" # Custom persona ```bash rho path # Developer path readiness (setup + security) -rho doctor # Full health report (eyrie + token pipeline panel) +rho doctor # Full health report (flux + token pipeline panel) rho ecosystem # Ecosystem panel only rho preflight # Quick ready-to-chat check make path # Developer path verification @@ -336,7 +336,7 @@ make smoke # Build + quick verification script See [docs/SECURITY-DEVELOPER.md](docs/SECURITY-DEVELOPER.md). -See [docs/ecosystem-message-flow.md](docs/ecosystem-message-flow.md) for how eyrie connects during a chat session, and [docs/ECOSYSTEM-WIRING.md](docs/ECOSYSTEM-WIRING.md) for the current-to-proposed architecture and repository boundaries. +See [docs/ecosystem-message-flow.md](docs/ecosystem-message-flow.md) for how flux connects during a chat session, and [docs/ECOSYSTEM-WIRING.md](docs/ECOSYSTEM-WIRING.md) for the current-to-proposed architecture and repository boundaries. In the TUI: `/path`, `/ecosystem`, `/memory` (AGENTS.md). @@ -379,12 +379,12 @@ rho works with any LLM provider. **Developer path:** paste keys in `/config` (st | Xiaomi (MiMo) Token Plan | `xiaomi_mimo_token_plan` | `XIAOMI_MIMO_TOKEN_PLAN_API_KEY` (pick region in `/config`) | | Ollama (local) | `ollama` | `OLLAMA_BASE_URL` (no API key) | -Provider routing, model resolution, and retries are handled by [eyrie](https://github.com/GrayCodeAI/eyrie). +Provider routing, model resolution, and retries are handled by [flux](https://github.com/GrayCodeAI/flux). For deployment-aware routing, set `"deployment_routing": true` in `.rho/settings.json` or export `RHO_DEPLOYMENT_ROUTING=true`. Rho will route canonical model IDs through -Eyrie's deployment catalog, so new models can be exposed by refreshing the catalog +Flux's deployment catalog, so new models can be exposed by refreshing the catalog instead of changing Rho. In chat, run `/refresh-model-catalog` to fetch the latest -deployment-aware catalog into `~/.eyrie/model_catalog.json`. +deployment-aware catalog into `~/.flux/model_catalog.json`. ## Architecture @@ -420,7 +420,7 @@ rho/ Ecosystem sibling repos (independent Git repos in the `graycode-eco` parent folder): -├── eyrie/ # LLM provider runtime +├── flux/ # LLM provider runtime ``` ### Ecosystem @@ -429,7 +429,7 @@ rho is the main CLI/product and integrates these GrayCodeAI repositories in three runtime layers plus optional tooling/platform services: - **Primary product:** **rho** is the only end-user product surface in this ecosystem. -- **Provider engine mounted by Rho:** **eyrie** is the LLM provider runtime, consumed through its stable engine facade. +- **Provider engine mounted by Rho:** **flux** is the LLM provider runtime, consumed through its stable engine facade. - **API consumers/extensions:** **graycode-skills** provides Rho skills installed on demand (`rho skills install`). - **Tooling/platform:** **graycode-platform** contains the optional web/BFF/Rho @@ -437,7 +437,7 @@ three runtime layers plus optional tooling/platform services: Local development uses: -- **`go.mod` modules:** pinned requirements for `eyrie` +- **`go.mod` modules:** pinned requirements for `flux` - **Workspace + `go.work`:** sibling support repos are cloned in the `graycode-eco` workspace (as `../`); `go.work` resolves the module paths to those local checkouts - **Module-mode builds:** standalone builds resolve the pinned `go.mod` versions from the module proxy (no workspace) @@ -462,13 +462,13 @@ You may keep a **personal** parent **`go.work`** that lists alternate clones on | Component | Repository | Purpose | |---|---|---| | **rho** | This repo | AI coding agent | -| **eyrie** | [GrayCodeAI/eyrie](https://github.com/GrayCodeAI/eyrie) | LLM provider runtime | +| **flux** | [GrayCodeAI/flux](https://github.com/GrayCodeAI/flux) | LLM provider runtime | | **graycode-skills** | [GrayCodeAI/graycode-skills](https://github.com/GrayCodeAI/graycode-skills) | Community skill registry | | **graycode-platform** | [GrayCodeAI/graycode-platform](https://github.com/GrayCodeAI/graycode-platform) | Web, BFF, and Rho Cloud | `ecosystem.yaml` is the canonical inventory of repositories cloned as siblings in this local workspace; tooling reads it rather than carrying its -own repo-name list. `eyrie` is the only Go module dependency outside this +own repo-name list. `flux` is the only Go module dependency outside this repo; it is consumed through its stable engine facade. For the consolidated repo map and the current-vs-proposed architecture diagrams, see [docs/architecture/rho-current-vs-proposed.md](docs/architecture/rho-current-vs-proposed.md). diff --git a/SECURITY.md b/SECURITY.md index fc1f0c27..166bdc1a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -118,7 +118,7 @@ implements field-by-field merging with explicit precedence rules. ### Credential storage API keys and secrets are stored in the OS secret store (macOS Keychain, -Linux secret service, Windows Credential Manager) via the `eyrie/credentials` +Linux secret service, Windows Credential Manager) via the `flux/credentials` package. They are never written to `settings.json`, `.env`, or any file in the repository. The `/config` command manages credential storage interactively. diff --git a/api/openapi.yaml b/api/openapi.yaml index d27564d1..ca80217a 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -565,7 +565,7 @@ paths: tags: [system] summary: Readiness probe description: | - Returns 200 only when a session factory is configured and Eyrie's local + Returns 200 only when a session factory is configured and Flux's local preflight confirms provider state, catalog, credentials, and model selection are ready. Returns 503 with the failed dependency otherwise. security: [] diff --git a/cmd/chat.go b/cmd/chat.go index 739abe48..b937255d 100644 --- a/cmd/chat.go +++ b/cmd/chat.go @@ -286,7 +286,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings rhocon go func() { providerName := effectiveProvider entries, _ := rhoconfig.ListEngineModels(context.Background(), providerName, false) - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromFlux(entries) if len(opts) > 0 { modelCacheMu.Lock() modelCache[providerName] = opts @@ -673,7 +673,7 @@ func runChat() error { m.waiting = true } - // Suppress library log output (e.g. eyrie retry warnings) from corrupting the TUI. + // Suppress library log output (e.g. flux retry warnings) from corrupting the TUI. // Must be set BEFORE tea.NewProgram so no initialization logs leak through. log.SetOutput(io.Discard) diff --git a/cmd/chat_commands.go b/cmd/chat_commands.go index b2c368fa..695f3c48 100644 --- a/cmd/chat_commands.go +++ b/cmd/chat_commands.go @@ -256,7 +256,7 @@ var slashDescriptions = map[string]string{ "/version": "Show rho version", "/vim": "Toggle vim mode", "/welcome": "Re-print the welcome header", - "/ecosystem": "Show eyrie and token-engine integration status", + "/ecosystem": "Show flux and token-engine integration status", "/path": "Developer path readiness (setup, security)", "/cron": "Show scheduled jobs", "/keybindings": "Show keyboard shortcuts", diff --git a/cmd/chat_commands_config.go b/cmd/chat_commands_config.go index 3c32974f..ae47a2aa 100644 --- a/cmd/chat_commands_config.go +++ b/cmd/chat_commands_config.go @@ -26,7 +26,7 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, m.session.SetModel(cached[0].ID) _ = rhoconfig.SetGlobalSetting("model", cached[0].ID) } - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Provider set to: %s\nModel: %s\nSaved in eyrie (provider.json).", value, m.session.Model())}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Provider set to: %s\nModel: %s\nSaved in flux (provider.json).", value, m.session.Model())}) return m, nil } if len(parts) >= 3 && parts[1] == "model" { @@ -52,7 +52,7 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, return m, nil } m.syncSessionSelection() - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Model switched to: %s\nSaved in eyrie (provider.json).", m.session.Model())}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Model switched to: %s\nSaved in flux (provider.json).", m.session.Model())}) return m, nil } if len(parts) >= 2 && parts[1] == "keys" { diff --git a/cmd/chat_commands_image.go b/cmd/chat_commands_image.go index 3b935b9c..bfdab010 100644 --- a/cmd/chat_commands_image.go +++ b/cmd/chat_commands_image.go @@ -17,7 +17,7 @@ import ( // // It reads an image (or PDF) file, builds a multimodal user message gated on the // active model's vision capability, and starts a turn. For PDFs the text is -// extracted and injected inline (eyrie has no native document block). Without a +// extracted and injected inline (flux has no native document block). Without a // vision-capable model the image degrades to a text-only note. func (m *chatModel) handleImageCommand(parts []string, text string) (tea.Model, tea.Cmd) { if len(parts) < 2 { diff --git a/cmd/chat_commands_session.go b/cmd/chat_commands_session.go index 005d4c30..fbc33d83 100644 --- a/cmd/chat_commands_session.go +++ b/cmd/chat_commands_session.go @@ -534,7 +534,7 @@ func (m *chatModel) handleSessionCommand(cmd string, parts []string, text string case "/session": info := fmt.Sprintf("Session: %s\nModel: %s/%s\nSpec stage: %s\nMessages: %d\nTools: %d\n%s", m.sessionID, m.session.Provider(), m.session.Model(), - specStageLabel(m.session), m.session.MessageCount(), len(m.registry.EyrieTools()), m.session.CostValue().Summary()) + specStageLabel(m.session), m.session.MessageCount(), len(m.registry.FluxTools()), m.session.CostValue().Summary()) m.messages = append(m.messages, displayMsg{role: "system", content: info}) return m, nil diff --git a/cmd/chat_commands_util.go b/cmd/chat_commands_util.go index 00bd46dd..8e254955 100644 --- a/cmd/chat_commands_util.go +++ b/cmd/chat_commands_util.go @@ -108,7 +108,7 @@ func (m *chatModel) mcpSummary() string { } if m.registry != nil { var toolNames []string - for _, t := range m.registry.EyrieTools() { + for _, t := range m.registry.FluxTools() { if strings.HasPrefix(t.Name, "mcp__") { toolNames = append(toolNames, t.Name) } diff --git a/cmd/chat_config_deployment.go b/cmd/chat_config_deployment.go index 85e5ebca..62f15939 100644 --- a/cmd/chat_config_deployment.go +++ b/cmd/chat_config_deployment.go @@ -78,10 +78,10 @@ func saveCredentialAsync(inference rhoconfig.CredentialInference, secret string) } rhoconfig.InvalidateConfigUICache() rhoconfig.RefreshConfigCredSnapshot(ctx) - result, err := rhoconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) + result, err := rhoconfig.ApplyFluxCredentialsForProvider(ctx, inference.ProviderID) if err != nil && rhoconfig.IsCatalogCacheRequired(err) { if refreshErr := rhoconfig.RefreshCatalogAfterCredentials(ctx, nil); refreshErr == nil { - result, err = rhoconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) + result, err = rhoconfig.ApplyFluxCredentialsForProvider(ctx, inference.ProviderID) } else { err = fmt.Errorf("%w; automatic catalog refresh failed: %w", err, refreshErr) } @@ -102,7 +102,7 @@ func saveCredentialAsync(inference rhoconfig.CredentialInference, secret string) deploymentID: inference.DeploymentID, } } - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromFlux(entries) return configApplyCredentialsMsg{ summary: rhoconfig.FormatApplyCredentialsSummary(result), providerID: inference.ProviderID, diff --git a/cmd/chat_config_models.go b/cmd/chat_config_models.go index cc2e1303..426e5e57 100644 --- a/cmd/chat_config_models.go +++ b/cmd/chat_config_models.go @@ -11,7 +11,7 @@ import ( "github.com/GrayCodeAI/rho/internal/engine" ) -// configModelOption is one row in the /config model picker (display from eyrie, id for settings). +// configModelOption is one row in the /config model picker (display from flux, id for settings). type configModelOption struct { ID string CanonicalID string @@ -74,7 +74,7 @@ func fetchModelsAsync(provider string) tea.Cmd { if err != nil { return modelsFetchedMsg{provider: provider, err: err} } - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromFlux(entries) if len(opts) > 0 { modelCacheMu.Lock() modelCache[provider] = opts @@ -84,7 +84,7 @@ func fetchModelsAsync(provider string) tea.Cmd { } } -func configModelOptionsFromEyrie(entries []rhoconfig.EngineModel) []configModelOption { +func configModelOptionsFromFlux(entries []rhoconfig.EngineModel) []configModelOption { opts := make([]configModelOption, len(entries)) for i, e := range entries { opts[i] = configModelOption{ @@ -180,7 +180,7 @@ func ensureModelCacheLoaded(provider string) { modelSyncMu.Unlock() return } - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromFlux(entries) modelCacheMu.Lock() modelCache[provider] = opts modelCacheMu.Unlock() @@ -252,7 +252,7 @@ func loadConfigModelOptions(provider string) []configModelOption { modelCacheMu.RUnlock() entries, err := rhoconfig.ListEngineModels(context.Background(), provider, false) if err == nil && len(entries) > 0 { - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromFlux(entries) modelCacheMu.Lock() modelCache[provider] = opts modelCacheMu.Unlock() diff --git a/cmd/chat_config_models_test.go b/cmd/chat_config_models_test.go index 7526a7e4..1b37096e 100644 --- a/cmd/chat_config_models_test.go +++ b/cmd/chat_config_models_test.go @@ -45,7 +45,7 @@ func TestModelOptionIsActive(t *testing.T) { } func TestConfigModelOptionsCarryResolvedEngineIdentity(t *testing.T) { - opts := configModelOptionsFromEyrie([]rhoconfig.EngineModel{{ + opts := configModelOptionsFromFlux([]rhoconfig.EngineModel{{ ID: "models/gemini-pro", CanonicalID: "google/gemini-pro", ProviderID: "google", GatewayID: "gemini", Capabilities: []string{"tools", "vision"}, }}) diff --git a/cmd/chat_config_panel.go b/cmd/chat_config_panel.go index 648dd210..2f1da79c 100644 --- a/cmd/chat_config_panel.go +++ b/cmd/chat_config_panel.go @@ -116,7 +116,7 @@ func (m chatModel) configOllamaURLView() string { var b strings.Builder b.WriteString(titleStyle.Render(icons.Llama()+" Ollama local") + "\n") - b.WriteString(mutedStyle.Render("no API key · eyrie discovers installed models") + "\n\n") + b.WriteString(mutedStyle.Render("no API key · flux discovers installed models") + "\n\n") if notice := strings.TrimSpace(m.configNotice); notice != "" { b.WriteString(mutedStyle.Render(sanitizeConfigNotice(notice)) + "\n\n") } diff --git a/cmd/chat_config_save_flow_test.go b/cmd/chat_config_save_flow_test.go index d5be5faa..b029e54e 100644 --- a/cmd/chat_config_save_flow_test.go +++ b/cmd/chat_config_save_flow_test.go @@ -8,8 +8,8 @@ import ( "testing" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/eyrie/catalog" - "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/flux/catalog" + "github.com/GrayCodeAI/flux/credentials" rhoconfig "github.com/GrayCodeAI/rho/internal/config" ) @@ -134,7 +134,7 @@ func configSaveTestTransport(status int) http.RoundTripper { }) } -// roundTripFunc is defined in eyrie/runtime tests; duplicate here for rho cmd tests. +// roundTripFunc is defined in flux/runtime tests; duplicate here for rho cmd tests. type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { @@ -223,10 +223,10 @@ func TestHandleConfigApplyCredentialsMsg_CatalogFailureDoesNotBlameProvider(t *t } } -// Skipped: integration test requiring specific eyrie model catalog state +// Skipped: integration test requiring specific flux model catalog state func TestHandleConfigApplyCredentialsMsg_ValidationFailureDoesNotBlameProvider(t *testing.T) { - // TODO: enable once eyrie catalog fixtures pin the claude-fable-5 model state. - t.Skip("requires specific eyrie model catalog state (claude-fable-5)") + // TODO: enable once flux catalog fixtures pin the claude-fable-5 model state. + t.Skip("requires specific flux model catalog state (claude-fable-5)") rhoconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) @@ -252,10 +252,10 @@ func TestHandleConfigApplyCredentialsMsg_ValidationFailureDoesNotBlameProvider(t } } -// Skipped: integration test requiring specific eyrie model catalog state +// Skipped: integration test requiring specific flux model catalog state func TestHandleConfigApplyCredentialsMsg_AuthenticationFailureBlamesKey(t *testing.T) { - // TODO: enable once eyrie catalog fixtures pin the auth-failure model state. - t.Skip("requires specific eyrie model catalog state") + // TODO: enable once flux catalog fixtures pin the auth-failure model state. + t.Skip("requires specific flux model catalog state") rhoconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) diff --git a/cmd/chat_config_ui.go b/cmd/chat_config_ui.go index 5c287da9..23884ffc 100644 --- a/cmd/chat_config_ui.go +++ b/cmd/chat_config_ui.go @@ -109,7 +109,7 @@ func configNoticeStyle(notice string) lipgloss.Style { strings.Contains(n, "unauthorized"): return lipgloss.NewStyle().Foreground(errorCoral) case strings.HasPrefix(notice, "Refreshed"), - strings.HasPrefix(notice, "Eyrie:"), + strings.HasPrefix(notice, "Flux:"), strings.Contains(notice, "Removed API key"), strings.Contains(notice, "Setup complete"), strings.Contains(n, "key saved"), diff --git a/cmd/chat_errors_test.go b/cmd/chat_errors_test.go index 8d20c8c3..4eab4973 100644 --- a/cmd/chat_errors_test.go +++ b/cmd/chat_errors_test.go @@ -12,7 +12,7 @@ func TestFriendlyError(t *testing.T) { err string contains string }{ - {"rate limit 429", "eyrie: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, + {"rate limit 429", "flux: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, {"unauthorized 401", "HTTP 401 Unauthorized", "Authentication failed"}, {"forbidden 403", "HTTP 403 Forbidden", "Access denied"}, {"not found 404", "model not found", "/model"}, diff --git a/cmd/chat_model.go b/cmd/chat_model.go index 45ddebe0..4b064446 100644 --- a/cmd/chat_model.go +++ b/cmd/chat_model.go @@ -249,7 +249,7 @@ type chatModel struct { configNotice string configEntry string // configEntryNone, configEntryAPIKeyPaste, configEntryOllamaURL configProvider string // e.g. configProviderOllama while entry overlay is open - configModelOptions []configModelOption // labels + ids from eyrie catalog + configModelOptions []configModelOption // labels + ids from flux catalog configModelProvider string // filter models after API key paste configModelSearch string // active model filter query configModelSearchActive bool // typing into model search input diff --git a/cmd/chat_print.go b/cmd/chat_print.go index 29ce4810..76e0db41 100644 --- a/cmd/chat_print.go +++ b/cmd/chat_print.go @@ -140,7 +140,7 @@ func runPrint(text string) error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveEyrieSession(sessionID, sess) + saveFluxSession(sessionID, sess) } return nil } @@ -155,7 +155,7 @@ func runPrint(text string) error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveEyrieSession(sessionID, sess) + saveFluxSession(sessionID, sess) } return nil } @@ -311,7 +311,7 @@ func writePrintEvent(sessionID, eventType, content, toolName string) { fmt.Println(string(data)) } -func saveEyrieSession(id string, sess *engine.Session) { +func saveFluxSession(id string, sess *engine.Session) { raw := sess.RawMessages() if len(raw) == 0 { return @@ -485,7 +485,7 @@ func runRepl() error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveEyrieSession(sessionID, sess) + saveFluxSession(sessionID, sess) } } } diff --git a/cmd/chat_subcommand_mode.go b/cmd/chat_subcommand_mode.go index 4da0f82b..51deaddc 100644 --- a/cmd/chat_subcommand_mode.go +++ b/cmd/chat_subcommand_mode.go @@ -41,7 +41,7 @@ func (mo *modeSubcommand) Handle(m *chatModel, args []string, text string) (tea. } visible := 0 if m.session != nil && m.session.Tools() != nil && m.session.Tools().Registry() != nil { - visible = len(m.session.Tools().Registry().EyrieTools()) + visible = len(m.session.Tools().Registry().FluxTools()) } m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf( "Work mode: %s (plan | act | review)\nShell mode: %s (auto | shell | agent)\nTrust: %s\nAuto-commit: %s\nTools visible: %d\n\n/start · /trust · /branch-agent · /auto-commit", diff --git a/cmd/chat_subcommand_model.go b/cmd/chat_subcommand_model.go index f9c020a3..80a6e0c7 100644 --- a/cmd/chat_subcommand_model.go +++ b/cmd/chat_subcommand_model.go @@ -89,7 +89,7 @@ func (mo *modelSubcommand) Handle(m *chatModel, args []string, text string) (tea m.session.Provider(), ) m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf( - "Model switched: %s → %s (Think: %s)\nConversation history preserved (%d messages); new requests use the new model.\nSaved in eyrie (provider.json). Use /model and press t to toggle Think.", + "Model switched: %s → %s (Think: %s)\nConversation history preserved (%d messages); new requests use the new model.\nSaved in flux (provider.json). Use /model and press t to toggle Think.", prevModel, m.session.Model(), thinkLabel, msgCount, )}) return m, nil diff --git a/cmd/chat_subcommand_status.go b/cmd/chat_subcommand_status.go index b0c2489e..73e5581c 100644 --- a/cmd/chat_subcommand_status.go +++ b/cmd/chat_subcommand_status.go @@ -35,7 +35,7 @@ func buildStatusInfo(m *chatModel) string { visible := 0 if m.registry != nil { toolCount = len(m.registry.PrimaryTools()) - visible = len(m.registry.EyrieTools()) + visible = len(m.registry.FluxTools()) } work := m.session.WorkMode() if work == "" { diff --git a/cmd/chat_turn.go b/cmd/chat_turn.go index 048e991c..48009f0d 100644 --- a/cmd/chat_turn.go +++ b/cmd/chat_turn.go @@ -2,7 +2,7 @@ package cmd // turnHadThinkingOnly reports whether the latest user turn ended with internal // reasoning visible but no assistant reply or tool activity. This is the TUI -// symptom of eyrie's ResponseErrorOnlyReasoning health check. +// symptom of flux's ResponseErrorOnlyReasoning health check. func turnHadThinkingOnly(messages []displayMsg) bool { if len(messages) == 0 { return false diff --git a/cmd/chat_welcome.go b/cmd/chat_welcome.go index 180968b9..5e998d50 100644 --- a/cmd/chat_welcome.go +++ b/cmd/chat_welcome.go @@ -316,7 +316,7 @@ func toolListSummary(registry *tool.Registry) string { if registry == nil { return "No tools enabled." } - tools := registry.EyrieTools() + tools := registry.FluxTools() registered := len(registry.PrimaryTools()) if len(tools) == 0 { return "No tools enabled." @@ -381,8 +381,8 @@ func configCommandSummary(settings rhoconfig.Settings) string { %s %s %s %s -Model catalog and routing live in eyrie — rho is the UI only.`, - auditTint("Setup (eyrie)", textPrimary), +Model catalog and routing live in flux — rho is the UI only.`, + auditTint("Setup (flux)", textPrimary), auditTint("Current", textPrimary), auditTint("provider:", textMuted), auditTint(providerName, infoSky), auditTint("model:", textMuted), auditTint(modelName, infoSky), diff --git a/cmd/compat-test/drift.go b/cmd/compat-test/drift.go index f9820c35..84ca5bed 100644 --- a/cmd/compat-test/drift.go +++ b/cmd/compat-test/drift.go @@ -12,9 +12,9 @@ import ( // a sibling repo can pin an older version than what rho's own go.mod requires, // and Go's minimal version selection will silently pull in rho's newer version // at build time without the consumer's own CI ever having tested it. Rho's -// only external GrayCodeAI dependency is eyrie. See docs/compatibility.md. +// only external GrayCodeAI dependency is flux. See docs/compatibility.md. var trackedPins = []string{ - "github.com/GrayCodeAI/eyrie", + "github.com/GrayCodeAI/flux", } // checkDrift compares rho's own go.mod requirements for trackedPins against diff --git a/cmd/compat-test/drift_test.go b/cmd/compat-test/drift_test.go index c6546ddf..e6c83476 100644 --- a/cmd/compat-test/drift_test.go +++ b/cmd/compat-test/drift_test.go @@ -28,7 +28,7 @@ func TestReadRequires(t *testing.T) { go 1.26 require ( - github.com/GrayCodeAI/eyrie v1.0.0 + github.com/GrayCodeAI/flux v1.0.0 github.com/spf13/cobra v1.8.0 ) @@ -40,7 +40,7 @@ require github.com/GrayCodeAI/other v1.9.0 // indirect t.Fatalf("readRequires: %v", err) } tests := map[string]string{ - "github.com/GrayCodeAI/eyrie": "v1.0.0", + "github.com/GrayCodeAI/flux": "v1.0.0", "github.com/spf13/cobra": "v1.8.0", "github.com/GrayCodeAI/other": "v1.9.0", } @@ -75,14 +75,14 @@ func TestCheckDrift_DetectsDrift(t *testing.T) { go 1.26 -require github.com/GrayCodeAI/eyrie v1.5.0 +require github.com/GrayCodeAI/flux v1.5.0 `) // Consumer sibling pins an older version of the shared dependency. writeMod(t, filepath.Join(ws, "consumer", "go.mod"), `module github.com/GrayCodeAI/consumer go 1.26 -require github.com/GrayCodeAI/eyrie v1.2.0 +require github.com/GrayCodeAI/flux v1.2.0 `) var buf bytes.Buffer @@ -110,13 +110,13 @@ func TestCheckDrift_NoDriftWhenVersionsMatch(t *testing.T) { go 1.26 -require github.com/GrayCodeAI/eyrie v1.5.0 +require github.com/GrayCodeAI/flux v1.5.0 `) writeMod(t, filepath.Join(ws, "consumer", "go.mod"), `module github.com/GrayCodeAI/consumer go 1.26 -require github.com/GrayCodeAI/eyrie v1.5.0 +require github.com/GrayCodeAI/flux v1.5.0 `) var buf bytes.Buffer @@ -144,7 +144,7 @@ func TestCheckDrift_SkipsMissingSiblings(t *testing.T) { go 1.26 -require github.com/GrayCodeAI/eyrie v1.5.0 +require github.com/GrayCodeAI/flux v1.5.0 `) // Directory present but no go.mod — must be skipped silently. if err := os.MkdirAll(filepath.Join(ws, "not-checked-out"), 0o755); err != nil { diff --git a/cmd/completions_test.go b/cmd/completions_test.go index ee5dd0c7..e9e1ddfe 100644 --- a/cmd/completions_test.go +++ b/cmd/completions_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/eyrie/catalog/registry" + "github.com/GrayCodeAI/flux/catalog/registry" ) func TestNewCompletionGenerator(t *testing.T) { diff --git a/cmd/control_plane_hints.go b/cmd/control_plane_hints.go index 44346610..71be5625 100644 --- a/cmd/control_plane_hints.go +++ b/cmd/control_plane_hints.go @@ -24,7 +24,7 @@ func workModeSwitchSummary(sess *engine.Session, wm engine.WorkMode) string { } visible := 0 if sess != nil && sess.Tools() != nil && sess.Tools().Registry() != nil { - visible = len(sess.Tools().Registry().EyrieTools()) + visible = len(sess.Tools().Registry().FluxTools()) } return fmt.Sprintf("Work mode → %s\n%s\nModel-visible tools: %d · /status for full control plane", wm, hint, visible) } diff --git a/cmd/daemon.go b/cmd/daemon.go index 7daab7d3..6951e2fd 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -209,9 +209,9 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { ) }) - // Wire Eyrie's authoritative local preflight into GET /v1/ready. A session + // Wire Flux's authoritative local preflight into GET /v1/ready. A session // factory only proves Rho can attempt construction; readiness additionally - // requires Eyrie's provider state, catalog, credentials, and model selection. + // requires Flux's provider state, catalog, credentials, and model selection. srv.SetReadyFn(daemonReadyProbe(factory)) addr, err := srv.Start() if err != nil { @@ -327,14 +327,14 @@ func slogLevelFromString(s string) slog.Level { } // daemonReadyProbe builds the readiness function installed via SetReadyFn. It -// performs Eyrie's local preflight (provider state, catalog, credentials, and +// performs Flux's local preflight (provider state, catalog, credentials, and // model selection) under a short timeout: // // - No session factory wired -> not ready ("engine not configured"). // - Preflight reports ready -> ready. -// - Preflight reports incomplete -> not ready with the failed Eyrie check. +// - Preflight reports incomplete -> not ready with the failed Flux check. // -// This never performs a paid/live model call — Eyrie preflight only inspects +// This never performs a paid/live model call — Flux preflight only inspects // local catalog/credential/model state — so it is safe to call on every probe. func daemonReadyProbe(factory daemon.SessionFactory) func() (bool, string) { return daemonReadyProbeWithPreflight(factory, rhoconfig.EnginePreflightReport) @@ -346,7 +346,7 @@ func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func return false, "engine not configured" } if preflight == nil { - return false, "Eyrie readiness probe not configured" + return false, "Flux readiness probe not configured" } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -360,13 +360,13 @@ func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func if detail == "" { detail = "failed" } - return false, fmt.Sprintf("Eyrie %s: %s", check.Name, detail) + return false, fmt.Sprintf("Flux %s: %s", check.Name, detail) } } if ctx.Err() != nil { - return false, "Eyrie preflight timed out" + return false, "Flux preflight timed out" } - return false, "Eyrie preflight is not ready" + return false, "Flux preflight is not ready" } } diff --git a/cmd/daemon_ready_test.go b/cmd/daemon_ready_test.go index 82581e85..179f428d 100644 --- a/cmd/daemon_ready_test.go +++ b/cmd/daemon_ready_test.go @@ -30,30 +30,30 @@ func TestDaemonReadyProbe_NilFactory(t *testing.T) { } } -// TestDaemonReadyProbe_FailedEyriePreflight verifies a factory alone does not -// make the daemon ready when Eyrie's authoritative preflight is incomplete. -func TestDaemonReadyProbe_FailedEyriePreflight(t *testing.T) { +// TestDaemonReadyProbe_FailedFluxPreflight verifies a factory alone does not +// make the daemon ready when Flux's authoritative preflight is incomplete. +func TestDaemonReadyProbe_FailedFluxPreflight(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } probe := daemonReadyProbeWithPreflight(factory, func(context.Context) rhoconfig.EnginePreflight { return rhoconfig.EnginePreflight{Ready: false} }) ok, reason := probe() if ok { - t.Fatal("failed Eyrie preflight must report not ready") + t.Fatal("failed Flux preflight must report not ready") } if reason == "" { - t.Fatal("failed Eyrie preflight must include a reason") + t.Fatal("failed Flux preflight must include a reason") } } -func TestDaemonReadyProbe_ReadyEyriePreflight(t *testing.T) { +func TestDaemonReadyProbe_ReadyFluxPreflight(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } probe := daemonReadyProbeWithPreflight(factory, func(context.Context) rhoconfig.EnginePreflight { return rhoconfig.EnginePreflight{Ready: true} }) ok, reason := probe() if !ok || reason != "" { - t.Fatalf("ready Eyrie preflight = (%v, %q), want (true, empty)", ok, reason) + t.Fatalf("ready Flux preflight = (%v, %q), want (true, empty)", ok, reason) } } @@ -93,7 +93,7 @@ func TestDaemonReadyProbe_AffectsReadyEndpoint(t *testing.T) { defer resp.Body.Close() if resp.StatusCode != http.StatusServiceUnavailable { - t.Errorf("expected 503 from /v1/ready when Eyrie is not ready, got %d", resp.StatusCode) + t.Errorf("expected 503 from /v1/ready when Flux is not ready, got %d", resp.StatusCode) } var body daemon.ReadyResponse if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { diff --git a/cmd/diagnostics.go b/cmd/diagnostics.go index a2a3b9a3..19926777 100644 --- a/cmd/diagnostics.go +++ b/cmd/diagnostics.go @@ -54,7 +54,7 @@ func doctorReport(settings rhoconfig.Settings) string { // Ecosystem versions b.WriteString("\nEcosystem versions:\n") for _, component := range []struct{ directory, product string }{ - {directory: "eyrie", product: "Eyrie"}, + {directory: "flux", product: "Flux"}, } { versionFile := filepath.Join(filepath.Dir(cwd), component.directory, "VERSION") // #nosec G304 -- versionFile is built from a fixed sibling-repo list diff --git a/cmd/diagnostics_test.go b/cmd/diagnostics_test.go index 49b026fb..66a4987a 100644 --- a/cmd/diagnostics_test.go +++ b/cmd/diagnostics_test.go @@ -33,7 +33,7 @@ func TestDoctorReport(t *testing.T) { if !strings.Contains(report, "Version") { t.Error("report should mention version") } - if !strings.Contains(report, "Ecosystem (eyrie · token engine)") { + if !strings.Contains(report, "Ecosystem (flux · token engine)") { t.Error("report should include ecosystem panel") } } @@ -74,7 +74,7 @@ func TestDoctorReportUsesResolvedProviderForChecks(t *testing.T) { func TestProviderCredentialHealthCheckerResolvesAuto(t *testing.T) { isolateCredentialHome(t) - t.Setenv("EYRIE_CONFIG_DIR", t.TempDir()) + t.Setenv("FLUX_CONFIG_DIR", t.TempDir()) rhoconfig.InvalidateConfigUICache() store := &contextRecordingCredentialStore{} gateway.SetDefaultStore(store) @@ -117,7 +117,7 @@ func TestProviderCredentialHealthCheckerResolvesAuto(t *testing.T) { func TestProviderCredentialHealthCheckerMissingIsUnhealthy(t *testing.T) { isolateCredentialHome(t) - t.Setenv("EYRIE_CONFIG_DIR", t.TempDir()) + t.Setenv("FLUX_CONFIG_DIR", t.TempDir()) rhoconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) t.Cleanup(func() { diff --git a/cmd/dx.go b/cmd/dx.go index 7222c50a..ab3bfcca 100644 --- a/cmd/dx.go +++ b/cmd/dx.go @@ -69,7 +69,7 @@ func doctorOutput(settings rhoconfig.Settings) string { b.WriteString(fmt.Sprintf(" Provider: %s\n", effectiveProvider)) b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(rhoconfig.ActiveProvider(context.Background())))) - // Model configured (eyrie provider.json) + // Model configured (flux provider.json) effectiveModel := strings.TrimSpace(rhoconfig.ActiveModel(context.Background())) if effectiveModel == "" { effectiveModel = "(not configured)" diff --git a/cmd/ecosystem.go b/cmd/ecosystem.go index d76604a3..aefec218 100644 --- a/cmd/ecosystem.go +++ b/cmd/ecosystem.go @@ -12,8 +12,8 @@ var ecosystemJSON bool var ecosystemCmd = &cobra.Command{ Use: "ecosystem", - Short: "Show eyrie and token-pipeline integration status", - Long: "Print the ecosystem panel summarizing the LLM provider runtime (eyrie) and the local token/compression pipeline. Same block as the top of rho doctor.", + Short: "Show flux and token-pipeline integration status", + Long: "Print the ecosystem panel summarizing the LLM provider runtime (flux) and the local token/compression pipeline. Same block as the top of rho doctor.", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { diff --git a/cmd/ecosystem_test.go b/cmd/ecosystem_test.go index 244e24f7..9b2578e6 100644 --- a/cmd/ecosystem_test.go +++ b/cmd/ecosystem_test.go @@ -14,7 +14,7 @@ func TestEcosystemCmdRuns(t *testing.T) { provider = "auto" } out := rhoconfig.FormatEcosystemPanel(t.Context(), provider, model) - if !strings.Contains(out, "Ecosystem (eyrie · token engine)") { + if !strings.Contains(out, "Ecosystem (flux · token engine)") { t.Fatalf("unexpected panel: %q", out) } if err := ecosystemCmd.RunE(ecosystemCmd, nil); err != nil { diff --git a/cmd/error_classify.go b/cmd/error_classify.go index 849b485e..4fc8b216 100644 --- a/cmd/error_classify.go +++ b/cmd/error_classify.go @@ -30,7 +30,7 @@ func friendlyErrorMessage(err error) string { strings.Contains(low, "does not exist") { ex1, ex2 := rhoconfig.ExampleModelHints() msg = fmt.Sprintf( - "Model not found. Check your model name with /model.\n Examples from the eyrie catalog: %s, %s\n Use /models to list all models, or /config to change provider.", + "Model not found. Check your model name with /model.\n Examples from the flux catalog: %s, %s\n Use /models to list all models, or /config to change provider.", ex1, ex2, ) } diff --git a/cmd/errors_test.go b/cmd/errors_test.go index ae29dc98..aed33822 100644 --- a/cmd/errors_test.go +++ b/cmd/errors_test.go @@ -582,7 +582,7 @@ func TestFriendlyErrorBackwardCompat(t *testing.T) { err string contains string }{ - {"rate limit 429", "eyrie: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, + {"rate limit 429", "flux: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, {"unauthorized 401", "HTTP 401 Unauthorized", "Authentication failed"}, {"forbidden 403", "HTTP 403 Forbidden", "Access denied"}, {"not found 404", "model not found", "/model"}, diff --git a/cmd/eval.go b/cmd/eval.go index 237c4579..5edcce22 100644 --- a/cmd/eval.go +++ b/cmd/eval.go @@ -101,7 +101,7 @@ func runEvalLoop(cmd *cobra.Command, _ []string) error { settings := rhoconfig.LoadGlobalSettings() ctx := context.Background() - gw, err := rhoconfig.NewEyrieEngineForSettings(settings) + gw, err := rhoconfig.NewFluxEngineForSettings(settings) if err != nil { return fmt.Errorf("eval loop: build engine client: %w", err) } diff --git a/cmd/eval_tools.go b/cmd/eval_tools.go index 79024457..fc07dea4 100644 --- a/cmd/eval_tools.go +++ b/cmd/eval_tools.go @@ -84,13 +84,13 @@ func runEvalTools(cmd *cobra.Command, _ []string) error { return err } - tools := registry.EyrieTools() + tools := registry.FluxTools() // caller performs one tool-aware turn and reports the first tool the model // chose (if any). It does not execute the tool — we are scoring selection, // not effects. caller := func(ctx context.Context, c eval.ToolUseCase) (eval.ObservedCall, error) { - resp, err := sess.Chat(ctx, []types.EyrieMessage{ + resp, err := sess.Chat(ctx, []types.FluxMessage{ {Role: "user", Content: c.Prompt}, }, types.ChatOptions{Model: model, Tools: tools}) if err != nil { diff --git a/cmd/image.go b/cmd/image.go index 71b9f825..25af0270 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -188,7 +188,7 @@ func IsPDFFile(path string) bool { // ReadPDFText reads a PDF and extracts its text content using stdlib only. // -// eyrie does not expose a native document/PDF content block, so PDFs are +// flux does not expose a native document/PDF content block, so PDFs are // degraded to text and injected inline. This is a best-effort extractor: it // inflates FlateDecode content streams and pulls text from PDF text-showing // operators (Tj / TJ). It does not handle every PDF (encrypted, scanned-image, diff --git a/cmd/manpage.go b/cmd/manpage.go index d4533824..73156477 100644 --- a/cmd/manpage.go +++ b/cmd/manpage.go @@ -39,7 +39,7 @@ func GenerateManPage() string { b.WriteString("\n") // Name - b.WriteString(".SH NAME\nrho \\- AI coding agent powered by eyrie\n") + b.WriteString(".SH NAME\nrho \\- AI coding agent powered by flux\n") // Synopsis b.WriteString(".SH SYNOPSIS\n") @@ -48,7 +48,7 @@ func GenerateManPage() string { // Description b.WriteString(".SH DESCRIPTION\n") b.WriteString("rho is an AI coding agent that reads, writes, and runs code in your terminal.\n") - fmt.Fprintf(&b, "It connects to %d first-class LLM providers through eyrie, executes tools (file I/O,\n", registeredProviderCount()) + fmt.Fprintf(&b, "It connects to %d first-class LLM providers through flux, executes tools (file I/O,\n", registeredProviderCount()) b.WriteString("shell, git, web search), and manages sessions from a keyboard-driven TUI\n") b.WriteString("or headless mode for scripts and CI.\n") diff --git a/cmd/models.go b/cmd/models.go index 20b10e31..c21da469 100644 --- a/cmd/models.go +++ b/cmd/models.go @@ -19,10 +19,10 @@ var ( var modelsCmd = &cobra.Command{ Use: "models", - Short: "Deployment-aware model catalog (via eyrie)", - Long: `Manage the eyrie model catalog used by rho for models, pricing, and deployment routing. + Short: "Deployment-aware model catalog (via flux)", + Long: `Manage the flux model catalog used by rho for models, pricing, and deployment routing. -The catalog is stored at ~/.eyrie/model_catalog.json (override with EYRIE_MODEL_CATALOG_PATH). +The catalog is stored at ~/.flux/model_catalog.json (override with FLUX_MODEL_CATALOG_PATH). Rho refreshes the catalog automatically on startup when the cache is missing, empty, or stale (disable with --no-auto-catalog-refresh or RHO_AUTO_REFRESH_CATALOG=0). Use 'rho models refresh' for a manual refresh or full discover report.`, } @@ -30,7 +30,7 @@ Use 'rho models refresh' for a manual refresh or full discover report.`, var modelsRefreshCmd = &cobra.Command{ Use: "refresh", Aliases: []string{"update"}, - Short: "Discover model catalog (eyrie remote + live provider APIs) into ~/.eyrie/model_catalog.json", + Short: "Discover model catalog (flux remote + live provider APIs) into ~/.flux/model_catalog.json", RunE: func(cmd *cobra.Command, _ []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -98,7 +98,7 @@ var modelsRoutingPreviewCmd = &cobra.Command{ var modelsListCmd = &cobra.Command{ Use: "list [provider]", - Short: "List models from the eyrie catalog cache (or live provider API)", + Short: "List models from the flux catalog cache (or live provider API)", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -160,7 +160,7 @@ var modelsListCmd = &cobra.Command{ } // modelListJSONEntry is Rho's versioned command-output contract. Keep this -// separate from Eyrie's host-facing Model DTO so engine-only fields can evolve +// separate from Flux's host-facing Model DTO so engine-only fields can evolve // without breaking users that consume `rho models list --json`. type modelListJSONEntry struct { ID string `json:"id"` diff --git a/cmd/ocg_live_test.go b/cmd/ocg_live_test.go index 09a6bec7..4652069f 100644 --- a/cmd/ocg_live_test.go +++ b/cmd/ocg_live_test.go @@ -15,9 +15,9 @@ import ( "testing" "time" - eyriecfg "github.com/GrayCodeAI/eyrie/config" - "github.com/GrayCodeAI/eyrie/credentials" - "github.com/GrayCodeAI/eyrie/setup" + fluxcfg "github.com/GrayCodeAI/flux/config" + "github.com/GrayCodeAI/flux/credentials" + "github.com/GrayCodeAI/flux/setup" "github.com/GrayCodeAI/rho/internal/observability/logger" ) @@ -38,9 +38,9 @@ func TestLiveOpenCodeGoMiniMaxM3FullRhoPath(t *testing.T) { if err != nil { t.Fatal(err) } - t.Logf("provider=%s model=%s tools=%d system_len=%d", effectiveProvider, effectiveModel, len(registry.EyrieTools()), len(systemPrompt)) + t.Logf("provider=%s model=%s tools=%d system_len=%d", effectiveProvider, effectiveModel, len(registry.FluxTools()), len(systemPrompt)) - adapter := setup.ConfiguredDeploymentAdapters(eyriecfg.LoadProviderConfig(""))["opencodego"] + adapter := setup.ConfiguredDeploymentAdapters(fluxcfg.LoadProviderConfig(""))["opencodego"] t.Logf("adapter_type=%T", adapter.Provider) sess := newRhoSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) diff --git a/cmd/options_welcome_test.go b/cmd/options_welcome_test.go index 36fb4832..f949a09e 100644 --- a/cmd/options_welcome_test.go +++ b/cmd/options_welcome_test.go @@ -18,7 +18,7 @@ func isolateCredentialHome(t *testing.T) { _ = os.MkdirAll(rhoDir, 0o700) t.Setenv("HOME", home) t.Setenv("RHO_CONFIG_DIR", rhoDir) - t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) + t.Setenv("FLUX_CONFIG_DIR", filepath.Join(home, "flux")) } func TestEffectiveModelAndProvider_ClearsWithoutCredentials(t *testing.T) { diff --git a/cmd/path.go b/cmd/path.go index b6a3f35a..0313dd5b 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -19,7 +19,7 @@ var pathCmd = &cobra.Command{ Short: "Developer path readiness (setup, security, ecosystem)", Long: `Check whether rho is configured on the developer path: API keys in OS secret store, model selected, no secrets on disk, -and eyrie integration. +and flux integration. Built for individual developers first — teams and enterprise later. diff --git a/cmd/preflight_test.go b/cmd/preflight_test.go index bd173c71..b8455d97 100644 --- a/cmd/preflight_test.go +++ b/cmd/preflight_test.go @@ -9,7 +9,7 @@ import ( // preflightReportShape mirrors the JSON-tagged PreflightReport returned by // EnginePreflightReportWithSettings. We assert against a local shape to -// avoid coupling the test to the exact gateway/eyrie import path. +// avoid coupling the test to the exact gateway/flux import path. type preflightReportShape struct { Ready bool `json:"ready"` Checks []preflightCheckShape `json:"checks"` diff --git a/cmd/review_run.go b/cmd/review_run.go index 163e6de6..2b442474 100644 --- a/cmd/review_run.go +++ b/cmd/review_run.go @@ -121,7 +121,7 @@ func runReviewRun(_ *cobra.Command, args []string) error { } } - // Resolve the provider through Rho's Eyrie engine boundary. + // Resolve the provider through Rho's Flux engine boundary. ctx := context.Background() selection := rhoconfig.EffectiveSelection(ctx, rhoconfig.SelectionOptions{ ProviderOverride: strings.TrimSpace(provider), @@ -169,7 +169,7 @@ func runReviewRun(_ *cobra.Command, args []string) error { model = reviewRunModel } chatFn := func(chatCtx context.Context, prompt string) (string, error) { - resp, chatErr := chatProvider.Chat(chatCtx, []types.EyrieMessage{{Role: "user", Content: prompt}}, types.ChatOptions{ + resp, chatErr := chatProvider.Chat(chatCtx, []types.FluxMessage{{Role: "user", Content: prompt}}, types.ChatOptions{ Provider: providerID, Model: model, }) diff --git a/cmd/root.go b/cmd/root.go index 942e5c6a..06715a99 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -94,10 +94,10 @@ func registeredProviderCount() int { var rootCmd = &cobra.Command{ Use: "rho [prompt]", - Short: "AI coding agent powered by eyrie", + Short: "AI coding agent powered by flux", Long: fmt.Sprintf(`rho is an AI coding agent that reads, writes, and runs code in your terminal. -It connects to %d first-class LLM providers through eyrie, executes tools (file I/O, shell, +It connects to %d first-class LLM providers through flux, executes tools (file I/O, shell, git, web search), and manages sessions — all from a keyboard-driven TUI or headless mode for scripts and CI. @@ -212,13 +212,13 @@ Run rho and use /config to set up your first provider.`, registeredProviderCount return fmt.Errorf("cannot start CLI: folder not trusted (%s)\nProject-scoped hooks, MCP servers, and custom specialists are blocked.\nRun 'rho trust add' to trust this folder before starting rho", tr.Path) } - // Launch TUI — use /config to set API keys; eyrie supplies providers and models + // Launch TUI — use /config to set API keys; flux supplies providers and models return runChat() }, } func init() { - rootCmd.Flags().StringVarP(&model, "model", "m", "", "model to use (from eyrie catalog; see /models)") + rootCmd.Flags().StringVarP(&model, "model", "m", "", "model to use (from flux catalog; see /models)") rootCmd.Flags().BoolVarP(&printMode, "print", "p", false, "print response and exit") rootCmd.Flags().StringVar(&promptFlag, "prompt", "", "send a single prompt and exit (legacy alias for --print)") rootCmd.Flags().StringVar(&outputFormat, "output-format", "text", `output format for --print: "text", "json", "stream-json", or "transcript"`) @@ -259,7 +259,7 @@ func init() { rootCmd.Flags().IntVar(&teachDepth, "teach-depth", 2, "explanation depth: 1=what, 2=why, 3=how") rootCmd.Flags().BoolVar(&autoSkillFlag, "auto-skill", false, "auto-detect project and install matching skills") rootCmd.Flags().BoolVarP(&versionFlag, "version", "v", false, "output the version number") - rootCmd.Flags().BoolVar(&refreshCatalogFlag, "refresh-catalog", false, "refresh the eyrie model catalog before starting") + rootCmd.Flags().BoolVar(&refreshCatalogFlag, "refresh-catalog", false, "refresh the flux model catalog before starting") rootCmd.Flags().BoolVar(&skipCatalogRefreshFlag, "no-auto-catalog-refresh", false, "disable automatic catalog refresh when cache is missing, empty, or stale") rootCmd.Flags().BoolVar(&recoverFlag, "recover", false, "scan for interrupted sessions and offer to resume") rootCmd.Flags().BoolVar(&startupProfileFlag, "startup-profile", false, "print startup performance profile") diff --git a/cmd/session_sync.go b/cmd/session_sync.go index 2c6413e8..39bd88bc 100644 --- a/cmd/session_sync.go +++ b/cmd/session_sync.go @@ -35,7 +35,7 @@ func explicitSelection(ctx context.Context) (provider, model string) { return strings.TrimSpace(rhoconfig.ActiveGateway(ctx)), strings.TrimSpace(rhoconfig.ActiveModel(ctx)) } -// syncSessionFromPersistedSelection copies explicit eyrie provider.json +// syncSessionFromPersistedSelection copies explicit flux provider.json // selection into the live session when the session fields are empty. // It intentionally avoids runtime defaults so Rho can preserve the // "gateway selected, model still missing" setup state. diff --git a/cmd/skills_create.go b/cmd/skills_create.go index 895eac33..730f38ad 100644 --- a/cmd/skills_create.go +++ b/cmd/skills_create.go @@ -33,7 +33,7 @@ func runSkillsCreate(_ *cobra.Command, args []string) error { sess := newRhoSession(settings, provider, model, "You are a skill author.", tool.NewRegistry()) prompt := plugin.BuildNewSkillPrompt(desc) - resp, err := sess.Chat(context.Background(), []types.EyrieMessage{ + resp, err := sess.Chat(context.Background(), []types.FluxMessage{ {Role: "user", Content: prompt}, }, types.ChatOptions{Model: model, MaxTokens: 4096}) if err != nil { diff --git a/cmd/tips.go b/cmd/tips.go index 076b257b..26a7a01c 100644 --- a/cmd/tips.go +++ b/cmd/tips.go @@ -44,7 +44,7 @@ func allTips() []Tip { {ID: "slash-review", Text: "Use /review to get a code review of current changes.", Category: "workflow"}, {ID: "slash-init", Text: "Use /init to analyze a new project automatically.", Category: "project"}, {ID: "add-dir", Text: "Use /add-dir to add extra directories to context.", Category: "context"}, - {ID: "slash-memory", Text: "Use /memory for AGENTS.md; /learn for lessons; /ecosystem for eyrie and token-engine status.", Category: "context"}, + {ID: "slash-memory", Text: "Use /memory for AGENTS.md; /learn for lessons; /ecosystem for flux and token-engine status.", Category: "context"}, {ID: "slash-rewind", Text: "Use /rewind to undo the last exchange.", Category: "session"}, {ID: "slash-fork", Text: "Use /fork to branch off the current conversation.", Category: "session"}, {ID: "slash-context", Text: "Use /context to see what the agent knows about your project.", Category: "context"}, diff --git a/docs/COMPETITIVE.md b/docs/COMPETITIVE.md index 22f4fb37..1efdd980 100644 --- a/docs/COMPETITIVE.md +++ b/docs/COMPETITIVE.md @@ -13,7 +13,7 @@ Verified from source in this repo: - Sandbox: mandatory Docker, fail-closed, never host fallback — `docs/SECURITY-DEVELOPER.md:71-73`, `internal/sandbox/container.go:72-74`. - Creds: OS secret store only, no `.env`/env read — `docs/SECURITY-DEVELOPER.md:7-12`. - Share: local deeplink only — `internal/session/export.go:801-819` returns `rho://share/`, no hosted URL. -- Custom providers: supported — `internal/config/settings.go:50` (`custom_providers`), `internal/config/eyrie_engine.go:32-50`. +- Custom providers: supported — `internal/config/settings.go:50` (`custom_providers`), `internal/config/engine.go:32-50`. - Unwired backends: `internal/tool/computer_use.go:67-94` (`SetComputerBackend`, nil default), `internal/tool/media_generation.go:69-71` (`SetMediaEngine`, nil default). - Terminal detect covers kitty/ghostty/wezterm/alacritty names (`internal/ui/icons/detect_test.go:56`); Kitty graphics protocol not implemented (see `docs/plans/pi-adoption-plan.md:25`). - Bench infra exists (`internal/feature/eval/`, `make bench`) but README publishes no numbers. @@ -63,7 +63,7 @@ External star counts below are approximate web-search snapshots (2026-09-08), no 1. **Traction.** rho has no star-moat (pre-release). Leaders won via day-1 provider-agnostic + one-liner install + Web/Desktop alongside TUI. rho already ships script/brew/npm paths (`README.md:48-59`) — keep, don't add Desktop. 2. **Language/distro.** Go+MIT+zero-CGO (`Makefile:54`, `go.mod:3`) matches `gh/fzf/lazygit` enterprise-safe profile. Avoid GPL/EUPL patterns (kitty/eza). Rust wave wins on published benchmarks — rho has `make bench` but publishes none (Gap-04). -3. **Providers.** rho routes only via `eyrie/engine` facade (`docs/SECURITY-DEVELOPER.md:51-56`, `ecosystem.yaml:29-30`); custom OpenAI-compat supported (`internal/config/settings.go:50`). Count messaging ("28 first-class" per README) trails OpenCode 75+ / Hermes 300+ — fix by exposing catalog count dynamically, not by forking providers into CLI (ownership lives in router per AGENTS.md). +3. **Providers.** rho routes only via `flux/engine` facade (`docs/SECURITY-DEVELOPER.md:51-56`, `ecosystem.yaml:29-30`); custom OpenAI-compat supported (`internal/config/settings.go:50`). Count messaging ("28 first-class" per README) trails OpenCode 75+ / Hermes 300+ — fix by exposing catalog count dynamically, not by forking providers into CLI (ownership lives in router per AGENTS.md). 4. **TUI/UX.** Bubble Tea v2 + vim keys + `/autonomy` + `/spec` + watch `AI!`/`AI?` + visual diff is competitive. Missing vs field: hosted share-link (ours is local `rho://` deeplink), multi-session grid (we have `mission` worktrees + daemon — unsurfaced like herdr/cmux). Gap-02. 5. **Sandbox.** Docker-only fail-closed is strictest default alongside Codex net-off and Gemini gVisor. Tradeoff is onboarding friction without Docker. Must not add host-exec fallback (violates `docs/SECURITY-DEVELOPER.md:71-73`); fix with preflight/path/doctor messaging + image pull/build guidance. Gap-01. 6. **Memory/context.** AST repomap + Harrier graph + compaction segments + relevance-prune + conversation-arc + 80% tool-result clearing exceeds most. Missing: Hermes-style auto-skill learning loop (we have curator archive + harness — surface it). diff --git a/docs/DEVELOPER-PATH.md b/docs/DEVELOPER-PATH.md index b2fdc2f3..4766fb0a 100644 --- a/docs/DEVELOPER-PATH.md +++ b/docs/DEVELOPER-PATH.md @@ -8,8 +8,8 @@ For Rho, the developer path is the minimum local setup required to chat, edit co - A provider credential stored in the OS secret store - A model selected in Rho settings -- A local model catalog available through eyrie -- No plaintext API keys left in Eyrie's configured `provider.json` or legacy env files +- A local model catalog available through flux +- No plaintext API keys left in Flux's configured `provider.json` or legacy env files - Safe defaults for Bash execution and filesystem access - Optional but healthy local memory and token-pipeline state @@ -33,7 +33,7 @@ workspace; they are not nested under Rho: ```bash mkdir graycode-eco && cd graycode-eco git clone https://github.com/GrayCodeAI/rho -git clone https://github.com/GrayCodeAI/eyrie +git clone https://github.com/GrayCodeAI/flux cd rho make setup go build -o rho ./cmd/rho @@ -64,7 +64,7 @@ validation or `rho models list --live`. ### 3. Select a model -Pick a model in `/config`. Rho stores the selected model in settings and uses eyrie for provider routing and catalog resolution. +Pick a model in `/config`. Rho stores the selected model in settings and uses flux for provider routing and catalog resolution. If the catalog is missing or empty: @@ -76,10 +76,10 @@ rho models refresh `rho path` treats these as important security conditions: -- Eyrie's resolved `provider.json` must not contain secret fields +- Flux's resolved `provider.json` must not contain secret fields - sensitive files like provider config and SSH paths should be blocked from agent reads -Eyrie resolves provider state from `EYRIE_CONFIG_DIR` first, then the +Flux resolves provider state from `FLUX_CONFIG_DIR` first, then the platform user-config directory. Rho protects that resolved path even when it is customized or symlinked. @@ -96,7 +96,7 @@ container runtime is required, and there is no container fallback to configure. `rho path` also verifies the provider layer behind Rho: -- `eyrie` for provider routing and local preflight readiness +- `flux` for provider routing and local preflight readiness - the embedded token pipeline for estimation and compression If you want the broader status summary: diff --git a/docs/DYNAMIC-MODELS.md b/docs/DYNAMIC-MODELS.md index d409d69c..214728d9 100644 --- a/docs/DYNAMIC-MODELS.md +++ b/docs/DYNAMIC-MODELS.md @@ -2,9 +2,9 @@ ## Ownership and flow -Rho is the product face; Eyrie is the provider engine. Rho owns the CLI/TUI, +Rho is the product face; Flux is the provider engine. Rho owns the CLI/TUI, model-picker presentation, user intent, and output compatibility. The -`eyrie/engine` facade alone owns provider registry details, credentials, +`flux/engine` facade alone owns provider registry details, credentials, discovery, catalog/cache policy, model aliases, deployment routing, and chat transport. @@ -12,7 +12,7 @@ transport. provider APIs / remote catalog / local cache | v - eyrie/engine.Engine + flux/engine.Engine catalog + credentials + routing | stable host DTOs and methods @@ -21,14 +21,14 @@ provider APIs / remote catalog / local cache /config | models | conversation ``` -Production Rho packages must not import Eyrie packages below -`github.com/GrayCodeAI/eyrie/engine`. Shell and AST guards enforce a +Production Rho packages must not import Flux packages below +`github.com/GrayCodeAI/flux/engine`. Shell and AST guards enforce a zero-exception boundary. ## Rho composition boundary `internal/config` is Rho's control-plane composition root. It creates an -Eyrie engine and projects engine models into Rho UI and command contracts: +Flux engine and projects engine models into Rho UI and command contracts: ```go models, err := config.ListEngineModels(ctx, "anthropic", false) @@ -38,14 +38,14 @@ public, err := config.ListPublicEngineModels(ctx, "xiaomi_mimo_payg") Conversation construction goes through Rho's `internal/engine` adapter. The adapter translates Rho-owned message, tool, usage, and stream DTOs to the -Eyrie engine facade; conversation history, WAL, resume, approvals, and tool +Flux engine facade; conversation history, WAL, resume, approvals, and tool execution remain Rho-owned. ## Catalog and live discovery -The Eyrie engine combines its provider registry, provider-scoped live -discovery, and the cache at `~/.eyrie/model_catalog.json` by default. Override -the cache with `EYRIE_MODEL_CATALOG_PATH`. Credential and state dependencies +The Flux engine combines its provider registry, provider-scoped live +discovery, and the cache at `~/.flux/model_catalog.json` by default. Override +the cache with `FLUX_MODEL_CATALOG_PATH`. Credential and state dependencies are injected into each Engine instance, so discovery does not need hidden process-global credentials. @@ -74,7 +74,7 @@ validation) as **live verified**. ## Stable command output `rho models list --json` is a Rho-owned compatibility contract, not a direct -serialization of Eyrie's evolving `engine.Model` DTO. Its stable fields are: +serialization of Flux's evolving `engine.Model` DTO. Its stable fields are: ```text id, input_price_per_1m, output_price_per_1m, context_window, max_output, @@ -90,18 +90,18 @@ metadata, it returns the stable Rho compatibility row instead of `null`. Rho converts effective `custom_providers` settings into `engine.Options.CustomGateways` at its composition root. Custom gateway metadata is snapshotted per Engine instance. Do not register custom gateways -in Eyrie process-global state: tests, parallel sessions, and future multi-tenant +in Flux process-global state: tests, parallel sessions, and future multi-tenant hosts must be isolated from one another. ## Adding or changing a provider 1. Implement registry, discovery, credentials, aliases, and transport behavior - behind Eyrie's engine facade. -2. Add Eyrie tests for cache and live discovery, credential status, selection, + behind Flux's engine facade. +2. Add Flux tests for cache and live discovery, credential status, selection, and generation/streaming. -3. Commit and verify standalone Eyrie. -4. Advance Rho's `../eyrie` sibling checkout to that exact commit, then update - Rho's module version when the Eyrie revision is published. +3. Commit and verify standalone Flux. +4. Advance Rho's `../flux` sibling checkout to that exact commit, then update + Rho's module version when the Flux revision is published. 5. Verify both the workspace (`go.work`) and published-module (`GOWORK=off`) build modes. @@ -114,6 +114,6 @@ Rho-owned presentation field—not for provider-specific mechanics. rho models refresh rho models status rho preflight -make eyrie-engine-guard +make flux-engine-guard go test ./cmd ./internal/config ./internal/engine -count=1 ``` diff --git a/docs/ECOSYSTEM-CONFIG.md b/docs/ECOSYSTEM-CONFIG.md index f029c5c9..72e33b32 100644 --- a/docs/ECOSYSTEM-CONFIG.md +++ b/docs/ECOSYSTEM-CONFIG.md @@ -3,7 +3,7 @@ Status: Draft / historical spec (aspirational; not implemented) Applies to: the wider graycode-eco ecosystem -> Note: Rho now depends only on eyrie and embeds its own token engine. +> Note: Rho now depends only on flux and embeds its own token engine. > References below to harrier, shrike, and swift describe a proposed > ecosystem-wide schema, not Rho's current runtime. @@ -15,7 +15,7 @@ file is the source of truth, version-controlled alongside a project, and each repo reads the slice of the schema it owns. Today each repo configures itself independently through its own env vars, -flags, and config files (rho: `config.json` + `RHO_*`/`RHO_*` env; eyrie: +flags, and config files (rho: `config.json` + `RHO_*`/`RHO_*` env; flux: provider env vars; harrier: `~/.harrier/config.toml`; shrike: `TOK_*` env; swift: `SWIFT_*` env). This spec does **not** replace those mechanisms — it defines a superset schema and maps every setting back to the repo + existing env @@ -31,7 +31,7 @@ any runtime behavior. `graycode-eco.yaml` value > repo default. This preserves current behavior where env/flags are authoritative. 3. **Repo-owned sections.** Each top-level section is owned by one repo (with - `model`/`providers` shared by rho + eyrie). A repo only reads its sections. + `model`/`providers` shared by rho + flux). A repo only reads its sections. 4. **Two encodings, one schema.** YAML is canonical for humans; the identical structure is valid JSON for machine generation. (harrier's on-disk format is TOML; its section maps 1:1 to `~/.harrier/config.toml`.) @@ -53,7 +53,7 @@ runtime precedence above): ```yaml version: 1 -# ─── Shared: model + providers (rho + eyrie) ─────────────────────────────── +# ─── Shared: model + providers (rho + flux) ─────────────────────────────── model: default: anthropic/claude-sonnet-4-5 # provider/model the agent uses small_fast: anthropic/claude-haiku # cheap model for trivial steps @@ -70,16 +70,16 @@ providers: api_key_env: GEMINI_API_KEY model: gemini-2.0-flash -# ─── eyrie: gateway / runtime ─────────────────────────────────────────────── +# ─── flux: gateway / runtime ─────────────────────────────────────────────── gateway: - base_url: http://localhost:8080 # eyrie endpoint rho talks to - api_key_env: EYRIE_API_KEY + base_url: http://localhost:8080 # flux endpoint rho talks to + api_key_env: FLUX_API_KEY allow_insecure_public_api: false - deployment_routing: "" # EYRIE_DEPLOYMENT_ROUTING + deployment_routing: "" # FLUX_DEPLOYMENT_ROUTING model_catalog: - path_env: EYRIE_MODEL_CATALOG_PATH - url_env: EYRIE_MODEL_CATALOG_URL - refresh: EYRIE_MODEL_CATALOG_REFRESH + path_env: FLUX_MODEL_CATALOG_PATH + url_env: FLUX_MODEL_CATALOG_URL + refresh: FLUX_MODEL_CATALOG_REFRESH # ─── harrier: memory ─────────────────────────────────────────────────────────── memory: @@ -133,30 +133,30 @@ telemetry: The authoritative mapping. "Mechanism today" is what already implements the setting; the unified key is rendered down to it. -### Shared: model / providers (rho + eyrie) +### Shared: model / providers (rho + flux) | Unified key | Repo | Mechanism today | |---------------------------------|-------------|--------------------------------------------------| | `model.default` | rho | `RHO_MODEL` env / `config.json` | | `model.small_fast` | rho | `RHO_SMALL_FAST_MODEL` env | -| `providers[].api_key_env` | eyrie/rho | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`, `ZAI_API_KEY`, `CANOPYWAVE_API_KEY`, `FIREWORKS_API_KEY` | -| `providers[].base_url_env` | eyrie | `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` / `OPENAI_API_BASE`, `OLLAMA_BASE_URL`, `FIREWORKS_BASE_URL` | -| `providers[].model` (openai) | eyrie | `OPENAI_MODEL` env | -| `providers[].model` (gemini) | eyrie | `GEMINI_MODEL` env | -| `providers[].model` (anthropic) | eyrie | `ANTHROPIC_MODEL` env | +| `providers[].api_key_env` | flux/rho | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`, `ZAI_API_KEY`, `CANOPYWAVE_API_KEY`, `FIREWORKS_API_KEY` | +| `providers[].base_url_env` | flux | `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` / `OPENAI_API_BASE`, `OLLAMA_BASE_URL`, `FIREWORKS_BASE_URL` | +| `providers[].model` (openai) | flux | `OPENAI_MODEL` env | +| `providers[].model` (gemini) | flux | `GEMINI_MODEL` env | +| `providers[].model` (anthropic) | flux | `ANTHROPIC_MODEL` env | -### eyrie: gateway / runtime +### flux: gateway / runtime -| Unified key | Mechanism today (eyrie) | +| Unified key | Mechanism today (flux) | |--------------------------------------|--------------------------------------| -| `gateway.base_url` | `EYRIE_BASE_URL` (rho→eyrie link) | -| `gateway.api_key_env` | `EYRIE_API_KEY` | -| `gateway.allow_insecure_public_api` | `EYRIE_ALLOW_INSECURE_PUBLIC_API` | -| `gateway.deployment_routing` | `EYRIE_DEPLOYMENT_ROUTING` (also `RHO_DEPLOYMENT_ROUTING`) | -| `gateway.model_catalog.path_env` | `EYRIE_MODEL_CATALOG_PATH` | -| `gateway.model_catalog.url_env` | `EYRIE_MODEL_CATALOG_URL` | -| `gateway.model_catalog.refresh` | `EYRIE_MODEL_CATALOG_REFRESH` / `RHO_AUTO_REFRESH_CATALOG` / `RHO_CATALOG_REFRESH_ALWAYS` | -| `gateway` config dir | `EYRIE_CONFIG_DIR` (default `/eyrie`) | +| `gateway.base_url` | `FLUX_BASE_URL` (rho→flux link) | +| `gateway.api_key_env` | `FLUX_API_KEY` | +| `gateway.allow_insecure_public_api` | `FLUX_ALLOW_INSECURE_PUBLIC_API` | +| `gateway.deployment_routing` | `FLUX_DEPLOYMENT_ROUTING` (also `RHO_DEPLOYMENT_ROUTING`) | +| `gateway.model_catalog.path_env` | `FLUX_MODEL_CATALOG_PATH` | +| `gateway.model_catalog.url_env` | `FLUX_MODEL_CATALOG_URL` | +| `gateway.model_catalog.refresh` | `FLUX_MODEL_CATALOG_REFRESH` / `RHO_AUTO_REFRESH_CATALOG` / `RHO_CATALOG_REFRESH_ALWAYS` | +| `gateway` config dir | `FLUX_CONFIG_DIR` (default `/flux`) | ### harrier: memory @@ -212,7 +212,7 @@ few env vars. The unified file is designed to be **resolved** into the existing mechanisms: -- **env-based repos** (rho, eyrie, shrike, swift): export the mapped env var for +- **env-based repos** (rho, flux, shrike, swift): export the mapped env var for any key set in `graycode-eco.yaml` that is not already present in the process environment (preserving "env wins" precedence). - **file-based repos** (harrier): write/merge the `memory.*` section into @@ -234,7 +234,7 @@ The schema is encoding-agnostic. The YAML above is identical in structure to: "providers": [ { "name": "anthropic", "api_key_env": "ANTHROPIC_API_KEY" } ], - "gateway": { "base_url": "http://localhost:8080", "api_key_env": "EYRIE_API_KEY" }, + "gateway": { "base_url": "http://localhost:8080", "api_key_env": "FLUX_API_KEY" }, "memory": { "addr": "127.0.0.1:3456", "data_dir": "~/.harrier" }, "compression": { "enabled": true, "db_path": "~/.shrike/usage.db" }, "swift": { "telemetry_optout": false }, @@ -246,5 +246,5 @@ The schema is encoding-agnostic. The YAML above is identical in structure to: The `telemetry` and `swift` sections only configure *transport/opt-out*. The *attribute vocabulary* emitted on spans is defined separately in -[`OTEL-CONVENTIONS.md`](./OTEL-CONVENTIONS.md), with eyrie's +[`OTEL-CONVENTIONS.md`](./OTEL-CONVENTIONS.md), with flux's `internal/observability` package as the reference implementation. diff --git a/docs/ECOSYSTEM-ROADMAP.md b/docs/ECOSYSTEM-ROADMAP.md index 61831d4b..81963649 100644 --- a/docs/ECOSYSTEM-ROADMAP.md +++ b/docs/ECOSYSTEM-ROADMAP.md @@ -1,7 +1,7 @@ # GrayCode Ecosystem Roadmap (2026) **Status:** Active · **Last updated:** 2026-09-09 -**Scope:** the 4 in-workspace repos — `rho`, `eyrie`, +**Scope:** the 4 in-workspace repos — `rho`, `flux`, `graycode-skills`, `graycode-platform` — plus the 5 external engine repos they depend on. **Evidence base:** `docs/RESEARCH.md`, `docs/COMPETITIVE.md`, `docs/plans/competitive-gap-*.md` @@ -29,7 +29,7 @@ load-bearing bets: 3. **Portable execution graph** — every agent run is exported as a verifiable, hash-addressed graph (provenance for replay/audit). 4. **Router-facade-only provider access** — the CLI never talks to an LLM API - directly; `eyrie/engine` is the sole boundary. + directly; `flux/engine` is the sole boundary. The roadmap is organized around making those bets *true and honest* end-to-end, then extending the surface where the field (Codex, OpenCode, Gemini CLI, Goose, @@ -56,7 +56,7 @@ dropped. | Repo | Role | Build | Tests | Health | |---|---|---|---|---| | `rho` | Product face (Go, Bubble Tea v2) | ✅ `go build ./...` | ✅ 179 pkgs green | Engines restored; memory/token/review/audit/correlation live | -| `eyrie` | Provider runtime (Go) | ✅ | ✅ 37 pkgs green | Healthy; 28 providers; gRPC ChatService wired | +| `flux` | Provider runtime (Go) | ✅ | ✅ 37 pkgs green | Healthy; 28 providers; gRPC ChatService wired | | `graycode-skills` | Skill marketplace (Python) | ✅ | ✅ 382/382 | Healthy; single parsed schema | | `graycode-platform` | Web + Cloud control plane (TS) | ✅ | ✅ 320 worker / 33 web | Healthy; worker+bff routes added; migration deduped | @@ -83,7 +83,7 @@ dropped. `shrike: embedded · token/compress pipeline OK (sample=0 tokens)` — sample 0 should be a red flag, not OK. -### 2.2 eyrie +### 2.2 flux - Healthy and self-contained. 28 `ProviderSpec`s, ~25 adapters, weighted/strategy LB, retries, semantic cache, circuit breakers, deployment router, OpenAI-compat @@ -241,7 +241,7 @@ green with real engines. **Effort:** 4-8 weeks total across the 5 repos. - IDE integration (VS Code extension) — only after engines restored. - Hosted share links / multi-session grid (herdr-style) — Gap-02 follow-through. -- Provider-count messaging: expose `eyrie` catalog count dynamically. +- Provider-count messaging: expose `flux` catalog count dynamically. - OpenTelemetry/metrics consolidation across engines (per `OTEL-CONVENTIONS.md`). --- diff --git a/docs/ECOSYSTEM-WIRING.md b/docs/ECOSYSTEM-WIRING.md index 6e498d01..c5f796a6 100644 --- a/docs/ECOSYSTEM-WIRING.md +++ b/docs/ECOSYSTEM-WIRING.md @@ -48,14 +48,14 @@ flowchart TB subgraph Local[Local-first runtime] User[CLI / daemon user] --> Rho[Rho composition root] - Rho --> Eyrie[Eyrie facade] + Rho --> Flux[Flux facade] Rho --> Harrier[Harrier / Harrier facade] Rho --> Shrike[Shrike / Shrike facade] Rho --> Swift[Swift / Swift facade] Rho --> Kestrel[Kestrel / Kestrel facade] Rho --> Merlin[Merlin / Merlin facade] Rho --> Eagle[Eagle neutral contracts] - Eyrie --> Eagle + Flux --> Eagle Harrier --> Eagle Shrike --> Eagle Swift --> Eagle @@ -88,7 +88,7 @@ flowchart TB | Boundary | Owner | Rule | |---|---|---| | Product orchestration | Rho | Engines never orchestrate Rho or one another. | -| Provider runtime | Eyrie | Rho imports its supported `engine` facade. | +| Provider runtime | Flux | Rho imports its supported `engine` facade. | | Shared data contracts | Eagle | Neutral types only; no product behavior. | | Portable local graph | Source engine/Rho | Emit bounded `rho.graph/v1` facts without raw secrets or prompts. | | Daemon HTTP API | Rho | `rho/api/openapi.yaml` is authoritative. | @@ -101,7 +101,7 @@ flowchart TB ## Repository identity Repository directories are repository and Go module identities. Product names -remain the user-facing labels. In particular, `eyrie` is Eyrie. Scripts and CI +remain the user-facing labels. In particular, `flux` is Flux. Scripts and CI must use `directory`/`github_repo`; UI copy may use `product_name`. ## Contract and event flow @@ -136,15 +136,15 @@ must use `directory`/`github_repo`; UI copy may use `product_name`. ### Coordinated Go publication gate -The compatible Eagle-migrated Eyrie source is currently ahead of Eyrie's -published `origin/main`. Do not merge or release Rho against the older Eyrie +The compatible Eagle-migrated Flux source is currently ahead of Flux's +published `origin/main`. Do not merge or release Rho against the older Flux pseudo-version: it still exposes the retired `rho-core-contracts` types and is not type-compatible with Rho's Eagle boundary. -1. Merge and publish the Eyrie ecosystem-wiring branch first. +1. Merge and publish the Flux ecosystem-wiring branch first. 2. Resolve that final remote commit to its canonical Go pseudo-version with - `go list -m github.com/GrayCodeAI/eyrie@`. -3. Update Rho's Eyrie requirement, run `GOWORK=off go mod tidy`, and remove any + `go list -m github.com/GrayCodeAI/flux@`. +3. Update Rho's Flux requirement, run `GOWORK=off go mod tidy`, and remove any transition excludes no longer required by the published engine graphs. 4. Require Rho's `public-modules` and `release-parity` CI jobs to pass before merging or tagging Rho. diff --git a/docs/IMPLEMENTATION-ROADMAP.md b/docs/IMPLEMENTATION-ROADMAP.md index 74cbe8c7..4ff1688e 100644 --- a/docs/IMPLEMENTATION-ROADMAP.md +++ b/docs/IMPLEMENTATION-ROADMAP.md @@ -499,7 +499,7 @@ func main() { ## Phase 3: Low Priority (Backlog - Score Impact: +0.2) -### 6. Add AI Code Completion (eyrie) +### 6. Add AI Code Completion (flux) **Effort:** Medium (1-2 weeks) **Priority:** LOW @@ -562,7 +562,7 @@ func main() { --- -### 10. Add Completion Endpoint to eyrie +### 10. Add Completion Endpoint to flux **Effort:** Medium (1 week) **Priority:** LOW @@ -628,7 +628,7 @@ Current architecture status: see `docs/architecture/rho-architecture-baseline.md --- -### **eyrie** (LLM Runtime) +### **flux** (LLM Runtime) | Phase | Improvement | Effort | Impact | Status | |-------|--------------|--------|--------|--------| diff --git a/docs/OTEL-CONVENTIONS.md b/docs/OTEL-CONVENTIONS.md index d8187354..196b37d1 100644 --- a/docs/OTEL-CONVENTIONS.md +++ b/docs/OTEL-CONVENTIONS.md @@ -1,10 +1,10 @@ # graycode-eco OpenTelemetry Semantic Conventions for AI Agent Spans Status: Draft / shared spec -Applies to: rho, eyrie +Applies to: rho, flux This document defines the OpenTelemetry (OTel) semantic conventions Rho and -Eyrie should follow when emitting spans for AI agent and LLM operations. The +Flux should follow when emitting spans for AI agent and LLM operations. The goal is that a single tracing backend (Jaeger, Tempo, Honeycomb, an OTLP collector, etc.) can correlate model calls, tool invocations, token usage, and cost across both repos using one common attribute vocabulary. @@ -18,23 +18,23 @@ concerns that the GenAI spec does not yet standardize. ## Reference ownership -Eyrie owns provider-call instrumentation behind its `eyrie/engine` facade. +Flux owns provider-call instrumentation behind its `flux/engine` facade. Its lower provider layer contains the reference OTel decorator for chat and stream calls: it starts a client span, records provider/model/usage attributes, sets status from the result, and ends a streamed span on completion. That -decorator is an Eyrie implementation detail; Rho must not import or compose it +decorator is an Flux implementation detail; Rho must not import or compose it directly. -- `eyrie/internal/observability/observability.go` provides a stdlib-only, +- `flux/internal/observability/observability.go` provides a stdlib-only, zero-dependency telemetry/metrics layer (spans, latency histograms, Prometheus + JSON export) for environments that cannot pull in the OTel SDK. -- `eyrie/internal/observability/genai_semconv.go` exports the canonical +- `flux/internal/observability/genai_semconv.go` exports the canonical attribute-key constants defined below, so Go code can reference them instead of hard-coding strings. A pinning test (`genai_semconv_test.go`) guards the exact key values. When adding tracing to Rho, propagate trace context through the Engine call -and use the attribute keys in this document. Eyrie wraps provider operations; +and use the attribute keys in this document. Flux wraps provider operations; Rho wraps product turns, tools, and the embedded token engine. ## Span kinds and names @@ -50,7 +50,7 @@ Rho wraps product turns, tools, and the embedded token engine. LLM/provider request spans SHOULD use span kind `CLIENT`. Tool invocations and agent steps that represent internal work SHOULD use span kind `INTERNAL`. -eyrie's existing stdlib layer also defines short span names +flux's existing stdlib layer also defines short span names (`llm.chat`, `llm.stream`, `llm.retry`, `llm.cache_hit`) — these remain valid for the internal metrics collector; the names above are the cross-repo convention for OTLP-exported spans. @@ -58,10 +58,10 @@ convention for OTLP-exported spans. ## Required / recommended attributes The required attribute set for any AI agent span. Keys are exported as Go -constants in `eyrie/internal/observability/genai_semconv.go` (constant name in +constants in `flux/internal/observability/genai_semconv.go` (constant name in parentheses). -| Attribute key | Go const (`eyrie` observability) | Type | Required | Meaning | +| Attribute key | Go const (`flux` observability) | Type | Required | Meaning | |---------------------------------|------------------------------------|--------|----------|----------------------------------------------------------------| | `gen_ai.system` | `AttrGenAISystem` | string | yes | Provider/system: `openai`, `anthropic`, `gemini`, etc. | | `gen_ai.request.model` | `AttrGenAIRequestModel` | string | yes | Model requested by the caller, e.g. `gpt-4o`. | @@ -77,13 +77,13 @@ parentheses). ### Notes - **Tokens as integers.** Emit `gen_ai.usage.input_tokens` / - `gen_ai.usage.output_tokens` as OTel integer attributes. eyrie's stdlib + `gen_ai.usage.output_tokens` as OTel integer attributes. flux's stdlib `Span.Attributes` is `map[string]string`; when bridging that layer to OTLP, convert to integer attributes. - **Cost is a double in USD.** `cost.usd` is the ecosystem standard. - eyrie's metrics collector stores cost internally in micro-USD for precision + flux's metrics collector stores cost internally in micro-USD for precision (`costMicroUSD`) but exports USD; emit the USD value on spans. -- **No prompt/response bodies by default.** Following eyrie's audit design +- **No prompt/response bodies by default.** Following flux's audit design (`observability/audit.go`), spans MUST NOT carry raw prompt/response text by default. If content capture is enabled, use the OTel GenAI event/log channel, not span attributes, and gate it behind explicit opt-in. @@ -92,7 +92,7 @@ parentheses). ## Legacy attribute mapping -eyrie's older stdlib constants (`AttrLLMProvider`, `AttrLLMModel`, +flux's older stdlib constants (`AttrLLMProvider`, `AttrLLMModel`, `AttrLLMInputTokens`, …, keyed `llm.*`) predate this spec and remain for backwards compatibility. New instrumentation should use the `gen_ai.*` keys. Mapping: @@ -109,12 +109,12 @@ Mapping: ## Per-repo guidance -- **eyrie** — owns provider/model/usage spans behind `eyrie/engine`; align +- **flux** — owns provider/model/usage spans behind `flux/engine`; align attribute keys to `gen_ai.*` over time. - **rho** — daemon/orchestrator. Already has OTel hooks (`RHO_ENABLE_TELEMETRY`, `RHO_OTEL_SHUTDOWN_TIMEOUT_MS`). Emit `agent.id` and `session.id` on agent-turn spans; propagate them downstream to - eyrie via context so provider spans inherit the same IDs. + flux via context so provider spans inherit the same IDs. - **internal/token** — embedded token engine. Token accounting is its domain; when it emits spans, populate `gen_ai.usage.input_tokens` / `output_tokens` and `cost.usd` so compression savings are visible in the same trace. @@ -122,5 +122,5 @@ Mapping: ## Versioning This spec tracks OTel GenAI conventions, which are still evolving. Pin changes -to the constants via `genai_semconv_test.go` in eyrie and update this table in +to the constants via `genai_semconv_test.go` in flux and update this table in the same change. diff --git a/docs/RESEARCH.md b/docs/RESEARCH.md index 0c1e895b..ac880aff 100644 --- a/docs/RESEARCH.md +++ b/docs/RESEARCH.md @@ -1,7 +1,7 @@ # Research & Competitive Comparison — graycode-eco Status: Implemented (branch `feat/competitive-analysis-top20`, 2026-09-09) -Scope: 4 repos (`rho`, `eyrie`, `graycode-platform`, `graycode-skills`) vs +Scope: 4 repos (`rho`, `flux`, `graycode-platform`, `graycode-skills`) vs top-20 OSS competitors and top-20 AI-coding-agent research papers. This document maps the field (competitors + research) to our repos, records what @@ -12,7 +12,7 @@ plans in `docs/plans/competitive-gap-*.md` and the extra fixes they surfaced. - Competitors: `docs/COMPETITIVE.md` (20 OSS tools, source-cited). - Research: 20 papers read via arXiv (abstracts) 2026-09-09 — listed below. -- Repo capability inventory: `rho/README.md`, `eyrie/README.md`, +- Repo capability inventory: `rho/README.md`, `flux/README.md`, `graycode-platform/README.md`, `graycode-skills/README.md`, plus source audits. ## Our repos at a glance @@ -20,7 +20,7 @@ plans in `docs/plans/competitive-gap-*.md` and the extra fixes they surfaced. | Repo | Role | Key strengths | |---|---|---| | rho | Product face (Go, Bubble Tea v2) | 120+ tools, Docker fail-closed sandbox, `/autonomy`+`/spec` gates, `mission` multi-agent, execution-graph export, AST repomap + Harrier memory, MCP/LSP | -| eyrie | Provider engine facade | 22 gateways, routing/retry/caching/compaction, OpenAI-compat proxy, model catalog | +| flux | Provider engine facade | 22 gateways, routing/retry/caching/compaction, OpenAI-compat proxy, model catalog | | graycode-platform | Optional cloud/BFF plane | web + identity BFF + control-plane worker (not a runtime dep) | | graycode-skills | Skill marketplace | 14,015 skills, 27 categories, SKILL.md frontmatter + validation | diff --git a/docs/SECURITY-DEVELOPER.md b/docs/SECURITY-DEVELOPER.md index 953ded11..59a4445a 100644 --- a/docs/SECURITY-DEVELOPER.md +++ b/docs/SECURITY-DEVELOPER.md @@ -1,13 +1,13 @@ # Rho developer security model -This document describes how rho and eyrie handle API keys and agent isolation for an individual developer on macOS or Linux (no Vault, no proxy). Teams and enterprise deployment models come later. +This document describes how rho and flux handle API keys and agent isolation for an individual developer on macOS or Linux (no Vault, no proxy). Teams and enterprise deployment models come later. ## Goals - API keys live only in the OS secret store (macOS Keychain / Linux GNOME Keyring or KWallet). - Rho does not read API keys from `.env`, shell env, or plaintext files. -- Eyrie's `provider.json` holds routing and deployment metadata only — never secrets on disk. -- Rho talks to eyrie without putting keys in JSON or chat messages. +- Flux's `provider.json` holds routing and deployment metadata only — never secrets on disk. +- Rho talks to flux without putting keys in JSON or chat messages. - Agent commands run on the host under the permission engine; file tools cannot read credential paths. @@ -15,7 +15,7 @@ This document describes how rho and eyrie handle API keys and agent isolation fo | Write | Read | Remove | |-------|------|--------| -| `/config` paste flow → `eyrie/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `rho credentials remove` | +| `/config` paste flow → `flux/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `rho credentials remove` | Rho does not import keys from legacy `~/.rho/env` / `~/.rho/.env` files or from secret fields in `provider.json`. Save every key through `/config`. @@ -28,27 +28,27 @@ Check status: `rho credentials status`, `rho path`, or `rho preflight`. User pastes API key in /config | v -Rho /config -> Eyrie engine credential service (OS secret store) +Rho /config -> Flux engine credential service (OS secret store) | v -Eyrie engine discover/apply (credentials from store, not JSON body) +Flux engine discover/apply (credentials from store, not JSON body) | v SetupUI JSON (display_name + canonical_id per model) | v -User picks model -> Eyrie provider.json (canonical id only) +User picks model -> Flux provider.json (canonical id only) ``` Remove a stored key: `/config key remove` (interactive picker). -## Rho to Eyrie +## Rho to Flux -- **Control plane**: Rho calls only `eyrie/engine`; no lower Eyrie package is a +- **Control plane**: Rho calls only `flux/engine`; no lower Flux package is a production import. - **Discovery/apply**: credentials are resolved from the Engine's injected secret store; provider state and request bodies remain sanitized. -- **Chat**: Rho sends model intent, messages, and tool definitions; Eyrie +- **Chat**: Rho sends model intent, messages, and tool definitions; Flux resolves the gateway and reads secrets internally. ## Agent execution @@ -72,7 +72,7 @@ is required. ### Blocked for agents -- **Read** tool: legacy Rho env files, Eyrie's configured `provider.json`, +- **Read** tool: legacy Rho env files, Flux's configured `provider.json`, `~/.ssh/*`, etc. - **Bash**: `printenv`, `env`, reading rho env paths, echoing `*_API_KEY` variables. @@ -82,18 +82,18 @@ is required. check when the file still holds secret fields. Rho does not remove them automatically: back up the file, delete the secret fields, and save the keys again through `/config`. -- **Provider state writes**: the Eyrie engine sanitizes provider state and +- **Provider state writes**: the Flux engine sanitizes provider state and writes it atomically, so secret fields are never written back to disk. ## Provider state path -Eyrie owns the provider-state path. Resolution order is: +Flux owns the provider-state path. Resolution order is: -1. `EYRIE_CONFIG_DIR/provider.json` -2. the platform user-config directory under `eyrie/provider.json` +1. `FLUX_CONFIG_DIR/provider.json` +2. the platform user-config directory under `flux/provider.json` Rho's Read/Edit/Write and Bash safety checks protect the resolved path, -including a custom or symlinked `EYRIE_CONFIG_DIR`; protection is not limited +including a custom or symlinked `FLUX_CONFIG_DIR`; protection is not limited to the historical default provider-state location. ## Environment variables @@ -103,13 +103,13 @@ Non-secret overrides only (rho does not load provider API keys from env): | Variable | Meaning | |----------|---------| | `RHO_CONFIG_DIR` | Override rho config directory | -| `EYRIE_CONFIG_DIR` | Override Eyrie provider-state directory; takes precedence for `provider.json` | +| `FLUX_CONFIG_DIR` | Override Flux provider-state directory; takes precedence for `provider.json` | | `OPENAI_MODEL` | Override default OpenAI model | | `OLLAMA_BASE_URL` | Ollama server URL (also saved via `/config` for Ollama) | ## Related code -- Rho: `internal/config/eyrie_engine.go`, `internal/tool/safety.go`, +- Rho: `internal/config/engine.go`, `internal/tool/safety.go`, `internal/storage/paths.go`, `cmd/credentials.go` -- Eyrie public host boundary: `engine/` +- Flux public host boundary: `engine/` - Daemon HTTP surface: [`docs/DAEMON-PORT-THREAT-MODEL.md`](DAEMON-PORT-THREAT-MODEL.md) diff --git a/docs/architecture.md b/docs/architecture.md index 588d4d9d..50ce4ea2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -87,7 +87,7 @@ rho/ | Service | Role | Connection | |---------|------|------------| -| bird **eyrie** | LLM provider runtime | Library — all LLM calls routed through its engine facade | +| bird **flux** | LLM provider runtime | Library — all LLM calls routed through its engine facade | | scissors **token** | Token optimization | Embedded — counting, compression, secrets, usage tracking | ### Client SDKs @@ -100,7 +100,7 @@ rho/ All three SDKs share types from **eagle** and consume the daemon REST API (:4590). -> lightbulb **rho never talks to LLM APIs directly** — all calls go through eyrie. +> lightbulb **rho never talks to LLM APIs directly** — all calls go through flux. --- diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 80e61b95..05baaa1b 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -32,7 +32,7 @@ to it through the boundaries documented here. Final target shape: - `rho` is the orchestrator and only primary product surface -- `eyrie` sits below Rho as the provider engine, consumed through its stable +- `flux` sits below Rho as the provider engine, consumed through its stable engine facade - Rho's token/context engine is embedded (`internal/token`), not a peer repo - shared vocabulary lives in Rho's vendored `internal/contracts` diff --git a/docs/architecture/adr/ADR-0003-grok-behavioral-port-go-multirepo.md b/docs/architecture/adr/ADR-0003-grok-behavioral-port-go-multirepo.md index 41e99223..96f35840 100644 --- a/docs/architecture/adr/ADR-0003-grok-behavioral-port-go-multirepo.md +++ b/docs/architecture/adr/ADR-0003-grok-behavioral-port-go-multirepo.md @@ -11,7 +11,7 @@ Grok Build (`grok-eco/grok-build`) is a large Rust monorepo for a terminal AI coding agent. Rho is a multi-repo Go platform: product (`rho`) plus peer -engines (`eyrie`, `harrier`, `shrike`, `swift`, `kestrel`, `merlin`), foundation +engines (`flux`, `harrier`, `shrike`, `swift`, `kestrel`, `merlin`), foundation contracts (`eagle`, `falcon`), SDKs, cloud, and skills. The product goal is Grok-class **agent control-plane quality** (typed @@ -26,7 +26,7 @@ peer-engine advantages. 2. **Rho remains multi-repo.** Map Grok capabilities onto existing owners: - product surface → `rho` - shared DTOs → `eagle` - - LLM routing/stream → `eyrie` + - LLM routing/stream → `flux` - memory → `harrier` - tokens/secrets/compress → `shrike` - session capture/import → `swift` diff --git a/docs/architecture/control-plane.md b/docs/architecture/control-plane.md index 63ae466c..e9490f87 100644 --- a/docs/architecture/control-plane.md +++ b/docs/architecture/control-plane.md @@ -19,7 +19,7 @@ Faces (TUI / headless / ACP / daemon) Session kernel (agentLoop · tools) │ - eyrie · embedded token engine + flux · embedded token engine ``` ## Pieces @@ -36,7 +36,7 @@ Single entry for subagents + background tasks: ### 2. Lazy model surface (`tool.Registry`) - Essential tools registered and **model-visible** -- Optional tools registered for **execution + ToolSearch**, hidden from `EyrieTools` +- Optional tools registered for **execution + ToolSearch**, hidden from `FluxTools` - `ToolSearch` `select:Name` **promotes** tool onto the model surface - APIs: `EnableLazyModelSurface`, `SetModelVisibility`, `PromoteModelTool` diff --git a/docs/architecture/ecosystem-architecture.md b/docs/architecture/ecosystem-architecture.md index f3cfef23..437e8b20 100644 --- a/docs/architecture/ecosystem-architecture.md +++ b/docs/architecture/ecosystem-architecture.md @@ -1,7 +1,7 @@ # GrayCode Ecosystem Architecture > **Historical.** This document describes the pre-2026-09 multi-engine -> ecosystem. Rho now depends only on `eyrie` and embeds its own token engine; +> ecosystem. Rho now depends only on `flux` and embeds its own token engine; > the harrier, kestrel, merlin, shrike, and swift integrations have been > removed. Kept for record. @@ -16,7 +16,7 @@ repository, product, or runtime module. | `rho` | Main CLI, daemon, orchestration, policy, and public API | Main product and integration root | | `eagle` | Neutral cross-repository contracts | Go dependency of Rho and selected engines | | `falcon` | Shared MCP transport and handler scaffolding | Go dependency of MCP-serving engines | -| `eyrie` | Provider credentials, catalog, routing, transport, streaming | Rho consumes `eyrie/engine` | +| `flux` | Provider credentials, catalog, routing, transport, streaming | Rho consumes `flux/engine` | | `harrier` | Harrier memory and retrieval engine | Rho integration | | `shrike` | Shrike token budgeting and context compression | Rho integration | | `swift` | Swift provenance and replay engine | Rho consumes `swift/cli` | @@ -37,7 +37,7 @@ Shrike, `swift` is Swift, `kestrel` is Kestrel, and `merlin` is Merlin. ```text sparrow / robin / wren ── HTTP/OpenAPI ──> rho <── skill surface ── starling │ - ├── eyrie/engine + ├── flux/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -83,7 +83,7 @@ use published module versions with `GOWORK=off`. ## Current versus proposed The proposed repository shape is already present locally. Remaining work is -release and boundary refinement: publish the Eagle-compatible Eyrie revision, +release and boundary refinement: publish the Eagle-compatible Flux revision, remove its transitional legacy contract dependency from the standalone graph, and decide whether Rho's graph/projection integrations should be hidden behind engine-owned facades. diff --git a/docs/architecture/ecosystem-design.md b/docs/architecture/ecosystem-design.md index f98767ee..3f8f745c 100644 --- a/docs/architecture/ecosystem-design.md +++ b/docs/architecture/ecosystem-design.md @@ -17,7 +17,7 @@ graycode-eco/ ├── rho # product, CLI, daemon, orchestration, policy ├── eagle # shared neutral contracts ├── falcon # shared MCP transport/handler kit -├── eyrie # provider runtime (product label: Eyrie) +├── flux # provider runtime (product label: Flux) ├── harrier # memory engine (product label: Harrier) ├── shrike # token/context engine (product label: Shrike) ├── swift # provenance engine (product label: Swift) @@ -36,7 +36,7 @@ graycode-eco/ ```text sparrow / robin / wren ── Rho daemon API ──> rho <── skill API ── starling │ - ├── eyrie/engine + ├── flux/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -44,7 +44,7 @@ sparrow / robin / wren ── Rho daemon API ──> rho <── skill API ─ ├── merlin (Merlin) └── eagle -eyrie / harrier / shrike / swift / kestrel / merlin ──> eagle (as needed) +flux / harrier / shrike / swift / kestrel / merlin ──> eagle (as needed) harrier / kestrel / merlin ──> falcon ──> mark3labs/mcp-go ``` @@ -67,7 +67,7 @@ CI must test both workspace mode and `GOWORK=off` module mode. ```text Rho local runtime - ├── Eyrie provider generation + ├── Flux provider generation ├── Harrier memory and retrieval ├── Shrike token budgeting/compression ├── Swift swift/provenance @@ -127,7 +127,7 @@ corresponding engine facade rather than exporting implementation types further. 5. Deploy GrayCode Platform independently after its Worker/BFF contract tests pass. -The current local Eyrie checkout is Eagle-compatible, but Rho's published -Eyrie pin still has a transitive `rho-core-contracts` dependency. That is a +The current local Flux checkout is Eagle-compatible, but Rho's published +Flux pin still has a transitive `rho-core-contracts` dependency. That is a release-order issue, not a reason to add a local `replace` directive or a platform dependency. diff --git a/docs/architecture/ecosystem-implementation-plan.md b/docs/architecture/ecosystem-implementation-plan.md index 650cc39a..03e062ea 100644 --- a/docs/architecture/ecosystem-implementation-plan.md +++ b/docs/architecture/ecosystem-implementation-plan.md @@ -15,7 +15,7 @@ SDKs (Sparrow / Robin / Wren) +----------+----------+ | | | v v v -Eyrie Harrier Shrike +Flux Harrier Shrike models memory tokens | | | +----------+----------+ @@ -65,7 +65,7 @@ bash owl/scripts/sync-ecosystem.sh - Keep Eagle imports in Rho and engines limited to shared contracts. - Keep Falcon imports limited to MCP-serving components. -- Keep Rho's Eyrie integration behind `eyrie/engine`. +- Keep Rho's Flux integration behind `flux/engine`. - Keep Rho's SDKs outside the Go workspace and outside engine dependencies. - Keep platform integration at the HTTP/Service Binding boundary. - Keep graph and quality projections as explicit, reviewed Rho integration @@ -76,20 +76,20 @@ Acceptance checks: ```text bash rho/scripts/check-ecosystem-boundaries.sh bash rho/scripts/check-support-repo-coupling.sh -bash rho/scripts/check-eyrie-engine-boundary.sh -bash rho/scripts/check-eyrie-client-imports.sh +bash rho/scripts/check-flux-engine-boundary.sh +bash rho/scripts/check-flux-client-imports.sh bash rho/scripts/check-no-replace-directives.sh ``` ## Phase 3: Published-module cutover — pending external release -The local Eyrie source already uses Eagle contracts. Rho's current published -Eyrie pseudo-version still declares the retired compatibility contract -transitively, so this phase requires publishing the compatible Eyrie revision. +The local Flux source already uses Eagle contracts. Rho's current published +Flux pseudo-version still declares the retired compatibility contract +transitively, so this phase requires publishing the compatible Flux revision. -1. Publish the current Eagle-compatible Eyrie revision. +1. Publish the current Eagle-compatible Flux revision. 2. Resolve its canonical Go pseudo-version from the published commit. -3. Update Rho's Eyrie requirement to that version. +3. Update Rho's Flux requirement to that version. 4. Run `GOWORK=off go mod tidy` in Rho. 5. Confirm the retired compatibility module is absent from Rho's module graph. 6. Remove any obsolete transition excludes. @@ -148,7 +148,7 @@ The architecture is complete when: - all repositories match the canonical manifest; - Rho builds and tests with `GOWORK=off`; - SDK and platform contract checks pass; and -- the published Eyrie revision no longer brings the retired compatibility +- the published Flux revision no longer brings the retired compatibility contract into Rho's standalone module graph. Current state: Phases 1, 2, and the local portion of Phase 4 are complete. diff --git a/docs/architecture/execution-graph.md b/docs/architecture/execution-graph.md index 6ff46e0f..276fae53 100644 --- a/docs/architecture/execution-graph.md +++ b/docs/architecture/execution-graph.md @@ -14,7 +14,7 @@ runtime owners verification bridges graph observation journal Embedded token-pipeline runtime projection - Eyrie operations projection + Flux operations projection | v internal/executiongraph @@ -41,7 +41,7 @@ eagle/graph | Context compression operations | `internal/token` | Compression statistics are automatically captured during persisted-session context compaction | | Response redaction quality | `internal/token` | Aggregate secret matches are automatically captured after persisted-session response redaction | | Token usage and budget decisions | `internal/token` | Deduplicated provider usage updates the tracker; usage and next-turn budget decisions are automatically captured | -| Model routing and generation usage | Eyrie | Route and normalized usage operations are automatically captured after each persisted-session model turn | +| Model routing and generation usage | Flux | Route and normalized usage operations are automatically captured after each persisted-session model turn | | Observation history | `internal/graphjournal` | Append-only, privacy-safe runtime evidence | The graph package never mutates these owners and never schedules work. @@ -92,7 +92,7 @@ Nodes: - `rho/tool-call//` — tool invocation metadata; - `rho/policy/` — permission verdict; - `rho/verification/` — neutral verification result; -- `eyrie/route/` and `eyrie/generation/` — model route and +- `flux/route/` and `flux/generation/` — model route and normalized generation operations. Edges: @@ -202,7 +202,7 @@ The central tool-execution seam automatically records: - secret matches removed from persisted-session responses; - token usage summaries and budget decisions emitted from the central, deduplicated provider-accounting seam; -- Eyrie model route and usage reported for persisted-session turns. +- Flux model route and usage reported for persisted-session turns. Mission execution now also persists a portable graph artifact for local mission runs. Plain `rho mission` runs emit mission and feature execution nodes; the diff --git a/docs/architecture/graycode-ecosystem-summary.md b/docs/architecture/graycode-ecosystem-summary.md index 700361a2..e1725dca 100644 --- a/docs/architecture/graycode-ecosystem-summary.md +++ b/docs/architecture/graycode-ecosystem-summary.md @@ -1,7 +1,7 @@ # GrayCode Ecosystem Summary > **Historical.** This document describes the pre-2026-09 multi-engine -> ecosystem. Rho now depends only on `eyrie` and embeds its own token engine; +> ecosystem. Rho now depends only on `flux` and embeds its own token engine; > the harrier, kestrel, merlin, shrike, and swift integrations have been > removed. Kept for record. @@ -19,7 +19,7 @@ API consumers/extensions (main CLI and daemon) | Support engines - eyrie harrier shrike swift kestrel merlin + flux harrier shrike swift kestrel merlin | Foundations eagle falcon @@ -35,7 +35,7 @@ Product labels map to directories as follows: `harrier`/Harrier, `shrike`/Shrike ## Dependency direction ```text -rho -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle +rho -> flux / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # when shared contracts are needed harrier / kestrel / merlin -> falcon sparrow / robin / wren -> Rho daemon API @@ -49,7 +49,7 @@ skills-to-engine, and any Go-module dependency on GrayCode Platform. ```text Rho main CLI - ├── Eyrie provider execution + ├── Flux provider execution ├── Harrier memory ├── Shrike token/context management ├── Swift swift/provenance @@ -70,7 +70,7 @@ Rho's cloud usage path is fail-open; graph synchronization is explicit. | Repository | Role | Direct dependency rule | |---|---|---| | `rho` | Main CLI, daemon, orchestration, policy | Integrates engines and contracts | -| `eyrie` | Provider runtime | Uses Eagle contracts; exposes `engine` | +| `flux` | Provider runtime | Uses Eagle contracts; exposes `engine` | | `harrier` | Harrier memory | Uses Eagle/Falcon where required | | `shrike` | Shrike context engine | Uses Eagle where required | | `swift` | Swift/provenance | Uses Eagle where required | @@ -90,6 +90,6 @@ Rho's cloud usage path is fail-open; graph synchronization is explicit. The repository-level target is implemented: independent Git repositories, canonical manifest, generated Owl inventory, sibling Go workspace, Eagle parity, and dependency boundary checks are all present. Remaining work is to publish -the Eagle-compatible Eyrie revision, remove the transitional +the Eagle-compatible Flux revision, remove the transitional `rho-core-contracts` dependency from the standalone graph, and decide whether Rho's graph/projection packages need additional engine-owned facades. diff --git a/docs/architecture/plan.md b/docs/architecture/plan.md index 90980e62..ac343cf3 100644 --- a/docs/architecture/plan.md +++ b/docs/architecture/plan.md @@ -151,7 +151,7 @@ services; it is not a claim that those fields have already moved. ## Dependencies -- **eyrie:** LLM provider engine behind `eyrie/engine` (workspace sibling) +- **flux:** LLM provider engine behind `flux/engine` (workspace sibling) - **harrier:** Graph-based persistent memory (workspace sibling) - **shrike:** Tokenizer, compression (workspace sibling) - **eagle:** Shared types (workspace sibling) diff --git a/docs/architecture/rho-architecture-baseline.md b/docs/architecture/rho-architecture-baseline.md index f727ccb5..2790be4d 100644 --- a/docs/architecture/rho-architecture-baseline.md +++ b/docs/architecture/rho-architecture-baseline.md @@ -1,7 +1,7 @@ # Rho Architecture Baseline > **Historical.** This document describes the pre-2026-09 multi-engine -> ecosystem. Rho now depends only on `eyrie` and embeds its own token engine; +> ecosystem. Rho now depends only on `flux` and embeds its own token engine; > the harrier, kestrel, merlin, shrike, and swift integrations have been > removed. Kept for record. @@ -38,7 +38,7 @@ users / SDKs / skills / daemon clients v rho / | \ - eyrie harrier shrike + flux harrier shrike swift kestrel merlin | v @@ -49,8 +49,8 @@ The graph is intentionally directional: - Rho owns user-facing orchestration, sessions, tools, permissions, composition, and public product surfaces. -- Eyrie owns provider protocols, routing, credentials, catalogs, and provider - execution behind `eyrie/engine`. +- Flux owns provider protocols, routing, credentials, catalogs, and provider + execution behind `flux/engine`. - Harrier, Shrike, Swift, Kestrel, and Merlin are support engines and must not import Rho internals or one another. - Core contracts contain stable cross-repository vocabulary and DTOs, not @@ -61,14 +61,14 @@ The graph is intentionally directional: ### Complete or enforced -- Rho production code uses Eyrie through the `eyrie/engine` facade. +- Rho production code uses Flux through the `flux/engine` facade. - Kestrel and Merlin are integrated through Rho bridge packages. - Support-engine sibling imports and imports of Rho internals are guarded. - The AST/package-graph guard reports production boundary violations with file/line diagnostics across Rho and available support repositories. - Persisted tool, review, verification, event, and policy contracts use the implemented portions of `eagle`. -- Native-compaction capability contracts use `eagle/llm`; Eyrie +- Native-compaction capability contracts use `eagle/llm`; Flux request translation remains inside `internal/provider/gateway`, keeping the engine layer independent of the provider adapter package for this path. - Container-required state and its executor are owned by `ToolService` and @@ -95,7 +95,7 @@ The graph is intentionally directional: - Rho's Harrier and Shrike implementation imports are now concentrated in `HarrierBridge` and `internal/token` for the migrated production paths. The graph/projection packages used for capture remain explicit integration - surfaces; replaceability is improved, but still not equivalent to the Eyrie + surfaces; replaceability is improved, but still not equivalent to the Flux boundary. - `PersistenceService` is the in-memory runtime owner for transcript/context state and checkpoint metadata. The active durable session path remains @@ -122,11 +122,11 @@ The `graycode-eco` parent workspace and Rho's published module pins provide local integration without removing independent ownership and release boundaries. -### ADR-B02 — Preserve the Eyrie boundary +### ADR-B02 — Preserve the Flux boundary Provider implementation, catalog metadata, credential mapping, and protocol -adapters remain owned by Eyrie. Rho may own product policy and user-facing -selection, but production provider access remains through `eyrie/engine`. +adapters remain owned by Flux. Rho may own product policy and user-facing +selection, but production provider access remains through `flux/engine`. ### ADR-B03 — Complete internal consolidation before adding new seams diff --git a/docs/architecture/rho-architecture-v1-definition-of-done.md b/docs/architecture/rho-architecture-v1-definition-of-done.md index e0c5d741..d62ea398 100644 --- a/docs/architecture/rho-architecture-v1-definition-of-done.md +++ b/docs/architecture/rho-architecture-v1-definition-of-done.md @@ -36,7 +36,7 @@ Status note: - [x] no support repo imports `rho/internal/*` - [x] no SDK/skills repo references support engines as primary dependencies -- [x] Rho production code imports Eyrie only through `eyrie/engine` +- [x] Rho production code imports Flux only through `flux/engine` - [x] Rho's graph/projection imports are documented as explicit integration surfaces and do not create engine-to-engine dependencies @@ -55,18 +55,18 @@ Adoption bar: - [x] `kestrel` and `merlin` import contracts for shared severity/findings and expose boundary adapters - [x] `shrike/types` compatibility shim removed from the local ecosystem -- [x] `eyrie`, `harrier`, and `swift` remain contract-free unless they gain a true cross-repo type +- [x] `flux`, `harrier`, and `swift` remain contract-free unless they gain a true cross-repo type ### Rho integration seams - [x] session persistence uses `eagle/tools`, not lower-level provider tool types - [x] review persistence and merlin/review bridge paths use neutral `review` / `verify` contracts -- [x] Rho owns runtime DTOs in `internal/types` and translates them to `eyrie/engine` in `internal/engine` +- [x] Rho owns runtime DTOs in `internal/types` and translates them to `flux/engine` in `internal/engine` - [x] `rho swift ...` remains a Rho-mounted subcommand, not a competing product surface ### Enforcement -- [x] Rho CI runs ecosystem, eyrie-client, and peer-coupling guards +- [x] Rho CI runs ecosystem, flux-client, and peer-coupling guards - [x] each support repo runs `check-ecosystem-boundaries.sh` in CI - [x] Go SDK runs consumer boundary guard in CI - [x] Python SDK and community skills run consumer boundary guards in CI @@ -110,7 +110,7 @@ Remove compatibility shims only when: From `rho`: ```bash -make ecosystem-guard eyrie-client-guard eyrie-engine-guard peer-guard +make ecosystem-guard flux-client-guard flux-engine-guard peer-guard go test ./internal/testaudit/... -count=1 go test ./... -count=1 ``` diff --git a/docs/architecture/rho-contract-spec.md b/docs/architecture/rho-contract-spec.md index c5564843..12394273 100644 --- a/docs/architecture/rho-contract-spec.md +++ b/docs/architecture/rho-contract-spec.md @@ -106,7 +106,7 @@ list in sync with the code (it is the inventory the dependency rules assume). - Rho session persistence has started migrating to provider-neutral tool contracts - Rho review storage and merlin/review bridge paths now consume neutral review/verify contracts - Rho runtime conversation DTOs and the `ChatClient` port are Rho-owned and - translated to the stable `eyrie/engine` contract at the integration edge + translated to the stable `flux/engine` contract at the integration edge ## Versioning rule diff --git a/docs/architecture/rho-current-vs-proposed.md b/docs/architecture/rho-current-vs-proposed.md index 24679d28..5bfa9dde 100644 --- a/docs/architecture/rho-current-vs-proposed.md +++ b/docs/architecture/rho-current-vs-proposed.md @@ -11,7 +11,7 @@ The repository directory and product-name distinction is intentional: | Directory/repository | Product or role | |---|---| | `rho` | Rho product and orchestration root | -| `eyrie` | Eyrie provider runtime | +| `flux` | Flux provider runtime | | `harrier` | Harrier memory engine | | `shrike` | Shrike token/context engine | | `swift` | Swift provenance engine | @@ -38,7 +38,7 @@ graycode-eco/ ├── rho # product / composition root ├── eagle # neutral contracts foundation ├── falcon # MCP foundation -├── eyrie # Eyrie provider engine +├── flux # Flux provider engine ├── harrier # Harrier memory engine ├── shrike # Shrike context engine ├── swift # Swift engine @@ -61,7 +61,7 @@ published versions pinned in each `go.mod`. ```text sparrow / robin / wren ── HTTP/OpenAPI ──> rho <── skill surface ── starling │ - ├── eyrie/engine + ├── flux/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -98,7 +98,7 @@ contract parity checks, and Rho-centered dependency direction all exist. The remaining proposed work is boundary refinement rather than repository reorganization: -1. publish the Eagle-migrated Eyrie revision and update Rho's standalone pin; +1. publish the Eagle-migrated Flux revision and update Rho's standalone pin; 2. remove the transitional `rho-core-contracts` dependency from the published module graph; 3. decide whether graph/projection packages should remain explicit Rho @@ -109,7 +109,7 @@ reorganization: ## Allowed dependency edges ```text -rho -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle +rho -> flux / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # only for shared contracts harrier / kestrel / merlin -> falcon sparrow / robin / wren -> Rho public HTTP/OpenAPI surface diff --git a/docs/architecture/rho-dependency-rules.md b/docs/architecture/rho-dependency-rules.md index ac63ea18..aa072c46 100644 --- a/docs/architecture/rho-dependency-rules.md +++ b/docs/architecture/rho-dependency-rules.md @@ -9,7 +9,7 @@ product labels are shown in parentheses where they differ. ```text sparrow / robin / wren ── Rho daemon API ──> rho <── skill API ── starling │ - ├── eyrie/engine + ├── flux/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -58,16 +58,16 @@ Engines may share concepts through Eagle contracts, but may not import each other. Product names do not change this rule: `harrier` is Harrier, `shrike` is Shrike, `swift` is Swift, `kestrel` is Kestrel, and `merlin` is Merlin. -### 3. Provider logic stays behind Eyrie +### 3. Provider logic stays behind Flux Rho production code reaches provider credentials, catalogs, routing, and -transport only through `github.com/GrayCodeAI/eyrie/engine`. This is enforced +transport only through `github.com/GrayCodeAI/flux/engine`. This is enforced by shell and AST/package-graph guards. ### 4. Rho schemas stay Rho-owned Rho conversation persistence and CLI/JSON output are explicit projections, -not aliases or direct serialization of Eyrie DTOs. +not aliases or direct serialization of Flux DTOs. ### 5. Graph integrations are explicit surfaces @@ -86,13 +86,13 @@ execution. ### 7. Engine configuration is instance-scoped -Rho supplies effective gateway settings while constructing an Eyrie Engine. -Product code must not mutate Eyrie process-global gateway state. +Rho supplies effective gateway settings while constructing an Flux Engine. +Product code must not mutate Flux process-global gateway state. ## Enforcement Rho CI runs manifest validation, no-local-replace checks, Eagle parity, -support-repository coupling, Eyrie facade checks, internal-layer checks, and +support-repository coupling, Flux facade checks, internal-layer checks, and the AST package-boundary audit. Support repositories run their own boundary guards. SDK contract tests compare their OpenAPI snapshots with Rho's public daemon contract. diff --git a/docs/architecture/rho-harness.md b/docs/architecture/rho-harness.md index 59ef92fe..cbaae10c 100644 --- a/docs/architecture/rho-harness.md +++ b/docs/architecture/rho-harness.md @@ -4,7 +4,7 @@ Rho treats the agent runtime as a product boundary around provider output. ## Tool-Call Path -1. Eyrie normalizes provider protocol responses. +1. Flux normalizes provider protocol responses. 2. Rho validates and resolves tool metadata. 3. Permission and sandbox policy runs before execution. 4. Tool execution observes timeouts, cancellation, and path boundaries. @@ -23,7 +23,7 @@ interchangeable. The harness is testable without live providers through scripted providers and recorded interactions. Provider protocol and behavioral conformance belongs in -Eyrie's verification package; Rho owns host-level UX, permissions, persistence, +Flux's verification package; Rho owns host-level UX, permissions, persistence, and review contracts. ## Security Rule diff --git a/docs/architecture/rho-product-architecture.md b/docs/architecture/rho-product-architecture.md index 37be29a8..e5df84c0 100644 --- a/docs/architecture/rho-product-architecture.md +++ b/docs/architecture/rho-product-architecture.md @@ -10,8 +10,8 @@ repositories. It is not a monorepo or a runtime product. Rho is the only primary product surface in the graycode-eco ecosystem. The support repos exist to power Rho, not to compete with it as standalone products. For model execution specifically: **Rho is the face and composition layer; -Eyrie is the engine.** Rho owns the conversation and product experience while -the `eyrie/engine` facade owns the complete provider path from credential and +Flux is the engine.** Rho owns the conversation and product experience while +the `flux/engine` facade owns the complete provider path from credential and catalog state through model selection and normalized generation/streaming. ## Goals @@ -26,11 +26,11 @@ catalog state through model selection and normalized generation/streaming. ## Target repo set - `rho` -- `eyrie` +- `flux` - `owl` - `graycode-platform` (outside the Rho runtime module graph) -Directory names are authoritative for dependencies: `eyrie` is Eyrie. The +Directory names are authoritative for dependencies: `flux` is Flux. The product label remains useful in CLI and user-facing documentation. ## Runtime architecture @@ -45,7 +45,7 @@ Users / SDKs / Skills | | v v core execution embedded token engine - eyrie/engine internal/token + flux/engine internal/token | | +----------+-----------+ | @@ -59,7 +59,7 @@ Current implementation in the workspace: ```text rho - -> eyrie/engine + -> flux/engine -> internal/token (embedded) -> internal/contracts @@ -77,7 +77,7 @@ SDKs / Skills / future integrations +------------+------------+ | | v v - Eyrie/engine internal/token + Flux/engine internal/token | | +------------+------------+ | @@ -116,8 +116,8 @@ Rho does not own: ## Engine responsibilities -### `eyrie` -- stable host control and generation facade (`eyrie/engine`) +### `flux` +- stable host control and generation facade (`flux/engine`) - credential storage and safe credential status - catalog discovery and model metadata - concrete provider/deployment selection @@ -137,18 +137,18 @@ Rho does not own: 1. User invokes `rho`. 2. Rho loads product settings, policy, and workspace state, then creates an - Eyrie Engine with effective per-instance custom gateway settings. + Flux Engine with effective per-instance custom gateway settings. 3. Rho creates or resumes a session. 4. Rho assembles context through the embedded token engine. 5. Rho recalls relevant local memories. -6. Rho routes provider execution through `eyrie`. +6. Rho routes provider execution through `flux`. 7. Rho invokes tools and records graph observations. 8. Rho persists results and returns output to the user. At step 6, Rho passes intent and Rho-owned conversation DTOs through its -adapter. Eyrie loads provider/catalog/credential state, resolves the gateway, +adapter. Flux loads provider/catalog/credential state, resolves the gateway, and returns normalized events. No production Rho package imports a lower -Eyrie package, and no Eyrie engine DTO is used as Rho's persistent or CLI +Flux package, and no Flux engine DTO is used as Rho's persistent or CLI schema. ## Implementation phases @@ -181,8 +181,8 @@ Status: Status: - completed for the local runtime boundary - Rho owns runtime DTOs and review/verify product-boundary contracts -- Rho's `ChatClient` anti-corruption port translates only to `eyrie/engine` -- all lower-level Eyrie production imports are forbidden by shell guards and meta-audit tests +- Rho's `ChatClient` anti-corruption port translates only to `flux/engine` +- all lower-level Flux production imports are forbidden by shell guards and meta-audit tests ### Phase 5 - align SDKs and skills to Rho public interfaces only diff --git a/docs/architecture/rho-provider-abstraction.md b/docs/architecture/rho-provider-abstraction.md index d5daac1b..e248c7ec 100644 --- a/docs/architecture/rho-provider-abstraction.md +++ b/docs/architecture/rho-provider-abstraction.md @@ -8,8 +8,8 @@ That means Rho should support multiple providers without leaking provider-specif ## Design principle -Provider-specific code lives behind Eyrie's stable `eyrie/engine` host facade. -Rho is the face and composition root; Eyrie is the engine. +Provider-specific code lives behind Flux's stable `flux/engine` host facade. +Rho is the face and composition root; Flux is the engine. Rho decides: @@ -17,7 +17,7 @@ Rho decides: - the semantic intent (`fast`, `balanced`, `reasoning`, `economical`) - whether an exact user-selected model may fall back -`eyrie` handles: +`flux` handles: - capability-to-model resolution - provider and deployment selection @@ -43,7 +43,7 @@ Rho decides: ## Rho-facing abstraction Rho depends on its small `ChatClient` product port and adapts it only to -`eyrie/engine`, never to a vendor-specific or lower-level Eyrie client. +`flux/engine`, never to a vendor-specific or lower-level Flux client. Example concerns: @@ -60,9 +60,9 @@ Example concerns: - no provider-specific branches inside review/verify logic - no model-specific assumptions inside session persistence - keep task-semantic policy inside Rho orchestration -- keep provider/deployment routing, health, retry, and fallback inside Eyrie -- Rho production integrations use `github.com/GrayCodeAI/eyrie/engine` -- direct imports of lower Eyrie packages are forbidden and CI-enforced +- keep provider/deployment routing, health, retry, and fallback inside Flux +- Rho production integrations use `github.com/GrayCodeAI/flux/engine` +- direct imports of lower Flux packages are forbidden and CI-enforced - custom gateway settings enter as `engine.Options.CustomGateways` and are isolated per Engine instance - Rho-owned JSON, session, and conversation schemas are explicit projections; diff --git a/docs/architecture/rho-repo-roles.md b/docs/architecture/rho-repo-roles.md index 75b1b877..03fbaa1d 100644 --- a/docs/architecture/rho-repo-roles.md +++ b/docs/architecture/rho-repo-roles.md @@ -1,7 +1,7 @@ # Rho Repo Roles > **Historical.** This document describes the pre-2026-09 multi-engine -> ecosystem. Rho now depends only on `eyrie` and embeds its own token engine; +> ecosystem. Rho now depends only on `flux` and embeds its own token engine; > the harrier, kestrel, merlin, shrike, and swift integrations have been > removed. Kept for record. @@ -27,11 +27,11 @@ separate end-user products. ## Support engines -### `eyrie` -Rho provider engine. Its public host boundary is `eyrie/engine`, which owns +### `flux` +Rho provider engine. Its public host boundary is `flux/engine`, which owns credentials, provider state, catalog discovery, model/deployment selection, transport, resilience, and normalized generation/streaming. Rho production -code has zero imports of Eyrie's lower-level packages. +code has zero imports of Flux's lower-level packages. ### `harrier` (Harrier) Rho memory engine. @@ -50,7 +50,7 @@ Rho verification engine. All six support engines are peers: -- `eyrie` +- `flux` - `harrier` (Harrier) - `shrike` (Shrike) - `swift` (Swift) diff --git a/docs/architecture/rho-review-verify-lifecycle.md b/docs/architecture/rho-review-verify-lifecycle.md index e19d9f1f..6f45f60b 100644 --- a/docs/architecture/rho-review-verify-lifecycle.md +++ b/docs/architecture/rho-review-verify-lifecycle.md @@ -1,7 +1,7 @@ # Rho Review and Verify Lifecycle > **Historical.** This document describes the pre-2026-09 multi-engine -> ecosystem. Rho now depends only on `eyrie` and embeds its own token engine; +> ecosystem. Rho now depends only on `flux` and embeds its own token engine; > the harrier, kestrel, merlin, shrike, and swift integrations have been > removed. Kept for record. diff --git a/docs/architecture/rho-swift-event-model.md b/docs/architecture/rho-swift-event-model.md index bd5863b3..82983d0b 100644 --- a/docs/architecture/rho-swift-event-model.md +++ b/docs/architecture/rho-swift-event-model.md @@ -1,7 +1,7 @@ # Rho Swift Event Model > **Historical.** This document describes the pre-2026-09 multi-engine -> ecosystem. Rho now depends only on `eyrie` and embeds its own token engine; +> ecosystem. Rho now depends only on `flux` and embeds its own token engine; > the harrier, kestrel, merlin, shrike, and swift integrations have been > removed. Kept for record. diff --git a/docs/architecture/spec.md b/docs/architecture/spec.md index 4c043512..e094e8a9 100644 --- a/docs/architecture/spec.md +++ b/docs/architecture/spec.md @@ -28,7 +28,7 @@ layout: |-----------|---------| | `cmd/` | CLI entry point (Cobra) and TUI (Bubble Tea) | | `internal/` | Private Go packages (not importable by external repos) | -| Parent `go.work` | Resolves the nine local Go siblings: rho, eagle, falcon, eyrie, harrier (Harrier), shrike (Shrike), swift (Swift), kestrel (Kestrel), and merlin (Merlin) | +| Parent `go.work` | Resolves the nine local Go siblings: rho, eagle, falcon, flux, harrier (Harrier), shrike (Shrike), swift (Swift), kestrel (Kestrel), and merlin (Merlin) | | `spec/` | OpenSpec schema consumed by `internal/spec` | | `docs/` | Architecture docs, design docs, plans | | `rules/` | User-defined rules | @@ -45,7 +45,7 @@ The `internal/` directory SHALL contain the following packages: | `tool/` | 40+ built-in tools (file edit, git, codegen, spec tools, etc.) | | `permissions/` | Guardian, rules DSL, boundary checker | | `plugin/` | Skills loader, registry, auto-skill, marketplace install | -| `config/` | Product settings, Eyrie composition, state migration | +| `config/` | Product settings, Flux composition, state migration | | `session/` | SQLite persistence, search, export, replay | | `hooks/` | Event-driven plugin system | | `mcp/` | Model Context Protocol client/server | @@ -99,12 +99,12 @@ Rho CLI/TUI + conversation + tools Rho-owned ports/DTOs | v - eyrie/engine + flux/engine credentials -> catalog -> routing -> generate/stream ``` -No production Rho package may import a lower Eyrie package. Custom gateways -are supplied per Engine instance, and Eyrie DTOs are not Rho persistence or +No production Rho package may import a lower Flux package. Custom gateways +are supplied per Engine instance, and Flux DTOs are not Rho persistence or CLI output schemas. ### REQ-4: Agent Loop Lifecycle @@ -129,7 +129,7 @@ The agent loop in `stream.go` SHALL execute the following phases: 6. Refresh Harrier memories 7. Build LLM ChatOptions (system prompt, tools, model) 8. Inject ephemeral context (beliefs, matched skills, spec stage) -9. Execute the LLM call via `ChatService.Stream()` and the `eyrie/engine` adapter +9. Execute the LLM call via `ChatService.Stream()` and the `flux/engine` adapter 10. Process response (tool calls, messages, cost tracking) 11. Check termination conditions diff --git a/docs/compatibility.md b/docs/compatibility.md index 33300ba9..52a98a0b 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -12,11 +12,11 @@ Platform/provider capability metadata is separate: [`platform-capabilities.json` ```jsonc { - "components": ["rho", "eyrie", ...], // canonical eco roster - "dependencies": { "rho": ["eyrie", ...] }, // who depends on who + "components": ["rho", "flux", ...], // canonical eco roster + "dependencies": { "rho": ["flux", ...] }, // who depends on who "matrices": [ - { "name": "stable", "components": { "rho": "0.1.0", "eyrie": "0.1.0", ... } }, - { "name": "next", "components": { "rho": "main", "eyrie": "main", ... } } + { "name": "stable", "components": { "rho": "0.1.0", "flux": "0.1.0", ... } }, + { "name": "next", "components": { "rho": "main", "flux": "main", ... } } ] } ``` @@ -59,7 +59,7 @@ It runs on: ## Why bother -- **Bug reports become triageable.** "I ran rho 0.4 with eyrie 0.2" — you +- **Bug reports become triageable.** "I ran rho 0.4 with flux 0.2" — you immediately know whether that combination was ever tested. - **Consumers can pin reliably.** Downstream projects that vendor multiple eco packages can pin to a known-good stable matrix instead of guessing. diff --git a/docs/design/ECOSYSTEM-MARKETPLACE.md b/docs/design/ECOSYSTEM-MARKETPLACE.md index 61087cb6..be88c788 100644 --- a/docs/design/ECOSYSTEM-MARKETPLACE.md +++ b/docs/design/ECOSYSTEM-MARKETPLACE.md @@ -3,7 +3,7 @@ **Status:** Draft **Author:** Ecosystem / DX working group **Last updated:** 2026-06-06 -**Scope:** Multi-month effort spanning all 5 repos (rho, eyrie, harrier, shrike, swift) + a new gallery web property + a unified docs site. +**Scope:** Multi-month effort spanning all 5 repos (rho, flux, harrier, shrike, swift) + a new gallery web property + a unified docs site. > This is a design a team executes against, not a code-session deliverable. It grounds every claim in the actual graycode-eco codebase (cited as `path:line`) and reuses what already exists rather than greenfielding. @@ -36,7 +36,7 @@ graycode-eco is **not** starting from zero. A large fraction of the marketplace - **A V2 manifest already supports MCP-adjacent bundles.** `rho/internal/plugin/manifest_v2.go:11` (`ManifestV2`) carries `Tools`, `Permissions`, `Hooks` (`:32` `ManifestHook` with `Event`/`Command`/`Priority`), `Config`, `Dependencies`, `Mode` (subprocess/daemon). - **Trust scaffolding already exists.** `rho/internal/plugin/malware_check.go:19` blocks `eval()`, pipe-to-shell, reverse shells; `rho/internal/plugin/audit.go` flags hidden-Unicode / homoglyph attacks (`rho skills audit`). -The gap is therefore **consolidation and elevation**, not invention: (a) a *standardized, multi-asset* extension format (today's registry is skills-only); (b) a *browsable web gallery*; (c) a *trust/signing* story beyond static malware regexes; and (d) a *unified docs site* that today is five disconnected `README.md`/`ARCHITECTURE.md` trees (`rho/docs/`, `eyrie/docs/`, `harrier/`, `shrike/docs/`, `swift/docs/`). +The gap is therefore **consolidation and elevation**, not invention: (a) a *standardized, multi-asset* extension format (today's registry is skills-only); (b) a *browsable web gallery*; (c) a *trust/signing* story beyond static malware regexes; and (d) a *unified docs site* that today is five disconnected `README.md`/`ARCHITECTURE.md` trees (`rho/docs/`, `flux/docs/`, `harrier/`, `shrike/docs/`, `swift/docs/`). --- @@ -81,7 +81,7 @@ The gap is therefore **consolidation and elevation**, not invention: (a) a *stan │ rho CLI (Go) │ │ Gallery + Docs site (static) │ │ internal/plugin/registry.go │ │ Docusaurus or Mintlify │ │ + new: trust.go, extman.go │ │ - /extensions (gallery) │ -│ cmd/skills_cmd.go (+ ext cmd)│ │ - /docs/{rho,eyrie,...} │ +│ cmd/skills_cmd.go (+ ext cmd)│ │ - /docs/{rho,flux,...} │ │ internal/plugin/manifest_v2 │ │ - generated from registry.v2 │ └───────────────────────────────┘ └──────────────────────────────┘ ``` @@ -203,7 +203,7 @@ browser → rhoskills.dev/extensions (static Docusaurus/Mintlify page) **Flow C — Docs build (unified site, all 5 repos)** ``` CI (docs repo) → - pull README.md / ARCHITECTURE.md / docs/** from each of rho, eyrie, + pull README.md / ARCHITECTURE.md / docs/** from each of rho, flux, harrier (Harrier), shrike (Shrike), swift (Swift) (independent checkouts or sparse checkout) → transform: inject sidebars, rewrite intra-repo links → generate /extensions/* from registry.v2.json @@ -262,7 +262,7 @@ The discovery-precedence engine (`rules.go:20` `DefaultRuleSources`, walk-up + l - **Exit:** `rho ext install` works for at least 5 multi-asset extensions, signature-verified, on a v2 index. ### P1 — Gallery + Docs site (the visible surface) -- **M1.1** Stand up the static site (decision in §6). Pages: `/extensions` (gallery), per-extension detail, `/docs/{rho,eyrie,harrier,shrike,swift}` (getting-started + API ref + architecture), `/comparison` (rendered from `TOP20_COMPARISON.md`). +- **M1.1** Stand up the static site (decision in §6). Pages: `/extensions` (gallery), per-extension detail, `/docs/{rho,flux,harrier,shrike,swift}` (getting-started + API ref + architecture), `/comparison` (rendered from `TOP20_COMPARISON.md`). - **M1.2** Build-time generation of `/extensions/index.json` from `registry.v2.json`; client-side fuzzy search, category + capability-badge filters. - **M1.3** Aggregate the 5 independent repositories' docs via checkout or sparse checkout in CI (Flow C); link-rewriting + unified sidebar. - **M1.4** `rho ext update` (diff installed version vs index, re-verify, atomic replace) and `rho ext verify ` (re-check signature/audit of an installed extension). @@ -272,7 +272,7 @@ The discovery-precedence engine (`rules.go:20` `DefaultRuleSources`, walk-up + l - **M2.1** `rho ext publish` — scaffolds `extension.yaml`, runs validators (`tools/validate_skill.py` extended), signs locally, opens a PR to `starling`. - **M2.2** Publisher trust tiers: verified-publisher badge (key in rho's bundled trust DB) vs community (signed but unverified) vs unsigned. - **M2.3** Trending/installs analytics — privacy-preserving (aggregate counts from PR-based opt-in pings or GitHub stars only; **no per-user tracking**, see §7). -- **M2.4** Cross-repo extension kinds: eyrie provider plug-ins, shrike compression profiles (`TOP20_COMPARISON.md:181` team profiles), swift exporters — registered through the same `extension.yaml` `provides` mechanism with new `kind`s. +- **M2.4** Cross-repo extension kinds: flux provider plug-ins, shrike compression profiles (`TOP20_COMPARISON.md:181` team profiles), swift exporters — registered through the same `extension.yaml` `provides` mechanism with new `kind`s. - **Exit:** External contributors can publish a verified, multi-asset extension end-to-end; gallery shows mixed-kind extensions across the ecosystem. --- @@ -330,7 +330,7 @@ These repos are privacy-first; the marketplace must not regress that. 1. **Signing backend:** minisign (simple, key-in-binary) for P0, or go straight to sigstore/cosign keyless (transparency log, no key custody) and align with the SBOM effort (`TOP20_COMPARISON.md:240`)? Tradeoff: operational simplicity now vs. provenance rigor later. 2. **One repo or two?** Keep gallery + docs in `starling`, or split docs into a new `rho-docs` repo with the 5 repos as submodules? (Submodules complicate contributor flow but isolate doc build from skill content.) -3. **Cross-repo extension kinds (P2):** do eyrie/shrike/swift assets (provider plugins, compression profiles, exporters) belong in the *same* `starling` registry, or a per-repo registry federated into one gallery index? +3. **Cross-repo extension kinds (P2):** do flux/shrike/swift assets (provider plugins, compression profiles, exporters) belong in the *same* `starling` registry, or a per-repo registry federated into one gallery index? 4. **Versioning & compat matrix:** `compat.minRhoVersion` exists (`manifest_v2.go:20`), but do we also need per-asset compat (e.g., an MCP server needing a specific transport rho supports)? Tie-in to the tri-modal MCP transport gap (`TOP20_COMPARISON.md:83`). 5. **Install integrity for non-skill assets:** today install only copies `SKILL.md` trees (`registry.go` discovers `SKILL.md`); multi-asset copy (commands/hooks/mcp/themes/subagents) needs a defined on-disk layout under `~/.rho/`. What are the canonical install dirs for each asset kind? 6. **Domain & hosting:** confirm `rhoskills.dev` (referenced `TOP20_COMPARISON.md:65`) ownership and Pages target (GitHub vs Cloudflare). @@ -349,7 +349,7 @@ These repos are privacy-first; the marketplace must not regress that. | **P1 — CLI update/verify** | `rho ext update`, `rho ext verify`, atomic replace | **2 ew** | | **P2 — publisher self-service** | `rho ext publish` scaffolder + validators, PR automation | **3 ew** | | **P2 — trust tiers + sigstore upgrade** | verified-publisher badges, optional cosign/sigstore keyless + transparency log | **3–4 ew** | -| **P2 — cross-repo kinds** | eyrie/shrike/swift extension kinds + federated index | **3–4 ew** | +| **P2 — cross-repo kinds** | flux/shrike/swift extension kinds + federated index | **3–4 ew** | | **Cross-cutting** | docs writing (content, not framework), security review, CI, design iteration | **4–6 ew** | | **Total** | | **≈ 31–44 eng-weeks** (~7.5–11 eng-months; 2 engineers ~4–5 calendar months) | diff --git a/docs/design/RHO-CLOUD-SAAS.md b/docs/design/RHO-CLOUD-SAAS.md index 532cd562..26868d34 100644 --- a/docs/design/RHO-CLOUD-SAAS.md +++ b/docs/design/RHO-CLOUD-SAAS.md @@ -45,9 +45,9 @@ The comparison doc is blunt that this is a **fundamental product-tier gap** RBAC "requires a multi-tenant SaaS backend ... beyond the current single-user token store at `/rho/internal/auth/auth.go`" (`TOP20_COMPARISON.md:72`). -The eyrie side of the house has the parallel gaps: a LiteLLM-compatible proxy +The flux side of the house has the parallel gaps: a LiteLLM-compatible proxy endpoint and "multi-tenant team/project management with SSO/RBAC" with per-key -budgets (`TOP20_COMPARISON.md:95-96`). Rho Cloud and eyrie multi-tenancy share +budgets (`TOP20_COMPARISON.md:95-96`). Rho Cloud and flux multi-tenancy share the same identity, billing, and RBAC substrate; this doc designs that substrate once and shows how both consume it. @@ -87,9 +87,9 @@ once and shows how both consume it. privacy-first posture (`TOP20_COMPARISON.md:20`) is preserved — cloud is opt-in. - **Not** building our own microVM hypervisor. We integrate E2B/Daytona (build-vs-buy in §6), not write Firecracker orchestration from scratch. -- **Not** an LLM gateway rewrite. Eyrie remains the provider engine behind - Rho's `eyrie/engine` integration; the plane calls a Rho agent worker, which - composes Eyrie. The plane does not re-implement routing, caching, or provider +- **Not** an LLM gateway rewrite. Flux remains the provider engine behind + Rho's `flux/engine` integration; the plane calls a Rho agent worker, which + composes Flux. The plane does not re-implement routing, caching, or provider adapters. - **Not** SSO/SAML in P0 (deferred to P2; OIDC social login + API keys first). - **Not** on-prem/self-hosted enterprise distribution in P0 (P2). @@ -121,7 +121,7 @@ once and shows how both consume it. │ │ │ │ ▼ │ │ ┌──────────────┐ ┌─────────────┐ │ - │ │ Agent Worker │─────▶│ eyrie │ │ + │ │ Agent Worker │─────▶│ flux │ │ │ │ (engine. │ │ (model │ │ │ │ Session) │ │ runtime) │ │ │ └──────┬───────┘ └─────────────┘ │ @@ -144,7 +144,7 @@ middleware (`daemon.go:185`) and `routes()` table (`daemon.go:174-183`). Net-new. Wraps/extends `internal/auth` (see §4). **Billing service** — credit ledger, metering ingestion, server-side pricing, -hard-limit enforcement, and invoicing. Rho Cloud owns this authority; Eyrie +hard-limit enforcement, and invoicing. Rho Cloud owns this authority; Flux supplies normalized model and token usage through Rho's Engine boundary. **Workspace service** — workspace membership, shared session state, share links, @@ -256,7 +256,7 @@ Edge: check credit balance(org) > floor [Billing svc] ← rejec Edge → Router: route(org, ws, user, session_id?) Router → Worker: acquire/lease Agent Worker (+ Sandbox) Worker: engine.Session.Stream(ctx) [stream.go:20] -Worker → eyrie: Chat/StreamChat with ctx carrying virtual key +Worker → flux: Chat/StreamChat with ctx carrying virtual key (WithVirtualKey, budget_provider.go:23) Worker → Edge: SSE content events (daemon.go:321-337, reused verbatim) Edge → Client: SSE relay @@ -311,7 +311,7 @@ This is the crux: **what is reusable today vs. net-new.** | Autonomy / permission gating | `daemon.go:286-298` (`PresetConfig`, `NeedsPermission`) | Server-side auto-approval policy already exists for non-interactive runs; cloud reuses it per-workspace policy. | | Auth primitives | `internal/auth/auth.go` (`TokenStore`, `SecureStorage`), `device_flow.go` (full RFC 8628) | Device-grant **client** is done. `SecureStorage` (macOS keychain + file fallback, `auth.go:54-121`) stays for local credential caching of cloud tokens. `GenerateNonce` (`auth.go:124`) for OAuth state/PKCE. | | Constant-time key compare | `daemon.go:207-224` | API-key verification logic carries over (but keys move to hashed storage; see net-new). | -| Eyrie normalized usage | `eyrie/engine` response and stream usage DTOs through Rho's adapter | Reuse model/token dimensions as metering input. Do not import lower Eyrie packages or trust client-computed prices for billing. | +| Flux normalized usage | `flux/engine` response and stream usage DTOs through Rho's adapter | Reuse model/token dimensions as metering input. Do not import lower Flux packages or trust client-computed prices for billing. | | Sandbox executor interface | `internal/sandbox/container.go:18-21` (`containerExecutor`: `Exec`, `Running`) | `CloudSandbox` implements the same interface → drop-in. Callers don't know if they're on local Docker or a cloud microVM. | | Sandbox lifecycle manager | `internal/sandbox/snapshot_sandbox.go:52-228` (`Create/Pause/Resume/Snapshot/Restore/List/Cleanup`) | Existing pause/resume/snapshot semantics map cleanly onto E2B/Daytona pause+snapshot APIs; the manager abstraction guides the `CloudSandbox` API shape. | | Messaging gateways | `internal/daemon/gateway.go`, `telegram.go`, `discord.go`, `slack.go` | Already forward to `/v1/chat` via `forwardToRho` (`gateway.go:17`) with bearer auth. In cloud they forward to the tenant-scoped chat endpoint with the org's key — minimal change. | @@ -339,7 +339,7 @@ This is the crux: **what is reusable today vs. net-new.** in-process engine. Net-new control loop. 6. **`CloudSandbox`** — E2B/Daytona-backed implementation of `containerExecutor`. The interface and call sites exist; the cloud backend does not. -7. **Billing/credit ledger + invoicing + Stripe integration** — eyrie gives +7. **Billing/credit ledger + invoicing + Stripe integration** — flux gives enforcement and cost math; the ledger, top-ups, plans, and payment processor are net-new. 8. **Workspace sharing** — `session_shares` and the share/continue flows. @@ -385,7 +385,7 @@ credits hit zero. - **M2.1** SSO/SAML + SCIM provisioning. - **M2.2** Multi-region worker pools + autoscaling; second sandbox provider for redundancy. - **M2.3** JetBrains extension (ACP). -- **M2.4** Org-scoped provider config (BYO keys / BYO model endpoints), aligning with eyrie multi-tenant proxy (`TOP20_COMPARISON.md:96`). +- **M2.4** Org-scoped provider config (BYO keys / BYO model endpoints), aligning with flux multi-tenant proxy (`TOP20_COMPARISON.md:96`). - **M2.5** Audit logging, IT-managed policy tiers, self-hosted/on-prem distribution. - **M2.6** Cloud Routines (scheduled/triggered runs) on the worker plane (`TOP20_COMPARISON.md:48`). @@ -397,9 +397,9 @@ credits hit zero. |---|---|---| | MicroVM sandboxing | **Buy/integrate** E2B (P0) + Daytona (P2 redundancy) | Building Firecracker orchestration is a multi-quarter effort orthogonal to product value. E2B SDK is open source (Apache-2.0/MIT family); Daytona is Apache-2.0. Both have hosted offerings; we can also self-host E2B later. The `containerExecutor` seam (`container.go:18`) keeps us provider-agnostic. | | Identity / OAuth server | **Buy** (Ory Hydra/Kratos, Auth0, or WorkOS) for P0; revisit P2 | Writing a compliant OAuth2/OIDC AS is risky. Ory is Apache-2.0 (self-hostable, privacy-aligned). WorkOS/Auth0 are SaaS — faster but introduce a third party in the auth path (weigh against privacy posture). Prefer **Ory self-hosted** to keep the privacy-first promise. | -| Billing / payments | **Buy** Stripe for payment + invoicing; **build** the credit ledger | Never build a card processor. The ledger and metering are ours (and partly exist in eyrie). Stripe SDK is permissive. | +| Billing / payments | **Buy** Stripe for payment + invoicing; **build** the credit ledger | Never build a card processor. The ledger and metering are ours (and partly exist in flux). Stripe SDK is permissive. | | Database | **Buy** managed Postgres | Standard. | -| Metering enforcement | **Build in Rho Cloud** on normalized Engine usage | Billing authority must be server-owned and versioned; lower Eyrie packages remain engine implementation details. | +| Metering enforcement | **Build in Rho Cloud** on normalized Engine usage | Billing authority must be server-owned and versioned; lower Flux packages remain engine implementation details. | | Web UI framework | **Build** small React SPA, `go:embed` served (Aider/OpenHands pattern) | Keeps deployment a single binary; matches `TOP20_COMPARISON.md:31`. | | IDE protocol | **Adopt** ACP (Agent Client Protocol) | Industry trajectory (Gemini CLI, Zed, JetBrains), `TOP20_COMPARISON.md:32,36`. Avoid bespoke per-IDE protocols. | @@ -430,7 +430,7 @@ erode that. 4. **Per-line SSE escaping is a real injection defense** already present (`daemon.go:324-329`) and must be preserved in the relay path — LLM output can contain newlines that would otherwise forge SSE events. -5. **Credit enforcement is a DoS control too.** Hard floors (HTTP 402) via eyrie +5. **Credit enforcement is a DoS control too.** Hard floors (HTTP 402) via flux `CheckBudget` (`budget_provider.go:181`) bound runaway agent spend per tenant. 6. **Code privacy.** Customer code lives only in the ephemeral microVM and the session store; offer per-org data-retention controls and a "no-retention" mode @@ -460,7 +460,7 @@ erode that. server-priced LLM usage and sandbox-minutes — what's the blended margin? 4. **BYO keys vs. managed keys:** if an org brings its own provider keys, do their LLM tokens still consume credits (we only charge sandbox/orchestration), or run - free? Affects eyrie provider-config plumbing (`TOP20_COMPARISON.md:96`). + free? Affects flux provider-config plumbing (`TOP20_COMPARISON.md:96`). 5. **Identity build-vs-buy:** Ory self-hosted (privacy, ops burden) vs. WorkOS/Auth0 (speed, third party in auth path). Privacy posture argues for Ory. 6. **Session store backend:** Postgres for everything vs. Postgres (metadata) + @@ -497,7 +497,7 @@ Assumes a small senior team (3-4 engineers). Estimates are for engineering only with P0 a hard ~8-10 calendar weeks for a usable single-team beta.** Leverage note: the agent loop, SSE, sandbox interface, device-flow client, and -eyrie budget machinery are **already built** — they collapse what would otherwise +flux budget machinery are **already built** — they collapse what would otherwise be the most expensive parts (the agent runtime and the metering enforcement engine) into integration work. The genuinely greenfield cost is identity/multi-tenancy, worker orchestration, and the cloud sandbox backend. diff --git a/docs/design/three-repo-architecture.md b/docs/design/three-repo-architecture.md index 70909dab..290fd04d 100644 --- a/docs/design/three-repo-architecture.md +++ b/docs/design/three-repo-architecture.md @@ -10,10 +10,10 @@ proposal. See also `gateway-architecture.md` (human-readable version), flowchart LR subgraph CLI["rho"] TUI["chat · exec · review\nagent loop · sessions"] - ENG["eyrie engine\n(compiled in)"] + ENG["flux engine\n(compiled in)"] end - subgraph ROUTER["eyrie"] + subgraph ROUTER["flux"] LIB["engine library\n(router, adapters)"] SRV["HTTP server (skeleton)\ninternal/api + openapi.yaml"] end @@ -32,7 +32,7 @@ Wires, with evidence: | From → To | Mechanism | Evidence | |---|---|---| -| CLI → Router | Go module dep, in-process construction | `go.mod` requires `github.com/GrayCodeAI/eyrie`; `internal/provider/gateway/gateway.go` is the sole importer | +| CLI → Router | Go module dep, in-process construction | `go.mod` requires `github.com/GrayCodeAI/flux`; `internal/provider/gateway/gateway.go` is the sole importer | | CLI → Platform | Opt-in HTTPS (login, usage, graph sync) | `internal/platform/cloud/client.go`; `Enabled()` requires endpoint + token; no default URL | | Router → CLI | none | router `go.mod`/`go.sum` contain zero rho refs | | Router ↔ Platform | none | no imports, package deps, or API calls either direction | @@ -43,7 +43,7 @@ Wires, with evidence: flowchart LR CLI2["rho\n(default: in-process)"] -. "opt-in --model-gateway" .-> GW WEB2["platform web/BFF"] -->|per-user keys| GW - subgraph GW["eyrie: common service"] + subgraph GW["flux: common service"] API["OpenAI-compatible API\n/v1/chat/completions + SSE"] POL["routing · retry · guardrails\nspend tracking"] end diff --git a/docs/ecosystem-message-flow.md b/docs/ecosystem-message-flow.md index 8b98528d..43bf5481 100644 --- a/docs/ecosystem-message-flow.md +++ b/docs/ecosystem-message-flow.md @@ -1,4 +1,4 @@ -# Message flow (eyrie + embedded token engine) +# Message flow (flux + embedded token engine) How one user message travels through rho. @@ -26,7 +26,7 @@ User prompt (TUI or rho exec) │ ▼ ┌───────────────────┐ ┌─────────────┐ -│ Rho ChatClient │────►│ eyrie/engine│ catalog, credentials, routing +│ Rho ChatClient │────►│ flux/engine│ catalog, credentials, routing │ port + adapter │ │ generate/ │────► provider API └─────────┬─────────┘ │ stream │ └─────────────┘ @@ -40,7 +40,7 @@ User prompt (TUI or rho exec) ▼ (when context grows) ┌───────────────────┐ │ token Compress │ fast path before LLM summarization -│ + eyrie compact │ +│ + flux compact │ └───────────────────┘ ``` @@ -48,8 +48,8 @@ User prompt (TUI or rho exec) ### 1. Session start (`rho` or `rho exec`) -- **eyrie**: The Rho composition root creates an `eyrie/engine.Engine` with - Eyrie-owned state paths, an injected secret store, and per-engine custom +- **flux**: The Rho composition root creates an `flux/engine.Engine` with + Flux-owned state paths, an injected secret store, and per-engine custom gateway metadata. The engine loads provider state and the model catalog, then builds transport behind Rho's `ChatClient` port. - **memory**: `configureSession` initializes the local memory manager @@ -68,8 +68,8 @@ User prompt (TUI or rho exec) Each turn: 1. **memory** — recall memories matching the latest user message (token budget ~2000). -2. **eyrie** — Rho's adapter calls engine generate/stream with Rho-owned tool - definitions; Eyrie normalizes provider events and tool requests. +2. **flux** — Rho's adapter calls engine generate/stream with Rho-owned tool + definitions; Flux normalizes provider events and tool requests. 3. Tools run with the session's memory service in context. 4. **memory** — post-session bookkeeping records the session goal and outcome. @@ -78,7 +78,7 @@ Each turn: When messages exceed limits (`internal/engine/compact.go`): 1. **token** — `token.Compress()` tries a fast compression path for summaries. -2. **eyrie** — if token reduction is insufficient, rho calls the LLM to summarize, then keeps recent messages. +2. **flux** — if token reduction is insufficient, rho calls the LLM to summarize, then keeps recent messages. ### 5. Token accounting @@ -88,7 +88,7 @@ When messages exceed limits (`internal/engine/compact.go`): ## Verify locally ```bash -rho doctor # ecosystem panel + eyrie preflight +rho doctor # ecosystem panel + flux preflight ./scripts/smoke-rho.sh # build + quick tests ``` @@ -96,13 +96,13 @@ rho doctor # ecosystem panel + eyrie preflight | Module | Role in rho | Required? | |--------|----------------|-----------| -| **eyrie** | LLM APIs, catalog, credentials, routing | Yes | +| **flux** | LLM APIs, catalog, credentials, routing | Yes | | **internal/token** | Token estimate + context compression + secrets + usage | Yes (embedded, no config) | -`eyrie` is an independent sibling checkout; the parent `go.work` wires the +`flux` is an independent sibling checkout; the parent `go.work` wires the local Go workspace. -Production Rho code imports Eyrie only through `eyrie/engine`. Conversation +Production Rho code imports Flux only through `flux/engine`. Conversation history, WAL/resume, permissions, tool execution, and memory remain in Rho; provider credentials, discovery, selection, transport, resilience, and -normalized streaming remain in Eyrie. \ No newline at end of file +normalized streaming remain in Flux. \ No newline at end of file diff --git a/docs/monitoring-guide.md b/docs/monitoring-guide.md index e560631f..08444dc3 100644 --- a/docs/monitoring-guide.md +++ b/docs/monitoring-guide.md @@ -18,7 +18,7 @@ curl -sf http://localhost:4590/v1/health ### `GET /v1/ready` — Readiness probe Returns 200 when the daemon is fully ready to serve traffic (session factory -configured, Eyrie preflight checks pass). Returns 503 with the failed +configured, Flux preflight checks pass). Returns 503 with the failed dependency during startup or when dependencies are unavailable. ```bash diff --git a/docs/plans/FULL-GROK-ECO-TO-RHO-ECO-PORT-PLAN.md b/docs/plans/FULL-GROK-ECO-TO-RHO-ECO-PORT-PLAN.md index 55848590..c263c45f 100644 --- a/docs/plans/FULL-GROK-ECO-TO-RHO-ECO-PORT-PLAN.md +++ b/docs/plans/FULL-GROK-ECO-TO-RHO-ECO-PORT-PLAN.md @@ -21,7 +21,7 @@ 2. **Go multi-repo stays** — map Grok crates into rho / engines / contracts / cloud / skills. 3. **Prefer wire-first** — Rho already has partial types; complete wiring before greenfield. 4. **Privacy-first defaults** — Grok Mixpanel/Sentry defaults become **opt-in** OTEL/privacy-safe telemetry. -5. **Multi-provider stays** — Grok sampler/auth maps to **eyrie**, not a single-vendor clone. +5. **Multi-provider stays** — Grok sampler/auth maps to **flux**, not a single-vendor clone. 6. **Memory stays harrier** — Grok markdown memory maps to **harrier APIs + UX**, not a second store. 7. **Tokens stay shrike** — Grok `xai-token-estimation` is obsolete relative to shrike. 8. **Track everything** — even 70-line crates appear in the matrix (Done / Partial / Port / Skip / N/A). @@ -69,7 +69,7 @@ grok-eco/grok-build (one Rust workspace) │ ├─► rho product CLI/TUI/engine/tools/hooks/plugins/ACP ├─► eagle pure DTOs (tool/spawn/hooks/policy) - ├─► eyrie LLM routing/stream/retry/catalog/auth credentials + ├─► flux LLM routing/stream/retry/catalog/auth credentials ├─► harrier memory graph + dream UX ├─► shrike compression / secrets / token cost ├─► swift session capture/import/replay @@ -116,8 +116,8 @@ grok-eco/grok-build (one Rust workspace) | `xai-file-utils` | 15k | Event tracking / upload | rho observability | Partial | M | Privacy opt-in | | `xai-grok-telemetry` | 14k | Events + OTEL + Mixpanel + Sentry | rho observability | Partial | L | **Skip Mixpanel default**; keep OTEL | | `xai-hunk-tracker` | 13k | Agent vs external hunks | rho | Port | L | | -| `xai-grok-sampling-types` | 13k | Chat API types | **eyrie** | Done* | — | Different shape; eyrie owns | -| `xai-grok-sampler` | 11k | HTTP stream + retry | **eyrie** | Done* | — | Do not replace eyrie | +| `xai-grok-sampling-types` | 13k | Chat API types | **flux** | Done* | — | Different shape; flux owns | +| `xai-grok-sampler` | 11k | HTTP stream + retry | **flux** | Done* | — | Do not replace flux | | `xai-grok-update` | 11k | Auto-update | rho | Partial | M | | | `xai-grok-mcp` | 10k | MCP client (oauth, wire) | rho `mcp` | Partial | L | | | `xai-codebase-graph` | 9.7k | Tree-sitter graph | rho codegraph/repomap | Partial | L | | @@ -144,16 +144,16 @@ grok-eco/grok-build (one Rust workspace) | `xai-agent-lifecycle` | 0.6k | Lifecycle hooks data | rho hooks/engine | Partial | S | | | `xai-gix-status` | 0.6k | Fast git status | rho git tools | Partial | S | | | `xai-grok-paths` | 0.6k | AbsPath types | rho | N/A | XS | Go path.Clean enough | -| `xai-grok-secrets` | 0.6k | Secrets helpers | shrike + eyrie | Partial | S | | +| `xai-grok-secrets` | 0.6k | Secrets helpers | shrike + flux | Partial | S | | | `xai-grok-announcements` | 0.4k | Release announcements | rho tips/notify | Port | S | | -| `xai-grok-auth` | 0.4k | Auth seam | eyrie + rho auth | Partial | M | Browser OAuth optional | +| `xai-grok-auth` | 0.4k | Auth seam | flux + rho auth | Partial | M | Browser OAuth optional | | `xai-token-estimation` | 0.3k | Bytes/4 heuristic | **shrike** | Skip | — | shrike superior | | `xai-tracing-macros` | 0.2k | Log macros | rho observability | N/A | XS | | -| `xai-grok-env` | 0.2k | Backend env presets | eyrie/rho | Partial | XS | | +| `xai-grok-env` | 0.2k | Backend env presets | flux/rho | Partial | XS | | | `xai-prompt-queue` | 0.2k | Prompt queue types | rho | Port | S | | | `xai-mixpanel` | 0.1k | Mixpanel client | — | **Skip** | — | Privacy; OTEL opt-in | | `xai-grok-version` | 0.1k | Version | rho VERSION | Done | — | | -| `xai-grok-models` | 0.1k | Default model IDs | eyrie catalog | Done* | — | | +| `xai-grok-models` | 0.1k | Default model IDs | flux catalog | Done* | — | | ### 2.2 Common crates (tool protocol / hub / compaction) @@ -166,7 +166,7 @@ grok-eco/grok-build (one Rust workspace) | `xai-tool-runtime` | 5.4k | Tool trait runtime | rho tool | Partial | L | | | `xai-tool-types` | 3.6k | Spawn/task types | **contracts** | Port | M | **P0** | | `xai-grok-compaction` | 6.8k | Compaction engine | rho engine + shrike | Partial | L | | -| `xai-circuit-breaker` | 2.2k | HTTP breaker | eyrie/resilience | Partial | S | | +| `xai-circuit-breaker` | 2.2k | HTTP breaker | flux/resilience | Partial | S | | | `xai-tracing` | 0.8k | Tracing | rho OTEL | Partial | S | | | `xai-test-utils` | 0.4k | Hermetic git tests | rho testutil | Partial | S | | | `xai-interjection-core` | 0.3k | Interjection messaging | rho | Port | S | Mid-turn user inject | @@ -269,7 +269,7 @@ Grok slash modules (port checklist → rho `/` commands or CLI): | 08 skills | same | | 09 plugins | same | | 10 hooks | same | -| 11 custom models | eyrie + rho | +| 11 custom models | flux + rho | | 12 project rules AGENTS.md | exists Partial | | 13 memory | harrier UX | | 14 headless | CLI flags doc | @@ -553,7 +553,7 @@ Port/finish: spawn, tools, sandbox, trust, hooks, plugins, marketplace client, T Port: spawn/capability/isolation types, hook events, optional sandbox policy DTO, tool protocol types (later). -### `eyrie` +### `flux` Absorb: sampling/stream/retry/circuit-breaker lessons; catalog already ahead; managed deployment hooks; **do not** become Grok-only auth. @@ -605,7 +605,7 @@ Dashboard/usage/marketplace web UI only. | SpaceXAI-only browser auth as sole path | Multi-provider + keychain | | `xai-token-estimation` | shrike wins | | Replace harrier with markdown memory files | Graph better | -| Replace eyrie with xai-grok-sampler | Multi-provider better | +| Replace flux with xai-grok-sampler | Multi-provider better | | ratatui widgets | Bubble Tea stack | | `xai-proto-build` | Rust build | | Closed contribution policy | Rho is open | @@ -650,7 +650,7 @@ Declare complete when: 6. Folder trust + sandbox deny globs pass security tests on macOS+Linux. 7. Marketplace can install a multi-component plugin. 8. Subagent explore/plan/general + resume + worktree isolation work. -9. No second memory/token/provider stack duplicated against harrier/shrike/eyrie. +9. No second memory/token/provider stack duplicated against harrier/shrike/flux. 10. Continuous parity process (PACK-17) running for 2 releases. --- diff --git a/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md b/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md index 2e6f73b3..0677becc 100644 --- a/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md +++ b/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md @@ -5,7 +5,7 @@ **Horizon:** ~12–18 months (phased; can compress with parallel teams) **Language:** Go only (no Rust ports; reimplement contracts and behavior) **Primary product:** `rho` -**Supporting repositories:** `eagle`, `eyrie`, `harrier` (Harrier), `shrike` (Shrike), `swift` (Swift), `kestrel` (Kestrel), `merlin` (Merlin), `falcon`, `starling`, `sparrow`, `robin`, `wren`, `owl`, and `graycode-platform` (web/BFF/Rho Cloud; outside the Rho Go runtime graph) +**Supporting repositories:** `eagle`, `flux`, `harrier` (Harrier), `shrike` (Shrike), `swift` (Swift), `kestrel` (Kestrel), `merlin` (Merlin), `falcon`, `starling`, `sparrow`, `robin`, `wren`, `owl`, and `graycode-platform` (web/BFF/Rho Cloud; outside the Rho Go runtime graph) --- @@ -13,13 +13,13 @@ ### Goal -Bring Rho to **Grok-class agent control-plane quality** (typed subagents, sandbox profiles, folder trust, hooks, plugins/marketplace, ACP depth, monitor/scheduler, enterprise managed policy) **without** abandoning Rho’s multi-repo Go platform advantages (eyrie multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). +Bring Rho to **Grok-class agent control-plane quality** (typed subagents, sandbox profiles, folder trust, hooks, plugins/marketplace, ACP depth, monitor/scheduler, enterprise managed policy) **without** abandoning Rho’s multi-repo Go platform advantages (flux multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). ### Non-goals - Rewrite Rho in Rust or monorepo-collapse engines into rho. - Vendor lock-in to a single LLM host auth model. -- Replace eyrie/harrier/shrike/swift with Grok-shaped internal crates. +- Replace flux/harrier/shrike/swift with Grok-shaped internal crates. - Default opt-in product telemetry that violates privacy-first posture. ### Strategy @@ -130,7 +130,7 @@ This is the single highest-ROI fix: **wire what already exists**, then extend. │ ┌────┴────┬──────────┬──────────┬──────────┐ ▼ ▼ ▼ ▼ ▼ - eyrie harrier shrike swift kestrel/merlin + flux harrier shrike swift kestrel/merlin ``` **New core package (proposed):** `rho/internal/spawn` (or expand `engine/agent`) owning: @@ -699,7 +699,7 @@ Depends on Phase 2 unified `taskruntime`. ### Phase 9 — Enterprise managed policy (4–6 weeks) -**Repos:** `graycode-platform/apps/worker` (deployed as `graycode-cloud`), `rho`, optional `eyrie` +**Repos:** `graycode-platform/apps/worker` (deployed as `graycode-cloud`), `rho`, optional `flux` | Piece | Detail | |-------|--------| @@ -768,7 +768,7 @@ Build on existing enterprise policyInput (model/capability lists). | Foreign import | swift | rho | optional | | Memory dream UX | harrier | rho | no | | Token | shrike | — | no change | -| Providers | eyrie | rho | no change | +| Providers | flux | rho | no change | | Review engines | kestrel/merlin | rho | findings already | --- @@ -976,7 +976,7 @@ Week 4 | Trust default | On | Security | | Marketplace before trust? | No | Supply chain | | Replace harrier? | No | Graph memory superior | -| Replace eyrie? | No | Multi-provider is differentiator | +| Replace flux? | No | Multi-provider is differentiator | | Three bg systems | Unify early | Blocks monitor/wait | --- diff --git a/docs/plans/YEAR-0-ACTIVE.md b/docs/plans/YEAR-0-ACTIVE.md index 1ff11547..aaf9be48 100644 --- a/docs/plans/YEAR-0-ACTIVE.md +++ b/docs/plans/YEAR-0-ACTIVE.md @@ -1,7 +1,7 @@ # Year 0 Active Track (Grok → Rho) > **Historical.** This track predates the removal of the harrier, kestrel, -> merlin, shrike, and swift engines. Rho now depends only on `eyrie` and +> merlin, shrike, and swift engines. Rho now depends only on `flux` and > embeds its own token engine. Kept for record. **Status:** Superseded @@ -19,7 +19,7 @@ port matrices; it freezes what “Year 0 done” means and tracks pack status. |----|--------| | Reimplement Grok **behavior** in Go | Copy Rust crates or depend on Grok | | Map capabilities to graycode-eco repos | Collapse engines into rho monorepo | -| Wire existing modes/budgets first | Rebuild eyrie/harrier/shrike as Grok clones | +| Wire existing modes/budgets first | Rebuild flux/harrier/shrike as Grok clones | | Privacy-first telemetry (OTEL opt-in) | Port Mixpanel defaults | ## Pack status diff --git a/docs/plans/codex-adoption-plan.md b/docs/plans/codex-adoption-plan.md index b1f666c6..a3282048 100644 --- a/docs/plans/codex-adoption-plan.md +++ b/docs/plans/codex-adoption-plan.md @@ -31,8 +31,8 @@ Three codex ideas are deliberately deferred as future RFCs; see | `app-server-daemon`, `app-server-protocol` (JSON-RPC for IDE/desktop) | `internal/daemon` HTTP/SSE on 4590 + `internal/acp` | Keep rho | | `mcp-server`, `codex-mcp`, `rmcp-client`, `connectors` | `internal/mcp` client+server, sibling `falcon` scaffolding | Keep rho | | `skills`, `plugin`, `hooks` | community skill registry + structural validator, plugins, expanded lifecycle hook events | Keep rho | -| `login`, `keyring-store`, `aws-auth` | eyrie credential store in OS keychain across 28 providers | Keep rho (broader) | -| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `eyrie` adapters, catalog, cascade routing | Keep rho (much broader) | +| `login`, `keyring-store`, `aws-auth` | flux credential store in OS keychain across 28 providers | Keep rho (broader) | +| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `flux` adapters, catalog, cascade routing | Keep rho (much broader) | | `memories`, `agent-graph-store`, `context-fragments` | sibling `harrier` (Harrier) graph memory; eventlog/graphjournal projections | Keep rho | | `apply-patch`, `file-search`, `file-watcher`, `git-utils` | edit tools, codegraph, git tooling, watcher hooks | Keep rho | | `external-agent-migration` | swift reads Claude Code / Codex / Gemini CLI / OpenCode / Cursor sessions | Parity | diff --git a/docs/plans/commandcodeai-adoption-plan.md b/docs/plans/commandcodeai-adoption-plan.md index 29586625..dcfc1e0c 100644 --- a/docs/plans/commandcodeai-adoption-plan.md +++ b/docs/plans/commandcodeai-adoption-plan.md @@ -43,7 +43,7 @@ adding a second agent runtime: The comparison also found no reason to adopt Kimi Code's two-engine split or replace Rho's stronger Harrier memory, Shrike token controls, Swift replay, Kestrel -review, Merlin auditing, or Eyrie provider runtime. +review, Merlin auditing, or Flux provider runtime. ### 1. Skill metadata interoperability @@ -111,7 +111,7 @@ tool, depth, time, token, and cost budgets. ## Deliberately Not Adopted -- CommandCodeAI provider adapters: Eyrie owns provider protocols and routing. +- CommandCodeAI provider adapters: Flux owns provider protocols and routing. - BaseAI remote pipes: incompatible with Rho's local authority and durable event model. - BaseAI `lowdb` JSON memory: weaker than Harrier and Rho persistence. @@ -128,5 +128,5 @@ tool, depth, time, token, and cost budgets. 4. Repeat the focused and full checks independently. 5. Merlin the final diff, worktree, and sibling-repository status. -The Eyrie repository remains a separate repository change and must be published +The Flux repository remains a separate repository change and must be published through its own feature branch and PR before updating Rho's module pin. diff --git a/docs/plans/competitive-gap-05-backend-wiring.md b/docs/plans/competitive-gap-05-backend-wiring.md index 04f5b2be..177ea64c 100644 --- a/docs/plans/competitive-gap-05-backend-wiring.md +++ b/docs/plans/competitive-gap-05-backend-wiring.md @@ -5,8 +5,8 @@ Source: field comparison vs Qwen `computer_use`, Codex browser/screenshot, Goose Constraints (non-negotiable): -- Provider ownership lives in `../eyrie`; rho consumes only the stable engine facade (`AGENTS.md`, `docs/SECURITY-DEVELOPER.md:51-56`). -- Boundary guards must stay green: `make boundaries` (incl. `eyrie-client-guard`, `eyrie-engine-guard`). +- Provider ownership lives in `../flux`; rho consumes only the stable engine facade (`AGENTS.md`, `docs/SECURITY-DEVELOPER.md:51-56`). +- Boundary guards must stay green: `make boundaries` (incl. `flux-client-guard`, `flux-engine-guard`). - Tools fail safe today with explicit errors when unwired — preserve that behavior when disabled. ## Existing rho capabilities (verified) @@ -20,7 +20,7 @@ Constraints (non-negotiable): Adopt: opt-in host wiring through the router facade, env-gated, off by default. -Do not adopt: direct `eyrie/client` production imports, new secrets paths, always-on media/computer-use. +Do not adopt: direct `flux/client` production imports, new secrets paths, always-on media/computer-use. ## Priority model @@ -30,7 +30,7 @@ Do not adopt: direct `eyrie/client` production imports, new secrets paths, alway ## Steps -1. Confirm facade methods exist in sibling `../eyrie/engine`; if missing, file the change there first (router repo owns providers). +1. Confirm facade methods exist in sibling `../flux/engine`; if missing, file the change there first (router repo owns providers). 2. Implement wiring in rho behind env gates; keep `Set*` seams for tests. 3. Run `make boundaries` + `make vet` + targeted `go test ./internal/tool/ -run 'TestComputer|TestMedia'`. diff --git a/docs/plans/dsh-harness-port-plan.md b/docs/plans/dsh-harness-port-plan.md index 8e8b868c..9c4dba9b 100644 --- a/docs/plans/dsh-harness-port-plan.md +++ b/docs/plans/dsh-harness-port-plan.md @@ -234,7 +234,7 @@ surface operations, ignorable markers, format version enforcement, and session header parity. The port is functionally complete. Remaining optional depth: - ~~DSH `surface.ts` `SurfaceManager`/`SurfaceReplacePlan` (live correction protocol — replacement operations).~~ Delivered: `internal/eventlog/surface.go` — `FoldSurface` (complete replay → surface nodes + replacement history) and an incremental `SurfaceManager` (bound to a `*Log`, with `Nodes`/`ReplaceGeneration`/`ValidateNext` atomic pre-flight), mirroring DSH's surface provenance, replacement-range, contiguity, and tool-result-rewrite invariants. - ~~Zstd compression in persistence (DSH `session-persistence-jsonl/src/zstd.ts`).~~ Delivered: `internal/eventlog/zstdz/zstd.go` + `internal/session/session.go` (`.jsonl.zstd` saves) — see Phase 9. -- DSH `packages/llm/llm/src/assembler.ts` (response normalization — covered by Eyrie facade). +- DSH `packages/llm/llm/src/assembler.ts` (response normalization — covered by Flux facade). This matrix is the honest anchor: the skeleton is ported; the deep fidelity is the actual remaining work. diff --git a/docs/plans/fix-critical-and-high-review.md b/docs/plans/fix-critical-and-high-review.md index a4aa5e82..ac38c3c8 100644 --- a/docs/plans/fix-critical-and-high-review.md +++ b/docs/plans/fix-critical-and-high-review.md @@ -77,8 +77,8 @@ These were documented in the original plan as out-of-scope-for-this-PR. They rem ## Context -A deep code review of `eyrie` and `rho` (companion plan at -`../eyrie/docs/plans/fix-critical-and-high-review.md`) surfaced 7 critical +A deep code review of `flux` and `rho` (companion plan at +`../flux/docs/plans/fix-critical-and-high-review.md`) surfaced 7 critical and 9 high items. This plan covers **all rho items** (C3, C4, C5, H5, H6, H7, H8, H9) broken into a sequence of small, reviewable PRs. @@ -97,7 +97,7 @@ H6, H7, H8, H9) broken into a sequence of small, reviewable PRs. ## Out of scope (deferred to next plan) -- H10 from eyrie: `//nolint:errcheck` on type-assertion that can panic. +- H10 from flux: `//nolint:errcheck` on type-assertion that can panic. - M1–M20 medium items. - L-tier quick wins (e.g. `cosmenticFlags` typo, `cmd/.rho/` leaked state). - `internal/intelligence/repomap/` documentation (large, separate effort). @@ -492,10 +492,10 @@ Coverage target: maintained at 60%+ (CI gate). ## Cross-repo coordination -- **eyrie PR 4 (C2 — Vertex fix)** and **rho PR 4 (H6 — Session +- **flux PR 4 (C2 — Vertex fix)** and **rho PR 4 (H6 — Session decomposition)** are independent. -- **eyrie PR 8 (H4 — EyrieError)** is a prerequisite for any future - rho-side `errors.As(err, &eyrieErr)` use (currently none). No +- **flux PR 8 (H4 — FluxError)** is a prerequisite for any future + rho-side `errors.As(err, &fluxErr)` use (currently none). No ordering dependency. - The two repos' branches are independent and can be merged in any order. diff --git a/docs/plans/fx-adoption-plan.md b/docs/plans/fx-adoption-plan.md index 9b57a98f..eeb7129d 100644 --- a/docs/plans/fx-adoption-plan.md +++ b/docs/plans/fx-adoption-plan.md @@ -45,7 +45,7 @@ features: | ACP | `internal/acp` | Extend status/config parity where useful | | Swift and replay | sibling `swift` (Swift), `internal/session/replay` | Add terminal-level tape capability | | Persistent memory | sibling `harrier` (Harrier) | Do not replace with flat JSON state | -| Provider runtime | sibling `eyrie` | Do not add provider logic to Rho | +| Provider runtime | sibling `flux` | Do not add provider logic to Rho | ## Priority Model @@ -67,7 +67,7 @@ when their original command, path, or workspace has changed. `internal/engine/safety`. - Shared contract changes, if needed: sibling `eagle/policy`. - User-facing commands: `cmd` and the existing slash-command surface. -- No changes to sibling `eyrie` or `falcon`. +- No changes to sibling `flux` or `falcon`. ### Required behavior @@ -422,7 +422,7 @@ rho verify For sibling-repository changes, run that repository's own tests and boundary checks before updating the Rho pointer. Do not modify provider protocols or adapters in Rho; -those belong in the Eyrie repository. +those belong in the Flux repository. ## Delivery Sequence diff --git a/docs/plans/goose-adoption-plan.md b/docs/plans/goose-adoption-plan.md index f0959f35..1c876e9f 100644 --- a/docs/plans/goose-adoption-plan.md +++ b/docs/plans/goose-adoption-plan.md @@ -8,7 +8,7 @@ Linux Foundation / Agentic AI Foundation) ## Executive Decision Goose's audit against rho found that most of its runtime concepts already have -a native rho implementation (providers via eyrie, sessions, MCP, ACP, skills, +a native rho implementation (providers via flux, sessions, MCP, ACP, skills, native sandboxing, permissions). The genuinely novel, rho-relevant ideas are adopted here in Go, without copying Rust code or weakening rho's native sandboxing model. @@ -17,7 +17,7 @@ sandboxing model. | Goose package/concept | Rho implementation | Decision | |---|---|---| -| Provider abstraction (~36) | sibling `eyrie` (28 built-in + 75+ live) | Keep rho | +| Provider abstraction (~36) | sibling `flux` (28 built-in + 75+ live) | Keep rho | | Sessions (SQLite WAL) | `internal/session` JSONL+WAL+zstd + sibling `swift` (Swift) | Keep rho | | MCP (client+server) | `internal/mcp` + sibling `falcon` | Keep rho | | ACP | `internal/acp` | Keep rho | @@ -50,7 +50,7 @@ or MCP stdio processes by filtering dangerous environment overrides checker) — a `sanitizeEnv` / `SafeEnv` helper. - Consumers: `internal/mcp` stdio launch path and `internal/plugin` package execution. -- No changes to sibling `eyrie`. +- No changes to sibling `flux`. ### Required behavior @@ -147,7 +147,7 @@ accounting, matching `goose-context-management`. change and is tracked separately. - MCP Apps / agent-provided HTML UIs: novel but requires UI-layer design. - ACP-as-provider wrapping other CLIs: larger provider abstraction change. -- Local-inference tool emulation / toolshim: depends on eyrie's local-model +- Local-inference tool emulation / toolshim: depends on flux's local-model path. - Recipe security scanner / cron recipes: rho already has schedule/cron. diff --git a/docs/plans/minimax-adoption-plan.md b/docs/plans/minimax-adoption-plan.md index 09bbb062..0dc41b40 100644 --- a/docs/plans/minimax-adoption-plan.md +++ b/docs/plans/minimax-adoption-plan.md @@ -3,7 +3,7 @@ Status: Implemented in working tree; sibling-repository PRs and skills curation remain follow-ups Date: 2026-08-21 Scope: Adopt the high-value, verified concepts from MiniMax-AI's open-source -repos into the Rho ecosystem (Rho plus independent repositories: Eyrie, Eagle, +repos into the Rho ecosystem (Rho plus independent repositories: Flux, Eagle, Falcon, Kestrel, and Merlin). ## Findings summary @@ -14,27 +14,27 @@ internals produced these adoptions, ordered by value: | # | MiniMax repo | Adopt into | Action | |---|---|---|---| -| 1 | MiniMax-Provider-Verifier | sibling `eyrie` | Add provider-conformance metrics + wire the orphaned `verify` harness into CI | +| 1 | MiniMax-Provider-Verifier | sibling `flux` | Add provider-conformance metrics + wire the orphaned `verify` harness into CI | | 2 | MiniMax/skills | `starling` | Curate high-quality skill content (content, not mechanism) | | 3 | MiniMax-Coding-Plan-MCP | rho / `falcon` | MCP v2 no-network test pattern + image-source normalization (patterns only) | | 4 | minimax_search | rho tooling | Jina page→Markdown browse extractor + evidence-extraction prompt (pattern) | | 5 | Mini-Agent | rho engine | Cooperative-cancellation-with-cleanup + token-aware lossy summarization (patterns) | | 6 | MiniMax-MCP / JS | — | Not adoptable (media generation, out of scope) | -## 1. Eyrie provider-conformance metrics + CI wiring (highest value) +## 1. Flux provider-conformance metrics + CI wiring (highest value) -### Verified current state (eyrie repository) +### Verified current state (flux repository) -- `eyrie/verify/verify.go` — a complete behavioral conformance harness +- `flux/verify/verify.go` — a complete behavioral conformance harness exists: `Case`/`CaseResult`/`Expectation`/`Report`, `Run()`, `scoreResponse()`, `ToolCallF1()`, `DiffBaseline()`, `Markdown()`. -- `eyrie/verify/cases.go` — `CanonicalCases()` with 3 cases: +- `flux/verify/cases.go` — `CanonicalCases()` with 3 cases: `basic-chat`, `deterministic-answer`, `tool-call`. -- `eyrie/verify/metrics.go` — `ToolCallF1()` already implemented. +- `flux/verify/metrics.go` — `ToolCallF1()` already implemented. - **Confirmed gap:** the `verify` package is referenced ONLY by its own tests. It is not wired into any CLI, Makefile target, CI job, or provider-registration gate. Only structural registry/parity tests are enforced. -- eyrie already ships MiniMax providers (`minimax_token_plan`, `minimax_payg`) +- flux already ships MiniMax providers (`minimax_token_plan`, `minimax_payg`) as OpenAI-compatible adapters with `ThinkingFormat: "minimax"`. - `client/structured.go` already has `ValidateStructuredOutput()` — a recursive JSON-schema validator (`validateValue`, `validateObject`, `validateArray`) @@ -43,7 +43,7 @@ internals produced these adoptions, ordered by value: ### What to implement **A. Add a `SchemaValidate` helper (argument-level) — new file -`eyrie/verify/schema.go`** +`flux/verify/schema.go`** Add `SchemaValidate(args map[string]any, schema map[string]any) error` that checks tool-call arguments against the tool's `Parameters` JSON schema: @@ -52,7 +52,7 @@ checks tool-call arguments against the tool's `Parameters` JSON schema: This is the `ToolCalls-Schema-Accuracy` dimension from MiniMax-Provider-Verifier. -**B. Add `MatchRate` to `Report` — edit `eyrie/verify/verify.go`** +**B. Add `MatchRate` to `Report` — edit `flux/verify/verify.go`** Compute the `ToolCalls-Match-Rate` (trigger-vs-stop correctness) alongside F1. Extend `CaseResult` already has `ExpectedTool`/`CalledAnyTool`/`CorrectTool`, so @@ -63,9 +63,9 @@ tool_calls_match_rate = (TP + TN) / expected_tool_call_total_count mirroring MiniMax's confusion-matrix metric. **C. Add a `verify` CLI entrypoint — new file -`eyrie/cmd/verify/main.go`** +`flux/cmd/verify/main.go`** -A small `go run`-able binary (or a `verify` subcommand if eyrie has a cmd/) that +A small `go run`-able binary (or a `verify` subcommand if flux has a cmd/) that runs `verify.Run` against a provider endpoint (live or cassette) and exits non-zero on unmet thresholds: - `--model`, `--base-url`, `--api-key`, `--provider` @@ -74,8 +74,8 @@ non-zero on unmet thresholds: This makes the orphaned harness operational and enables CI gating. -**D. Add a Makefile target + CI job — edit `eyrie/Makefile` and -`eyrie/.github/workflows/ci.yml`** +**D. Add a Makefile target + CI job — edit `flux/Makefile` and +`flux/.github/workflows/ci.yml`** - Makefile: `verify` target that runs the harness in cassette mode (no tokens) and `verify-live` for manual live runs. @@ -83,13 +83,13 @@ This makes the orphaned harness operational and enables CI gating. on PRs, ensuring the harness is exercised and providers don't regress. ### Files -- `eyrie/verify/schema.go` (new) -- `eyrie/verify/schema_test.go` (new) -- `eyrie/verify/verify.go` (add MatchRate) -- `eyrie/verify/verify_test.go` (add tests) -- `eyrie/cmd/verify/main.go` (new) -- `eyrie/Makefile` (verify target) -- `eyrie/.github/workflows/ci.yml` (verify job) +- `flux/verify/schema.go` (new) +- `flux/verify/schema_test.go` (new) +- `flux/verify/verify.go` (add MatchRate) +- `flux/verify/verify_test.go` (add tests) +- `flux/cmd/verify/main.go` (new) +- `flux/Makefile` (verify target) +- `flux/.github/workflows/ci.yml` (verify job) ## 2. Curate MiniMax/skills content into starling @@ -181,8 +181,8 @@ This makes the orphaned harness operational and enables CI gating. - The `verify` harness's live-token path: optional, off by default. ## Execution order -1. Eyrie verify metrics + schema validation + tests (highest value) -2. Eyrie verify CLI + Makefile + CI wiring +1. Flux verify metrics + schema validation + tests (highest value) +2. Flux verify CLI + Makefile + CI wiring 3. rho image-source normalization helper + tests 4. rho Jina browse extractor + tests 5. Agent-loop cancellation/summarization patterns @@ -190,12 +190,12 @@ This makes the orphaned harness operational and enables CI gating. ## Implementation status -- [x] Eyrie schema-accuracy validation and tool-call match-rate metrics. -- [x] Eyrie verification CLI, deterministic Makefile target, and CI job. +- [x] Flux schema-accuracy validation and tool-call match-rate metrics. +- [x] Flux verification CLI, deterministic Makefile target, and CI job. - [x] Rho image-source normalization for data URIs, URLs, local files, and raw base64. - [x] Rho Jina Reader page-to-Markdown client with opt-in configuration and tests. - [x] Rho cancellation cleanup for incomplete assistant tool-use/tool-result turns. - [x] Confirmed rho's existing `internal/engine/compact` already provides token-triggered compaction; no duplicate summarizer was added. -- [ ] Publish the Eyrie repository changes through its own feature branch and PR. +- [ ] Publish the Flux repository changes through its own feature branch and PR. - [ ] Curate MiniMax skills into `starling` through its own feature branch and PR. diff --git a/docs/plans/pi-adoption-plan.md b/docs/plans/pi-adoption-plan.md index 145b4caa..471c616b 100644 --- a/docs/plans/pi-adoption-plan.md +++ b/docs/plans/pi-adoption-plan.md @@ -34,14 +34,14 @@ The audit found that rho already provides the foundation for most Pi features: | Pi package | Rho implementation | Current decision | |---|---|---| -| `pi-ai` (multi-provider) | sibling `eyrie` client + engine facade + catalog + router + credentials | Keep rho (broader) | +| `pi-ai` (multi-provider) | sibling `flux` client + engine facade + catalog + router + credentials | Keep rho (broader) | | `pi-agent-core` (agent loop) | `internal/engine` | Keep rho | | `pi-coding-agent` (CLI) | `cmd` TUI + CLI + daemon | Keep rho | | `pi-session-backends` (storage) | `internal/session` JSONL + zstd + WAL + SQLite index, sibling `swift` (Swift) | Keep rho | | `pi-server` (RPC) | `internal/daemon` + `internal/acp` + `internal/mcp` | Keep rho (broader) | | Permissions/sandbox | `internal/engine/safety` + `internal/sandbox` (seatbelt/landlock/seccomp/ACL/netproxy) | Keep rho (native, ahead) | | `pi-tui` differential rendering | Bubble Tea v2 full-frame redraw | Adopt line-diff engine | -| `pi-telemetry` conformance | `docs/OTEL-CONVENTIONS.md`, eyrie `genai_semconv` pinning | Adopt conformance suite | +| `pi-telemetry` conformance | `docs/OTEL-CONVENTIONS.md`, flux `genai_semconv` pinning | Adopt conformance suite | | `pi-evals` agent-level eval | `internal/feature/eval` (model benchmark only) | Adopt agent-runtime eval | ## Priority Model @@ -63,10 +63,10 @@ ecosystem repositories. ### Scope and ownership - Primary implementation: `internal/observability` and - `eyrie/internal/observability` (the canonical `genai_semconv` + `flux/internal/observability` (the canonical `genai_semconv` constants and their pinning test). - Shared schema constants: sibling `eagle` if a cross-repo - contract is needed, otherwise keep them in eyrie as today. + contract is needed, otherwise keep them in flux as today. - No changes to `internal/mcp`, `internal/sandbox`, or `internal/daemon`. ### Required behavior @@ -82,7 +82,7 @@ ecosystem repositories. - asserts sensitive attributes never carry raw prompt/response text, - asserts parent/child span relationships and settlement semantics, - is runner-independent (usable from any Go test harness). -4. Wire the conformance harness into the rho and eyrie test suites so CI +4. Wire the conformance harness into the rho and flux test suites so CI enforces the contract. 5. Keep the conformance layer passive and non-throwing: malformed or unreadable telemetry payloads must not break agent execution. @@ -93,7 +93,7 @@ ecosystem repositories. - A deliberate schema drift (adding a span or attribute) fails the conformance test until the schema is updated. - No raw prompt/response text appears in recorded attributes. -- The conformance harness runs green in rho and eyrie CI. +- The conformance harness runs green in rho and flux CI. ## P0: Agent-Runtime Eval Harness @@ -273,7 +273,7 @@ Every implementation phase must preserve: - Eval runs the real tool loop end-to-end in an isolated dir. - Daemon and ACP parity for leased sessions. - fxtape recording/replay across the new renderer. -- Cross-repo telemetry conformance in rho and eyrie CI. +- Cross-repo telemetry conformance in rho and flux CI. ### Security tests @@ -310,7 +310,7 @@ updating the Rho pointer. - [x] Define the typed span/attribute schema. - [x] Implement the conformance harness. -- [x] Wire rho and eyrie CI. +- [x] Wire rho and flux CI. - [x] Add schema-drift regression tests. ### Milestone 2: Agent-runtime eval (P0) diff --git a/docs/plans/qwen-code-adoption-plan.md b/docs/plans/qwen-code-adoption-plan.md index 35452b04..0f41d70e 100644 --- a/docs/plans/qwen-code-adoption-plan.md +++ b/docs/plans/qwen-code-adoption-plan.md @@ -5,14 +5,14 @@ Status: Implemented selectively in the current Rho feature branch. ## Guardrails Qwen Code is Apache-2.0 TypeScript software with a Gemini-shaped core. Rho -will independently reimplement behavioral contracts in Go, preserve the Eyrie +will independently reimplement behavioral contracts in Go, preserve the Flux provider boundary, and retain Rho's event-sourced sessions and OS sandbox. No Qwen source or dependency is vendored. ## Existing Rho Capabilities Rho already has durable sessions, event logging, WAL/recovery, branching, -review contracts, provider routing in Eyrie, MCP integration, skills, memory, +review contracts, provider routing in Flux, MCP integration, skills, memory, context compaction, policy snapshots, subagents, daemon security, and filesystem/process sandboxing. These systems will not be duplicated. diff --git a/docs/plans/rho-contracts-migration-backlog.md b/docs/plans/rho-contracts-migration-backlog.md index 6889ad86..34cc8ff6 100644 --- a/docs/plans/rho-contracts-migration-backlog.md +++ b/docs/plans/rho-contracts-migration-backlog.md @@ -39,7 +39,7 @@ These items are already completed in the current workspace. cmd/session restore paths - replaced the provider-owned message alias with a Rho-owned runtime DTO - added explicit Rho-owned runtime DTOs; the final boundary translates them - through `eyrie/engine` + through `flux/engine` ### Event contract migration - added `eagle/events` @@ -56,14 +56,14 @@ These items are already completed in the current workspace. ### Governance - added `check-shared-types-imports.sh` - added `check-ecosystem-boundaries.sh` -- added `check-eyrie-client-imports.sh` +- added `check-flux-client-imports.sh` - wired both guards into `Makefile` and CI -- wired the Eyrie engine-boundary guards into `Makefile` and CI +- wired the Flux engine-boundary guards into `Makefile` and CI - added a legacy import guard so the removed `shared/types` path cannot return - extended the ecosystem boundary guard to scan sibling engine repos when present locally - updated docs across Rho, kestrel, merlin, and external workspace copies - added standalone boundary guards in `kestrel` and `merlin` -- added standalone boundary guards in `shrike`, `eyrie`, `harrier`, and `swift` +- added standalone boundary guards in `shrike`, `flux`, `harrier`, and `swift` - updated support repo READMEs with ecosystem boundary rules ### Review and verification contract migration @@ -96,21 +96,21 @@ Local state: Still external: - confirm released module versions used by Rho match the merged contract changes -### 3. Remove Rho production dependency on lower Eyrie packages +### 3. Remove Rho production dependency on lower Flux packages Current state: - session persistence uses neutral tool contracts -- Rho now owns the runtime message DTO in `internal/types.EyrieMessage` +- Rho now owns the runtime message DTO in `internal/types.FluxMessage` - Rho now owns runtime tool call/result DTOs in `internal/types` - Rho now owns runtime response/usage/stream DTOs in `internal/types` - Rho now owns runtime chat options, response format, tool choice, continuation config, and tool definition DTOs in `internal/types` - Rho now owns the transport-provider seam in `internal/types.ChatProvider` - Rho session, review, setup, catalog, diagnostics, and custom-provider paths - now enter through `eyrie/engine` -- production imports of every lower Eyrie package are zero + now enter through `flux/engine` +- production imports of every lower Flux package are zero - cmd/session restore paths now go through centralized `session.ToRuntimeMessages` and `session.FromRuntimeMessages` Decision: -- completed: Rho owns the product DTO/port seam and Eyrie owns the engine, +- completed: Rho owns the product DTO/port seam and Flux owns the engine, provider, credential, catalog, routing, resilience, and normalization layers - keep the zero-exception boundary enforced; lower packages are test-fixture-only @@ -178,7 +178,7 @@ Do not do these without a separate decision: - completed: moved provider/config interfaces behind Rho-owned transport adapters ### PR 3 -- completed: removed all lower-level Eyrie imports from Rho production code and +- completed: removed all lower-level Flux imports from Rho production code and replaced the compatibility allowlist with a zero-exception guard ### PR 4 diff --git a/docs/plans/tau-feature-parity.md b/docs/plans/tau-feature-parity.md index 30d4d320..e7e919d8 100644 --- a/docs/plans/tau-feature-parity.md +++ b/docs/plans/tau-feature-parity.md @@ -38,7 +38,7 @@ Tau offers a documented feature Rho does not. 3. **`transcript` output format** — add to `--output-format`; render the turn as a plain, timestamped transcript. 4. **User catalog `~/.rho/catalog.toml`** — let users add providers/models - without code changes, merged over the eyrie catalog. + without code changes, merged over the flux catalog. 5. **`.agents/` instruction resources** — load `AGENTS.md`-style instructions from `.agents/` in addition to the existing skill/provider/recipe dirs. diff --git a/docs/platform-capabilities.json b/docs/platform-capabilities.json index 792cc49b..df481c4f 100644 --- a/docs/platform-capabilities.json +++ b/docs/platform-capabilities.json @@ -136,6 +136,6 @@ ] }, "ecosystem_compatibility": { - "rho+eyrie": "full" + "rho+flux": "full" } } diff --git a/docs/session-decomposition.md b/docs/session-decomposition.md index 9be9d48c..a9ca457b 100644 --- a/docs/session-decomposition.md +++ b/docs/session-decomposition.md @@ -84,8 +84,8 @@ backends without an explicit migration and recovery decision. **Owns:** `client ChatClient`, `provider string`, `model string`, `apiKeys map[string]string`, `Router *modelPkg.Router`, `DeploymentRouting bool`, `RateLimiter *ratelimit.Limiter`, `OutputSchema string`, `GLMThinkingEnabled *bool`, `ContCfg types.ContinuationConfig`, `ChatOptions{}` builder. **Methods:** -- `BuildOptions(systemPrompt string, tools []client.EyrieTool, activeModel string, maxTokens int) types.ChatOptions` -- `Chat(ctx, msgs, opts) (*types.EyrieResponse, error)` — non-streaming, used by background goroutines +- `BuildOptions(systemPrompt string, tools []client.FluxTool, activeModel string, maxTokens int) types.ChatOptions` +- `Chat(ctx, msgs, opts) (*types.FluxResponse, error)` — non-streaming, used by background goroutines - `Stream(ctx, msgs, opts) (*types.StreamResult, error)` — with retry, circuit-breaker record, rate-limit wait - `RecordSuccess(latency time.Duration)` / `RecordFailure(err error)` - `BuildContinuationConfig() types.ContinuationConfig` @@ -99,7 +99,7 @@ backends without an explicit migration and recovery decision. **Methods:** - `RecallContext(ctx, lastUserMsg string, budget int) string` — unifies backend recall behind one nil-safe call - `Remember(ctx, content, category string)` — wraps memory.Remember -- `RunSleeptimeConsolidation(ctx, provider ChatService, messages []types.EyrieMessage)` — background +- `RunSleeptimeConsolidation(ctx, provider ChatService, messages []types.FluxMessage)` — background - `RunSkillDistillation(ctx, provider ChatService, ...)` — background - `RecordFeedback(ctx, content string, action string)` — wraps EnhancedMemory feedback - `ShouldRemember(text string) bool` — heuristic for auto-remember @@ -139,14 +139,14 @@ backends without an explicit migration and recovery decision. **Methods:** - `OnSessionStart(ctx, lastUserMsg string) (learnedCtx string)` -- `OnSessionEnd(ctx, success bool, duration time.Duration, messages []types.EyrieMessage)` +- `OnSessionEnd(ctx, success bool, duration time.Duration, messages []types.FluxMessage)` - `SelectModel(taskType, currentModel, hint string) string` — wraps Cascade -- `OnToolFailure(ctx, intent string, history []types.EyrieMessage, errOutput string) (*Reflection, error)` -- `RecordDecision(turn int, toolNames string, msgs []types.EyrieMessage)` +- `OnToolFailure(ctx, intent string, history []types.FluxMessage, errOutput string) (*Reflection, error)` +- `RecordDecision(turn int, toolNames string, msgs []types.FluxMessage)` - `CheckLimits(turnCount int) (cont bool, reason string)` — wraps Limits - `RecordTurn(turn int)`, `RecordToolCall(name string)` - `DetectDoomLoop(steps []ToolStep) bool` -- `InjectSteering(messages []types.EyrieMessage) []types.EyrieMessage` +- `InjectSteering(messages []types.FluxMessage) []types.FluxMessage` **File:** `rho/internal/engine/lifecycle_service.go` (~250 LOC) @@ -161,7 +161,7 @@ backends without an explicit migration and recovery decision. - `AddUser(content string)`, `AddAssistant(content string)`, `AddUserWithImage(...)`, `AddUserWithAttachment(...)` - `AddUserWithDocumentText(content, label, extracted string)` - `AppendSystemContext(content string)`, `ReplaceSystemContextSection(header, content string)` -- `Messages() []types.EyrieMessage`, `RawMessages()`, `MessageCount()`, `LoadMessages([]types.EyrieMessage)` +- `Messages() []types.FluxMessage`, `RawMessages()`, `MessageCount()`, `LoadMessages([]types.FluxMessage)` - `RemoveLastExchange()` - `ConvoHead() string`, `ForkConversation(nodeID string) (string, error)`, `SwitchBranch(nodeID string) error`, `ListBranches(nodeID string) ([]*storage.DAGNode, error)` - `CheckpointOnCompaction(strategy string, before, after int, manual bool)` @@ -222,7 +222,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { for turn := 0; ; turn++ { if !s.Lifecycle.CheckLimits(turn) { return } - opts := s.Chat.BuildOptions(s.system, s.Tools.Registry().EyrieTools(), activeModel, maxTok) + opts := s.Chat.BuildOptions(s.system, s.Tools.Registry().FluxTools(), activeModel, maxTok) result, err := s.Chat.Stream(ctx, s.Persistence.Messages(), opts) if err != nil { /* error path */ } defer result.Close() @@ -256,7 +256,7 @@ The loop body shrinks from 800 lines to ~250 because: 3. **Phase 3**: extract `MemoryService` (most fields, but only consumed in the preamble/postamble of `agentLoop`). 4. **Phase 4**: extract `PermissionService` (smallest surface). 5. **Phase 5**: extract `LifecycleService` (biggest behavioral surface, but mostly reads `messages` and `tools`). -6. **Phase 6**: extract `PersistenceService` (last because it owns the `messages []types.EyrieMessage` field which is touched everywhere). +6. **Phase 6**: extract `PersistenceService` (last because it owns the `messages []types.FluxMessage` field which is touched everywhere). 7. **Phase 7**: extract `ToolService` (depends on 4, 5; do last). 8. **Phase 8**: delete fields from `Session`, add backwards-compatible shims that delegate to the new services, mark for removal in v0.2.0. diff --git a/docs/troubleshooting-guide.md b/docs/troubleshooting-guide.md index f03aa521..d629aa2c 100644 --- a/docs/troubleshooting-guide.md +++ b/docs/troubleshooting-guide.md @@ -73,7 +73,7 @@ rho daemon start --port 4591 ### `GET /v1/ready` returns 503 -The readiness probe fails when Eyrie's local preflight doesn't pass. This +The readiness probe fails when Flux's local preflight doesn't pass. This checks provider state, catalog, credentials, and model selection. **Diagnosis:** @@ -85,7 +85,7 @@ curl -v http://localhost:4590/v1/ready Check the response body for the specific failed check. Common causes: - No model configured — set `RHO_MODEL` or provider credentials. -- Eyrie catalog not initialized — the Go modules are independent sibling +- Flux catalog not initialized — the Go modules are independent sibling repositories; from a full parent workspace, run `make setup` in rho to regenerate the parent `go.work`: ```bash @@ -198,7 +198,7 @@ mode) or when the engine is not ready (daemon mode). curl http://localhost:4590/v1/ready ``` -If readiness fails, the session factory exists but Eyrie preflight is not +If readiness fails, the session factory exists but Flux preflight is not satisfied (missing model, credentials, etc.). --- diff --git a/docs/user-guide/02-authentication.md b/docs/user-guide/02-authentication.md index 9db643e7..8f36e99a 100644 --- a/docs/user-guide/02-authentication.md +++ b/docs/user-guide/02-authentication.md @@ -1,6 +1,6 @@ # Authentication -Rho supports several authentication methods, including API key configuration through the TUI and multi-provider support via Eyrie. +Rho supports several authentication methods, including API key configuration through the TUI and multi-provider support via Flux. --- @@ -49,7 +49,7 @@ base URL is `https://api.fireworks.ai/inference/v1`. See the official ## Provider Configuration -Rho uses Eyrie for provider routing, health checks, and retry logic. To configure providers: +Rho uses Flux for provider routing, health checks, and retry logic. To configure providers: ```bash # In the TUI, press /config to open provider settings @@ -75,7 +75,7 @@ Or export: export RHO_DEPLOYMENT_ROUTING=true ``` -Rho will route canonical model IDs through Eyrie's deployment catalog. Refresh the catalog with: +Rho will route canonical model IDs through Flux's deployment catalog. Refresh the catalog with: ``` /refresh-model-catalog @@ -185,7 +185,7 @@ Check credential status at any time: rho credentials status ``` -This verifies keychain entries and validates Eyrie's provider status. +This verifies keychain entries and validates Flux's provider status. --- @@ -203,7 +203,7 @@ During a session, the active method handles all refreshes. ## Multi-Provider Support -Rho works with any LLM provider through Eyrie's adapter system: +Rho works with any LLM provider through Flux's adapter system: | Provider | Status | |----------|--------| diff --git a/docs/user-guide/04-slash-commands.md b/docs/user-guide/04-slash-commands.md index 178de2cf..c5877c97 100644 --- a/docs/user-guide/04-slash-commands.md +++ b/docs/user-guide/04-slash-commands.md @@ -317,7 +317,7 @@ Quick ready-to-chat check. ### `/ecosystem` -Show ecosystem component status (Eyrie and the token pipeline). +Show ecosystem component status (Flux and the token pipeline). ``` /ecosystem @@ -413,7 +413,7 @@ Toggle vim-style keybindings. ### `/refresh-model-catalog` -Fetch the latest deployment-aware model catalog from Eyrie. +Fetch the latest deployment-aware model catalog from Flux. ``` /refresh-model-catalog diff --git a/docs/user-guide/11-custom-models.md b/docs/user-guide/11-custom-models.md index aee6b759..1ce0e2f4 100644 --- a/docs/user-guide/11-custom-models.md +++ b/docs/user-guide/11-custom-models.md @@ -1,6 +1,6 @@ # Custom Models -Rho connects to custom model endpoints through Eyrie for alternative providers, self-hosted models, and overriding built-in settings. +Rho connects to custom model endpoints through Flux for alternative providers, self-hosted models, and overriding built-in settings. --- @@ -127,7 +127,7 @@ ollama pull llama-3.1-70b ## Deployment-Aware Routing -Enable deployment-aware routing to use Eyrie's model catalog: +Enable deployment-aware routing to use Flux's model catalog: ```bash export RHO_DEPLOYMENT_ROUTING=true diff --git a/docs/user-guide/23-dashboard.md b/docs/user-guide/23-dashboard.md index ec392b0a..cdd2ae20 100644 --- a/docs/user-guide/23-dashboard.md +++ b/docs/user-guide/23-dashboard.md @@ -9,7 +9,7 @@ The Rho dashboard provides system status and monitoring information. In the TUI: ``` -/ecosystem # Ecosystem status (Eyrie and token pipeline) +/ecosystem # Ecosystem status (Flux and token pipeline) /path # Developer path readiness /preflight # Quick health check ``` @@ -22,7 +22,7 @@ Shows the status of all Rho components: | Component | Status | |-----------|--------| -| Eyrie (providers) | Ready / Error | +| Flux (providers) | Ready / Error | | token pipeline | Available | | memory | Ready | diff --git a/docs/versioning.md b/docs/versioning.md index fe7d944d..f8e6c280 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -38,7 +38,7 @@ repository that follows this layout. Adopted 2026-05-14. the `VERSION` file because release-please bumps both atomically). - This is the Kubernetes / Helm / gh-cli pattern. -### Go libraries (`eyrie`) +### Go libraries (`flux`) - `VERSION` at the repo root. - A `version.go` file co-located with `VERSION` uses `//go:embed` to read it at diff --git a/ecosystem.yaml b/ecosystem.yaml index 9a8146a8..b08fc0b2 100644 --- a/ecosystem.yaml +++ b/ecosystem.yaml @@ -19,14 +19,14 @@ repositories: module: github.com/GrayCodeAI/rho workspace: true - - directory: eyrie - github_repo: eyrie - product_name: Eyrie + - directory: flux + github_repo: flux + product_name: Flux kind: engine language: go - module: github.com/GrayCodeAI/eyrie + module: github.com/GrayCodeAI/flux workspace: true - facade: github.com/GrayCodeAI/eyrie/engine + facade: github.com/GrayCodeAI/flux/engine - directory: graycode-skills github_repo: graycode-skills diff --git a/flake.nix b/flake.nix index de0af69e..0e7c2658 100644 --- a/flake.nix +++ b/flake.nix @@ -1,12 +1,12 @@ { - description = "Rho - AI coding agent powered by eyrie"; + description = "Rho - AI coding agent powered by flux"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; # GrayCodeAI sibling repos — the public Go proxy has stale v0.1.0 tags # (post-history-rewrite), so resolve them locally like the Dockerfile. - eyrie = { url = "github:GrayCodeAI/eyrie"; flake = false; }; + flux = { url = "github:GrayCodeAI/flux"; flake = false; }; merlin = { url = "github:GrayCodeAI/merlin"; flake = false; }; kestrel = { url = "github:GrayCodeAI/kestrel"; flake = false; }; shrike = { url = "github:GrayCodeAI/shrike"; flake = false; }; @@ -14,14 +14,14 @@ harrier = { url = "github:GrayCodeAI/harrier"; flake = false; }; }; - outputs = { self, nixpkgs, flake-utils, eyrie, merlin, kestrel, shrike, swift, harrier }: + outputs = { self, nixpkgs, flake-utils, flux, merlin, kestrel, shrike, swift, harrier }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; siblings = { - "github.com/GrayCodeAI/eyrie" = eyrie; + "github.com/GrayCodeAI/flux" = flux; "github.com/GrayCodeAI/merlin" = merlin; "github.com/GrayCodeAI/kestrel" = kestrel; "github.com/GrayCodeAI/shrike" = shrike; diff --git a/go.mod b/go.mod index 71604f46..b40b3277 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( charm.land/bubbles/v2 v2.1.1 charm.land/bubbletea/v2 v2.0.8 charm.land/lipgloss/v2 v2.0.5 - github.com/GrayCodeAI/eyrie v0.6.0 + github.com/GrayCodeAI/flux v0.0.1 github.com/alecthomas/chroma/v2 v2.26.1 github.com/bwmarrin/discordgo v0.28.1 github.com/charmbracelet/x/ansi v0.11.7 diff --git a/go.sum b/go.sum index ded309d4..fc45ea4b 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ charm.land/bubbletea/v2 v2.0.8 h1:SxTJMhCAI3lbPmy4SgX5LWZ24AdINr4I6UEqzZvYJuY= charm.land/bubbletea/v2 v2.0.8/go.mod h1:2SkdgoTXluXJHOUwAoRlRXF/28vklb1rFl6GcgV1/ss= charm.land/lipgloss/v2 v2.0.5 h1:kbNxgeeUOYv5J0YdpxFjfvf3dFvqH8Aci4zB6xqFtrY= charm.land/lipgloss/v2 v2.0.5/go.mod h1:9oqhxt4yxIMe6q5A4kHr44DremZk7J9UNh74GlWa5nc= -github.com/GrayCodeAI/eyrie v0.6.0 h1:RnbfwbspE38lPFwbdleOA3z1SpsjnrM3cSsgEaCIKVM= -github.com/GrayCodeAI/eyrie v0.6.0/go.mod h1:gphUZ6Vcml7zyXfKU+704HZcxtHbJLFqgLDzke/rvFc= +github.com/GrayCodeAI/flux v0.0.1 h1:HGY6SIEUf1kPQUt4am/5kdgc0GmQY3bh9otJagz651Q= +github.com/GrayCodeAI/flux v0.0.1/go.mod h1:NpD0KF8gRaZuPChgpe3tEcwG9zOzwoJPOTZtUEiUz3c= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= diff --git a/internal/auth/auth.go b/internal/auth/auth.go index a74a2a05..02e59131 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -61,7 +61,7 @@ func (s *SecureStorage) Get(account string) (string, error) { if runtime.GOOS == "windows" { return s.getWindows(account) } - // Fallback to file-based storage for Linux (keyring handled by eyrie layer) + // Fallback to file-based storage for Linux (keyring handled by flux layer) return s.getFile(account) } diff --git a/internal/catalogtest/install.go b/internal/catalogtest/install.go index 5de29a6b..d2ead883 100644 --- a/internal/catalogtest/install.go +++ b/internal/catalogtest/install.go @@ -16,7 +16,7 @@ var ( globalPath string ) -// InstallGlobal writes the test catalog to a temp file and sets EYRIE_MODEL_CATALOG_PATH. +// InstallGlobal writes the test catalog to a temp file and sets FLUX_MODEL_CATALOG_PATH. // Call from TestMain; returns cleanup to unset env. func InstallGlobal() (cleanup func()) { globalOnce.Do(func() { @@ -30,19 +30,19 @@ func InstallGlobal() (cleanup func()) { if err := os.WriteFile(globalPath, minimalCatalogJSON, 0o600); err != nil { panic("catalogtest: failed to write catalog: " + err.Error()) } - _ = os.Setenv("EYRIE_MODEL_CATALOG_PATH", globalPath) + _ = os.Setenv("FLUX_MODEL_CATALOG_PATH", globalPath) }) return func() { - _ = os.Unsetenv("EYRIE_MODEL_CATALOG_PATH") + _ = os.Unsetenv("FLUX_MODEL_CATALOG_PATH") } } -// Install sets EYRIE_MODEL_CATALOG_PATH for a single test (per-test temp file). +// Install sets FLUX_MODEL_CATALOG_PATH for a single test (per-test temp file). func Install(t testing.TB) { t.Helper() path := filepath.Join(t.TempDir(), "model_catalog.json") if err := os.WriteFile(path, minimalCatalogJSON, 0o600); err != nil { t.Fatalf("catalogtest: failed to write catalog: %v", err) } - t.Setenv("EYRIE_MODEL_CATALOG_PATH", path) + t.Setenv("FLUX_MODEL_CATALOG_PATH", path) } diff --git a/internal/codegraph/algorithms_cgo_more.go b/internal/codegraph/algorithms_cgo_more.go index a9e448b4..43079550 100644 --- a/internal/codegraph/algorithms_cgo_more.go +++ b/internal/codegraph/algorithms_cgo_more.go @@ -255,7 +255,7 @@ func (cg *CodeGraph) AnalyzeCoupling(topN int) ([]CouplingMetric, error) { } // CrossRepoQuery queries across multiple codegraph databases. -// Useful for finding relationships between rho, eyrie, and other packages. +// Useful for finding relationships between rho, flux, and other packages. func CrossRepoQuery(repos []string, query string, limit int) (map[string][]Node, error) { results := make(map[string][]Node) @@ -278,7 +278,7 @@ func CrossRepoQuery(repos []string, query string, limit int) (map[string][]Node, } // CrossRepoImpact finds the impact of changing a symbol across multiple repos. -// If a symbol in rho calls a symbol in eyrie, this traces that cross-repo dependency. +// If a symbol in rho calls a symbol in flux, this traces that cross-repo dependency. func CrossRepoImpact(repos []string, symbol string, maxDepth int) (map[string]*ImpactResult, error) { results := make(map[string]*ImpactResult) @@ -311,7 +311,7 @@ func CrossRepoImpact(repos []string, symbol string, maxDepth int) (map[string]*I } // FindCrossRepoCalls finds function calls that cross repo boundaries. -// For example, rho calling eyrie functions. +// For example, rho calling flux functions. func FindCrossRepoCalls(repos []string) ([]CrossRepoCall, error) { type repoSymbol struct { repo string diff --git a/internal/config/catalog_api.go b/internal/config/catalog_api.go index 1968c83b..d9428feb 100644 --- a/internal/config/catalog_api.go +++ b/internal/config/catalog_api.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - llm "github.com/GrayCodeAI/eyrie/llm" + llm "github.com/GrayCodeAI/flux/llm" gw "github.com/GrayCodeAI/rho/internal/provider/gateway" ) @@ -22,19 +22,19 @@ type GatewayStatus struct { DNSHost string } -// IsCatalogCacheRequired reports whether an Eyrie operation failed because +// IsCatalogCacheRequired reports whether an Flux operation failed because // the local model catalog has not been created yet. func IsCatalogCacheRequired(err error) bool { return gw.IsCatalogCacheRequired(err) } -// RegisteredProviderCount returns Eyrie's canonical first-class provider count. +// RegisteredProviderCount returns Flux's canonical first-class provider count. func RegisteredProviderCount() int { return gw.RegisteredProviderCount() } func AllCatalogProviders() []string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil } @@ -57,7 +57,7 @@ func AllCatalogProviders() []string { } func AllSetupGateways() []string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil } @@ -122,7 +122,7 @@ func GatewaySupportsLiveDiscovery(providerID string) bool { } func ActiveGateway(ctx context.Context) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } @@ -137,7 +137,7 @@ func ActiveGateway(ctx context.Context) string { } func GatewayStatuses(ctx context.Context, activeProvider, activeModel string) []GatewayStatus { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil } @@ -163,7 +163,7 @@ func GatewayStatuses(ctx context.Context, activeProvider, activeModel string) [] } func GatewayForModel(modelID string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } @@ -171,7 +171,7 @@ func GatewayForModel(modelID string) string { } func ShouldClearSelectionAfterCredentialRemove(ctx context.Context, removedProvider string) bool { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return true } @@ -183,7 +183,7 @@ func ShouldClearSelectionAfterCredentialRemove(ctx context.Context, removedProvi } func ClearActiveSelection(ctx context.Context) error { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return err } @@ -191,7 +191,7 @@ func ClearActiveSelection(ctx context.Context) error { } func SyncSelectionWithCredentials(ctx context.Context) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return } @@ -213,7 +213,7 @@ func SyncSelectionWithCredentials(ctx context.Context) { } func DefaultModelForProvider(provider string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } @@ -221,7 +221,7 @@ func DefaultModelForProvider(provider string) string { } func DefaultModelForProviderWithSettings(settings Settings, provider string) string { - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { return "" } @@ -248,7 +248,7 @@ func ModelIDsForProvider(provider string) ([]string, error) { } func CheapestModelForProvider(provider, fallback string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return fallback } @@ -256,7 +256,7 @@ func CheapestModelForProvider(provider, fallback string) string { } func ProviderOfModel(modelName string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } @@ -264,7 +264,7 @@ func ProviderOfModel(modelName string) string { } func ProviderOfModelWithSettings(settings Settings, modelName string) string { - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { return "" } @@ -272,7 +272,7 @@ func ProviderOfModelWithSettings(settings Settings, modelName string) string { } func ExampleModelHints() (string, string) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "an Anthropic model", "an OpenAI model" } @@ -281,7 +281,7 @@ func ExampleModelHints() (string, string) { } func AllCanonicalModelIDs() []string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil } @@ -298,7 +298,7 @@ func AllCanonicalModelIDs() []string { } func ProviderIDForDeployment(deploymentID string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } @@ -311,7 +311,7 @@ func ProviderIDForDeployment(deploymentID string) string { } func PrimaryAPIKeyEnvForDeployment(deploymentID string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } @@ -324,7 +324,7 @@ func PrimaryAPIKeyEnvForDeployment(deploymentID string) string { } func engineGateway(providerID string) (gw.GatewayDefs, bool) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return gw.GatewayDefs{}, false } diff --git a/internal/config/catalog_compat_test.go b/internal/config/catalog_compat_test.go index cd75b2df..44e83330 100644 --- a/internal/config/catalog_compat_test.go +++ b/internal/config/catalog_compat_test.go @@ -4,8 +4,8 @@ import ( "context" "strings" - "github.com/GrayCodeAI/eyrie/catalog" - eyrieconfig "github.com/GrayCodeAI/eyrie/config" + "github.com/GrayCodeAI/flux/catalog" + fluxconfig "github.com/GrayCodeAI/flux/config" ) // CompiledCatalogV1 is retained only for lower-level migration/security tests. @@ -20,7 +20,7 @@ func CompiledCatalogV1() *catalog.CompiledCatalog { return compiled } -func deploymentHasSecrets(deployment eyrieconfig.DeploymentConfig) bool { +func deploymentHasSecrets(deployment fluxconfig.DeploymentConfig) bool { return strings.TrimSpace(deployment.APIKey) != "" || strings.TrimSpace(deployment.Token) != "" || strings.TrimSpace(deployment.SecretAccessKey) != "" || strings.TrimSpace(deployment.AccessKeyID) != "" || strings.TrimSpace(deployment.SessionToken) != "" diff --git a/internal/config/catalog_gateways_test.go b/internal/config/catalog_gateways_test.go index 465e43d6..75686a4e 100644 --- a/internal/config/catalog_gateways_test.go +++ b/internal/config/catalog_gateways_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - "github.com/GrayCodeAI/eyrie/catalog" - "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/flux/catalog" + "github.com/GrayCodeAI/flux/credentials" "github.com/GrayCodeAI/rho/internal/catalogtest" ) @@ -30,7 +30,7 @@ func TestAllSetupGateways_RegistryOnly(t *testing.T) { t.Fatalf("owner slug %q should not be a gateway", id) } } - // Required gateways that exist in the published eyrie v0.1.3. + // Required gateways that exist in the published flux v0.1.3. want := map[string]bool{"azure": true, "bedrock": true, "gemini": true, "grok": true, "openrouter": true, "kimi": true, "vertex": true, "xiaomi_mimo_payg": true, "xiaomi_mimo_token_plan": true, "deepseek": true, "minimax_token_plan": true, "minimax_payg": true, "zai_payg": true, "zai_coding": true, "agnes": true} for id := range want { found := false @@ -44,12 +44,12 @@ func TestAllSetupGateways_RegistryOnly(t *testing.T) { t.Fatalf("missing setup gateway %q in %v", id, gws) } } - // Newly-added gateways (groq, poolside) present in the local Eyrie checkout - // but not yet in a published eyrie release — log if present, don't + // Newly-added gateways (groq, poolside) present in the local Flux checkout + // but not yet in a published flux release — log if present, don't // require them so the test passes with both GOWORK=on and GOWORK=off. for _, extra := range []string{"groq", "poolside"} { if containsString(gws, extra) { - t.Logf("extra gateway %q present (local eyrie build)", extra) + t.Logf("extra gateway %q present (local flux build)", extra) } } if containsString(gws, "google") || containsString(gws, "xai") { @@ -140,7 +140,7 @@ func TestGatewayDisplayName(t *testing.T) { } } -func TestCachedModelCountForProvider_MatchesEyrieList(t *testing.T) { +func TestCachedModelCountForProvider_MatchesFluxList(t *testing.T) { catalogtest.Install(t) compiled := CompiledCatalogV1() for _, gw := range AllSetupGateways() { diff --git a/internal/config/catalog_health.go b/internal/config/catalog_health.go index b3ef178c..daf8e38e 100644 --- a/internal/config/catalog_health.go +++ b/internal/config/catalog_health.go @@ -18,7 +18,7 @@ var ( const catalogHealthCacheTTL = 15 * time.Second -// CatalogHealth summarizes the on-disk eyrie model catalog for doctor / status output. +// CatalogHealth summarizes the on-disk flux model catalog for doctor / status output. type CatalogHealth struct { CachePath string `json:"cache_path"` Exists bool `json:"exists"` @@ -33,7 +33,7 @@ type CatalogHealth struct { Error string `json:"error,omitempty"` } -// CatalogHealthReport inspects ~/.eyrie/model_catalog.json (or EYRIE_MODEL_CATALOG_PATH). +// CatalogHealthReport inspects ~/.flux/model_catalog.json (or FLUX_MODEL_CATALOG_PATH). func CatalogHealthReport(ctx context.Context) CatalogHealth { path := CatalogCachePathForDisplay() catalogHealthMu.Lock() @@ -61,7 +61,7 @@ func InvalidateCatalogHealthCache() { } func catalogHealthReportUncached(ctx context.Context) CatalogHealth { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return CatalogHealth{Error: err.Error()} } @@ -81,7 +81,7 @@ func catalogHealthReportUncached(ctx context.Context) CatalogHealth { // FormatCatalogHealth returns human-readable catalog status for rho doctor. func FormatCatalogHealth(h CatalogHealth) string { var b strings.Builder - b.WriteString(theme.Tint("Model catalog (eyrie):", theme.ReportInfo) + "\n") + b.WriteString(theme.Tint("Model catalog (flux):", theme.ReportInfo) + "\n") b.WriteString(" " + theme.Tint("path:", theme.ReportMuted) + " " + theme.Tint(h.CachePath, theme.ReportInfo) + "\n") if h.Error != "" { b.WriteString(" " + theme.Tint("status:", theme.ReportMuted) + " " + theme.Tint(h.Error, theme.ReportError) + "\n") @@ -128,7 +128,7 @@ func EnsureCatalogAvailable(ctx context.Context) error { // CatalogCachePathForDisplay returns the path users should care about. func CatalogCachePathForDisplay() string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err == nil { return engine.StatePaths().Catalog } diff --git a/internal/config/catalog_health_test.go b/internal/config/catalog_health_test.go index 8e03452b..5988a63a 100644 --- a/internal/config/catalog_health_test.go +++ b/internal/config/catalog_health_test.go @@ -43,7 +43,7 @@ func TestCatalogEmptyHint_WithCredentials(t *testing.T) { func TestEnsureCatalogAvailable_MissingCache(t *testing.T) { dir := t.TempDir() - t.Setenv("EYRIE_MODEL_CATALOG_PATH", dir+"/missing.json") + t.Setenv("FLUX_MODEL_CATALOG_PATH", dir+"/missing.json") err := EnsureCatalogAvailable(context.Background()) if err == nil || !strings.Contains(err.Error(), "/config") { @@ -53,7 +53,7 @@ func TestEnsureCatalogAvailable_MissingCache(t *testing.T) { func TestCatalogStatusLine_Empty(t *testing.T) { dir := t.TempDir() - t.Setenv("EYRIE_MODEL_CATALOG_PATH", dir+"/missing.json") + t.Setenv("FLUX_MODEL_CATALOG_PATH", dir+"/missing.json") line := CatalogStatusLine(context.Background()) if !strings.Contains(line, "missing") && !strings.Contains(line, "empty") { diff --git a/internal/config/catalog_startup.go b/internal/config/catalog_startup.go index 651fcce0..5b13e13b 100644 --- a/internal/config/catalog_startup.go +++ b/internal/config/catalog_startup.go @@ -72,7 +72,7 @@ func CatalogStatusLine(ctx context.Context) string { return formatCatalogGatewayStatus("Catalog: ", rows, h.Models) } -// CatalogReady reports whether the eyrie catalog cache exists and has models. +// CatalogReady reports whether the flux catalog cache exists and has models. func CatalogReady(ctx context.Context) bool { h := CatalogHealthReport(ctx) return h.Error == "" && h.Models > 0 && !h.Stale @@ -146,7 +146,7 @@ func catalogNeedsAutoRefresh(h CatalogHealth, opts CatalogStartupOptions) bool { return h.Stale } -// AutoRefreshCatalog runs eyrie discover (remote + live APIs when keys are set). +// AutoRefreshCatalog runs flux discover (remote + live APIs when keys are set). func AutoRefreshCatalog(ctx context.Context, out io.Writer, verbose bool) error { if out != nil { if verbose { @@ -185,7 +185,7 @@ func TryAutoRefreshCatalog(ctx context.Context) error { return AutoRefreshCatalog(ctx, nil, false) } -// RefreshCatalogAfterCredentials runs eyrie discover after /config saves API keys. +// RefreshCatalogAfterCredentials runs flux discover after /config saves API keys. func RefreshCatalogAfterCredentials(ctx context.Context, out io.Writer) error { if !autoRefreshCatalogEnabled() { return nil diff --git a/internal/config/catalog_startup_robust_test.go b/internal/config/catalog_startup_robust_test.go index a5311507..ccf9868e 100644 --- a/internal/config/catalog_startup_robust_test.go +++ b/internal/config/catalog_startup_robust_test.go @@ -27,7 +27,7 @@ func TestPrepareCatalogForSession_StaleCacheRefreshFailureContinues(t *testing.T func TestCatalogCachePathForDisplay_RespectsEnv(t *testing.T) { custom := filepath.Join(t.TempDir(), "custom.json") - t.Setenv("EYRIE_MODEL_CATALOG_PATH", custom) + t.Setenv("FLUX_MODEL_CATALOG_PATH", custom) if got := rhoconfig.CatalogCachePathForDisplay(); got != custom { t.Fatalf("path = %q want %q", got, custom) } diff --git a/internal/config/catalog_startup_test.go b/internal/config/catalog_startup_test.go index cba7f95e..f1bb5096 100644 --- a/internal/config/catalog_startup_test.go +++ b/internal/config/catalog_startup_test.go @@ -13,7 +13,7 @@ import ( func TestCatalogReady_MissingCache(t *testing.T) { InvalidateCatalogHealthCache() dir := t.TempDir() - t.Setenv("EYRIE_MODEL_CATALOG_PATH", filepath.Join(dir, "missing.json")) + t.Setenv("FLUX_MODEL_CATALOG_PATH", filepath.Join(dir, "missing.json")) if CatalogReady(context.Background()) { t.Fatal("expected not ready without cache") } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 3e4eb247..842446df 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -170,7 +170,7 @@ func TestLoadSettingsUsesUserConfigOnly(t *testing.T) { t.Setenv("HOME", home) configDir := filepath.Join(home, "config") t.Setenv("RHO_CONFIG_DIR", configDir) - t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) + t.Setenv("FLUX_CONFIG_DIR", filepath.Join(home, "flux")) if err := os.MkdirAll(configDir, 0o755); err != nil { t.Fatal(err) } diff --git a/internal/config/credentials_store.go b/internal/config/credentials_store.go index d41c4345..09119eed 100644 --- a/internal/config/credentials_store.go +++ b/internal/config/credentials_store.go @@ -9,14 +9,14 @@ import ( "github.com/GrayCodeAI/rho/internal/theme" ) -// PersistAPIKey saves a provider API key via eyrie (OS secret store). +// PersistAPIKey saves a provider API key via flux (OS secret store). func PersistAPIKey(ctx context.Context, envKey, secret string) error { secret = strings.TrimSpace(secret) envKey = strings.TrimSpace(envKey) if secret == "" || envKey == "" { return nil } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return err } @@ -27,7 +27,7 @@ func PersistAPIKey(ctx context.Context, envKey, secret string) error { return nil } -// CredentialInference is one eyrie provider match for a pasted API key. +// CredentialInference is one flux provider match for a pasted API key. type CredentialInference struct { ProviderID string DeploymentID string @@ -35,7 +35,7 @@ type CredentialInference struct { DisplayName string } -// CredentialProviderOption is one eyrie provider row for /config pickers. +// CredentialProviderOption is one flux provider row for /config pickers. type CredentialProviderOption struct { ProviderID string DeploymentID string @@ -46,7 +46,7 @@ type CredentialProviderOption struct { Rank int } -// CredentialResolveResult is eyrie paste-key resolution (format check + full provider list; no prefix inference). +// CredentialResolveResult is flux paste-key resolution (format check + full provider list; no prefix inference). type CredentialResolveResult struct { FormatOK bool FormatError string @@ -54,9 +54,9 @@ type CredentialResolveResult struct { ProbeDisambiguationUsed bool } -// ResolveCredential validates format and lists all providers from eyrie registry. +// ResolveCredential validates format and lists all providers from flux registry. func ResolveCredential(ctx context.Context, secret string) CredentialResolveResult { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return CredentialResolveResult{FormatError: err.Error()} } @@ -91,9 +91,9 @@ func InferenceFromOption(opt CredentialProviderOption) CredentialInference { } } -// SaveCredential validates, probes, and stores via eyrie keychain. +// SaveCredential validates, probes, and stores via flux keychain. func SaveCredential(ctx context.Context, inference CredentialInference, secret string) error { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return err } @@ -106,7 +106,7 @@ func SaveCredential(ctx context.Context, inference CredentialInference, secret s // HasStoredCredentialForProvider reports whether the OS secret store has a key for this gateway. func HasStoredCredentialForProvider(ctx context.Context, providerID string) bool { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return false } @@ -117,7 +117,7 @@ func HasStoredCredentialForProvider(ctx context.Context, providerID string) bool // CredentialEnvironmentConflict reports a safe environment-vs-keychain // mismatch for a provider. It returns identifiers only, never secret values. func CredentialEnvironmentConflict(ctx context.Context, providerID string) (envVar string, conflict bool) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "", false } @@ -165,7 +165,7 @@ func RemoveStoredCredential(ctx context.Context, target string) ([]string, error if target == "" { return nil, fmt.Errorf("provider or env var name required") } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil, err } @@ -189,7 +189,7 @@ func MaskCredentialForProvider(ctx context.Context, provider string) string { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err == nil { status, statusErr := engine.CredentialStatus(ctx, provider) if statusErr == nil && status.Masked != "" { @@ -214,7 +214,7 @@ func maskCredentialSecret(secret string) string { // CredentialInferenceForProvider returns save metadata for a gateway chosen in /config. func CredentialInferenceForProvider(providerID string) (CredentialInference, error) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return CredentialInference{}, err } @@ -235,7 +235,7 @@ func LocalCredentialInference(providerID string) (CredentialInference, error) { return CredentialInferenceForProvider(providerID) } -// FormatConfigProviderError maps eyrie setup errors to user-facing /config hints. +// FormatConfigProviderError maps flux setup errors to user-facing /config hints. func FormatConfigProviderError(providerID string, err error) string { if err == nil { return "" diff --git a/internal/config/deployment.go b/internal/config/deployment.go index 2767002b..5b96e6ad 100644 --- a/internal/config/deployment.go +++ b/internal/config/deployment.go @@ -1,7 +1,7 @@ package config -// DeploymentRoutingEnabled delegates deployment-routing policy ownership to Eyrie runtime. +// DeploymentRoutingEnabled delegates deployment-routing policy ownership to Flux runtime. func DeploymentRoutingEnabled(s Settings) bool { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() return err == nil && engine.DeploymentRoutingEnabled(s.DeploymentRouting) } diff --git a/internal/config/deployment_status.go b/internal/config/deployment_status.go index 8fdb061c..1d6626eb 100644 --- a/internal/config/deployment_status.go +++ b/internal/config/deployment_status.go @@ -12,7 +12,7 @@ func ResolveCanonicalModel(model string) string { // DeploymentStatusReport returns rho deployment routing diagnostics. func DeploymentStatusReport(ctx context.Context, activeModel string) (string, error) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "", err } @@ -20,16 +20,16 @@ func DeploymentStatusReport(ctx context.Context, activeModel string) (string, er } func DeploymentStatusReportWithSettings(ctx context.Context, settings Settings, activeModel string) (string, error) { - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { return "", err } return engine.DeploymentStatus(ctx, activeModel) } -// RoutingPreviewJSON returns effective routing for a model (eyrie routing JSON preview). +// RoutingPreviewJSON returns effective routing for a model (flux routing JSON preview). func RoutingPreviewJSON(ctx context.Context, model string) (string, error) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "", err } @@ -37,7 +37,7 @@ func RoutingPreviewJSON(ctx context.Context, model string) (string, error) { } func RoutingPreviewJSONWithSettings(ctx context.Context, settings Settings, model string) (string, error) { - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { return "", err } diff --git a/internal/config/developer_path.go b/internal/config/developer_path.go index d98540a4..95dc3c16 100644 --- a/internal/config/developer_path.go +++ b/internal/config/developer_path.go @@ -155,7 +155,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { pre := EnginePreflightReport(ctx) if pre.Ready { checks = append(checks, PathCheck{ - Section: "Ecosystem", Name: "eyrie", Status: PathPass, + Section: "Ecosystem", Name: "flux", Status: PathPass, Detail: "Preflight ready to chat", Blocking: true, }) @@ -168,7 +168,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { } } checks = append(checks, PathCheck{ - Section: "Ecosystem", Name: "eyrie", Status: status, + Section: "Ecosystem", Name: "flux", Status: status, Detail: "Preflight not ready — see rho preflight", FixHint: "Complete /config (credentials + model)", Blocking: true, @@ -232,7 +232,7 @@ func pathStatusColor(s PathCheckStatus) color.Color { func FormatDeveloperPathReport(ctx context.Context) string { r := EvaluateDeveloperPath(ctx) var b strings.Builder - b.WriteString(theme.Tint("Developer path (rho · eyrie · token engine)", theme.ReportInfo) + "\n\n") + b.WriteString(theme.Tint("Developer path (rho · flux · token engine)", theme.ReportInfo) + "\n\n") status := "NEEDS SETUP" statusColor := theme.ReportWarn diff --git a/internal/config/ecosystem_report.go b/internal/config/ecosystem_report.go index 14947393..1c89bb6a 100644 --- a/internal/config/ecosystem_report.go +++ b/internal/config/ecosystem_report.go @@ -11,11 +11,11 @@ import ( // EcosystemReport is the structured view of the ecosystem panel. type EcosystemReport struct { - Eyrie EcosystemEyrie `json:"eyrie"` - Token EngineToken `json:"token"` + Flux EcosystemFlux `json:"flux"` + Token EngineToken `json:"token"` } -type EcosystemEyrie struct { +type EcosystemFlux struct { CatalogExists bool `json:"catalog_exists"` ModelCount int `json:"model_count,omitempty"` Ready bool `json:"ready"` @@ -33,18 +33,18 @@ type EngineToken struct { func BuildEcosystemReport(ctx context.Context, provider, model string) EcosystemReport { var r EcosystemReport - // eyrie + // flux cat := CatalogHealthReport(ctx) - r.Eyrie.CatalogExists = cat.Exists - r.Eyrie.ModelCount = cat.Models + r.Flux.CatalogExists = cat.Exists + r.Flux.ModelCount = cat.Models pre := EnginePreflightReport(ctx) - r.Eyrie.Ready = pre.Ready + r.Flux.Ready = pre.Ready if strings.TrimSpace(provider) != "" && provider != "auto" { - r.Eyrie.Provider = provider + r.Flux.Provider = provider } if dep, err := EngineDeploymentSummary(ctx, model); err == nil { - r.Eyrie.RoutingSource = dep.RoutingSource - r.Eyrie.RoutingStages = dep.RoutingStages + r.Flux.RoutingSource = dep.RoutingSource + r.Flux.RoutingStages = dep.RoutingStages } // embedded token engine @@ -54,36 +54,36 @@ func BuildEcosystemReport(ctx context.Context, provider, model string) Ecosystem return r } -// FormatEcosystemPanel summarizes eyrie and token-engine integration for doctor and status output. +// FormatEcosystemPanel summarizes flux and token-engine integration for doctor and status output. func FormatEcosystemPanel(ctx context.Context, provider, model string) string { var b strings.Builder - b.WriteString(theme.Tint("Ecosystem (eyrie · token engine):", theme.ReportInfo) + "\n") + b.WriteString(theme.Tint("Ecosystem (flux · token engine):", theme.ReportInfo) + "\n") - // eyrie — LLM provider layer + // flux — LLM provider layer cat := CatalogHealthReport(ctx) - eyrieLine := " " + theme.Tint("eyrie:", theme.ReportMuted) + " " + fluxLine := " " + theme.Tint("flux:", theme.ReportMuted) + " " if cat.Exists { - eyrieLine += theme.Tint(fmt.Sprintf("catalog %d models", cat.Models), theme.ReportInfo) + fluxLine += theme.Tint(fmt.Sprintf("catalog %d models", cat.Models), theme.ReportInfo) } else { - eyrieLine += theme.Tint("catalog missing (run rho models refresh)", theme.ReportWarn) + fluxLine += theme.Tint("catalog missing (run rho models refresh)", theme.ReportWarn) } pre := EnginePreflightReport(ctx) if pre.Ready { - eyrieLine += " · " + theme.Tint("locally ready", theme.ReportSuccess) + fluxLine += " · " + theme.Tint("locally ready", theme.ReportSuccess) } else { - eyrieLine += " · " + theme.Tint("setup incomplete", theme.ReportWarn) + fluxLine += " · " + theme.Tint("setup incomplete", theme.ReportWarn) } if strings.TrimSpace(provider) != "" && provider != "auto" { - eyrieLine += " · " + theme.Tint("provider "+provider, theme.ReportInfo) + fluxLine += " · " + theme.Tint("provider "+provider, theme.ReportInfo) } if dep, err := EngineDeploymentSummary(ctx, model); err == nil { if dep.RoutingStages > 0 { - eyrieLine += " · " + theme.Tint(fmt.Sprintf("routing %s (%d stages)", dep.RoutingSource, dep.RoutingStages), theme.ReportInfo) + fluxLine += " · " + theme.Tint(fmt.Sprintf("routing %s (%d stages)", dep.RoutingSource, dep.RoutingStages), theme.ReportInfo) } else { - eyrieLine += " · " + theme.Tint("routing "+dep.RoutingSource, theme.ReportInfo) + fluxLine += " · " + theme.Tint("routing "+dep.RoutingSource, theme.ReportInfo) } } - b.WriteString(eyrieLine + "\n") + b.WriteString(fluxLine + "\n") // Embedded token engine — token counting and context compression. sample := token.CountTokensFast("rho context compression pipeline") diff --git a/internal/config/ecosystem_report_test.go b/internal/config/ecosystem_report_test.go index cbc10194..ad6a6877 100644 --- a/internal/config/ecosystem_report_test.go +++ b/internal/config/ecosystem_report_test.go @@ -10,8 +10,8 @@ func TestFormatEcosystemPanel(t *testing.T) { t.Parallel() out := FormatEcosystemPanel(context.Background(), "anthropic", "claude-sonnet-4-20250514") for _, want := range []string{ - "Ecosystem (eyrie · token engine):", - "eyrie:", + "Ecosystem (flux · token engine):", + "flux:", "token:", } { if !strings.Contains(out, want) { diff --git a/internal/config/eyrie_apply.go b/internal/config/flux_apply.go similarity index 74% rename from internal/config/eyrie_apply.go rename to internal/config/flux_apply.go index 348a0038..4559fed3 100644 --- a/internal/config/eyrie_apply.go +++ b/internal/config/flux_apply.go @@ -8,19 +8,19 @@ import ( "github.com/GrayCodeAI/rho/internal/provider/gateway" ) -// ApplyCredentialsResult is Rho's UI-safe view of an Eyrie catalog/routing -// application. It intentionally excludes Eyrie setup/config implementation +// ApplyCredentialsResult is Rho's UI-safe view of an Flux catalog/routing +// application. It intentionally excludes Flux setup/config implementation // types from the product boundary. type ApplyCredentialsResult struct { Catalog gateway.CatalogSnapshot } -// ApplyEyrieCredentialsForProvider refreshes live models and writes sanitized +// ApplyFluxCredentialsForProvider refreshes live models and writes sanitized // deployment routing after /config saves a key. -func ApplyEyrieCredentialsForProvider(ctx context.Context, providerID string) (*ApplyCredentialsResult, error) { +func ApplyFluxCredentialsForProvider(ctx context.Context, providerID string) (*ApplyCredentialsResult, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil, err } @@ -32,12 +32,12 @@ func ApplyEyrieCredentialsForProvider(ctx context.Context, providerID string) (* return &ApplyCredentialsResult{Catalog: snapshot}, nil } -// ApplyEyrieCredentials refreshes all configured providers and writes +// ApplyFluxCredentials refreshes all configured providers and writes // sanitized deployment routing. -func ApplyEyrieCredentials(ctx context.Context) (*ApplyCredentialsResult, error) { +func ApplyFluxCredentials(ctx context.Context) (*ApplyCredentialsResult, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return nil, err } @@ -53,7 +53,7 @@ func ApplyEyrieCredentials(ctx context.Context) (*ApplyCredentialsResult, error) func RefreshGatewayCatalog(ctx context.Context, providerID string) (string, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "", err } diff --git a/internal/config/eyrie_engine.go b/internal/config/flux_engine.go similarity index 84% rename from internal/config/eyrie_engine.go rename to internal/config/flux_engine.go index 8d62d963..d282abc8 100644 --- a/internal/config/eyrie_engine.go +++ b/internal/config/flux_engine.go @@ -12,16 +12,16 @@ type ( EnginePreflightOptions = gateway.PreflightOptions ) -func newEyrieEngine() (*gateway.Gateway, error) { +func newFluxEngine() (*gateway.Gateway, error) { return gateway.New(context.Background(), globalCustomProviders()) } -func NewEyrieEngine() (*gateway.Gateway, error) { return newEyrieEngine() } +func NewFluxEngine() (*gateway.Gateway, error) { return newFluxEngine() } -// NewEyrieEngineForSettings composes a fresh gateway for one effective +// NewFluxEngineForSettings composes a fresh gateway for one effective // Rho settings snapshot. It performs no package-global registration and does // not mutate provider environment variables. -func NewEyrieEngineForSettings(settings Settings) (*gateway.Gateway, error) { +func NewFluxEngineForSettings(settings Settings) (*gateway.Gateway, error) { return gateway.New(context.Background(), gatewayCustomGateways(settings.CustomProviders)) } @@ -80,7 +80,7 @@ func FormatEnginePreflight(report EnginePreflight) string { } func EngineGatewayRegion(ctx context.Context, providerID string) (string, bool) { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { return "", false } @@ -88,7 +88,7 @@ func EngineGatewayRegion(ctx context.Context, providerID string) (string, bool) } func SetEngineGatewayRegion(ctx context.Context, providerID, region string) error { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { return err } @@ -96,7 +96,7 @@ func SetEngineGatewayRegion(ctx context.Context, providerID, region string) erro } func CanonicalModelID(ctx context.Context, modelID string) string { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { return modelID } @@ -104,7 +104,7 @@ func CanonicalModelID(ctx context.Context, modelID string) string { } func HasCredentialEnv(ctx context.Context, envVar string) bool { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() return err == nil && gw.HasCredentialEnv(ctx, envVar) } @@ -113,30 +113,30 @@ func CredentialGuidance(providerID, secret string) string { } func ProviderStateSecurityStatus() gateway.ProviderStateSecurity { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { - return gateway.ProviderStateSecurity{Error: err.Error(), Detail: "Eyrie engine initialization failed"} + return gateway.ProviderStateSecurity{Error: err.Error(), Detail: "Flux engine initialization failed"} } return gw.ProviderStateSecurityStatus() } func EngineDeploymentSummary(ctx context.Context, model string) (gateway.DeploymentSummary, error) { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { return gateway.DeploymentSummary{}, err } return gw.DeploymentSummary(ctx, model) } -// newEyrieEngine is Rho's default composition root for Eyrie's stable host +// newFluxEngine is Rho's default composition root for Flux's stable host // facade. Command paths that support --settings must use -// NewEyrieEngineForSettings instead of relying on this global-settings default. +// NewFluxEngineForSettings instead of relying on this global-settings default. -// ListEngineModels returns model-picker rows through Eyrie's stable facade. +// ListEngineModels returns model-picker rows through Flux's stable facade. // EngineModel is an alias of gateway.ModelInfo, so model lists returned by the // gateway pass through without conversion. func ListEngineModels(ctx context.Context, providerID string, refresh bool) ([]EngineModel, error) { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { return nil, err } @@ -144,7 +144,7 @@ func ListEngineModels(ctx context.Context, providerID string, refresh bool) ([]E } func ListEngineModelsWithSettings(ctx context.Context, settings Settings, providerID string, refresh bool) ([]EngineModel, error) { - gw, err := NewEyrieEngineForSettings(settings) + gw, err := NewFluxEngineForSettings(settings) if err != nil { return nil, err } @@ -152,7 +152,7 @@ func ListEngineModelsWithSettings(ctx context.Context, settings Settings, provid } func ListLiveEngineModelsWithSettings(ctx context.Context, settings Settings, providerID string) ([]EngineModel, error) { - gw, err := NewEyrieEngineForSettings(settings) + gw, err := NewFluxEngineForSettings(settings) if err != nil { return nil, err } @@ -160,7 +160,7 @@ func ListLiveEngineModelsWithSettings(ctx context.Context, settings Settings, pr } func ListPublicEngineModels(ctx context.Context, providerID string) ([]EngineModel, error) { - gw, err := newEyrieEngine() + gw, err := newFluxEngine() if err != nil { return nil, err } diff --git a/internal/config/eyrie_engine_test.go b/internal/config/flux_engine_test.go similarity index 87% rename from internal/config/eyrie_engine_test.go rename to internal/config/flux_engine_test.go index 3815f664..076686c2 100644 --- a/internal/config/eyrie_engine_test.go +++ b/internal/config/flux_engine_test.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/rho/internal/provider/gateway" ) -func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { +func TestNewFluxEngineForSettingsIsInvocationScoped(t *testing.T) { previousStore := gateway.DefaultStore() store := &gateway.MapStore{} gateway.SetDefaultStore(store) @@ -17,7 +17,7 @@ func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { Name: "private-gateway", BaseURL: "https://private.example.test/v1", APIKeyEnv: "PRIVATE_GATEWAY_API_KEY", Model: "private/model-v1", }}} - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { t.Fatal(err) } @@ -44,7 +44,7 @@ func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { t.Fatalf("custom credential surface = %+v, err=%v", status, err) } - if _, collisionErr := NewEyrieEngineForSettings(Settings{CustomProviders: []CustomProviderConfig{{ + if _, collisionErr := NewFluxEngineForSettings(Settings{CustomProviders: []CustomProviderConfig{{ Name: "openai", BaseURL: "https://collision.example.test/v1", }}}); collisionErr == nil { t.Fatal("built-in custom gateway collision was accepted") @@ -53,7 +53,7 @@ func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { if err != nil || len(models) != 1 { t.Fatalf("independent invalid construction changed existing engine: %+v, err=%v", models, err) } - other, err := NewEyrieEngineForSettings(Settings{}) + other, err := NewFluxEngineForSettings(Settings{}) if err != nil { t.Fatal(err) } diff --git a/internal/config/eyrie_selection.go b/internal/config/flux_selection.go similarity index 76% rename from internal/config/eyrie_selection.go rename to internal/config/flux_selection.go index c4c506c9..703f96c6 100644 --- a/internal/config/eyrie_selection.go +++ b/internal/config/flux_selection.go @@ -12,9 +12,9 @@ type ( ) // EffectiveSelection resolves persisted selection and optional host overrides -// through Eyrie's host-neutral engine contract. +// through Flux's host-neutral engine contract. func EffectiveSelection(ctx context.Context, opts SelectionOptions) Selection { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return Selection{} } @@ -22,60 +22,60 @@ func EffectiveSelection(ctx context.Context, opts SelectionOptions) Selection { } func EffectiveSelectionWithSettings(ctx context.Context, settings Settings, opts SelectionOptions) Selection { - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { return Selection{} } return engine.EffectiveSelection(ctx, opts) } -// ActiveModel returns the selected model from eyrie provider.json (not rho settings). +// ActiveModel returns the selected model from flux provider.json (not rho settings). func ActiveModel(ctx context.Context) string { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } return engine.ActiveSelection(ctx).Model } -// ActiveProvider returns the selected provider from eyrie provider.json. +// ActiveProvider returns the selected provider from flux provider.json. func ActiveProvider(ctx context.Context) string { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } return engine.ActiveSelection(ctx).Provider } -// ActiveProviderID canonicalizes a host-facing provider/gateway id through Eyrie runtime. +// ActiveProviderID canonicalizes a host-facing provider/gateway id through Flux runtime. func ActiveProviderID(provider string) string { return gateway.NormalizeProviderID(provider) } -// SetActiveModel persists model selection to eyrie provider.json. +// SetActiveModel persists model selection to flux provider.json. func SetActiveModel(ctx context.Context, modelID string) error { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return err } return engine.SetActiveModel(ctx, modelID) } -// SetActiveProvider persists provider selection to eyrie provider.json. +// SetActiveProvider persists provider selection to flux provider.json. func SetActiveProvider(ctx context.Context, provider string) error { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return err } diff --git a/internal/config/milestone_verify_test.go b/internal/config/milestone_verify_test.go index 59b3f936..d19c3cb1 100644 --- a/internal/config/milestone_verify_test.go +++ b/internal/config/milestone_verify_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/eyrie/catalog" - eyriecfg "github.com/GrayCodeAI/eyrie/config" - "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/flux/catalog" + fluxcfg "github.com/GrayCodeAI/flux/config" + "github.com/GrayCodeAI/flux/credentials" ) // isolateMilestoneTest uses a temp HOME and RHO_CONFIG_DIR so verification does not touch the user machine. @@ -23,7 +23,7 @@ func isolateMilestoneTest(t *testing.T) string { } t.Setenv("HOME", home) t.Setenv("RHO_CONFIG_DIR", rhoDir) - t.Setenv("EYRIE_CONFIG_DIR", rhoDir) + t.Setenv("FLUX_CONFIG_DIR", rhoDir) return rhoDir } @@ -34,12 +34,12 @@ func TestVerify_ProviderJSONOnDiskHasNoSecrets(t *testing.T) { t.Fatal("compiled catalog required") } env := map[string]string{"ANTHROPIC_API_KEY": "sk-ant-verify-test-key-1234567890"} - cfg := eyriecfg.SyncProviderConfigFromCatalog(compiled, env) - path, err := eyriecfg.GetProviderConfigPath() + cfg := fluxcfg.SyncProviderConfigFromCatalog(compiled, env) + path, err := fluxcfg.GetProviderConfigPath() if err != nil { t.Fatalf("GetProviderConfigPath: %v", err) } - if err := eyriecfg.SaveProviderConfig(cfg, path); err != nil { + if err := fluxcfg.SaveProviderConfig(cfg, path); err != nil { t.Fatal(err) } assertProviderJSONFileHasNoSecrets(t, path) @@ -100,12 +100,12 @@ func TestVerify_EvaluateSetupFlow(t *testing.T) { } providerPath := filepath.Join(os.Getenv("HOME"), ".rho", "provider.json") - cfg := &eyriecfg.ProviderConfig{ + cfg := &fluxcfg.ProviderConfig{ ActiveProvider: "anthropic", ActiveModel: "claude-sonnet-4-20250514", AnthropicModel: "claude-sonnet-4-20250514", } - if err := eyriecfg.SaveProviderConfig(cfg, providerPath); err != nil { + if err := fluxcfg.SaveProviderConfig(cfg, providerPath); err != nil { t.Fatal(err) } st = EvaluateSetup(ctx) @@ -133,7 +133,7 @@ func assertProviderJSONFileHasNoSecrets(t *testing.T, path string) { t.Fatalf("provider.json at %s contains non-empty %s", path, needle) } } - var cfg eyriecfg.ProviderConfig + var cfg fluxcfg.ProviderConfig if err := json.Unmarshal(data, &cfg); err != nil { t.Fatal(err) } diff --git a/internal/config/provider_filter.go b/internal/config/provider_filter.go index 183dd965..4b0cd571 100644 --- a/internal/config/provider_filter.go +++ b/internal/config/provider_filter.go @@ -4,13 +4,13 @@ import ( "context" ) -// DefaultModelProviderFilter picks which eyrie provider to list models for when the UI -// has no explicit filter. Host prefs (settings) win; otherwise eyrie routing/deployments decide. +// DefaultModelProviderFilter picks which flux provider to list models for when the UI +// has no explicit filter. Host prefs (settings) win; otherwise flux routing/deployments decide. func DefaultModelProviderFilter(ctx context.Context) string { if p := ActiveGateway(ctx); p != "" { return p } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "" } diff --git a/internal/config/security_test.go b/internal/config/security_test.go index b6ffc44f..f5d04b1e 100644 --- a/internal/config/security_test.go +++ b/internal/config/security_test.go @@ -88,7 +88,7 @@ func TestValidateSettings_ValidConfig(t *testing.T) { // This test uses the global catalog test setup from main_test.go dir := t.TempDir() t.Setenv("RHO_CONFIG_DIR", dir) - t.Setenv("EYRIE_CONFIG_DIR", dir) + t.Setenv("FLUX_CONFIG_DIR", dir) s := Settings{ MaxBudgetUSD: 10.0, } diff --git a/internal/config/settings.go b/internal/config/settings.go index b06dd720..d93761f3 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -27,13 +27,13 @@ func fetchModelsViaRuntime(ctx context.Context, provider string) ([]EngineModel, } // Settings holds rho configuration. -// Rho: no API keys stored here. Secrets come from the OS secret store via eyrie. +// Rho: no API keys stored here. Secrets come from the OS secret store via flux. type Settings struct { // PolicySchemaVersion versions permission/autonomy/sandbox fields. Zero is // the legacy format and is migrated to CurrentPolicySchemaVersion on load. PolicySchemaVersion int `json:"policy_schema_version,omitempty"` // Model and Provider carry runtime host overrides only (e.g. --settings). - // Eyrie owns the stored selection (SetActiveModel / SetActiveProvider); + // Flux owns the stored selection (SetActiveModel / SetActiveProvider); // values found in settings.json are dropped on load and on save. Model string `json:"model,omitempty"` Provider string `json:"provider,omitempty"` @@ -221,7 +221,7 @@ func LoadGlobalSettings() Settings { slog.Warn("failed to parse settings", "path", path, "error", err) } } - // Eyrie owns the stored model/provider selection. Stale values left in + // Flux owns the stored model/provider selection. Stale values left in // settings.json must not act as a host override. s = stripHostModelSelection(s) if s.PolicySchemaVersion == 0 { @@ -700,7 +700,7 @@ func ProviderAPIKeyEnv(provider string) string { // EnvKeyStatus returns set, empty, or local from the OS credential store. func EnvKeyStatus(provider string) string { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return "empty" } @@ -736,7 +736,7 @@ func providerCredentialEnvAliases(provider string) []string { primary := strings.TrimSpace(ProviderAPIKeyEnv(provider)) seen := map[string]bool{} var out []string - engine, _ := newEyrieEngine() + engine, _ := newFluxEngine() var aliases []string if engine != nil { aliases = engine.CredentialEnvKeys(provider) @@ -753,10 +753,10 @@ func providerCredentialEnvAliases(provider string) []string { } // ───────────────────────────────────────────────────────────── -// Live model catalog fetch from eyrie +// Live model catalog fetch from flux // ───────────────────────────────────────────────────────────── -// FetchModelsForProvider returns models from the eyrie catalog (dynamic; no rho hardcoded lists). +// FetchModelsForProvider returns models from the flux catalog (dynamic; no rho hardcoded lists). // RefreshModelCatalogV1 is the explicit network refresh boundary. func FetchModelsForProvider(provider string) ([]EngineModel, error) { provider = gateway.NormalizeProviderID(provider) @@ -786,7 +786,7 @@ func FetchModelsForProvider(provider string) ([]EngineModel, error) { }}, nil } } - return nil, fmt.Errorf("no models found for provider %s in eyrie catalog (check API keys; rho will refresh automatically on next start)", provider) + return nil, fmt.Errorf("no models found for provider %s in flux catalog (check API keys; rho will refresh automatically on next start)", provider) } // FetchModelsForProviderWithSettings resolves cached models using one @@ -796,7 +796,7 @@ func FetchModelsForProviderWithSettings(ctx context.Context, settings Settings, if provider == "" { return nil, fmt.Errorf("no provider specified") } - engine, engineErr := NewEyrieEngineForSettings(settings) + engine, engineErr := NewFluxEngineForSettings(settings) if engineErr != nil { return nil, engineErr } @@ -810,18 +810,18 @@ func FetchModelsForProviderWithSettings(ctx context.Context, settings Settings, if err != nil { return nil, err } - return nil, fmt.Errorf("no models found for provider %s in eyrie catalog", provider) + return nil, fmt.Errorf("no models found for provider %s in flux catalog", provider) } func refreshModelCatalog(ctx context.Context, _ bool) (gateway.CatalogSnapshot, error) { - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err != nil { return gateway.CatalogSnapshot{}, err } return engine.RefreshCatalog(ctx, "") } -// RefreshModelCatalogV1 asks eyrie to refresh the remote catalog and provider APIs using env API keys. +// RefreshModelCatalogV1 asks flux to refresh the remote catalog and provider APIs using env API keys. func RefreshModelCatalogV1(ctx context.Context) (string, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() @@ -838,7 +838,7 @@ func RefreshModelCatalogV1(ctx context.Context) (string, error) { func RefreshModelCatalogV1WithSettings(ctx context.Context, settings Settings) (string, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := NewEyrieEngineForSettings(settings) + engine, err := NewFluxEngineForSettings(settings) if err != nil { return "", err } diff --git a/internal/config/settings_status_test.go b/internal/config/settings_status_test.go index 6dc70b41..93a46f37 100644 --- a/internal/config/settings_status_test.go +++ b/internal/config/settings_status_test.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/rho/internal/provider/gateway" ) -func TestEnvKeyStatusUsesEyrieCredentialStatus(t *testing.T) { +func TestEnvKeyStatusUsesFluxCredentialStatus(t *testing.T) { store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) }) diff --git a/internal/config/setup_status.go b/internal/config/setup_status.go index 6bef8dea..b5320336 100644 --- a/internal/config/setup_status.go +++ b/internal/config/setup_status.go @@ -45,16 +45,16 @@ func evaluateSetupFrom(hasCreds, hasModel bool) SetupState { return st } -// HasConfiguredDeployment reports whether at least one eyrie deployment has credentials. +// HasConfiguredDeployment reports whether at least one flux deployment has credentials. func HasConfiguredDeployment(ctx context.Context) bool { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newFluxEngine() return err == nil && engine.EffectiveSelection(ctx, SelectionOptions{}).HasConfiguredDeployment } -// HasSelectedModel reports whether eyrie provider.json has a selected model. +// HasSelectedModel reports whether flux provider.json has a selected model. func HasSelectedModel() bool { return strings.TrimSpace(ActiveModel(context.Background())) != "" } diff --git a/internal/config/setup_status_test.go b/internal/config/setup_status_test.go index f0b4e52b..893a8dd9 100644 --- a/internal/config/setup_status_test.go +++ b/internal/config/setup_status_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/eyrie/catalog" - "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/flux/catalog" + "github.com/GrayCodeAI/flux/credentials" "github.com/GrayCodeAI/rho/internal/testutil" ) diff --git a/internal/config/ui_cache.go b/internal/config/ui_cache.go index bf013cdd..004ac54b 100644 --- a/internal/config/ui_cache.go +++ b/internal/config/ui_cache.go @@ -74,7 +74,7 @@ func gatewayCredentialSnapshot(ctx context.Context) map[string]gatewayCredential credSnapMu.Unlock() out := make(map[string]gatewayCredentialState) - engine, err := newEyrieEngine() + engine, err := newFluxEngine() if err == nil { for _, gateway := range engine.Gateways(ctx) { out[gateway.ID] = gatewayCredentialState{ diff --git a/internal/config/validator.go b/internal/config/validator.go index c75db7fb..c6596a37 100644 --- a/internal/config/validator.go +++ b/internal/config/validator.go @@ -43,9 +43,9 @@ func (r ValidationResult) Error() string { func ValidateSettings(s Settings) ValidationResult { var errors []ValidationError - // Provider names are delegated to Eyrie. Do not hardcode/validate here. + // Provider names are delegated to Flux. Do not hardcode/validate here. - // Validate model selection (stored in eyrie provider.json) + // Validate model selection (stored in flux provider.json) activeModel := strings.TrimSpace(s.Model) if activeModel == "" { activeModel = ActiveModel(context.Background()) diff --git a/internal/config/validator_test.go b/internal/config/validator_test.go index ce7d7161..a083de6f 100644 --- a/internal/config/validator_test.go +++ b/internal/config/validator_test.go @@ -25,7 +25,7 @@ func TestValidateSettingsValid(t *testing.T) { } } -func TestValidateSettingsProviderDelegatedToEyrie(t *testing.T) { +func TestValidateSettingsProviderDelegatedToFlux(t *testing.T) { store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) }) @@ -33,7 +33,7 @@ func TestValidateSettingsProviderDelegatedToEyrie(t *testing.T) { s := Settings{Provider: "anthropic"} result := ValidateSettings(s) if result.Valid { - t.Fatal("expected invalid (missing env key for eyrie provider)") + t.Fatal("expected invalid (missing env key for flux provider)") } } diff --git a/internal/config/xiaomi_setup_test.go b/internal/config/xiaomi_setup_test.go index 0022ec87..33d75d77 100644 --- a/internal/config/xiaomi_setup_test.go +++ b/internal/config/xiaomi_setup_test.go @@ -4,27 +4,27 @@ import ( "os" "testing" - eyriecfg "github.com/GrayCodeAI/eyrie/config" + fluxcfg "github.com/GrayCodeAI/flux/config" ) func TestSetGatewayRegion_XiaomiClearsStaleBaseHost(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) t.Setenv("RHO_CONFIG_DIR", dir) - t.Setenv("EYRIE_CONFIG_DIR", dir) + t.Setenv("FLUX_CONFIG_DIR", dir) t.Setenv("XIAOMI_MIMO_TOKEN_PLAN_BASE_URL", "https://caller-owned.example.test/v1") - cfg := &eyriecfg.ProviderConfig{ + cfg := &fluxcfg.ProviderConfig{ Version: "1", XiaomiMimoTokenPlanRegion: "cn", XiaomiMimoTokenPlanBaseURL: "https://token-plan-cn.xiaomimimo.com/v1", } - if err := eyriecfg.SaveProviderConfig(cfg, ""); err != nil { + if err := fluxcfg.SaveProviderConfig(cfg, ""); err != nil { t.Fatal(err) } if err := SetGatewayRegion(ProviderXiaomiTokenPlan, "sgp"); err != nil { t.Fatal(err) } - loaded := eyriecfg.LoadProviderConfig("") + loaded := fluxcfg.LoadProviderConfig("") if loaded.XiaomiMimoTokenPlanRegion != "sgp" { t.Fatalf("region = %q", loaded.XiaomiMimoTokenPlanRegion) } @@ -37,12 +37,12 @@ func TestNeedsGatewayRegion_XiaomiInvalidAndMissing(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) t.Setenv("RHO_CONFIG_DIR", dir) - t.Setenv("EYRIE_CONFIG_DIR", dir) + t.Setenv("FLUX_CONFIG_DIR", dir) if !NeedsGatewayRegion(ProviderXiaomiTokenPlan) { t.Fatal("expected true when no config file") } - if err := eyriecfg.SaveProviderConfig(&eyriecfg.ProviderConfig{Version: "1", XiaomiMimoTokenPlanRegion: "tokyo"}, ""); err != nil { + if err := fluxcfg.SaveProviderConfig(&fluxcfg.ProviderConfig{Version: "1", XiaomiMimoTokenPlanRegion: "tokyo"}, ""); err != nil { t.Fatal(err) } if !NeedsGatewayRegion(ProviderXiaomiTokenPlan) { diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index e86a4bdf..5906a635 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -92,10 +92,10 @@ type Server struct { apiKey string gateways *gatewayManager - // readyFn reports whether Eyrie's preflight/readiness dependencies are + // readyFn reports whether Flux's preflight/readiness dependencies are // initialized and the server can serve real traffic. It is consulted by // GET /v1/ready. A nil probe fails closed: a session factory alone does not - // prove that Eyrie's catalog, credentials, and model selection are ready. + // prove that Flux's catalog, credentials, and model selection are ready. readyMu sync.RWMutex readyFn func() (bool, string) @@ -454,7 +454,7 @@ func (s *Server) SetGraphFactory(factory GraphFactory) { s.graphMu.Unlock() } -// ready evaluates readiness using the installed Eyrie probe. It fails closed +// ready evaluates readiness using the installed Flux probe. It fails closed // when the engine is absent or the composition root did not install a probe; // merely having a factory does not prove provider readiness. func (s *Server) ready() (bool, string) { @@ -467,7 +467,7 @@ func (s *Server) ready() (bool, string) { if s.newSession == nil { return false, "engine not configured" } - return false, "Eyrie readiness probe not configured" + return false, "Flux readiness probe not configured" } func (s *Server) routes() { diff --git a/internal/daemon/daemon_test.go b/internal/daemon/daemon_test.go index fb0eef3a..6e6a4e5b 100644 --- a/internal/daemon/daemon_test.go +++ b/internal/daemon/daemon_test.go @@ -94,7 +94,7 @@ func TestDaemon_Ready(t *testing.T) { wantReady: false, }, { - name: "engine wired without Eyrie probe is not ready", + name: "engine wired without Flux probe is not ready", factory: factory, wantStatus: http.StatusServiceUnavailable, wantReady: false, diff --git a/internal/daemon/routes_sessions_test.go b/internal/daemon/routes_sessions_test.go index 551b8ccf..344b876c 100644 --- a/internal/daemon/routes_sessions_test.go +++ b/internal/daemon/routes_sessions_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - contracts "github.com/GrayCodeAI/eyrie/tools" + contracts "github.com/GrayCodeAI/flux/tools" "github.com/GrayCodeAI/rho/internal/session" "github.com/GrayCodeAI/rho/internal/testutil" ) diff --git a/internal/engine/architect.go b/internal/engine/architect.go index cfa6fc42..08f816be 100644 --- a/internal/engine/architect.go +++ b/internal/engine/architect.go @@ -42,7 +42,7 @@ type PlanStep struct { } // Message is a lightweight chat message used by the architect pipeline. -// This avoids coupling to external message types from eyrie. +// This avoids coupling to external message types from flux. type ArchitectMessage struct { Role string // "system", "user", "assistant" Content string @@ -133,7 +133,7 @@ type architectProvider struct { a *Architect } -func (p *architectProvider) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (p *architectProvider) Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { archMsgs := make([]ArchitectMessage, len(messages)) for i, m := range messages { archMsgs[i] = ArchitectMessage{Role: m.Role, Content: m.Content} @@ -146,10 +146,10 @@ func (p *architectProvider) Chat(ctx context.Context, messages []types.EyrieMess if err != nil { return nil, err } - return &types.EyrieResponse{Content: out}, nil + return &types.FluxResponse{Content: out}, nil } -func (p *architectProvider) StreamChat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { +func (p *architectProvider) StreamChat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.StreamResult, error) { return nil, nil } func (p *architectProvider) Ping(ctx context.Context) error { return nil } diff --git a/internal/engine/branching/cascade.go b/internal/engine/branching/cascade.go index f5d34c4d..d947da30 100644 --- a/internal/engine/branching/cascade.go +++ b/internal/engine/branching/cascade.go @@ -211,7 +211,7 @@ func classifyPrompt(prompt string) string { } // modelForTask maps a task type to the appropriate model using configured roles -// and eyrie catalog tier defaults. +// and flux catalog tier defaults. func (cr *CascadeRouter) modelForTask(taskType string, frugal bool) string { tier := routing.SuggestTierForTask(taskType) @@ -250,7 +250,7 @@ func (cr *CascadeRouter) modelForTask(taskType string, frugal bool) string { } } -// defaultFor returns the best model for a given cost tier via eyrie catalog tier defaults. +// defaultFor returns the best model for a given cost tier via flux catalog tier defaults. func (cr *CascadeRouter) defaultFor(tier ModelTier) string { provider := "" if info, ok := routing.Find(cr.DefaultModel); ok { diff --git a/internal/engine/branching/cascade_test.go b/internal/engine/branching/cascade_test.go index de940ab0..4ed89c5b 100644 --- a/internal/engine/branching/cascade_test.go +++ b/internal/engine/branching/cascade_test.go @@ -8,7 +8,7 @@ import ( const testProvider = "anthropic" -// testTierModels loads haiku/sonnet/opus model IDs from eyrie's catalog (not hardcoded). +// testTierModels loads haiku/sonnet/opus model IDs from flux's catalog (not hardcoded). func testTierModels(t *testing.T, provider string) (haiku, sonnet, opus string) { t.Helper() haiku = routing.PreferredModelForTier(provider, routing.TierHaiku, "") diff --git a/internal/engine/cache_gate.go b/internal/engine/cache_gate.go index 82ea78ed..8311b0af 100644 --- a/internal/engine/cache_gate.go +++ b/internal/engine/cache_gate.go @@ -6,4 +6,4 @@ package engine // cache_planner.go (planCache / cacheDecision) implements this arithmetic: // segments the stable prefix, computes breakpoints, and enables caching only // when the expected reuse count beats the write premium. Full wire-format -// lowering and fleet-wide key-sharding remain eyrie-side. +// lowering and fleet-wide key-sharding remain flux-side. diff --git a/internal/engine/cache_gate_test.go b/internal/engine/cache_gate_test.go index 5ad20879..813445eb 100644 --- a/internal/engine/cache_gate_test.go +++ b/internal/engine/cache_gate_test.go @@ -31,7 +31,7 @@ func TestCacheDecisionLargePrefixOn(t *testing.T) { } func TestCacheDecisionCountsToolCatalog(t *testing.T) { - tools := []types.EyrieTool{ + tools := []types.FluxTool{ {Name: "t1", Description: strings.Repeat("d", 9000), Parameters: map[string]interface{}{"type": "object"}}, } sys := "small" diff --git a/internal/engine/cache_planner.go b/internal/engine/cache_planner.go index ff8c0487..e73eb7de 100644 --- a/internal/engine/cache_planner.go +++ b/internal/engine/cache_planner.go @@ -12,7 +12,7 @@ import ( // catalog) is split into cacheable segments, breakpoints are computed at the // last tool and last system boundaries, and the whole thing is enabled only // when the provider economics pay off for the expected reuse count. Full -// wire-format lowering and key sharding across a fleet remain eyrie-side; +// wire-format lowering and key sharding across a fleet remain flux-side; // this is the deterministic client planner. // Anthropic 5m cache economics: uncached = 1.0x, cache write = 1.25x, @@ -52,7 +52,7 @@ type CachePlan struct { // planCache computes whether and how to cache the stable prefix for a call. // Deterministic and pure so it can be tested without a provider connection. -func planCache(provider, systemPrompt string, tools []types.EyrieTool, expectedReuse int) CachePlan { +func planCache(provider, systemPrompt string, tools []types.FluxTool, expectedReuse int) CachePlan { if expectedReuse <= 0 { expectedReuse = cacheDefaultReuse } @@ -112,6 +112,6 @@ func planCache(provider, systemPrompt string, tools []types.EyrieTool, expectedR // cacheDecision reports whether to request provider-native caching, delegating // to the planner at the default reuse. Kept for backward compatibility. -func cacheDecision(provider, systemPrompt string, tools []types.EyrieTool) bool { +func cacheDecision(provider, systemPrompt string, tools []types.FluxTool) bool { return planCache(provider, systemPrompt, tools, cacheDefaultReuse).Enabled } diff --git a/internal/engine/cache_planner_test.go b/internal/engine/cache_planner_test.go index 555bfc59..c87bdcb2 100644 --- a/internal/engine/cache_planner_test.go +++ b/internal/engine/cache_planner_test.go @@ -11,7 +11,7 @@ func bigSys(n int) string { return strings.Repeat("s", n) } func TestPlanCacheSegmentsAndBreakpoints(t *testing.T) { sys := bigSys(cacheMinPrefixBytes + 2048) - tools := []types.EyrieTool{{Name: "t", Description: strings.Repeat("d", 4096), Parameters: map[string]interface{}{"type": "object"}}} + tools := []types.FluxTool{{Name: "t", Description: strings.Repeat("d", 4096), Parameters: map[string]interface{}{"type": "object"}}} p := planCache("anthropic", sys, tools, 2) if !p.Enabled { t.Fatalf("expected enabled, reason=%q", p.Reason) diff --git a/internal/engine/chat_provider.go b/internal/engine/chat_provider.go index 993892ea..090ffeda 100644 --- a/internal/engine/chat_provider.go +++ b/internal/engine/chat_provider.go @@ -11,7 +11,7 @@ import ( // BuildChatProvider adapts Rho's engine-backed session client to the smaller // provider contract used by host integrations such as Kestrel. Model resolution, -// credentials, routing, and transport remain owned by Eyrie's engine facade. +// credentials, routing, and transport remain owned by Flux's engine facade. func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyProvider string) (types.ChatProvider, string, error) { client, provider, _, err := BuildChatClient(ctx, selection, legacyProvider) if err != nil { @@ -19,7 +19,7 @@ func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyP } provider = strings.TrimSpace(provider) if provider == "" { - return nil, "", fmt.Errorf("eyrie transport: provider unavailable") + return nil, "", fmt.Errorf("flux transport: provider unavailable") } return &engineChatProvider{ client: client, @@ -38,7 +38,7 @@ type engineChatProvider struct { var _ types.ChatProvider = (*engineChatProvider)(nil) -func (p *engineChatProvider) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (p *engineChatProvider) Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { if strings.TrimSpace(opts.Provider) == "" { opts.Provider = p.provider } @@ -48,7 +48,7 @@ func (p *engineChatProvider) Chat(ctx context.Context, messages []types.EyrieMes return p.client.Chat(ctx, messages, opts) } -func (p *engineChatProvider) StreamChat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { +func (p *engineChatProvider) StreamChat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.StreamResult, error) { if strings.TrimSpace(opts.Provider) == "" { opts.Provider = p.provider } diff --git a/internal/engine/chat_provider_test.go b/internal/engine/chat_provider_test.go index 77a6c0bf..35bc02d2 100644 --- a/internal/engine/chat_provider_test.go +++ b/internal/engine/chat_provider_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" "github.com/GrayCodeAI/rho/internal/types" ) @@ -14,14 +14,14 @@ type recordingChatClient struct { streamOptions types.ChatOptions } -func (c *recordingChatClient) Chat(_ context.Context, _ []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (c *recordingChatClient) Chat(_ context.Context, _ []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { c.chatOptions = opts - return &types.EyrieResponse{Content: "ok"}, nil + return &types.FluxResponse{Content: "ok"}, nil } -func (c *recordingChatClient) StreamChatContinue(_ context.Context, _ []types.EyrieMessage, opts types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (c *recordingChatClient) StreamChatContinue(_ context.Context, _ []types.FluxMessage, opts types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { c.streamOptions = opts - events := make(chan types.EyrieStreamEvent) + events := make(chan types.FluxStreamEvent) close(events) return llm.NewStreamResult(events, "", nil), nil } diff --git a/internal/engine/chat_replay.go b/internal/engine/chat_replay.go index 3f44a9ac..ca7174fa 100644 --- a/internal/engine/chat_replay.go +++ b/internal/engine/chat_replay.go @@ -19,14 +19,14 @@ const replayCacheDirEnv = "RHO_REPLAY_CACHE_DIR" const replayFingerprintEnv = "RHO_REPLAY_FINGERPRINT" // replayKey builds the cache key for one completion request. -func replayKey(opts types.ChatOptions, messages []types.EyrieMessage) string { +func replayKey(opts types.ChatOptions, messages []types.FluxMessage) string { return replaycache.Key(replaycache.Fingerprint(os.Getenv(replayFingerprintEnv)), opts.Provider, opts.Model, messages, opts.MaxTokens) } // chatWithReplay wraps client.Chat with the replay cache when // RHO_REPLAY_CACHE_DIR is set; otherwise it calls straight through. -func chatWithReplay(ctx context.Context, client ChatClient, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func chatWithReplay(ctx context.Context, client ChatClient, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { dir := os.Getenv(replayCacheDirEnv) if dir == "" { return client.Chat(ctx, messages, opts) diff --git a/internal/engine/chat_replay_test.go b/internal/engine/chat_replay_test.go index 69436453..ac25ea8b 100644 --- a/internal/engine/chat_replay_test.go +++ b/internal/engine/chat_replay_test.go @@ -14,7 +14,7 @@ func TestChatWithReplayDisabledPassesThrough(t *testing.T) { calls := 0 client := &countingClient{n: &calls, resp: "live"} resp, err := chatWithReplay(context.Background(), client, - []types.EyrieMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{Model: "m"}) + []types.FluxMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{Model: "m"}) if err != nil || resp.Content != "live" { t.Fatalf("resp=%v err=%v", resp, err) } @@ -29,7 +29,7 @@ func TestChatWithReplayCachesAndHits(t *testing.T) { calls := 0 client := &countingClient{n: &calls, resp: "live"} - msgs := []types.EyrieMessage{{Role: "user", Content: "deterministic"}} + msgs := []types.FluxMessage{{Role: "user", Content: "deterministic"}} opts := types.ChatOptions{Provider: "p", Model: "m", MaxTokens: 10} first, err := chatWithReplay(context.Background(), client, msgs, opts) @@ -57,7 +57,7 @@ func TestChatWithReplayFingerprintInvalidates(t *testing.T) { calls := 0 client := &countingClient{n: &calls, resp: "live"} - msgs := []types.EyrieMessage{{Role: "user", Content: "x"}} + msgs := []types.FluxMessage{{Role: "user", Content: "x"}} opts := types.ChatOptions{Provider: "p", Model: "m"} if _, err := chatWithReplay(context.Background(), client, msgs, opts); err != nil { @@ -80,7 +80,7 @@ func TestChatWithReplayDoesNotCacheErrors(t *testing.T) { client := &countingClient{n: &calls, err: errors.New("boom")} for i := 0; i < 2; i++ { if _, err := chatWithReplay(context.Background(), client, - []types.EyrieMessage{{Role: "user", Content: "e"}}, types.ChatOptions{}); err == nil { + []types.FluxMessage{{Role: "user", Content: "e"}}, types.ChatOptions{}); err == nil { t.Fatal("expected error to pass through") } } @@ -99,20 +99,20 @@ type countingClient struct { err error } -func (c *countingClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (c *countingClient) Chat(context.Context, []types.FluxMessage, types.ChatOptions) (*types.FluxResponse, error) { *c.n++ if c.err != nil { return nil, c.err } - return &types.EyrieResponse{Content: c.resp}, nil + return &types.FluxResponse{Content: c.resp}, nil } -func (c *countingClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *countingClient) StreamChatContinue(context.Context, []types.FluxMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { *c.n++ if c.err != nil { return nil, c.err } - ch := make(chan types.EyrieStreamEvent, 1) - ch <- types.EyrieStreamEvent{Type: "done"} + ch := make(chan types.FluxStreamEvent, 1) + ch <- types.FluxStreamEvent{Type: "done"} return &types.StreamResult{Events: ch}, nil } diff --git a/internal/engine/chat_service.go b/internal/engine/chat_service.go index 1bf51ace..96f1dd45 100644 --- a/internal/engine/chat_service.go +++ b/internal/engine/chat_service.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/eyrie/engine" + "github.com/GrayCodeAI/flux/engine" "github.com/GrayCodeAI/rho/internal/observability/metrics" "github.com/GrayCodeAI/rho/internal/resilience/ratelimit" "github.com/GrayCodeAI/rho/internal/resilience/retry" @@ -15,8 +15,8 @@ import ( ) // ChatService is the Session's view of the LLM transport. It owns the -// Eyrie client, provider/model identity, and compatibility-only rate/retry -// controls. Production facade clients own resilience inside Eyrie. The service +// Flux client, provider/model identity, and compatibility-only rate/retry +// controls. Production facade clients own resilience inside Flux. The service // is constructed once in NewSessionWithClient and consulted by // agentLoop every turn. // @@ -26,13 +26,13 @@ import ( // plan. type ChatService struct { mu sync.RWMutex - // client is the eyrie transport. Always non-nil after construction. + // client is the flux transport. Always non-nil after construction. client ChatClient // provider / model are the active LLM identity. provider string model string // deploymentRouting is true when the client is catalog-backed - // (e.g. DeploymentRouter from eyrie/runtime.ChatProvider). + // (e.g. DeploymentRouter from flux/runtime.ChatProvider). deploymentRouting bool // rateLimiter is the per-session token bucket. rateLimiter *ratelimit.Limiter @@ -43,7 +43,7 @@ type ChatService struct { // contCfg is the continuation config for StreamChatContinue. contCfg types.ContinuationConfig // outputSchema, when non-empty, requests a JSON-schema-constrained - // response. Plumbed into eyrie's ChatOptions.ResponseFormat. + // response. Plumbed into flux's ChatOptions.ResponseFormat. outputSchema string // thinkingEnabled is the generic host preference for provider thinking / // reasoning toggles (Z.AI, LongCat, Agnes, …). nil leaves provider default. @@ -99,7 +99,7 @@ func NewChatService(client ChatClient, cfg ChatServiceConfig) *ChatService { } } -// Client returns the underlying eyrie client. Exposed for callers (e.g. +// Client returns the underlying flux client. Exposed for callers (e.g. // background goroutines) that need to issue one-off LLM calls without // the agent-loop retry wrapper. func (c *ChatService) Client() ChatClient { @@ -185,7 +185,7 @@ func (c *ChatService) Reattach(client ChatClient, provider string) { // BuildOptions constructs a types.ChatOptions for an outgoing LLM call, // encoding all the knobs the agent loop needs (system prompt, model, // max tokens, tools, structured output, etc.). -func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens int, tools []types.EyrieTool) types.ChatOptions { +func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens int, tools []types.FluxTool) types.ChatOptions { c.mu.RLock() provider := c.provider thinkingEnabled := c.thinkingEnabled @@ -209,23 +209,23 @@ func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens i } // Opt-in tool-catalog compression (RHO_TOOL_SHRINK=1): fail-open, so the // returned tools equal the input whenever anything is off or drifts. - opts.Tools = shrinkEyrieTools(opts.Tools) + opts.Tools = shrinkFluxTools(opts.Tools) return opts } // Stream issues a streaming LLM call with retry, rate-limit, and // emergency-compact. The returned *types.StreamResult's Events channel -// emits EyrieStreamEvent values; the caller must Close() the result +// emits FluxStreamEvent values; the caller must Close() the result // when done. // // On context cancellation mid-call, returns the cancellation error wrapped // with whatever partial state the upstream had emitted (caller should // check ctx.Err()). // -// Eyrie facade clients advertise that they manage provider resilience. For +// Flux facade clients advertise that they manage provider resilience. For // those clients this service records the product metric and delegates exactly // once; injected legacy clients retain Rho's compatibility retry/rate layer. -func (c *ChatService) Stream(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { +func (c *ChatService) Stream(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.StreamResult, error) { c.mu.RLock() client := c.client rateLimiter := c.rateLimiter @@ -282,11 +282,11 @@ const ( // emergencyCompactWindow messages. This is a last-resort path (the normal // context governor in the agent loop does the real summarization); here we // only need the retry to succeed once. -func emergencyCompact(messages []types.EyrieMessage) []types.EyrieMessage { +func emergencyCompact(messages []types.FluxMessage) []types.FluxMessage { if len(messages) <= emergencyCompactMin { return messages } - out := make([]types.EyrieMessage, 0, emergencyCompactWindow+1) + out := make([]types.FluxMessage, 0, emergencyCompactWindow+1) for _, m := range messages { if m.Role == "system" { out = append(out, m) @@ -304,7 +304,7 @@ func emergencyCompact(messages []types.EyrieMessage) []types.EyrieMessage { // Chat issues a non-streaming LLM call. Used by background goroutines // (sleeptime consolidation, skill distillation) that don't need // incremental events. -func (c *ChatService) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (c *ChatService) Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { client := c.Client() if client == nil { return nil, errors.New("chat service: no client configured") diff --git a/internal/engine/chat_service_test.go b/internal/engine/chat_service_test.go index 1fadd95d..cd8c5119 100644 --- a/internal/engine/chat_service_test.go +++ b/internal/engine/chat_service_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" "github.com/GrayCodeAI/rho/internal/resilience/retry" "github.com/GrayCodeAI/rho/internal/types" ) @@ -137,7 +137,7 @@ func TestChatService_ChatDelegatesToClient(t *testing.T) { }) resp, err := svc.Chat( context.Background(), - []types.EyrieMessage{{Role: "user", Content: "hi"}}, + []types.FluxMessage{{Role: "user", Content: "hi"}}, svc.BuildOptions("sys", "claude-opus-4", 1024, nil), ) if err != nil { @@ -152,11 +152,11 @@ func TestChatService_ChatDelegatesToClient(t *testing.T) { // ChatService.Chat surfaces the underlying error unchanged. type errClient struct{ err error } -func (e *errClient) Chat(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions) (*types.EyrieResponse, error) { +func (e *errClient) Chat(_ context.Context, _ []types.FluxMessage, _ types.ChatOptions) (*types.FluxResponse, error) { return nil, e.err } -func (e *errClient) StreamChatContinue(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (e *errClient) StreamChatContinue(_ context.Context, _ []types.FluxMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { return nil, e.err } @@ -174,11 +174,11 @@ type resilienceManagingTestClient struct { calls int } -func (c *resilienceManagingTestClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (c *resilienceManagingTestClient) Chat(context.Context, []types.FluxMessage, types.ChatOptions) (*types.FluxResponse, error) { return nil, c.err } -func (c *resilienceManagingTestClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *resilienceManagingTestClient) StreamChatContinue(context.Context, []types.FluxMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { c.calls++ return nil, c.err } @@ -187,7 +187,7 @@ func (c *resilienceManagingTestClient) ManagesResilience() bool { return true } func TestChatService_DoesNotDuplicateEngineResilience(t *testing.T) { client := &resilienceManagingTestClient{err: errors.New("routed transport failed")} svc := NewChatService(client, ChatServiceConfig{}) - _, err := svc.Stream(context.Background(), []types.EyrieMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}) + _, err := svc.Stream(context.Background(), []types.FluxMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}) if err == nil { t.Fatal("expected stream error") } @@ -200,16 +200,16 @@ type flakyLegacyStartClient struct { calls int } -func (*flakyLegacyStartClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (*flakyLegacyStartClient) Chat(context.Context, []types.FluxMessage, types.ChatOptions) (*types.FluxResponse, error) { return nil, nil } -func (c *flakyLegacyStartClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *flakyLegacyStartClient) StreamChatContinue(context.Context, []types.FluxMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { c.calls++ if c.calls == 1 { return nil, errors.New("temporary transport failure") } - events := make(chan types.EyrieStreamEvent) + events := make(chan types.FluxStreamEvent) close(events) return llm.NewStreamResult(events, "", nil), nil } @@ -243,21 +243,21 @@ func retryConfigForBoundaryTest() retry.Config { // received on the retry so tests can assert the emergency compact (H3). type overflowThenCaptureClient struct { calls int - seen []types.EyrieMessage + seen []types.FluxMessage started bool } -func (*overflowThenCaptureClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (*overflowThenCaptureClient) Chat(context.Context, []types.FluxMessage, types.ChatOptions) (*types.FluxResponse, error) { return nil, nil } -func (c *overflowThenCaptureClient) StreamChatContinue(_ context.Context, messages []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (c *overflowThenCaptureClient) StreamChatContinue(_ context.Context, messages []types.FluxMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { c.calls++ if c.calls == 1 { return nil, errors.New("input too long: 120000 tokens exceeds the limit of 100000") } c.started = true - c.seen = append([]types.EyrieMessage(nil), messages...) + c.seen = append([]types.FluxMessage(nil), messages...) return &types.StreamResult{}, nil } @@ -265,10 +265,10 @@ func (c *overflowThenCaptureClient) StreamChatContinue(_ context.Context, messag // context-overflow error, the retry sends a compacted (smaller) transcript // instead of re-sending the same overflowing messages. func TestChatService_EmergencyCompactTrimsBeforeRetry(t *testing.T) { - var messages []types.EyrieMessage - messages = append(messages, types.EyrieMessage{Role: "system", Content: "sys"}) + var messages []types.FluxMessage + messages = append(messages, types.FluxMessage{Role: "system", Content: "sys"}) for i := 0; i < 100; i++ { - messages = append(messages, types.EyrieMessage{Role: "user", Content: "message-" + itoaForTest(i)}) + messages = append(messages, types.FluxMessage{Role: "user", Content: "message-" + itoaForTest(i)}) } client := &overflowThenCaptureClient{} diff --git a/internal/engine/client_interface.go b/internal/engine/client_interface.go index 700e6106..fb3560a6 100644 --- a/internal/engine/client_interface.go +++ b/internal/engine/client_interface.go @@ -3,15 +3,15 @@ package engine import ( "context" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" "github.com/GrayCodeAI/rho/internal/types" ) // ChatClient abstracts the LLM client methods used by Session. -// The production implementation is the Eyrie engine adapter; tests can inject a mock. +// The production implementation is the Flux engine adapter; tests can inject a mock. type ChatClient interface { - Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) - StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) + Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) + StreamChatContinue(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) } // resilienceManagingChatClient is an optional capability implemented by @@ -58,18 +58,18 @@ func NewMockClientForTest() ChatClient { type exportedMockClient struct{} -func (m *exportedMockClient) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { - return &types.EyrieResponse{ +func (m *exportedMockClient) Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { + return &types.FluxResponse{ Content: "mock test response", FinishReason: "end_turn", - Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}, + Usage: &types.FluxUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}, }, nil } -func (m *exportedMockClient) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { - ch := make(chan types.EyrieStreamEvent, 5) - ch <- types.EyrieStreamEvent{Type: "content", Content: "mock streamed response"} - ch <- types.EyrieStreamEvent{Type: "done", StopReason: "end_turn", Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}} +func (m *exportedMockClient) StreamChatContinue(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { + ch := make(chan types.FluxStreamEvent, 5) + ch <- types.FluxStreamEvent{Type: "content", Content: "mock streamed response"} + ch <- types.FluxStreamEvent{Type: "done", StopReason: "end_turn", Usage: &types.FluxUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}} close(ch) return &types.StreamResult{Events: ch}, nil } diff --git a/internal/engine/compact.go b/internal/engine/compact.go index aecacd85..52b63162 100644 --- a/internal/engine/compact.go +++ b/internal/engine/compact.go @@ -111,15 +111,15 @@ func (s *Session) smartCompactBody(ctx context.Context) { } tail := raw[len(raw)-keepEnd:] - keep := make([]types.EyrieMessage, 0, len(tail)+2) - keep = append(keep, types.EyrieMessage{ + keep := make([]types.FluxMessage, 0, len(tail)+2) + keep = append(keep, types.FluxMessage{ Role: "user", Content: "[Conversation summary]\n" + summary + "\n\n[Continue from the recent messages below.]", }) // Only emit the assistant ack when the tail starts with a user message; // two adjacent assistant messages are rejected by OpenAI-compat providers. if len(tail) == 0 || tail[0].Role != "assistant" { - keep = append(keep, types.EyrieMessage{ + keep = append(keep, types.FluxMessage{ Role: "assistant", Content: "Understood. I have the context from the summary above. Continuing.", }) @@ -133,7 +133,7 @@ func (s *Session) smartCompactBody(ctx context.Context) { // window on very long transcripts. const summaryInputRuneCap = 32_000 -func (s *Session) generateSummary(ctx context.Context, raw []types.EyrieMessage) string { +func (s *Session) generateSummary(ctx context.Context, raw []types.FluxMessage) string { // Incremental compaction: if a prior summary was persisted by an earlier // compaction, merge the NEW messages into it rather than re-summarizing the // entire conversation from scratch. This preserves already-captured context @@ -152,9 +152,9 @@ func (s *Session) generateSummary(ctx context.Context, raw []types.EyrieMessage) // Build a compact version of the conversation for summarization // using the structured compaction prompt from compact_prompt.go - var summaryMsgs []types.EyrieMessage + var summaryMsgs []types.FluxMessage compactPrompt := BuildIncrementalCompactPrompt(prior) - summaryMsgs = append(summaryMsgs, types.EyrieMessage{ + summaryMsgs = append(summaryMsgs, types.FluxMessage{ Role: "user", Content: compactPrompt + "\n\nConversation:\n", }) @@ -269,7 +269,7 @@ func CompressMessageContent(content string, maxTokens int) string { } // compactModel returns the cheapest available model for the current provider. -// Queries eyrie's catalog at runtime — no hardcoded model names. +// Queries flux's catalog at runtime — no hardcoded model names. // Summarization doesn't need frontier reasoning, so the cheapest model suffices. func (s *Session) compactModel() string { provider := strings.ToLower(s.ChatLLM().Provider()) diff --git a/internal/engine/compact/aliases_extra_test.go b/internal/engine/compact/aliases_extra_test.go index 7fc6e04f..b67ab875 100644 --- a/internal/engine/compact/aliases_extra_test.go +++ b/internal/engine/compact/aliases_extra_test.go @@ -83,7 +83,7 @@ func TestCountClearableToolResults(t *testing.T) { func TestIsThinkingMessage(t *testing.T) { // Empty message - result := isThinkingMessage(types.EyrieMessage{}) + result := isThinkingMessage(types.FluxMessage{}) if result { t.Error("expected false for empty message") } @@ -130,7 +130,7 @@ func TestReadSessionMemory_NonExistent(t *testing.T) { func TestIsCompactBoundary(t *testing.T) { // Empty message - result := IsCompactBoundary(types.EyrieMessage{}) + result := IsCompactBoundary(types.FluxMessage{}) if result { t.Error("expected false for empty message") } diff --git a/internal/engine/compact/api.go b/internal/engine/compact/api.go index 8137f7f7..ad8f736d 100644 --- a/internal/engine/compact/api.go +++ b/internal/engine/compact/api.go @@ -30,7 +30,7 @@ var mutatingTools = map[string]bool{ "NotebookEdit": true, } -func APICompactMessages(msgs []types.EyrieMessage, cfg APICompactConfig) []types.EyrieMessage { +func APICompactMessages(msgs []types.FluxMessage, cfg APICompactConfig) []types.FluxMessage { totalTokens := token.EstimateTokens(msgs) if totalTokens < cfg.TriggerTokens { return msgs @@ -41,7 +41,7 @@ func APICompactMessages(msgs []types.EyrieMessage, cfg APICompactConfig) []types return msgs } - result := make([]types.EyrieMessage, len(msgs)) + result := make([]types.FluxMessage, len(msgs)) copy(result, msgs) freed := 0 @@ -101,7 +101,7 @@ func APICompactMessages(msgs []types.EyrieMessage, cfg APICompactConfig) []types return result } -func CountClearableToolResults(msgs []types.EyrieMessage) int { +func CountClearableToolResults(msgs []types.FluxMessage) int { count := 0 for _, m := range msgs { if len(m.ToolResults) > 0 && m.ToolResults[0].Content != "[Old tool result content cleared]" { @@ -114,6 +114,6 @@ func CountClearableToolResults(msgs []types.EyrieMessage) int { return count } -func isThinkingMessage(m types.EyrieMessage) bool { +func isThinkingMessage(m types.FluxMessage) bool { return len(m.Content) > 0 && m.Content[0] == '<' && len(m.ToolUse) == 0 } diff --git a/internal/engine/compact/files.go b/internal/engine/compact/files.go index f494ea6f..03dc3160 100644 --- a/internal/engine/compact/files.go +++ b/internal/engine/compact/files.go @@ -35,7 +35,7 @@ func (ft *FileTracker) RecordModified(path string) { ft.ModifiedFiles[path]++ } -func (ft *FileTracker) ExtractFromMessages(messages []types.EyrieMessage) { +func (ft *FileTracker) ExtractFromMessages(messages []types.FluxMessage) { for _, msg := range messages { if msg.Role != "assistant" { continue diff --git a/internal/engine/compact/files_test.go b/internal/engine/compact/files_test.go index a0a3fc59..cb02e5fd 100644 --- a/internal/engine/compact/files_test.go +++ b/internal/engine/compact/files_test.go @@ -67,7 +67,7 @@ func TestFileTracker_RecordModified_Empty(t *testing.T) { func TestFileTracker_ExtractFromMessages(t *testing.T) { t.Parallel() ft := NewFileTracker() - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ { Role: "assistant", ToolUse: []types.ToolCall{ @@ -88,7 +88,7 @@ func TestFileTracker_ExtractFromMessages(t *testing.T) { func TestFileTracker_ExtractFromMessages_SkipNonAssistant(t *testing.T) { t.Parallel() ft := NewFileTracker() - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ {Role: "user", ToolUse: []types.ToolCall{{Name: "Read", Arguments: map[string]interface{}{"path": "x.go"}}}}, } ft.ExtractFromMessages(messages) diff --git a/internal/engine/compact/incremental_test.go b/internal/engine/compact/incremental_test.go index d34bfbeb..93726db5 100644 --- a/internal/engine/compact/incremental_test.go +++ b/internal/engine/compact/incremental_test.go @@ -32,7 +32,7 @@ func TestBuildIncrementalCompactPromptEmptyPriorFallsBack(t *testing.T) { } func TestExtractPriorSummaryFound(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "[Conversation summary]\n## Goal\n- build\n\n[Continue from the recent messages below.]"}, {Role: "assistant", Content: "ok"}, } @@ -43,7 +43,7 @@ func TestExtractPriorSummaryFound(t *testing.T) { } func TestExtractPriorSummaryNone(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hello"}, } if got := ExtractPriorSummary(msgs); got != "" { @@ -52,7 +52,7 @@ func TestExtractPriorSummaryNone(t *testing.T) { } func TestExtractPriorSummaryIgnoresNonSummaryFirst(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "[Session memory summary]\nstuff"}, {Role: "user", Content: "[Conversation summary]\n## Goal\n- x"}, } diff --git a/internal/engine/compact/micro.go b/internal/engine/compact/micro.go index 3769fa89..3c74ec7f 100644 --- a/internal/engine/compact/micro.go +++ b/internal/engine/compact/micro.go @@ -25,7 +25,7 @@ type resultInfo struct { toolName string } -func MicrocompactMessages(msgs []types.EyrieMessage, cfg MicroCompactConfig) []types.EyrieMessage { +func MicrocompactMessages(msgs []types.FluxMessage, cfg MicroCompactConfig) []types.FluxMessage { var compactableResults []resultInfo for i, m := range msgs { if len(m.ToolResults) == 0 { @@ -47,7 +47,7 @@ func MicrocompactMessages(msgs []types.EyrieMessage, cfg MicroCompactConfig) []t clearSet[compactableResults[i].index] = true } - result := make([]types.EyrieMessage, len(msgs)) + result := make([]types.FluxMessage, len(msgs)) copy(result, msgs) for idx := range clearSet { oldResults := result[idx].ToolResults @@ -59,7 +59,7 @@ func MicrocompactMessages(msgs []types.EyrieMessage, cfg MicroCompactConfig) []t IsError: tr.IsError, } } - result[idx] = types.EyrieMessage{ + result[idx] = types.FluxMessage{ Role: result[idx].Role, ToolResults: newResults, } @@ -68,7 +68,7 @@ func MicrocompactMessages(msgs []types.EyrieMessage, cfg MicroCompactConfig) []t return result } -func ToolNameForResult(m types.EyrieMessage, msgs []types.EyrieMessage) string { +func ToolNameForResult(m types.FluxMessage, msgs []types.FluxMessage) string { if len(m.ToolResults) == 0 { return "" } @@ -83,7 +83,7 @@ func ToolNameForResult(m types.EyrieMessage, msgs []types.EyrieMessage) string { return "" } -func HasTimeGap(msgs []types.EyrieMessage, threshold time.Duration) bool { +func HasTimeGap(msgs []types.FluxMessage, threshold time.Duration) bool { lastTextIdx := -1 for i := len(msgs) - 1; i >= 0; i-- { if HasTextContent(msgs[i]) && msgs[i].Role == "assistant" { diff --git a/internal/engine/compact/prompt.go b/internal/engine/compact/prompt.go index 49886025..244dd2de 100644 --- a/internal/engine/compact/prompt.go +++ b/internal/engine/compact/prompt.go @@ -128,7 +128,7 @@ const PriorSummaryPrefix = "[Conversation summary]" // ExtractPriorSummary extracts the previously generated summary text from the // first message of a conversation if one was persisted by an earlier // compaction. It returns "" when no prior summary is present. -func ExtractPriorSummary(msgs []types.EyrieMessage) string { +func ExtractPriorSummary(msgs []types.FluxMessage) string { for _, m := range msgs { if m.Role != "user" { continue diff --git a/internal/engine/compact/session_memory.go b/internal/engine/compact/session_memory.go index 2c80b3e0..0ac6aa6b 100644 --- a/internal/engine/compact/session_memory.go +++ b/internal/engine/compact/session_memory.go @@ -25,7 +25,7 @@ func DefaultSessionMemoryConfig() SessionMemoryConfig { } } -func CalculateMessagesToKeepIndex(msgs []types.EyrieMessage, cfg SessionMemoryConfig) int { +func CalculateMessagesToKeepIndex(msgs []types.FluxMessage, cfg SessionMemoryConfig) int { if len(msgs) == 0 { return 0 } @@ -55,8 +55,8 @@ func CalculateMessagesToKeepIndex(msgs []types.EyrieMessage, cfg SessionMemoryCo return idx } -func FilterCompactBoundaries(msgs []types.EyrieMessage) []types.EyrieMessage { - result := make([]types.EyrieMessage, 0, len(msgs)) +func FilterCompactBoundaries(msgs []types.FluxMessage) []types.FluxMessage { + result := make([]types.FluxMessage, 0, len(msgs)) for _, m := range msgs { if IsCompactBoundary(m) { continue @@ -66,7 +66,7 @@ func FilterCompactBoundaries(msgs []types.EyrieMessage) []types.EyrieMessage { return result } -func IsCompactBoundary(m types.EyrieMessage) bool { +func IsCompactBoundary(m types.FluxMessage) bool { if m.Role != "user" { return false } diff --git a/internal/engine/compact/split_test.go b/internal/engine/compact/split_test.go index a1268b7e..21543392 100644 --- a/internal/engine/compact/split_test.go +++ b/internal/engine/compact/split_test.go @@ -39,28 +39,28 @@ func TestIsCompactableTool_Empty(t *testing.T) { // --------------------------------------------------------------------------- func TestHasTextContent_WithContent(t *testing.T) { - msg := types.EyrieMessage{Role: "assistant", Content: "Hello world"} + msg := types.FluxMessage{Role: "assistant", Content: "Hello world"} if !HasTextContent(msg) { t.Error("expected true for message with content") } } func TestHasTextContent_EmptyContent(t *testing.T) { - msg := types.EyrieMessage{Role: "assistant", Content: ""} + msg := types.FluxMessage{Role: "assistant", Content: ""} if HasTextContent(msg) { t.Error("expected false for empty content") } } func TestHasTextContent_WhitespaceOnly(t *testing.T) { - msg := types.EyrieMessage{Role: "assistant", Content: " \n\t "} + msg := types.FluxMessage{Role: "assistant", Content: " \n\t "} if HasTextContent(msg) { t.Error("expected false for whitespace-only content") } } func TestHasTextContent_ToolResultIgnored(t *testing.T) { - msg := types.EyrieMessage{ + msg := types.FluxMessage{ Role: "user", Content: "has content", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "output"}}, @@ -71,7 +71,7 @@ func TestHasTextContent_ToolResultIgnored(t *testing.T) { } func TestHasTextContent_WithToolUse(t *testing.T) { - msg := types.EyrieMessage{ + msg := types.FluxMessage{ Role: "assistant", Content: "Using tool", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}, @@ -134,7 +134,7 @@ func TestCompactResult_Fields(t *testing.T) { // --------------------------------------------------------------------------- func TestAdjustIndex_StartIndexZero(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "ok"}}}, } @@ -145,7 +145,7 @@ func TestAdjustIndex_StartIndexZero(t *testing.T) { } func TestAdjustIndex_StartBeyondRange(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hello"}, } got := AdjustIndexToPreserveAPIInvariants(msgs, 100) @@ -155,7 +155,7 @@ func TestAdjustIndex_StartBeyondRange(t *testing.T) { } func TestAdjustIndex_UnresolvedToolPair(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "do stuff"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "ok"}}}, diff --git a/internal/engine/compact/strategy.go b/internal/engine/compact/strategy.go index 2c1466a4..c065e25f 100644 --- a/internal/engine/compact/strategy.go +++ b/internal/engine/compact/strategy.go @@ -8,7 +8,7 @@ import ( ) type CompactResult struct { - Messages []types.EyrieMessage + Messages []types.FluxMessage Summary string TokensBefore int TokensAfter int @@ -52,7 +52,7 @@ func IsCompactableTool(name string) bool { return compactableTools[name] } -func AdjustIndexToPreserveAPIInvariants(msgs []types.EyrieMessage, startIdx int) int { +func AdjustIndexToPreserveAPIInvariants(msgs []types.FluxMessage, startIdx int) int { if startIdx <= 0 { return 0 } @@ -87,7 +87,7 @@ func AdjustIndexToPreserveAPIInvariants(msgs []types.EyrieMessage, startIdx int) return idx } -func HasTextContent(m types.EyrieMessage) bool { +func HasTextContent(m types.FluxMessage) bool { if len(m.ToolResults) > 0 { return false } diff --git a/internal/engine/compact/strategy_test.go b/internal/engine/compact/strategy_test.go index 5d527411..733c0880 100644 --- a/internal/engine/compact/strategy_test.go +++ b/internal/engine/compact/strategy_test.go @@ -10,7 +10,7 @@ import ( ) func TestCompactEstimateTokens(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "Hello world"}, {Role: "assistant", Content: strings.Repeat("x", 400)}, } @@ -18,7 +18,7 @@ func TestCompactEstimateTokens(t *testing.T) { if tokens < 1 { t.Errorf("expected at least 1 token, got %d", tokens) } - shortMsgs := []types.EyrieMessage{ + shortMsgs := []types.FluxMessage{ {Role: "user", Content: "hi"}, } shortTokens := token.EstimateTokens(shortMsgs) @@ -30,7 +30,7 @@ func TestCompactEstimateTokens(t *testing.T) { func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { tests := []struct { name string - msgs []types.EyrieMessage + msgs []types.FluxMessage startIdx int wantIdx int }{ @@ -42,7 +42,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "no tool pairs", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi"}, {Role: "user", Content: "bye"}, @@ -52,7 +52,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "tool_result at startIdx - moves back past tool_use", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "output"}}}, @@ -63,7 +63,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "at boundary already", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "response"}, }, @@ -83,7 +83,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { } func TestMicrocompactMessages(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "read file.go"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Read"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "package main\nfunc main() {}"}}}, @@ -132,7 +132,7 @@ func TestMicrocompactMessages(t *testing.T) { } func TestAPICompactMessages(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash", Arguments: map[string]interface{}{"command": strings.Repeat("x", 1000)}}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: strings.Repeat("output ", 1000)}}}, @@ -158,7 +158,7 @@ func TestAPICompactMessages(t *testing.T) { } func TestAPICompactPreservesMutatingTools(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "edit file"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Edit", Arguments: map[string]interface{}{"old_string": strings.Repeat("x", 1000), "new_string": "y"}}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: strings.Repeat("edited ", 500)}}}, @@ -180,7 +180,7 @@ func TestAPICompactPreservesMutatingTools(t *testing.T) { } func TestCalculateMessagesToKeepIndex(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: strings.Repeat("hello ", 100)}, {Role: "assistant", Content: strings.Repeat("response ", 100)}, {Role: "user", Content: strings.Repeat("follow up ", 100)}, @@ -205,7 +205,7 @@ func TestCalculateMessagesToKeepIndex(t *testing.T) { } func TestFilterCompactBoundaries(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "[Session memory summary]\nold stuff"}, {Role: "assistant", Content: "Understood."}, {Role: "user", Content: "real message"}, diff --git a/internal/engine/compact/transcript_segments.go b/internal/engine/compact/transcript_segments.go index bdadf4a6..d4049308 100644 --- a/internal/engine/compact/transcript_segments.go +++ b/internal/engine/compact/transcript_segments.go @@ -80,7 +80,7 @@ func IndexPath(sessionID string) string { // RenderSegmentToMarkdown renders messages into a self-contained markdown // segment. Pure: no I/O. -func RenderSegmentToMarkdown(msgs []types.EyrieMessage, segIndex int, detail CompactionDetail) string { +func RenderSegmentToMarkdown(msgs []types.FluxMessage, segIndex int, detail CompactionDetail) string { var b strings.Builder fmt.Fprintf(&b, "# Compaction segment %d\n\n", segIndex) fmt.Fprintf(&b, "Recorded: %s\n", time.Now().UTC().Format(time.RFC3339)) @@ -112,7 +112,7 @@ func RenderSegmentToMarkdown(msgs []types.EyrieMessage, segIndex int, detail Com return b.String() } -func renderTurn(m types.EyrieMessage, detail CompactionDetail) string { +func renderTurn(m types.FluxMessage, detail CompactionDetail) string { var b strings.Builder role := strings.ToUpper(m.Role) fmt.Fprintf(&b, "## %s\n", role) @@ -212,7 +212,7 @@ func NextSegmentIndex(sessionID string) int { // WriteCompactionSegment persists msgs as the next segment and appends an // INDEX.md row. It returns the written segment path. Callers should treat // errors as non-fatal: segment persistence must never block compaction. -func WriteCompactionSegment(sessionID string, msgs []types.EyrieMessage, detail CompactionDetail) (string, error) { +func WriteCompactionSegment(sessionID string, msgs []types.FluxMessage, detail CompactionDetail) (string, error) { if sessionID == "" { return "", fmt.Errorf("compact: empty session id") } diff --git a/internal/engine/compact/transcript_segments_test.go b/internal/engine/compact/transcript_segments_test.go index ca2a0aae..da543558 100644 --- a/internal/engine/compact/transcript_segments_test.go +++ b/internal/engine/compact/transcript_segments_test.go @@ -9,8 +9,8 @@ import ( "github.com/GrayCodeAI/rho/internal/types" ) -func segTestMessages() []types.EyrieMessage { - return []types.EyrieMessage{ +func segTestMessages() []types.FluxMessage { + return []types.FluxMessage{ {Role: "user", Content: "fix the flaky test in pkg/foo"}, { Role: "assistant", Content: "Looking into it.", diff --git a/internal/engine/compact_auto.go b/internal/engine/compact_auto.go index 7ffabac2..0bc85a39 100644 --- a/internal/engine/compact_auto.go +++ b/internal/engine/compact_auto.go @@ -173,7 +173,7 @@ type SmartCompactStrategy struct{} func (s *SmartCompactStrategy) Name() string { return "smart" } -func (s *SmartCompactStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { +func (s *SmartCompactStrategy) ShouldTrigger(msgs []types.FluxMessage, tokenCount, threshold int) bool { return tokenCount >= threshold && len(msgs) > 20 } @@ -196,7 +196,7 @@ type TruncateStrategy struct{} func (s *TruncateStrategy) Name() string { return "truncate" } -func (s *TruncateStrategy) ShouldTrigger(_ []types.EyrieMessage, tokenCount, threshold int) bool { +func (s *TruncateStrategy) ShouldTrigger(_ []types.FluxMessage, tokenCount, threshold int) bool { return tokenCount >= threshold } diff --git a/internal/engine/compact_clear.go b/internal/engine/compact_clear.go index 837a2cf7..abe30faf 100644 --- a/internal/engine/compact_clear.go +++ b/internal/engine/compact_clear.go @@ -28,7 +28,7 @@ const ( // recent clearKeepRecent tool-result messages intact, until estimatedTokens // drops below threshold. It returns the new messages and the estimated tokens // freed. Adopted from herm's clearOldToolResults. -func clearOldToolResults(msgs []types.EyrieMessage, estimatedTokens, threshold int) ([]types.EyrieMessage, int) { +func clearOldToolResults(msgs []types.FluxMessage, estimatedTokens, threshold int) ([]types.FluxMessage, int) { if threshold <= 0 || estimatedTokens <= 0 || len(msgs) == 0 { return msgs, 0 } @@ -94,7 +94,7 @@ func (s *Session) ClearOldToolResults(ctx context.Context) bool { } // toolResultBytes returns the total byte size of a message's tool results. -func toolResultBytes(m types.EyrieMessage) int { +func toolResultBytes(m types.FluxMessage) int { total := 0 for _, tr := range m.ToolResults { total += len(tr.Content) @@ -104,7 +104,7 @@ func toolResultBytes(m types.EyrieMessage) int { // clearMessageToolResults replaces each non-empty tool-result content with the // placeholder and returns the estimated tokens freed (~4 bytes per token). -func clearMessageToolResults(m *types.EyrieMessage) int { +func clearMessageToolResults(m *types.FluxMessage) int { freedBytes := 0 for i := range m.ToolResults { c := m.ToolResults[i].Content @@ -119,8 +119,8 @@ func clearMessageToolResults(m *types.EyrieMessage) int { // cloneToolResults copies msgs, deep-copying the ToolResults slices so callers // can mutate the copy without touching the persisted originals. -func cloneToolResults(msgs []types.EyrieMessage) []types.EyrieMessage { - out := make([]types.EyrieMessage, len(msgs)) +func cloneToolResults(msgs []types.FluxMessage) []types.FluxMessage { + out := make([]types.FluxMessage, len(msgs)) for i, m := range msgs { out[i] = m if m.ToolResults != nil { diff --git a/internal/engine/compact_clear_test.go b/internal/engine/compact_clear_test.go index 0ac81040..c4879a56 100644 --- a/internal/engine/compact_clear_test.go +++ b/internal/engine/compact_clear_test.go @@ -6,12 +6,12 @@ import ( "github.com/GrayCodeAI/rho/internal/types" ) -func mkMsg(role string, trs []types.ToolResult) types.EyrieMessage { - return types.EyrieMessage{Role: role, ToolResults: trs} +func mkMsg(role string, trs []types.ToolResult) types.FluxMessage { + return types.FluxMessage{Role: role, ToolResults: trs} } func TestClearOldToolResultsNoOpWhenBelowThreshold(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ mkMsg("user", []types.ToolResult{{Content: "aaaa"}}), } out, freed := clearOldToolResults(msgs, 100, 1000) @@ -22,7 +22,7 @@ func TestClearOldToolResultsNoOpWhenBelowThreshold(t *testing.T) { func TestClearOldToolResultsClearsBiggestFirst(t *testing.T) { // 6 tool-result messages; threshold low so clearing triggers; keep last 4. - msgs := make([]types.EyrieMessage, 6) + msgs := make([]types.FluxMessage, 6) for i := range msgs { msgs[i] = mkMsg("user", []types.ToolResult{{Content: "12345678"}}) // 8 bytes } @@ -46,7 +46,7 @@ func TestClearOldToolResultsClearsBiggestFirst(t *testing.T) { func TestClearOldToolResultsStopsWhenUnderThreshold(t *testing.T) { // Huge contents, small threshold: it clears until under threshold, then stops. - msgs := make([]types.EyrieMessage, 6) + msgs := make([]types.FluxMessage, 6) for i := range msgs { msgs[i] = mkMsg("user", []types.ToolResult{{Content: "12345678"}}) } @@ -63,7 +63,7 @@ func TestClearOldToolResultsStopsWhenUnderThreshold(t *testing.T) { } func TestClearOldToolResultsSkipsAlreadyCleared(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ mkMsg("user", []types.ToolResult{{Content: outputClearedPlaceholder}}), mkMsg("user", []types.ToolResult{{Content: outputClearedPlaceholder}}), mkMsg("user", []types.ToolResult{{Content: outputClearedPlaceholder}}), diff --git a/internal/engine/compact_micro_engine.go b/internal/engine/compact_micro_engine.go index f9307b5a..9a085183 100644 --- a/internal/engine/compact_micro_engine.go +++ b/internal/engine/compact_micro_engine.go @@ -13,7 +13,7 @@ type MicroCompactStrategy struct{} func (s *MicroCompactStrategy) Name() string { return "micro" } -func (s *MicroCompactStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { +func (s *MicroCompactStrategy) ShouldTrigger(msgs []types.FluxMessage, tokenCount, threshold int) bool { if tokenCount < threshold/2 { return false } diff --git a/internal/engine/compact_provider_native.go b/internal/engine/compact_provider_native.go index 3f0a172e..066c020d 100644 --- a/internal/engine/compact_provider_native.go +++ b/internal/engine/compact_provider_native.go @@ -4,18 +4,18 @@ import ( "context" "fmt" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" "github.com/GrayCodeAI/rho/internal/types" ) // ProviderNativeCompactStrategy delegates provider-specific compaction to -// Eyrie. Rho owns when conversation state is compacted and how the resulting -// summary is inserted; Eyrie owns credentials and provider transport details. +// Flux. Rho owns when conversation state is compacted and how the resulting +// summary is inserted; Flux owns credentials and provider transport details. type ProviderNativeCompactStrategy struct{} func (s *ProviderNativeCompactStrategy) Name() string { return "provider_native" } -func (s *ProviderNativeCompactStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { +func (s *ProviderNativeCompactStrategy) ShouldTrigger(msgs []types.FluxMessage, tokenCount, threshold int) bool { return tokenCount >= threshold && len(msgs) >= 8 } @@ -49,8 +49,8 @@ func (s *ProviderNativeCompactStrategy) Compact(ctx context.Context, sess *Sessi if keepEnd > len(messagesBefore) { keepEnd = len(messagesBefore) } - tail := append([]types.EyrieMessage(nil), messagesBefore[len(messagesBefore)-keepEnd:]...) - messages := append([]types.EyrieMessage{{Role: "user", Content: FormatCompactSummary(summary)}}, tail...) + tail := append([]types.FluxMessage(nil), messagesBefore[len(messagesBefore)-keepEnd:]...) + messages := append([]types.FluxMessage{{Role: "user", Content: FormatCompactSummary(summary)}}, tail...) compact := &CompactResult{ Messages: messages, TokensBefore: tokensBefore, diff --git a/internal/engine/compact_reexports.go b/internal/engine/compact_reexports.go index c851c857..0c9014e3 100644 --- a/internal/engine/compact_reexports.go +++ b/internal/engine/compact_reexports.go @@ -42,7 +42,7 @@ func BuildIncrementalCompactPrompt(priorSummary string) string { return compact.BuildIncrementalCompactPrompt(priorSummary) } -func ExtractPriorSummary(msgs []types.EyrieMessage) string { +func ExtractPriorSummary(msgs []types.FluxMessage) string { return compact.ExtractPriorSummary(msgs) } @@ -51,7 +51,7 @@ func ExtractPriorSummary(msgs []types.EyrieMessage) string { const PriorSummaryPrefix = compact.PriorSummaryPrefix func IsCompactableTool(name string) bool { return compact.IsCompactableTool(name) } -func AdjustIndexToPreserveAPIInvariants(msgs []types.EyrieMessage, startIdx int) int { +func AdjustIndexToPreserveAPIInvariants(msgs []types.FluxMessage, startIdx int) int { return compact.AdjustIndexToPreserveAPIInvariants(msgs, startIdx) } -func HasTextContent(m types.EyrieMessage) bool { return compact.HasTextContent(m) } +func HasTextContent(m types.FluxMessage) bool { return compact.HasTextContent(m) } diff --git a/internal/engine/compact_regression_test.go b/internal/engine/compact_regression_test.go index 62b31376..50f17cc2 100644 --- a/internal/engine/compact_regression_test.go +++ b/internal/engine/compact_regression_test.go @@ -24,18 +24,18 @@ func testSession(t *testing.T, window int) *Session { // toolPairMessages returns a transcript where an assistant tool_use is followed // by a user tool_result, embedded among filler messages. -func toolPairMessages(total int, toolUseAt int) []types.EyrieMessage { - msgs := make([]types.EyrieMessage, total) +func toolPairMessages(total int, toolUseAt int) []types.FluxMessage { + msgs := make([]types.FluxMessage, total) for i := range msgs { - msgs[i] = types.EyrieMessage{Role: "user", Content: "filler"} + msgs[i] = types.FluxMessage{Role: "user", Content: "filler"} } - msgs[toolUseAt] = types.EyrieMessage{ + msgs[toolUseAt] = types.FluxMessage{ Role: "assistant", Content: "calling tool", ToolUse: []types.ToolCall{{ID: "tu-1", Name: "read_file", Arguments: map[string]any{"path": "a.go"}}}, } if toolUseAt+1 < total { - msgs[toolUseAt+1] = types.EyrieMessage{ + msgs[toolUseAt+1] = types.FluxMessage{ Role: "user", Content: "tool responded", ToolResults: []types.ToolResult{ @@ -100,8 +100,8 @@ func TestSmartCompactAvoidsAdjacentAssistantRoles(t *testing.T) { // Build a long transcript whose kept tail starts with an assistant message // (conversation ended on a final assistant turn). msgs := makeMessages(40) - msgs[30] = types.EyrieMessage{Role: "assistant", Content: "tail starts with assistant"} - msgs[31] = types.EyrieMessage{Role: "user", Content: "next user turn"} + msgs[30] = types.FluxMessage{Role: "assistant", Content: "tail starts with assistant"} + msgs[31] = types.FluxMessage{Role: "user", Content: "next user turn"} s := testSession(t, 128000) s.Persistence().SetRawMessages(msgs) @@ -121,7 +121,7 @@ func TestSplitTurnCompactTruncatesOversizedMessageToSecondHalf(t *testing.T) { // and the retained message must contain exactly its second half. msgs := makeMessages(40) big := strings.Repeat("x", 20000) - msgs[35] = types.EyrieMessage{Role: "user", Content: big} + msgs[35] = types.FluxMessage{Role: "user", Content: big} s := testSession(t, 128000) s.Persistence().SetRawMessages(msgs) @@ -156,11 +156,11 @@ func TestApplyCompactionPreservesConcurrentAppends(t *testing.T) { raw := s.Persistence().RawMessages() // snapshot (len 30) // Concurrent AddUser between snapshot and apply: concurrent := s.Persistence().RawMessages() - concurrent = append(concurrent, types.EyrieMessage{Role: "user", Content: "concurrent user turn"}) + concurrent = append(concurrent, types.FluxMessage{Role: "user", Content: "concurrent user turn"}) s.Persistence().SetRawMessages(concurrent) // Compaction result computed from the stale snapshot (keeps only 20): - keep := append([]types.EyrieMessage(nil), raw[:20]...) + keep := append([]types.FluxMessage(nil), raw[:20]...) s.Persistence().ApplyCompaction(keep, len(raw)) kept := s.Persistence().RawMessages() @@ -176,12 +176,12 @@ func TestShouldAutoCompactCountsToolTokens(t *testing.T) { // Content alone stays under the threshold; adding tool_result payloads // crosses it. ShouldAutoCompact must count the tool tokens. s := testSession(t, 2000) // threshold = 85% of 2000 = 1700 - msgs := make([]types.EyrieMessage, 40) + msgs := make([]types.FluxMessage, 40) for i := range msgs { if i%2 == 0 { - msgs[i] = types.EyrieMessage{Role: "user", Content: "short"} + msgs[i] = types.FluxMessage{Role: "user", Content: "short"} } else { - msgs[i] = types.EyrieMessage{Role: "assistant", Content: "short reply"} + msgs[i] = types.FluxMessage{Role: "assistant", Content: "short reply"} } } s.Persistence().SetRawMessages(msgs) @@ -204,9 +204,9 @@ func TestShouldAutoCompactCountsToolTokens(t *testing.T) { func TestGenerateSummaryInputCapped(t *testing.T) { // generateSummary must not feed an unbounded prompt even if the transcript // is huge (summaryInputRuneCap applies). - msgs := make([]types.EyrieMessage, 0, 500) + msgs := make([]types.FluxMessage, 0, 500) for i := 0; i < 500; i++ { - msgs = append(msgs, types.EyrieMessage{Role: "user", Content: strings.Repeat("content ", 100)}) + msgs = append(msgs, types.FluxMessage{Role: "user", Content: strings.Repeat("content ", 100)}) } s := testSession(t, 128000) summary := s.generateSummary(context.Background(), msgs) diff --git a/internal/engine/compact_relevance.go b/internal/engine/compact_relevance.go index e4bccc4c..e2ca5444 100644 --- a/internal/engine/compact_relevance.go +++ b/internal/engine/compact_relevance.go @@ -23,7 +23,7 @@ type RelevancePruneStrategy struct { func (s *RelevancePruneStrategy) Name() string { return "relevance" } -func (s *RelevancePruneStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { +func (s *RelevancePruneStrategy) ShouldTrigger(msgs []types.FluxMessage, tokenCount, threshold int) bool { return tokenCount >= threshold && len(msgs) >= 20 } @@ -43,7 +43,7 @@ func (s *RelevancePruneStrategy) Compact(ctx context.Context, sess *Session) (*C }, ) - out := toEyrieMessages(pruned) + out := toFluxMessages(pruned) sess.Persistence().SetMessages(out) tokensAfter := EstimateTokens(sess.Persistence().RawMessages()) return &CompactResult{ @@ -55,7 +55,7 @@ func (s *RelevancePruneStrategy) Compact(ctx context.Context, sess *Session) (*C } // toPruneMessages adapts raw session messages to the relevance-prune shape. -func toPruneMessages(raw []types.EyrieMessage, now time.Time) []relevanceprune.Message { +func toPruneMessages(raw []types.FluxMessage, now time.Time) []relevanceprune.Message { out := make([]relevanceprune.Message, len(raw)) for i, m := range raw { out[i] = relevanceprune.Message{ @@ -69,15 +69,15 @@ func toPruneMessages(raw []types.EyrieMessage, now time.Time) []relevanceprune.M return out } -func toEyrieMessages(msgs []relevanceprune.Message) []types.EyrieMessage { - out := make([]types.EyrieMessage, len(msgs)) +func toFluxMessages(msgs []relevanceprune.Message) []types.FluxMessage { + out := make([]types.FluxMessage, len(msgs)) for i, m := range msgs { - out[i] = types.EyrieMessage{Role: m.Role, Content: m.Content} + out[i] = types.FluxMessage{Role: m.Role, Content: m.Content} } return out } -func hasErrorResult(m types.EyrieMessage) bool { +func hasErrorResult(m types.FluxMessage) bool { for _, tr := range m.ToolResults { if tr.IsError { return true @@ -86,7 +86,7 @@ func hasErrorResult(m types.EyrieMessage) bool { return false } -func lastUserText(raw []types.EyrieMessage) string { +func lastUserText(raw []types.FluxMessage) string { for i := len(raw) - 1; i >= 0; i-- { if raw[i].Role == "user" { return raw[i].Content diff --git a/internal/engine/compact_relevance_test.go b/internal/engine/compact_relevance_test.go index c17fcf01..313d1fb9 100644 --- a/internal/engine/compact_relevance_test.go +++ b/internal/engine/compact_relevance_test.go @@ -21,11 +21,11 @@ func TestRelevancePruneStrategy_ShouldTrigger(t *testing.T) { func TestRelevancePruneStrategy_Compact(t *testing.T) { sess := NewSessionWithClient(NewMockClientForTest(), "test", "test-model", "", nil, false) // A long transcript; last user message is the task context. - var msgs []types.EyrieMessage + var msgs []types.FluxMessage for i := 0; i < 60; i++ { - msgs = append(msgs, types.EyrieMessage{Role: "user", Content: "unrelated filler content that has no shared keywords"}) + msgs = append(msgs, types.FluxMessage{Role: "user", Content: "unrelated filler content that has no shared keywords"}) } - msgs = append(msgs, types.EyrieMessage{Role: "user", Content: "refactor the payment module and update all tests"}) + msgs = append(msgs, types.FluxMessage{Role: "user", Content: "refactor the payment module and update all tests"}) sess.Persistence().SetRawMessages(msgs) s := &RelevancePruneStrategy{TargetTokens: 200} @@ -54,7 +54,7 @@ func TestRelevancePruneStrategy_Compact(t *testing.T) { } func TestToPruneMessagesAdapter(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hi"}, {Role: "assistant", Content: "ok", ToolUse: []types.ToolCall{{Name: "Bash"}}}, {Role: "user", Content: "err", ToolResults: []types.ToolResult{{IsError: true}}}, diff --git a/internal/engine/compact_session_memory_engine.go b/internal/engine/compact_session_memory_engine.go index a2cddefe..c4592eea 100644 --- a/internal/engine/compact_session_memory_engine.go +++ b/internal/engine/compact_session_memory_engine.go @@ -15,7 +15,7 @@ type SessionMemoryStrategy struct{} func (s *SessionMemoryStrategy) Name() string { return "session_memory" } -func (s *SessionMemoryStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { +func (s *SessionMemoryStrategy) ShouldTrigger(msgs []types.FluxMessage, tokenCount, threshold int) bool { if tokenCount < threshold { return false } @@ -50,12 +50,12 @@ func (s *SessionMemoryStrategy) Compact(ctx context.Context, sess *Session) (*Co kept := messages[keepIdx:] kept = compact.FilterCompactBoundaries(kept) - result := make([]types.EyrieMessage, 0, len(kept)+2) - result = append(result, types.EyrieMessage{ + result := make([]types.FluxMessage, 0, len(kept)+2) + result = append(result, types.FluxMessage{ Role: "user", Content: "[Session memory summary]\n" + memContent + "\n\n[Continue from the recent messages below.]", }) - result = append(result, types.EyrieMessage{ + result = append(result, types.FluxMessage{ Role: "assistant", Content: "Understood. I have the context from the session memory above. Continuing with the recent conversation.", }) diff --git a/internal/engine/compact_split.go b/internal/engine/compact_split.go index 9f4abfc4..c65d3b14 100644 --- a/internal/engine/compact_split.go +++ b/internal/engine/compact_split.go @@ -21,7 +21,7 @@ import ( // average token count of the tail, with a 2000-token floor to avoid false // positives. A single message exceeding this budget is treated as an // oversized turn. -func turnTokenBudget(tail []types.EyrieMessage) int { +func turnTokenBudget(tail []types.FluxMessage) int { if len(tail) == 0 { return 0 } @@ -131,7 +131,7 @@ func (s *Session) splitTurnCompact(ctx context.Context) { // Reconstruct messages: summary + tail from oversized turn onward // Keep the second half of the oversized message + everything after // (its first half is covered by phase2Summary above). - remaining := append([]types.EyrieMessage(nil), raw[splitPoint:]...) + remaining := append([]types.FluxMessage(nil), raw[splitPoint:]...) if len(remaining) > 0 { first := remaining[0] first.Content = secondHalfRunes(first.Content) @@ -141,8 +141,8 @@ func (s *Session) splitTurnCompact(ctx context.Context) { remaining[0] = first } - keep := make([]types.EyrieMessage, 0, len(remaining)+2) - keep = append(keep, types.EyrieMessage{ + keep := make([]types.FluxMessage, 0, len(remaining)+2) + keep = append(keep, types.FluxMessage{ Role: "user", Content: combined.String() + "\n\n[Continue from the recent messages below.]", }) @@ -150,7 +150,7 @@ func (s *Session) splitTurnCompact(ctx context.Context) { // message; adjacent assistant messages are rejected by OpenAI-compat // providers. if len(remaining) == 0 || remaining[0].Role != "assistant" { - keep = append(keep, types.EyrieMessage{ + keep = append(keep, types.FluxMessage{ Role: "assistant", Content: "Understood. I have the context from the summary above. Continuing.", }) @@ -170,12 +170,12 @@ func secondHalfRunes(s string) string { } // generatePartialSummary generates an LLM summary for a subset of messages. -func (s *Session) generatePartialSummary(ctx context.Context, messages []types.EyrieMessage) string { +func (s *Session) generatePartialSummary(ctx context.Context, messages []types.FluxMessage) string { if len(messages) == 0 { return "" } - var summaryMsgs []types.EyrieMessage + var summaryMsgs []types.FluxMessage compactPrompt := BuildCompactPrompt(CompactPartial) var content strings.Builder content.WriteString(compactPrompt) @@ -191,7 +191,7 @@ func (s *Session) generatePartialSummary(ctx context.Context, messages []types.E } } - summaryMsgs = append(summaryMsgs, types.EyrieMessage{ + summaryMsgs = append(summaryMsgs, types.FluxMessage{ Role: "user", Content: content.String(), }) @@ -215,7 +215,7 @@ func (s *Session) generatePartialSummary(ctx context.Context, messages []types.E } // summarizeOversizedTurn summarizes the content of a single oversized message. -func (s *Session) summarizeOversizedTurn(ctx context.Context, msg types.EyrieMessage) string { +func (s *Session) summarizeOversizedTurn(ctx context.Context, msg types.FluxMessage) string { content := msg.Content if content == "" { // If it's a tool result, use that @@ -234,8 +234,8 @@ func (s *Session) summarizeOversizedTurn(ctx context.Context, msg types.EyrieMes halfLen = 4000 } - var summaryMsgs []types.EyrieMessage - summaryMsgs = append(summaryMsgs, types.EyrieMessage{ + var summaryMsgs []types.FluxMessage + summaryMsgs = append(summaryMsgs, types.FluxMessage{ Role: "user", Content: BuildCompactPrompt(CompactUpTo) + "\n\nSummarize the key information from this content prefix (the rest will be retained verbatim):\n\n" + msg.Role + ": " + truncateRunes(content, halfLen), diff --git a/internal/engine/compact_strategy_engine.go b/internal/engine/compact_strategy_engine.go index 5bc9d93d..34897489 100644 --- a/internal/engine/compact_strategy_engine.go +++ b/internal/engine/compact_strategy_engine.go @@ -8,7 +8,7 @@ import ( type CompactStrategy interface { Name() string - ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool + ShouldTrigger(msgs []types.FluxMessage, tokenCount, threshold int) bool Compact(ctx context.Context, s *Session) (*CompactResult, error) } @@ -31,7 +31,7 @@ func NewStrategyRegistry(config CompactConfig) *StrategyRegistry { return r } -func (r *StrategyRegistry) SelectStrategy(sess *Session, msgs []types.EyrieMessage, tokenCount int) CompactStrategy { +func (r *StrategyRegistry) SelectStrategy(sess *Session, msgs []types.FluxMessage, tokenCount int) CompactStrategy { threshold := r.config.ContextWindowSize - r.config.AutoCompactBuffer - r.config.MaxOutputTokens for _, s := range r.strategies { if _, ok := s.(*ProviderNativeCompactStrategy); ok && (sess == nil || !sess.supportsNativeCompaction()) { diff --git a/internal/engine/compact_strategy_test.go b/internal/engine/compact_strategy_test.go index f39052ff..33fdf6cb 100644 --- a/internal/engine/compact_strategy_test.go +++ b/internal/engine/compact_strategy_test.go @@ -100,14 +100,14 @@ func TestTurnTokenBudget(t *testing.T) { t.Errorf("turnTokenBudget(nil) = %d, want 0", got) } - small := []types.EyrieMessage{{Role: "user", Content: "short"}} + small := []types.FluxMessage{{Role: "user", Content: "short"}} if got := turnTokenBudget(small); got != 2000 { t.Errorf("turnTokenBudget(small) = %d, want minimum floor 2000", got) } - big := make([]types.EyrieMessage, 10) + big := make([]types.FluxMessage, 10) for i := range big { - big[i] = types.EyrieMessage{Role: "user", Content: strings.Repeat("x", 10000)} + big[i] = types.FluxMessage{Role: "user", Content: strings.Repeat("x", 10000)} } avg := EstimateMessageTokens(big[0]) if avg*3 <= 2000 { @@ -118,13 +118,13 @@ func TestTurnTokenBudget(t *testing.T) { } } -func makeMessages(n int) []types.EyrieMessage { - msgs := make([]types.EyrieMessage, n) +func makeMessages(n int) []types.FluxMessage { + msgs := make([]types.FluxMessage, n) for i := range msgs { if i%2 == 0 { - msgs[i] = types.EyrieMessage{Role: "user", Content: strings.Repeat("message ", 50)} + msgs[i] = types.FluxMessage{Role: "user", Content: strings.Repeat("message ", 50)} } else { - msgs[i] = types.EyrieMessage{Role: "assistant", Content: strings.Repeat("response ", 50)} + msgs[i] = types.FluxMessage{Role: "assistant", Content: strings.Repeat("response ", 50)} } } return msgs diff --git a/internal/engine/context_compaction.go b/internal/engine/context_compaction.go index 4e5482e6..5aaffbf8 100644 --- a/internal/engine/context_compaction.go +++ b/internal/engine/context_compaction.go @@ -133,7 +133,7 @@ func (s *Session) checkpointManager() *session.CheckpointManager { return p.CheckpointManager() } -func rawToSessionMessages(raw []types.EyrieMessage) []session.Message { +func rawToSessionMessages(raw []types.FluxMessage) []session.Message { return session.FromRuntimeMessages(raw) } diff --git a/internal/engine/context_compaction_test.go b/internal/engine/context_compaction_test.go index 78d7d06f..53462718 100644 --- a/internal/engine/context_compaction_test.go +++ b/internal/engine/context_compaction_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "github.com/GrayCodeAI/eyrie/credentials" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" + "github.com/GrayCodeAI/flux/credentials" + fluxengine "github.com/GrayCodeAI/flux/engine" "github.com/GrayCodeAI/rho/internal/provider/gateway" ) @@ -21,21 +21,21 @@ func TestContextUsedTokens_PrefersAPI(t *testing.T) { } } -func TestNativeCompactionSupportUsesEyrieCredentialStore(t *testing.T) { +func TestNativeCompactionSupportUsesFluxCredentialStore(t *testing.T) { ctx := context.Background() store := &credentials.MapStore{} - runtime, err := eyrieengine.New(eyrieengine.Options{SecretStore: store}) + runtime, err := fluxengine.New(fluxengine.Options{SecretStore: store}) if err != nil { t.Fatal(err) } s := NewSessionWithClient(gateway.NewFromEngine(runtime).ChatClient(), "anthropic", "claude-sonnet-4-6", "sys", nil, true) if s.supportsNativeCompaction() { - t.Fatal("expected no support before Eyrie has a credential") + t.Fatal("expected no support before Flux has a credential") } if err := store.Set(ctx, credentials.AccountForEnv("ANTHROPIC_API_KEY"), "sk-test"); err != nil { t.Fatal(err) } if !s.supportsNativeCompaction() { - t.Fatal("expected support from Eyrie's injected credential store") + t.Fatal("expected support from Flux's injected credential store") } } diff --git a/internal/engine/context_governor_test.go b/internal/engine/context_governor_test.go index 4c1d1bfd..293d6192 100644 --- a/internal/engine/context_governor_test.go +++ b/internal/engine/context_governor_test.go @@ -51,7 +51,7 @@ func TestMaybeSpillToolOutput_LargeSpills(t *testing.T) { func TestManageContextBeforeTurn_CollapseOnly(t *testing.T) { s := NewSession("", "test-model", "sys", nil) - s.Persistence().SetRawMessages([]types.EyrieMessage{ + s.Persistence().SetRawMessages([]types.FluxMessage{ {Role: "user", ToolResults: []types.ToolResult{{Content: "err", IsError: true}}}, {Role: "user", ToolResults: []types.ToolResult{{Content: "err", IsError: true}}}, }) diff --git a/internal/engine/control/backtrack.go b/internal/engine/control/backtrack.go index b92ab9df..f2af7ab1 100644 --- a/internal/engine/control/backtrack.go +++ b/internal/engine/control/backtrack.go @@ -11,10 +11,10 @@ import ( // choice that can potentially be rolled back if it leads to failure. type DecisionPoint struct { TurnIndex int - Description string // what was decided - Alternatives []string // other options available - Outcome string // "success", "failure", or "" (pending) - Messages []types.EyrieMessage // conversation state at this point + Description string // what was decided + Alternatives []string // other options available + Outcome string // "success", "failure", or "" (pending) + Messages []types.FluxMessage // conversation state at this point } // BacktrackEngine records decision points during agent execution and provides @@ -36,9 +36,9 @@ func NewBacktrackEngine() *BacktrackEngine { // RecordDecision saves a decision point with the current conversation state. // If the number of recorded points exceeds the maximum, the oldest point is // removed. -func (be *BacktrackEngine) RecordDecision(turnIdx int, desc string, alternatives []string, msgs []types.EyrieMessage) { +func (be *BacktrackEngine) RecordDecision(turnIdx int, desc string, alternatives []string, msgs []types.FluxMessage) { // Copy messages to avoid mutation from caller - snapshot := make([]types.EyrieMessage, len(msgs)) + snapshot := make([]types.FluxMessage, len(msgs)) copy(snapshot, msgs) // Copy alternatives @@ -110,12 +110,12 @@ func (be *BacktrackEngine) GenerateRetryPrompt(dp *DecisionPoint) string { // RestoreState returns the conversation messages captured at the given decision // point, representing the state just before (not including) the failed // decision. This allows the agent to retry from that point. -func (be *BacktrackEngine) RestoreState(dp *DecisionPoint) []types.EyrieMessage { +func (be *BacktrackEngine) RestoreState(dp *DecisionPoint) []types.FluxMessage { if dp == nil { return nil } // Return a copy to prevent mutation - restored := make([]types.EyrieMessage, len(dp.Messages)) + restored := make([]types.FluxMessage, len(dp.Messages)) copy(restored, dp.Messages) return restored } diff --git a/internal/engine/control/backtrack_test.go b/internal/engine/control/backtrack_test.go index aa9d748a..d35fb500 100644 --- a/internal/engine/control/backtrack_test.go +++ b/internal/engine/control/backtrack_test.go @@ -23,7 +23,7 @@ func TestNewBacktrackEngine(t *testing.T) { func TestBacktrackEngine_RecordAndFind(t *testing.T) { be := NewBacktrackEngine() - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "Fix the bug"}, {Role: "assistant", Content: "I'll try approach A"}, } @@ -61,7 +61,7 @@ func TestBacktrackEngine_RecordAndFind(t *testing.T) { func TestBacktrackEngine_GenerateRetryPrompt(t *testing.T) { be := NewBacktrackEngine() - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "Refactor the function"}, } @@ -94,7 +94,7 @@ func TestBacktrackEngine_GenerateRetryPrompt(t *testing.T) { func TestBacktrackEngine_RestoreState(t *testing.T) { be := NewBacktrackEngine() - originalMsgs := []types.EyrieMessage{ + originalMsgs := []types.FluxMessage{ {Role: "user", Content: "Step one"}, {Role: "assistant", Content: "Done with step one"}, {Role: "user", Content: "Step two"}, diff --git a/internal/engine/control_plane_test.go b/internal/engine/control_plane_test.go index 5ee76c06..6739e299 100644 --- a/internal/engine/control_plane_test.go +++ b/internal/engine/control_plane_test.go @@ -40,19 +40,19 @@ func TestWorkModePlanFiltersToolsAndBash(t *testing.T) { } } -func TestLazyEyrieToolsAndPromote(t *testing.T) { +func TestLazyFluxToolsAndPromote(t *testing.T) { reg := tool.NewRegistry(tool.FileReadTool{}, tool.ImpactTool{}) reg.EnableLazyModelSurface([]string{"Read"}) - eyrie := reg.EyrieTools() - if len(eyrie) != 1 || eyrie[0].Name != "Read" { - t.Fatalf("EyrieTools = %#v, want only Read", eyrie) + flux := reg.FluxTools() + if len(flux) != 1 || flux[0].Name != "Read" { + t.Fatalf("FluxTools = %#v, want only Read", flux) } if !reg.PromoteModelTool("Impact") { t.Fatal("promote Impact failed") } - eyrie = reg.EyrieTools() - if len(eyrie) != 2 { - t.Fatalf("after promote EyrieTools len = %d", len(eyrie)) + flux = reg.FluxTools() + if len(flux) != 2 { + t.Fatalf("after promote FluxTools len = %d", len(flux)) } } diff --git a/internal/engine/conversation_adapter.go b/internal/engine/conversation_adapter.go index 7200adc3..08c39bb2 100644 --- a/internal/engine/conversation_adapter.go +++ b/internal/engine/conversation_adapter.go @@ -28,7 +28,7 @@ type ConversationState struct { Summary string } -// ConversationManager manages a conversation lifecycle, bridging eyrie's +// ConversationManager manages a conversation lifecycle, bridging flux's // conversation management into rho's chat session flow. It is safe for // concurrent use. type ConversationManager struct { diff --git a/internal/engine/ctxmgr/context_collapse.go b/internal/engine/ctxmgr/context_collapse.go index 2191e109..66b64526 100644 --- a/internal/engine/ctxmgr/context_collapse.go +++ b/internal/engine/ctxmgr/context_collapse.go @@ -11,12 +11,12 @@ import ( // save context tokens. It collapses: // - 3+ consecutive tool_results with similar content into a summary // - Repeated error messages into a count -func CollapseRepeatedMessages(msgs []types.EyrieMessage) []types.EyrieMessage { +func CollapseRepeatedMessages(msgs []types.FluxMessage) []types.FluxMessage { if len(msgs) < 3 { return msgs } - result := make([]types.EyrieMessage, 0, len(msgs)) + result := make([]types.FluxMessage, 0, len(msgs)) i := 0 for i < len(msgs) { @@ -30,7 +30,7 @@ func CollapseRepeatedMessages(msgs []types.EyrieMessage) []types.EyrieMessage { runLen := j - i if runLen >= 2 { errText := extractErrorText(msgs[i]) - result = append(result, types.EyrieMessage{ + result = append(result, types.FluxMessage{ Role: msgs[i].Role, Content: fmt.Sprintf("[Error repeated %d times: %s]", runLen, errText), ToolResults: msgs[i].ToolResults, @@ -52,7 +52,7 @@ func CollapseRepeatedMessages(msgs []types.EyrieMessage) []types.EyrieMessage { result = append(result, msgs[i]) toolName := toolResultSource(msgs[i]) collapsed := runLen - 2 - result = append(result, types.EyrieMessage{ + result = append(result, types.FluxMessage{ Role: "user", Content: fmt.Sprintf("[Similar output from %s — %d results collapsed]", toolName, collapsed), ToolResults: []types.ToolResult{{ @@ -76,7 +76,7 @@ func CollapseRepeatedMessages(msgs []types.EyrieMessage) []types.EyrieMessage { // isSimilarToolResult checks whether two tool_result messages are similar // enough to collapse. Two results are similar if they come from the same tool // and their content shares the same first line or prefix (up to 100 chars). -func isSimilarToolResult(a, b types.EyrieMessage) bool { +func isSimilarToolResult(a, b types.FluxMessage) bool { if len(a.ToolResults) == 0 || len(b.ToolResults) == 0 { return false } @@ -88,7 +88,7 @@ func isSimilarToolResult(a, b types.EyrieMessage) bool { } // toolResultSource extracts the tool name from a tool_result message. -func toolResultSource(msg types.EyrieMessage) string { +func toolResultSource(msg types.FluxMessage) string { if len(msg.ToolResults) > 0 && msg.ToolResults[0].ToolUseID != "" { return msg.ToolResults[0].ToolUseID } @@ -108,7 +108,7 @@ func contentPrefix(s string, n int) string { } // isErrorMessage returns true if a message appears to be an error. -func isErrorMessage(msg types.EyrieMessage) bool { +func isErrorMessage(msg types.FluxMessage) bool { if len(msg.ToolResults) > 0 && msg.ToolResults[0].IsError { return true } @@ -117,7 +117,7 @@ func isErrorMessage(msg types.EyrieMessage) bool { } // extractErrorText extracts the error text from an error message. -func extractErrorText(msg types.EyrieMessage) string { +func extractErrorText(msg types.FluxMessage) string { if len(msg.ToolResults) > 0 && msg.ToolResults[0].IsError { return msg.ToolResults[0].Content } diff --git a/internal/engine/ctxmgr/context_collapse_test.go b/internal/engine/ctxmgr/context_collapse_test.go index 0caa47c1..a8700ad3 100644 --- a/internal/engine/ctxmgr/context_collapse_test.go +++ b/internal/engine/ctxmgr/context_collapse_test.go @@ -7,7 +7,7 @@ import ( ) func TestCollapseRepeatedMessages_NoCollapse(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi"}, {Role: "user", Content: "bye"}, @@ -19,7 +19,7 @@ func TestCollapseRepeatedMessages_NoCollapse(t *testing.T) { } func TestCollapseRepeatedMessages_ToolResults(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "run tests", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "PASS: all tests passed\ndetails..."}}}, {Role: "user", Content: "run tests", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "PASS: all tests passed\nother details"}}}, {Role: "user", Content: "run tests", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "PASS: all tests passed\nmore details"}}}, @@ -33,7 +33,7 @@ func TestCollapseRepeatedMessages_ToolResults(t *testing.T) { } func TestCollapseRepeatedMessages_Errors(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "connection refused", IsError: true}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t2", Content: "connection refused", IsError: true}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t3", Content: "connection refused", IsError: true}}}, @@ -48,7 +48,7 @@ func TestCollapseRepeatedMessages_Errors(t *testing.T) { } func TestCollapseRepeatedMessages_MixedContent(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "ok\ndetails"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "ok\nother"}}}, @@ -62,7 +62,7 @@ func TestCollapseRepeatedMessages_MixedContent(t *testing.T) { } func TestCollapseRepeatedMessages_ShortInput(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "hi"}, } result := CollapseRepeatedMessages(msgs) diff --git a/internal/engine/engine.go b/internal/engine/engine.go index 6267dd8e..86780750 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -106,9 +106,9 @@ func (s *Session) compact(ctx context.Context) { return // nothing to compact } - keep := make([]types.EyrieMessage, 0, len(raw)-(cutEnd-cutStart)+1) + keep := make([]types.FluxMessage, 0, len(raw)-(cutEnd-cutStart)+1) keep = append(keep, raw[:cutStart]...) - keep = append(keep, types.EyrieMessage{ + keep = append(keep, types.FluxMessage{ Role: "user", Content: "[Earlier conversation compacted to save context.]", }) diff --git a/internal/engine/engine_full_loop_test.go b/internal/engine/engine_full_loop_test.go index 9910b987..8da1a873 100644 --- a/internal/engine/engine_full_loop_test.go +++ b/internal/engine/engine_full_loop_test.go @@ -69,14 +69,14 @@ func TestEngine_FullLoop_MultiTurn(t *testing.T) { func TestEngine_FullLoop_ToolUse(t *testing.T) { mc := newMockClient( - &types.EyrieResponse{ + &types.FluxResponse{ FinishReason: "tool_use", ToolCalls: []types.ToolCall{{ ID: "call_1", Name: "Read", Arguments: map[string]interface{}{"file_path": "/nonexistent"}, }}, - Usage: &types.EyrieUsage{PromptTokens: 50, CompletionTokens: 30}, + Usage: &types.FluxUsage{PromptTokens: 50, CompletionTokens: 30}, }, mockTextResponse("I read the file and here's what I found."), ) diff --git a/internal/engine/engine_integration_test.go b/internal/engine/engine_integration_test.go index 3d9decce..9d56bd4c 100644 --- a/internal/engine/engine_integration_test.go +++ b/internal/engine/engine_integration_test.go @@ -68,14 +68,14 @@ func TestIntegration_FullSessionFlow(t *testing.T) { // Simulate the engine receiving an assistant text reply + tool call + tool result // by manually building the message sequence that agentLoop would produce. - sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.EyrieMessage{ + sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.FluxMessage{ Role: "assistant", Content: "Sure, let me check that file.", ToolUse: []types.ToolCall{ {ID: "tc-1", Name: "Bash", Arguments: map[string]interface{}{"command": "echo hello"}}, }, })) - sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.EyrieMessage{ + sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.FluxMessage{ Role: "user", ToolResults: []types.ToolResult{{ ToolUseID: "tc-1", @@ -83,7 +83,7 @@ func TestIntegration_FullSessionFlow(t *testing.T) { IsError: false, }}, })) - sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.EyrieMessage{ + sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.FluxMessage{ Role: "assistant", Content: "The command ran successfully and returned 'hello'.", })) diff --git a/internal/engine/engine_token_helpers.go b/internal/engine/engine_token_helpers.go index 5e5ce494..4ed037a0 100644 --- a/internal/engine/engine_token_helpers.go +++ b/internal/engine/engine_token_helpers.go @@ -6,7 +6,7 @@ import ( "github.com/GrayCodeAI/rho/internal/types" ) -func isRecentToolHeavy(messages []types.EyrieMessage) bool { +func isRecentToolHeavy(messages []types.FluxMessage) bool { const lookback = 3 toolTurns := 0 assistantSeen := 0 @@ -25,7 +25,7 @@ func isRecentToolHeavy(messages []types.EyrieMessage) bool { return assistantSeen >= lookback && toolTurns == assistantSeen } -func isTextQuestion(messages []types.EyrieMessage) bool { +func isTextQuestion(messages []types.FluxMessage) bool { var lastUserMsg string for i := len(messages) - 1; i >= 0; i-- { msg := messages[i] @@ -53,7 +53,7 @@ func isTextQuestion(messages []types.EyrieMessage) bool { return strings.HasSuffix(strings.TrimSpace(lower), "?") } -func classifyPromptForBudget(messages []types.EyrieMessage) string { +func classifyPromptForBudget(messages []types.FluxMessage) string { if isRecentToolHeavy(messages) { return "tool" } diff --git a/internal/engine/execution_graph_observations.go b/internal/engine/execution_graph_observations.go index 4a5ca368..dfb475a2 100644 --- a/internal/engine/execution_graph_observations.go +++ b/internal/engine/execution_graph_observations.go @@ -7,8 +7,8 @@ import ( "strings" "time" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" - eyriegraph "github.com/GrayCodeAI/eyrie/graph" + fluxengine "github.com/GrayCodeAI/flux/engine" + fluxgraph "github.com/GrayCodeAI/flux/graph" graphcontracts "github.com/GrayCodeAI/rho/internal/contracts/graph" policycontracts "github.com/GrayCodeAI/rho/internal/contracts/policy" "github.com/GrayCodeAI/rho/internal/engine/token" @@ -271,10 +271,10 @@ func (s *Session) usageCanProceed() (bool, string) { return tracker.CanProceed() } -func (s *Session) recordEyrieOperationObservation( +func (s *Session) recordFluxOperationObservation( provider, model, finishReason, content string, toolCallCount int, - usage *types.EyrieUsage, + usage *types.FluxUsage, ) { sessionID := s.executionGraphSessionID() if sessionID == "" || usage == nil { @@ -290,19 +290,19 @@ func (s *Session) recordEyrieOperationObservation( } observedAt := time.Now().UTC() route := types.ResolvedRoute{Provider: provider, Model: model} - export, err := eyrieengine.BuildOperationsGraph(eyrieengine.OperationsGraphInput{ + export, err := fluxengine.BuildOperationsGraph(fluxengine.OperationsGraphInput{ Route: &route, Usage: usage, FinishReason: finishReason, Content: content, ToolCallCount: toolCallCount, ObservedAt: observedAt, - Scope: eyriegraph.Scope{RepositoryID: repositoryID}, + Scope: fluxgraph.Scope{RepositoryID: repositoryID}, CorrelationID: sessionID, }) if err == nil { err = graphjournal.AppendRuntimeGraph( - sessionID, "", "model-generation", "eyrie", + sessionID, "", "model-generation", "flux", toContractNodes(export.Nodes), toContractEdges(export.Edges), toContractEvents(export.Events), observedAt, ) } @@ -314,11 +314,11 @@ func (s *Session) recordEyrieOperationObservation( } } -// The following helpers convert Eyrie's vendored graph contract types into +// The following helpers convert Flux's vendored graph contract types into // Rho's contracts/graph contract types. The definitions are byte-identical, so // conversion is a field-by-field copy at the sibling boundary. -func toContractNodes(nodes []eyriegraph.Node) []graphcontracts.Node { +func toContractNodes(nodes []fluxgraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { out[i] = toContractNode(n) @@ -326,7 +326,7 @@ func toContractNodes(nodes []eyriegraph.Node) []graphcontracts.Node { return out } -func toContractNode(n eyriegraph.Node) graphcontracts.Node { +func toContractNode(n fluxgraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), @@ -338,7 +338,7 @@ func toContractNode(n eyriegraph.Node) graphcontracts.Node { } } -func toContractEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { +func toContractEdges(edges []fluxgraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { out[i] = toContractEdge(e) @@ -346,7 +346,7 @@ func toContractEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { return out } -func toContractEdge(e eyriegraph.Edge) graphcontracts.Edge { +func toContractEdge(e fluxgraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), @@ -360,7 +360,7 @@ func toContractEdge(e eyriegraph.Edge) graphcontracts.Edge { } } -func toContractEvents(events []eyriegraph.Event) []graphcontracts.Event { +func toContractEvents(events []fluxgraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { out[i] = toContractEvent(ev) @@ -368,7 +368,7 @@ func toContractEvents(events []eyriegraph.Event) []graphcontracts.Event { return out } -func toContractEvent(ev eyriegraph.Event) graphcontracts.Event { +func toContractEvent(ev fluxgraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), @@ -382,15 +382,15 @@ func toContractEvent(ev eyriegraph.Event) graphcontracts.Event { } } -func toContractRef(r eyriegraph.Ref) graphcontracts.Ref { +func toContractRef(r fluxgraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func toContractScope(s eyriegraph.Scope) graphcontracts.Scope { +func toContractScope(s fluxgraph.Scope) graphcontracts.Scope { return graphcontracts.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toContractProvenance(p eyriegraph.Provenance) graphcontracts.Provenance { +func toContractProvenance(p fluxgraph.Provenance) graphcontracts.Provenance { evidence := make([]graphcontracts.ArtifactRef, len(p.Evidence)) for i, a := range p.Evidence { evidence[i] = graphcontracts.ArtifactRef{URI: a.URI, Digest: a.Digest, MediaType: a.MediaType} diff --git a/internal/engine/execution_graph_observations_test.go b/internal/engine/execution_graph_observations_test.go index 53f5694e..41c82a08 100644 --- a/internal/engine/execution_graph_observations_test.go +++ b/internal/engine/execution_graph_observations_test.go @@ -215,19 +215,19 @@ func TestDrainAlertsSurfacesHourlyWarning(t *testing.T) { } } -func TestEyrieOperationObservationIsPrivacySafe(t *testing.T) { +func TestFluxOperationObservationIsPrivacySafe(t *testing.T) { t.Setenv("RHO_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) - sess.SetPersistID("eyrie-runtime-session") - sess.recordEyrieOperationObservation( + sess.SetPersistID("flux-runtime-session") + sess.recordFluxOperationObservation( "private-provider", "private/model", "stop", "private generated content", 2, - &types.EyrieUsage{PromptTokens: 100, CompletionTokens: 20, TotalTokens: 120}, + &types.FluxUsage{PromptTokens: 100, CompletionTokens: 20, TotalTokens: 120}, ) - entries, err := graphjournal.Load("eyrie-runtime-session") + entries, err := graphjournal.Load("flux-runtime-session") if err != nil { t.Fatalf("graphjournal.Load() error = %v", err) } @@ -240,7 +240,7 @@ func TestEyrieOperationObservationIsPrivacySafe(t *testing.T) { } for _, secret := range []string{"private-provider", "private/model", "private generated content"} { if strings.Contains(string(payload), secret) { - t.Fatalf("Eyrie graph observation leaked %q", secret) + t.Fatalf("Flux graph observation leaked %q", secret) } } } diff --git a/internal/engine/history/file_mentions.go b/internal/engine/history/file_mentions.go index 65a920b3..038e4494 100644 --- a/internal/engine/history/file_mentions.go +++ b/internal/engine/history/file_mentions.go @@ -125,7 +125,7 @@ func (d *FileMentionDetector) BuildSuggestion(newFiles []string) string { // InjectFileMentionContext detects file mentions in the given text, // filters out files already discussed in the message history, and returns // a system context string if new files are found. Returns "" if none. -func (d *FileMentionDetector) InjectFileMentionContext(text string, messages []types.EyrieMessage) string { +func (d *FileMentionDetector) InjectFileMentionContext(text string, messages []types.FluxMessage) string { mentions := d.DetectMentions(text) if len(mentions) == 0 { return "" diff --git a/internal/engine/history/file_mentions_test.go b/internal/engine/history/file_mentions_test.go index 0b73e038..103be479 100644 --- a/internal/engine/history/file_mentions_test.go +++ b/internal/engine/history/file_mentions_test.go @@ -190,7 +190,7 @@ func TestInjectFileMentionContext_NewFiles(t *testing.T) { d := NewFileMentionDetector(dir) text := "You need to update `src/auth.go` and `pkg/middleware/rate.go`." - messages := []types.EyrieMessage{} // no prior messages + messages := []types.FluxMessage{} // no prior messages result := d.InjectFileMentionContext(text, messages) @@ -207,7 +207,7 @@ func TestInjectFileMentionContext_AllAlreadyDiscussed(t *testing.T) { d := NewFileMentionDetector(dir) text := "You need to update `src/auth.go`." - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ {Role: "user", Content: "I'm working on src/auth.go"}, } diff --git a/internal/engine/integration.go b/internal/engine/integration.go index d0a3892a..3e45ca07 100644 --- a/internal/engine/integration.go +++ b/internal/engine/integration.go @@ -288,7 +288,7 @@ func (p *IntegrationPipeline) SetJournal(j *eventlog.Log) { // PreQuery runs the full pre-query pipeline: classify intent, select tools, apply // context decay, allocate budget, predict cost, build system prompt, scan for // injection, and check the response cache. -func (p *IntegrationPipeline) PreQuery(messages []types.EyrieMessage, userInput string) *PreQueryResult { +func (p *IntegrationPipeline) PreQuery(messages []types.FluxMessage, userInput string) *PreQueryResult { p.mu.Lock() defer p.mu.Unlock() @@ -352,7 +352,7 @@ func (p *IntegrationPipeline) PreQuery(messages []types.EyrieMessage, userInput // PostResponse runs the full post-response pipeline: format, score quality, // detect file mentions, redact secrets, update timeline, record tokens, cache, // and update the experience store. -func (p *IntegrationPipeline) PostResponse(response string, messages []types.EyrieMessage) *PostResponseResult { +func (p *IntegrationPipeline) PostResponse(response string, messages []types.FluxMessage) *PostResponseResult { p.mu.Lock() defer p.mu.Unlock() @@ -653,7 +653,7 @@ func intentCategory(intent *Intent) string { } // lastUserMessage extracts the last user message from the conversation. -func lastUserMessage(messages []types.EyrieMessage) string { +func lastUserMessage(messages []types.FluxMessage) string { for i := len(messages) - 1; i >= 0; i-- { if messages[i].Role == "user" && len(messages[i].ToolResults) == 0 { return messages[i].Content @@ -663,7 +663,7 @@ func lastUserMessage(messages []types.EyrieMessage) string { } // integrationEstimateTokens gives a rough token count for a message slice. -func integrationEstimateTokens(messages []types.EyrieMessage) int { +func integrationEstimateTokens(messages []types.FluxMessage) int { total := 0 for _, m := range messages { total += EstimateStringTokens(m.Content) diff --git a/internal/engine/journal.go b/internal/engine/journal.go index 8679fc42..ebf7a9ee 100644 --- a/internal/engine/journal.go +++ b/internal/engine/journal.go @@ -73,7 +73,7 @@ func (s *PersistenceService) FlushWriteBehind() error { // AppendUserJournaled journals a user message and appends it to the transcript. // Safe on a nil receiver. -func (s *PersistenceService) AppendUserJournaled(msg types.EyrieMessage) { +func (s *PersistenceService) AppendUserJournaled(msg types.FluxMessage) { if s == nil { return } @@ -87,7 +87,7 @@ func (s *PersistenceService) AppendUserJournaled(msg types.EyrieMessage) { // AppendAssistantJournaled journals an assistant message and appends it to the // transcript. Safe on a nil receiver. -func (s *PersistenceService) AppendAssistantJournaled(msg types.EyrieMessage) { +func (s *PersistenceService) AppendAssistantJournaled(msg types.FluxMessage) { if s == nil { return } @@ -116,7 +116,7 @@ func (s *PersistenceService) JournalTitle() string { // Messages(); compactions and other non-append transcript edits are not yet // represented as journal events, so consistency is only expected for transcripts // built exclusively through the journaled append methods. -func (s *PersistenceService) JournalProjection() []types.EyrieMessage { +func (s *PersistenceService) JournalProjection() []types.FluxMessage { if s == nil { return nil } @@ -125,7 +125,7 @@ func (s *PersistenceService) JournalProjection() []types.EyrieMessage { return nil } msgs := eventlog.ProjectMessages(j.Snapshot()) - out := make([]types.EyrieMessage, len(msgs)) + out := make([]types.FluxMessage, len(msgs)) for i, m := range msgs { out[i] = fromJournalMessage(m) } @@ -149,7 +149,7 @@ func (s *PersistenceService) Reconstructible() error { if err := eventlog.Validate(j.Snapshot()); err != nil { return err } - var live []types.EyrieMessage + var live []types.FluxMessage s.mu.RLock() live = cloneMessages(s.messages) s.mu.RUnlock() @@ -160,7 +160,7 @@ func (s *PersistenceService) Reconstructible() error { return nil } -func toJournalMessage(m types.EyrieMessage) eventlog.Message { +func toJournalMessage(m types.FluxMessage) eventlog.Message { jm := eventlog.Message{ Role: m.Role, Content: m.Content, @@ -192,8 +192,8 @@ func toJournalMessage(m types.EyrieMessage) eventlog.Message { return jm } -func fromJournalMessage(m eventlog.Message) types.EyrieMessage { - out := types.EyrieMessage{ +func fromJournalMessage(m eventlog.Message) types.FluxMessage { + out := types.FluxMessage{ Role: m.Role, Content: m.Content, Thinking: m.Thinking, @@ -222,7 +222,7 @@ func fromJournalMessage(m eventlog.Message) types.EyrieMessage { return out } -func cloneSingleMessage(m types.EyrieMessage) types.EyrieMessage { - clones := cloneMessages([]types.EyrieMessage{m}) +func cloneSingleMessage(m types.FluxMessage) types.FluxMessage { + clones := cloneMessages([]types.FluxMessage{m}) return clones[0] } diff --git a/internal/engine/journal_test.go b/internal/engine/journal_test.go index f48e09ce..4c7faa85 100644 --- a/internal/engine/journal_test.go +++ b/internal/engine/journal_test.go @@ -13,7 +13,7 @@ func TestJournaledAppendKeepsTranscriptAndProjectionInSync(t *testing.T) { ps := NewPersistenceService(nil) // No journal yet: appends do not panic and behave like plain transcript writes. - ps.AppendUserJournaled(types.EyrieMessage{Role: "user", Content: "pre-journal"}) + ps.AppendUserJournaled(types.FluxMessage{Role: "user", Content: "pre-journal"}) if got := ps.MessageCount(); got != 1 { t.Fatalf("messages = %d, want 1", got) } @@ -22,7 +22,7 @@ func TestJournaledAppendKeepsTranscriptAndProjectionInSync(t *testing.T) { } ps.SetJournal(eventlog.New(nil)) - want := []types.EyrieMessage{ + want := []types.FluxMessage{ {Role: "user", Content: "hi"}, {Role: "assistant", Content: "hello"}, } @@ -43,7 +43,7 @@ func TestJournaledAppendKeepsTranscriptAndProjectionInSync(t *testing.T) { func TestJournaledAppendPreservesToolFactoryRoundTrip(t *testing.T) { ps := NewPersistenceService(nil) ps.SetJournal(eventlog.New(nil)) - in := types.EyrieMessage{ + in := types.FluxMessage{ Role: "assistant", Content: "did tool call", ToolUse: []types.ToolCall{ @@ -51,7 +51,7 @@ func TestJournaledAppendPreservesToolFactoryRoundTrip(t *testing.T) { }, } ps.AppendAssistantJournaled(in) - ps.AppendUserJournaled(types.EyrieMessage{ + ps.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: "tool result", ToolResults: []types.ToolResult{ @@ -80,7 +80,7 @@ func TestAddUserAddAssistantRoutesThroughJournal(t *testing.T) { ps.SetJournal(eventlog.New(nil)) ps.AddUser("hello") ps.AddAssistant("hi") - want := []types.EyrieMessage{ + want := []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi"}, } @@ -132,7 +132,7 @@ func TestReplayJournalRebuildsLogFromWire(t *testing.T) { } restored.SetJournal(log) - want := []types.EyrieMessage{ + want := []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi"}, } @@ -140,7 +140,7 @@ func TestReplayJournalRebuildsLogFromWire(t *testing.T) { t.Fatalf("JournalProjection() = %+v, want %+v", got, want) } // The rebuilt log must continue sequence assignment after the largest Seq. - restored.AppendUserJournaled(types.EyrieMessage{Role: "user", Content: "next"}) + restored.AppendUserJournaled(types.FluxMessage{Role: "user", Content: "next"}) if got := restored.JournalProjection(); len(got) != 3 { t.Fatalf("projected %d messages after append, want 3", len(got)) } @@ -160,7 +160,7 @@ func TestJournaledAppendContentPartsRoundTrip(t *testing.T) { ps := NewPersistenceService(nil) ps.SetJournal(eventlog.New(nil)) img := &types.ImageURLPart{URL: "data:image/png;base64,abc", Detail: "high"} - in := types.EyrieMessage{Role: "user", Content: "see image", ContentParts: []types.ContentPart{{Type: "image_url", ImageURL: img}}} + in := types.FluxMessage{Role: "user", Content: "see image", ContentParts: []types.ContentPart{{Type: "image_url", ImageURL: img}}} ps.AppendUserJournaled(in) got := ps.JournalProjection()[0] if len(got.ContentParts) != 1 { diff --git a/internal/engine/lifecycle_finalize_test.go b/internal/engine/lifecycle_finalize_test.go index f09e0e5a..526b619c 100644 --- a/internal/engine/lifecycle_finalize_test.go +++ b/internal/engine/lifecycle_finalize_test.go @@ -38,7 +38,7 @@ func TestFinalizePopulatesToolsAndFiles_TriggersSkillDistill(t *testing.T) { sk := &finalizeMockSkillStore{} svc := &LifecycleService{lifecycle: &SessionLifecycle{SkillStore: sk}} - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ {Role: "user", Content: "Refactor the auth module"}, {Role: "assistant", ToolUse: []types.ToolCall{ {Name: "Read", Arguments: map[string]interface{}{"path": "auth.go"}}, @@ -75,7 +75,7 @@ func TestFinalizeSimpleTaskNoSkillDistill(t *testing.T) { sk := &finalizeMockSkillStore{} svc := &LifecycleService{lifecycle: &SessionLifecycle{SkillStore: sk}} - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ {Role: "user", Content: "What is 2+2?"}, {Role: "assistant", Content: "4"}, } diff --git a/internal/engine/lifecycle_service.go b/internal/engine/lifecycle_service.go index da024860..4b5691e3 100644 --- a/internal/engine/lifecycle_service.go +++ b/internal/engine/lifecycle_service.go @@ -151,7 +151,7 @@ func (s *LifecycleService) StartContext(ctx context.Context, lastUserMsg string) // Finalize performs lifecycle bookkeeping from immutable session snapshots. // It intentionally accepts data rather than *Session so the lifecycle layer // cannot reach through the god object for unrelated state. -func (s *LifecycleService) Finalize(ctx context.Context, messages []types.EyrieMessage, success bool, duration time.Duration, totalCost float64) { +func (s *LifecycleService) Finalize(ctx context.Context, messages []types.FluxMessage, success bool, duration time.Duration, totalCost float64) { if s == nil { return } diff --git a/internal/engine/llm_client.go b/internal/engine/llm_client.go index 525383af..518d6cb2 100644 --- a/internal/engine/llm_client.go +++ b/internal/engine/llm_client.go @@ -12,7 +12,7 @@ import ( // LLMClient is the minimal interface for calling an LLM from engine components. type LLMClient interface { - Chat(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) + Chat(ctx context.Context, msgs []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) } // Reflector provides verbal self-reflection on agent actions. @@ -46,7 +46,7 @@ func (r *Reflector) History() []ReflectionEntry { } // Reflect analyzes a failure and records a lesson. -func (r *Reflector) Reflect(ctx context.Context, goal string, msgs []types.EyrieMessage, errorContext string) (*ReflectionEntry, error) { +func (r *Reflector) Reflect(ctx context.Context, goal string, msgs []types.FluxMessage, errorContext string) (*ReflectionEntry, error) { if r.llm == nil { return nil, fmt.Errorf("reflector: no LLM client configured") } @@ -55,7 +55,7 @@ func (r *Reflector) Reflect(ctx context.Context, goal string, msgs []types.Eyrie r.mu.Unlock() prompt := buildReflectionPrompt(goal, msgs, errorContext) - allMsgs := []types.EyrieMessage{{Role: "user", Content: prompt}} + allMsgs := []types.FluxMessage{{Role: "user", Content: prompt}} resp, err := r.llm.Chat(ctx, allMsgs, types.ChatOptions{Model: r.model, MaxTokens: 512}) if err != nil { return nil, err @@ -102,7 +102,7 @@ func parseReflectionEntry(content string, attempt int, goal string) ReflectionEn // buildReflectionPrompt constructs the reflection prompt from goal, messages, and error. // The instruction block is delegated to LearnPrompt so the failure-analysis // template lives in one place. -func buildReflectionPrompt(goal string, msgs []types.EyrieMessage, errorContext string) string { +func buildReflectionPrompt(goal string, msgs []types.FluxMessage, errorContext string) string { var sb strings.Builder sb.WriteString("TASK GOAL: " + goal + "\n\n") sb.WriteString("CONVERSATION TRANSCRIPT:\n") diff --git a/internal/engine/memory_service.go b/internal/engine/memory_service.go index 37e4a41a..47269711 100644 --- a/internal/engine/memory_service.go +++ b/internal/engine/memory_service.go @@ -106,7 +106,7 @@ func (s *MemoryService) OnSessionEnd(success bool) { // Finalize performs memory-side session bookkeeping from a transcript // snapshot. The agent loop does not need to know which backend is installed. -func (s *MemoryService) Finalize(messages []types.EyrieMessage, success bool) { +func (s *MemoryService) Finalize(messages []types.FluxMessage, success bool) { if s == nil { return } diff --git a/internal/engine/mock_client_test.go b/internal/engine/mock_client_test.go index cb16cd96..3e7930bb 100644 --- a/internal/engine/mock_client_test.go +++ b/internal/engine/mock_client_test.go @@ -10,29 +10,29 @@ import ( // mockClient implements ChatClient for testing without real LLM calls. type mockClient struct { mu sync.Mutex - responses []*types.EyrieResponse + responses []*types.FluxResponse idx int calls []mockCall } type mockCall struct { method string - messages []types.EyrieMessage + messages []types.FluxMessage } -func newMockClient(responses ...*types.EyrieResponse) *mockClient { +func newMockClient(responses ...*types.FluxResponse) *mockClient { return &mockClient{responses: responses} } -func mockTextResponse(text string) *types.EyrieResponse { - return &types.EyrieResponse{ +func mockTextResponse(text string) *types.FluxResponse { + return &types.FluxResponse{ Content: text, FinishReason: "end_turn", - Usage: &types.EyrieUsage{PromptTokens: 50, CompletionTokens: 20, TotalTokens: 70}, + Usage: &types.FluxUsage{PromptTokens: 50, CompletionTokens: 20, TotalTokens: 70}, } } -func (m *mockClient) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (m *mockClient) Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { m.mu.Lock() defer m.mu.Unlock() @@ -46,13 +46,13 @@ func (m *mockClient) Chat(ctx context.Context, messages []types.EyrieMessage, op return resp, nil } -func (m *mockClient) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { +func (m *mockClient) StreamChatContinue(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { m.mu.Lock() defer m.mu.Unlock() m.calls = append(m.calls, mockCall{method: "StreamChatContinue", messages: messages}) - ch := make(chan types.EyrieStreamEvent, 10) + ch := make(chan types.FluxStreamEvent, 10) var content string var finishReason string @@ -66,11 +66,11 @@ func (m *mockClient) StreamChatContinue(ctx context.Context, messages []types.Ey finishReason = "end_turn" } - ch <- types.EyrieStreamEvent{Type: "content", Content: content} - ch <- types.EyrieStreamEvent{ + ch <- types.FluxStreamEvent{Type: "content", Content: content} + ch <- types.FluxStreamEvent{ Type: "done", StopReason: finishReason, - Usage: &types.EyrieUsage{PromptTokens: 50, CompletionTokens: 20, TotalTokens: 70}, + Usage: &types.FluxUsage{PromptTokens: 50, CompletionTokens: 20, TotalTokens: 70}, } close(ch) diff --git a/internal/engine/model_switch_test.go b/internal/engine/model_switch_test.go index 61b04d30..719d00b5 100644 --- a/internal/engine/model_switch_test.go +++ b/internal/engine/model_switch_test.go @@ -12,7 +12,7 @@ import ( func TestSetModelPreservesHistory(t *testing.T) { s := NewSession("anthropic", "claude-sonnet-4-6", "system", nil) - history := []types.EyrieMessage{ + history := []types.FluxMessage{ {Role: "user", Content: "what is 2+2?"}, {Role: "assistant", Content: "4"}, {Role: "user", Content: "and 3+3?"}, diff --git a/internal/engine/persistence_service.go b/internal/engine/persistence_service.go index 057e8d30..1f49a339 100644 --- a/internal/engine/persistence_service.go +++ b/internal/engine/persistence_service.go @@ -26,7 +26,7 @@ type PersistenceService struct { // stateMu protects compaction, token, and checkpoint metadata. stateMu sync.RWMutex // messages is the full transcript (system + user + assistant + tool_use + tool_result). - messages []types.EyrieMessage + messages []types.FluxMessage // system is the system prompt (mutable, agents append learned guidelines). system string // pinnedMessages is the count of messages protected from compaction (from /pin). @@ -94,7 +94,7 @@ func (s *PersistenceService) SetLogger(l *logger.Logger) { } // Messages returns a snapshot copy of the current transcript. -func (s *PersistenceService) Messages() []types.EyrieMessage { +func (s *PersistenceService) Messages() []types.FluxMessage { s.mu.RLock() defer s.mu.RUnlock() // Read s.messages directly; the lock is already held. Calling @@ -108,7 +108,7 @@ func (s *PersistenceService) Messages() []types.EyrieMessage { // that previously wrote to s.messages directly. The input is deep-copied so // callers cannot mutate the live transcript after handing it to persistence. // Safe on a nil receiver. -func (s *PersistenceService) SetRawMessages(msgs []types.EyrieMessage) { +func (s *PersistenceService) SetRawMessages(msgs []types.FluxMessage) { if s == nil { return } @@ -121,7 +121,7 @@ func (s *PersistenceService) SetRawMessages(msgs []types.EyrieMessage) { // arguments and multimodal slices are cloned as well, so callers cannot // mutate live session state by retaining or editing the returned value. // Safe to call on a nil receiver (returns nil). -func (s *PersistenceService) RawMessages() []types.EyrieMessage { +func (s *PersistenceService) RawMessages() []types.FluxMessage { if s == nil { return nil } @@ -137,7 +137,7 @@ func (s *PersistenceService) RawMessages() []types.EyrieMessage { // as ephemeral: appends may reallocate the backing array, and nested tool // arguments are shared with live session state. Use RawMessages when a // stable snapshot is required. Safe on a nil receiver (returns nil). -func (s *PersistenceService) RawMessagesView() []types.EyrieMessage { +func (s *PersistenceService) RawMessagesView() []types.FluxMessage { if s == nil { return nil } @@ -161,7 +161,7 @@ func (s *PersistenceService) SetSteering(sq *SteeringQueue) { s.steering = sq } // AddAssistant appends an assistant message. func (s *PersistenceService) AddAssistant(content string) { - s.AppendAssistantJournaled(types.EyrieMessage{Role: "assistant", Content: content}) + s.AppendAssistantJournaled(types.FluxMessage{Role: "assistant", Content: content}) } // TrimIncompleteTurn removes a trailing partial turn so the transcript stays @@ -219,7 +219,7 @@ func (s *PersistenceService) TrimIncompleteTurn() { // lastAssistantToolUseIDs returns the tool-use IDs from the most recent // assistant message carrying ToolUse blocks, or nil if none exists in msgs. -func lastAssistantToolUseIDs(msgs []types.EyrieMessage) map[string]bool { +func lastAssistantToolUseIDs(msgs []types.FluxMessage) map[string]bool { for i := len(msgs) - 1; i >= 0; i-- { if msgs[i].Role == "assistant" && len(msgs[i].ToolUse) > 0 { ids := make(map[string]bool, len(msgs[i].ToolUse)) @@ -232,7 +232,7 @@ func lastAssistantToolUseIDs(msgs []types.EyrieMessage) map[string]bool { return nil } -func hasOwnersForToolResults(msgs []types.EyrieMessage, results []types.ToolResult) bool { +func hasOwnersForToolResults(msgs []types.FluxMessage, results []types.ToolResult) bool { owners := lastAssistantToolUseIDs(msgs) if len(owners) == 0 { return false @@ -247,7 +247,7 @@ func hasOwnersForToolResults(msgs []types.EyrieMessage, results []types.ToolResu // hasResultsForAllToolUses reports whether every tool-use ID has at least one // matching tool_result anywhere in msgs. -func hasResultsForAllToolUses(msgs []types.EyrieMessage, uses []types.ToolCall) bool { +func hasResultsForAllToolUses(msgs []types.FluxMessage, uses []types.ToolCall) bool { if len(uses) == 0 { return true } @@ -266,7 +266,7 @@ func hasResultsForAllToolUses(msgs []types.EyrieMessage, uses []types.ToolCall) } // SetMessages replaces the transcript. -func (s *PersistenceService) SetMessages(msgs []types.EyrieMessage) { +func (s *PersistenceService) SetMessages(msgs []types.FluxMessage) { s.mu.Lock() s.messages = cloneMessages(msgs) s.mu.Unlock() @@ -277,14 +277,14 @@ func (s *PersistenceService) SetMessages(msgs []types.EyrieMessage) { // snapshotLen is the length of the snapshot keep was derived from; messages // past that index are re-appended to the tail so a compaction racing an // AddUser can never drop the new message. Safe on a nil receiver. -func (s *PersistenceService) ApplyCompaction(keep []types.EyrieMessage, snapshotLen int) { +func (s *PersistenceService) ApplyCompaction(keep []types.FluxMessage, snapshotLen int) { if s == nil { return } s.mu.Lock() defer s.mu.Unlock() if snapshotLen >= 0 && snapshotLen < len(s.messages) { - kept := make([]types.EyrieMessage, 0, len(keep)+(len(s.messages)-snapshotLen)) + kept := make([]types.FluxMessage, 0, len(keep)+(len(s.messages)-snapshotLen)) kept = append(kept, keep...) kept = append(kept, s.messages[snapshotLen:]...) s.messages = cloneMessages(kept) @@ -295,7 +295,7 @@ func (s *PersistenceService) ApplyCompaction(keep []types.EyrieMessage, snapshot // AddUser appends a user message. Safe on a nil receiver. func (s *PersistenceService) AddUser(content string) { - s.AppendUserJournaled(types.EyrieMessage{Role: "user", Content: content}) + s.AppendUserJournaled(types.FluxMessage{Role: "user", Content: content}) } // AddMessage appends a message with the specified role and content. @@ -310,14 +310,14 @@ func (s *PersistenceService) AddMessage(role, content string) { s.AddUser(content) default: s.mu.Lock() - s.messages = append(s.messages, types.EyrieMessage{Role: role, Content: content}) + s.messages = append(s.messages, types.FluxMessage{Role: role, Content: content}) s.mu.Unlock() } } // AddUserWithImage appends a user message with an inline image. // The image is stored as a data URL ("data:;base64,") -// so the LLM-side eyrie client can decode it from the message body +// so the LLM-side flux client can decode it from the message body // without any extra metadata channel. imageType is "image/png", // "image/jpeg", etc. func (s *PersistenceService) AddUserWithImage(content, imageBase64, imageType string) { @@ -325,7 +325,7 @@ func (s *PersistenceService) AddUserWithImage(content, imageBase64, imageType st return } dataURL := "data:" + imageType + ";base64," + imageBase64 - s.AppendUserJournaled(types.EyrieMessage{ + s.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: content, Images: []string{dataURL}, @@ -428,7 +428,7 @@ func (s *PersistenceService) RemoveLastExchange() { } // LoadMessages replaces the transcript with a fresh slice. -func (s *PersistenceService) LoadMessages(msgs []types.EyrieMessage) { +func (s *PersistenceService) LoadMessages(msgs []types.FluxMessage) { s.mu.Lock() // Clone while holding the lock; callers retain no mutable alias to the // live transcript. @@ -439,11 +439,11 @@ func (s *PersistenceService) LoadMessages(msgs []types.EyrieMessage) { // cloneMessages performs a deep copy of the provider-neutral transcript. // Tool arguments are arbitrary JSON-shaped values, so cloneJSONValue walks // maps and slices recursively instead of relying on a shallow slice copy. -func cloneMessages(in []types.EyrieMessage) []types.EyrieMessage { +func cloneMessages(in []types.FluxMessage) []types.FluxMessage { if in == nil { return nil } - out := make([]types.EyrieMessage, len(in)) + out := make([]types.FluxMessage, len(in)) for i, msg := range in { out[i] = msg out[i].Images = append([]string(nil), msg.Images...) diff --git a/internal/engine/persistence_service_deadlock_test.go b/internal/engine/persistence_service_deadlock_test.go index 47382100..63141f83 100644 --- a/internal/engine/persistence_service_deadlock_test.go +++ b/internal/engine/persistence_service_deadlock_test.go @@ -17,7 +17,7 @@ func TestPersistenceServiceNoRecursiveLock(t *testing.T) { go func() { defer close(done) s := NewPersistenceService(nil) - s.LoadMessages([]types.EyrieMessage{ + s.LoadMessages([]types.FluxMessage{ {Role: "user", Content: "a"}, {Role: "assistant", Content: "b"}, }) @@ -41,7 +41,7 @@ func TestPersistenceServiceNoRecursiveLock(t *testing.T) { func TestPersistenceServiceSnapshotsAreDeepCopies(t *testing.T) { ps := NewPersistenceService(nil) - ps.LoadMessages([]types.EyrieMessage{{ + ps.LoadMessages([]types.FluxMessage{{ Role: "assistant", Images: []string{"data:image/png;base64,abc"}, ToolUse: []types.ToolCall{{ @@ -73,7 +73,7 @@ func TestPersistenceServiceSnapshotsAreDeepCopies(t *testing.T) { func TestPersistenceServiceSetRawMessagesCopiesInput(t *testing.T) { ps := NewPersistenceService(nil) - input := []types.EyrieMessage{{ + input := []types.FluxMessage{{ Role: "assistant", ToolUse: []types.ToolCall{{ Arguments: map[string]interface{}{"nested": map[string]interface{}{"value": "safe"}}, @@ -93,7 +93,7 @@ func TestPersistenceServiceSetRawMessagesCopiesInput(t *testing.T) { // O(1). It is a view — the caller must not mutate or retain it. func TestPersistenceServiceRawMessagesView(t *testing.T) { ps := NewPersistenceService(nil) - ps.LoadMessages([]types.EyrieMessage{{Role: "user", Content: "a"}}) + ps.LoadMessages([]types.FluxMessage{{Role: "user", Content: "a"}}) view := ps.RawMessagesView() if len(view) != 1 || view[0].Content != "a" { diff --git a/internal/engine/persistence_trim_test.go b/internal/engine/persistence_trim_test.go index 88d928c0..162cfe54 100644 --- a/internal/engine/persistence_trim_test.go +++ b/internal/engine/persistence_trim_test.go @@ -9,12 +9,12 @@ import ( func TestTrimIncompleteTurn_NoopOnCompleteTurn(t *testing.T) { t.Parallel() p := &PersistenceService{} - p.AppendAssistantJournaled(types.EyrieMessage{ + p.AppendAssistantJournaled(types.FluxMessage{ Role: "assistant", Content: " ", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}, }) - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: "out", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "out"}}, @@ -32,7 +32,7 @@ func TestTrimIncompleteTurn_DropsUnansweredAssistantToolUse(t *testing.T) { t.Parallel() p := &PersistenceService{} p.AddUser("do the thing") - p.AppendAssistantJournaled(types.EyrieMessage{ + p.AppendAssistantJournaled(types.FluxMessage{ Role: "assistant", Content: " ", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}, @@ -56,7 +56,7 @@ func TestTrimIncompleteTurn_DropsOrphanedToolResults(t *testing.T) { p.AddUser("do the thing") // Simulate a transcript where results landed but the owning assistant // tool_use is missing (e.g. partial restore). - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: "out", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "out"}}, @@ -77,14 +77,14 @@ func TestTrimIncompleteTurn_DropsMultipleDanglingLayers(t *testing.T) { t.Parallel() p := &PersistenceService{} p.AddUser("first") - p.AppendAssistantJournaled(types.EyrieMessage{Role: "assistant", Content: "done"}) + p.AppendAssistantJournaled(types.FluxMessage{Role: "assistant", Content: "done"}) // Then a new turn started and was cancelled mid-flight: - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: "orphan result", ToolResults: []types.ToolResult{{ToolUseID: "x9", Content: "r"}}, }) - p.AppendAssistantJournaled(types.EyrieMessage{ + p.AppendAssistantJournaled(types.FluxMessage{ Role: "assistant", Content: " ", ToolUse: []types.ToolCall{{ID: "x8", Name: "Read"}}, @@ -104,12 +104,12 @@ func TestTrimIncompleteTurn_DropsMultipleDanglingLayers(t *testing.T) { func TestTrimIncompleteTurn_KeepsResultsWhenOwnerPresent(t *testing.T) { t.Parallel() p := &PersistenceService{} - p.AppendAssistantJournaled(types.EyrieMessage{ + p.AppendAssistantJournaled(types.FluxMessage{ Role: "assistant", Content: " ", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}, {ID: "t2", Name: "Read"}}, }) - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: "out1", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "out1"}}, diff --git a/internal/engine/provider_chat_client.go b/internal/engine/provider_chat_client.go index 30427029..9521e8fc 100644 --- a/internal/engine/provider_chat_client.go +++ b/internal/engine/provider_chat_client.go @@ -8,7 +8,7 @@ import ( ) // NewUnavailableChatClient preserves Session construction while surfacing -// Eyrie transport setup failures at the first chat call. +// Flux transport setup failures at the first chat call. func NewUnavailableChatClient(err error) ChatClient { if err == nil { err = errors.New("rho: chat transport unavailable") @@ -20,10 +20,10 @@ type unavailableChatClient struct { err error } -func (c *unavailableChatClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (c *unavailableChatClient) Chat(context.Context, []types.FluxMessage, types.ChatOptions) (*types.FluxResponse, error) { return nil, c.err } -func (c *unavailableChatClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *unavailableChatClient) StreamChatContinue(context.Context, []types.FluxMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { return nil, c.err } diff --git a/internal/engine/reflect_test.go b/internal/engine/reflect_test.go index 126786e9..4d4a5468 100644 --- a/internal/engine/reflect_test.go +++ b/internal/engine/reflect_test.go @@ -16,12 +16,12 @@ type mockLLMClient struct { calls int } -func (m *mockLLMClient) Chat(_ context.Context, msgs []types.EyrieMessage, _ types.ChatOptions) (*types.EyrieResponse, error) { +func (m *mockLLMClient) Chat(_ context.Context, msgs []types.FluxMessage, _ types.ChatOptions) (*types.FluxResponse, error) { m.calls++ if m.err != nil { return nil, m.err } - return &types.EyrieResponse{Content: m.response}, nil + return &types.FluxResponse{Content: m.response}, nil } func TestNewReflector(t *testing.T) { @@ -43,7 +43,7 @@ WHAT_TO_DO: Use the project root from the session context instead of hardcoding } r := NewReflector(mock, "test-model") - ref, err := r.Reflect(context.Background(), "fix the config parser", []types.EyrieMessage{ + ref, err := r.Reflect(context.Background(), "fix the config parser", []types.FluxMessage{ {Role: "user", Content: "Fix the config parser to handle nested keys."}, {Role: "assistant", Content: "I will edit config.go."}, }, "permission denied: /tmp/config.go") @@ -252,7 +252,7 @@ what_to_do: lowercase fix` } func TestBuildReflectionPrompt_ContainsKeyElements(t *testing.T) { - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ {Role: "user", Content: "Fix the parser."}, { Role: "assistant", diff --git a/internal/engine/resilience_boundary_test.go b/internal/engine/resilience_boundary_test.go index e28fc3ae..de855601 100644 --- a/internal/engine/resilience_boundary_test.go +++ b/internal/engine/resilience_boundary_test.go @@ -7,17 +7,17 @@ import ( "testing" "time" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" "github.com/GrayCodeAI/rho/internal/types" ) type resilienceBoundaryClient struct { mu sync.Mutex - streams [][]types.EyrieStreamEvent - chatResponses []*types.EyrieResponse + streams [][]types.FluxStreamEvent + chatResponses []*types.FluxResponse streamCalls int chatCalls int - messages [][]types.EyrieMessage + messages [][]types.FluxMessage } type managedResilienceBoundaryClient struct { @@ -26,30 +26,30 @@ type managedResilienceBoundaryClient struct { func (*managedResilienceBoundaryClient) ManagesResilience() bool { return true } -func (c *resilienceBoundaryClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (c *resilienceBoundaryClient) Chat(context.Context, []types.FluxMessage, types.ChatOptions) (*types.FluxResponse, error) { c.mu.Lock() defer c.mu.Unlock() c.chatCalls++ if len(c.chatResponses) == 0 { - return &types.EyrieResponse{}, nil + return &types.FluxResponse{}, nil } response := c.chatResponses[0] c.chatResponses = c.chatResponses[1:] return response, nil } -func (c *resilienceBoundaryClient) StreamChatContinue(_ context.Context, messages []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (c *resilienceBoundaryClient) StreamChatContinue(_ context.Context, messages []types.FluxMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { c.mu.Lock() defer c.mu.Unlock() c.streamCalls++ c.messages = append(c.messages, cloneBoundaryMessages(messages)) - events := []types.EyrieStreamEvent{{Type: "done", StopReason: "end_turn"}} + events := []types.FluxStreamEvent{{Type: "done", StopReason: "end_turn"}} if len(c.streams) > 0 { events = c.streams[0] c.streams = c.streams[1:] } - ch := make(chan types.EyrieStreamEvent, len(events)) + ch := make(chan types.FluxStreamEvent, len(events)) for _, event := range events { ch <- event } @@ -63,14 +63,14 @@ func (c *resilienceBoundaryClient) counts() (stream, chat int) { return c.streamCalls, c.chatCalls } -func (c *resilienceBoundaryClient) messagesForCall(call int) []types.EyrieMessage { +func (c *resilienceBoundaryClient) messagesForCall(call int) []types.FluxMessage { c.mu.Lock() defer c.mu.Unlock() return cloneBoundaryMessages(c.messages[call]) } -func cloneBoundaryMessages(messages []types.EyrieMessage) []types.EyrieMessage { - out := make([]types.EyrieMessage, len(messages)) +func cloneBoundaryMessages(messages []types.FluxMessage) []types.FluxMessage { + out := make([]types.FluxMessage, len(messages)) copy(out, messages) for i := range out { out[i].ToolUse = append([]types.ToolCall(nil), messages[i].ToolUse...) @@ -102,14 +102,14 @@ func runResilienceBoundarySession(t *testing.T, client ChatClient, configure fun } func TestAgentLoopResilienceBoundary_StreamRetry(t *testing.T) { - transient := []types.EyrieStreamEvent{{Type: "error", Error: "temporary connection reset by peer"}} - success := []types.EyrieStreamEvent{ + transient := []types.FluxStreamEvent{{Type: "error", Error: "temporary connection reset by peer"}} + success := []types.FluxStreamEvent{ {Type: "content", Content: "recovered"}, {Type: "done", StopReason: "end_turn"}, } t.Run("engine facade gets one stream attempt", func(t *testing.T) { - base := &resilienceBoundaryClient{streams: [][]types.EyrieStreamEvent{transient, success}} + base := &resilienceBoundaryClient{streams: [][]types.FluxStreamEvent{transient, success}} events := runResilienceBoundarySession(t, &managedResilienceBoundaryClient{base}, nil) streamCalls, chatCalls := base.counts() if streamCalls != 1 || chatCalls != 0 { @@ -121,7 +121,7 @@ func TestAgentLoopResilienceBoundary_StreamRetry(t *testing.T) { }) t.Run("legacy client retains stream retry", func(t *testing.T) { - client := &resilienceBoundaryClient{streams: [][]types.EyrieStreamEvent{transient, success}} + client := &resilienceBoundaryClient{streams: [][]types.FluxStreamEvent{transient, success}} events := runResilienceBoundarySession(t, client, nil) streamCalls, chatCalls := client.counts() if streamCalls != 2 || chatCalls != 0 { @@ -134,16 +134,16 @@ func TestAgentLoopResilienceBoundary_StreamRetry(t *testing.T) { } func TestAgentLoopResilienceBoundary_ThinkingOnlyRecovery(t *testing.T) { - thinkingOnly := []types.EyrieStreamEvent{ + thinkingOnly := []types.FluxStreamEvent{ {Type: "thinking", Thinking: "internal reasoning"}, {Type: "done", StopReason: "end_turn"}, } - fallback := &types.EyrieResponse{Content: "compatibility reply", FinishReason: "end_turn"} + fallback := &types.FluxResponse{Content: "compatibility reply", FinishReason: "end_turn"} t.Run("engine facade does not trigger Rho protocol recovery", func(t *testing.T) { base := &resilienceBoundaryClient{ - streams: [][]types.EyrieStreamEvent{thinkingOnly}, - chatResponses: []*types.EyrieResponse{fallback}, + streams: [][]types.FluxStreamEvent{thinkingOnly}, + chatResponses: []*types.FluxResponse{fallback}, } events := runResilienceBoundarySession(t, &managedResilienceBoundaryClient{base}, nil) streamCalls, chatCalls := base.counts() @@ -157,8 +157,8 @@ func TestAgentLoopResilienceBoundary_ThinkingOnlyRecovery(t *testing.T) { t.Run("legacy client retains thinking-only fallback", func(t *testing.T) { client := &resilienceBoundaryClient{ - streams: [][]types.EyrieStreamEvent{thinkingOnly}, - chatResponses: []*types.EyrieResponse{fallback}, + streams: [][]types.FluxStreamEvent{thinkingOnly}, + chatResponses: []*types.FluxResponse{fallback}, } events := runResilienceBoundarySession(t, client, nil) streamCalls, chatCalls := client.counts() @@ -172,17 +172,17 @@ func TestAgentLoopResilienceBoundary_ThinkingOnlyRecovery(t *testing.T) { } func TestAgentLoopResilienceBoundary_MaxTokensContinuation(t *testing.T) { - partial := []types.EyrieStreamEvent{ + partial := []types.FluxStreamEvent{ {Type: "content", Content: "partial"}, {Type: "done", StopReason: "max_tokens"}, } - finished := []types.EyrieStreamEvent{ + finished := []types.FluxStreamEvent{ {Type: "content", Content: " finished"}, {Type: "done", StopReason: "end_turn"}, } t.Run("engine facade does not get a synthetic continuation turn", func(t *testing.T) { - base := &resilienceBoundaryClient{streams: [][]types.EyrieStreamEvent{partial, finished}} + base := &resilienceBoundaryClient{streams: [][]types.FluxStreamEvent{partial, finished}} session := NewSessionWithClient(&managedResilienceBoundaryClient{base}, "test", "test-model", "test system", nil, false) session.LifecycleSvc().Limits().SetMaxTurns(5) session.AddUser("continue test") @@ -204,7 +204,7 @@ func TestAgentLoopResilienceBoundary_MaxTokensContinuation(t *testing.T) { }) t.Run("legacy client retains synthetic continuation", func(t *testing.T) { - client := &resilienceBoundaryClient{streams: [][]types.EyrieStreamEvent{partial, finished}} + client := &resilienceBoundaryClient{streams: [][]types.FluxStreamEvent{partial, finished}} session := NewSessionWithClient(client, "test", "test-model", "test system", nil, false) session.LifecycleSvc().Limits().SetMaxTurns(5) session.AddUser("continue test") @@ -222,7 +222,7 @@ func TestAgentLoopResilienceBoundary_MaxTokensContinuation(t *testing.T) { } func TestAgentLoopResilienceBoundary_KeepsRhoToolAuthorizationAndMutation(t *testing.T) { - base := &resilienceBoundaryClient{streams: [][]types.EyrieStreamEvent{ + base := &resilienceBoundaryClient{streams: [][]types.FluxStreamEvent{ { {Type: "tool_call", ToolCall: &types.ToolCall{ID: "write-1", Name: "Write", Arguments: map[string]interface{}{"file_path": "blocked.txt", "content": "no"}}}, {Type: "done", StopReason: "tool_use"}, diff --git a/internal/engine/self_review.go b/internal/engine/self_review.go index baafff2b..1a93e7e4 100644 --- a/internal/engine/self_review.go +++ b/internal/engine/self_review.go @@ -102,8 +102,8 @@ SUGGESTIONS: `) } // buildReviewMessages wraps the review prompt in a message slice. -func buildReviewMessages(prompt string) []types.EyrieMessage { - return []types.EyrieMessage{ +func buildReviewMessages(prompt string) []types.FluxMessage { + return []types.FluxMessage{ {Role: "user", Content: prompt}, } } diff --git a/internal/engine/session.go b/internal/engine/session.go index 3c75a031..2ad62654 100644 --- a/internal/engine/session.go +++ b/internal/engine/session.go @@ -43,7 +43,7 @@ type SnapshotTracker interface { TrackCtx(ctx context.Context, message string) (string, error) } -// Session manages a conversation with an LLM via eyrie. +// Session manages a conversation with an LLM via flux. // The mu RWMutex protects the remaining session metadata for concurrent // access. Transcript and system-context state are owned by PersistenceService. // @@ -157,7 +157,7 @@ type Session struct { workMode WorkMode } -// NewSession creates a conversation session through Eyrie's engine facade. +// NewSession creates a conversation session through Flux's engine facade. func NewSession(provider, model, systemPrompt string, registry *tool.Registry) *Session { return NewRhoSession(context.Background(), gateway.Selection{ Provider: provider, @@ -661,10 +661,10 @@ func (s *Session) ForkConversation(nodeID string) (string, error) { if err != nil { return "", err } - msgs := make([]types.EyrieMessage, 0, len(history)) + msgs := make([]types.FluxMessage, 0, len(history)) for _, node := range history { if node.Role == "user" || node.Role == "assistant" { - msgs = append(msgs, types.EyrieMessage{Role: node.Role, Content: node.Content}) + msgs = append(msgs, types.FluxMessage{Role: node.Role, Content: node.Content}) } } p.SetRawMessages(msgs) @@ -688,10 +688,10 @@ func (s *Session) SwitchBranch(nodeID string) error { if err != nil { return err } - msgs := make([]types.EyrieMessage, 0, len(history)) + msgs := make([]types.FluxMessage, 0, len(history)) for _, node := range history { if node.Role == "user" || node.Role == "assistant" { - msgs = append(msgs, types.EyrieMessage{Role: node.Role, Content: node.Content}) + msgs = append(msgs, types.FluxMessage{Role: node.Role, Content: node.Content}) } } p.SetRawMessages(msgs) @@ -1024,7 +1024,7 @@ func (s *Session) EnsureSkillCatalogStatement() string { if digest != last { msg := plugin.RenderSkillCatalogMessage(invocable, digest) if p := s.Persistence(); p != nil { - p.AppendUserJournaled(types.EyrieMessage{Role: "user", Content: msg}) + p.AppendUserJournaled(types.FluxMessage{Role: "user", Content: msg}) } s.mu.Lock() s.lastSkillCatalogDigest = digest @@ -1041,7 +1041,7 @@ func (s *Session) CostValue() *Cost { return &s.Cost } -func (s *Session) LoadMessages(msgs []types.EyrieMessage) { +func (s *Session) LoadMessages(msgs []types.FluxMessage) { s.Persistence().SetRawMessages(msgs) } @@ -1055,7 +1055,7 @@ func (s *Session) MessageCount() int { // AddUser/AddAssistant and the agent loop (stream.go) all write through it, // and compaction/governor paths read it. Delegating here means TUI/CLI // consumers — notably saveSession — see the real, populated transcript. -func (s *Session) RawMessages() []types.EyrieMessage { +func (s *Session) RawMessages() []types.FluxMessage { if p := s.Persistence(); p != nil { return p.RawMessages() } @@ -1064,7 +1064,7 @@ func (s *Session) RawMessages() []types.EyrieMessage { // Chat implements the LLMClient interface by delegating to the underlying client. // This allows Session to be passed to components that need LLM access (e.g. Reflector, SelfReview). -func (s *Session) Chat(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (s *Session) Chat(ctx context.Context, msgs []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { if s.ChatLLM() == nil { return nil, fmt.Errorf("session: no LLM client configured") } @@ -1093,7 +1093,7 @@ func (s *Session) Schedule() *schedule.Manager { Priority: 1, }) } else { - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: content, }) diff --git a/internal/engine/session_factory.go b/internal/engine/session_factory.go index db58a643..092bafe6 100644 --- a/internal/engine/session_factory.go +++ b/internal/engine/session_factory.go @@ -17,7 +17,7 @@ import ( func BuildChatClient(ctx context.Context, selection gateway.Selection, legacyProvider string) (ChatClient, string, bool, error) { modelRuntime, err := gateway.New(ctx, nil) if err != nil { - return nil, requestedProvider(selection, legacyProvider), false, fmt.Errorf("eyrie transport: %w", err) + return nil, requestedProvider(selection, legacyProvider), false, fmt.Errorf("flux transport: %w", err) } return buildChatClientWithRuntime(ctx, modelRuntime, selection, legacyProvider) } @@ -33,7 +33,7 @@ func buildChatClientWithRuntime(ctx context.Context, modelRuntime *gateway.Gatew resolvedSelection.Provider = provider } if modelRuntime == nil { - return nil, provider, false, errors.New("eyrie transport: runtime is nil") + return nil, provider, false, errors.New("flux transport: runtime is nil") } label := strings.TrimSpace(resolvedSelection.Provider) if label == "" { @@ -47,7 +47,7 @@ func buildChatClientWithRuntime(ctx context.Context, modelRuntime *gateway.Gatew func BuildChatClientForSettings(ctx context.Context, settings rhoconfig.Settings, selection gateway.Selection, legacyProvider string) (ChatClient, string, bool, error) { modelRuntime, err := gateway.New(ctx, gatewayCustomGateways(settings.CustomProviders)) if err != nil { - return nil, requestedProvider(selection, legacyProvider), false, fmt.Errorf("eyrie transport: %w", err) + return nil, requestedProvider(selection, legacyProvider), false, fmt.Errorf("flux transport: %w", err) } return buildChatClientWithRuntime(ctx, modelRuntime, selection, legacyProvider) } @@ -96,7 +96,7 @@ func NewRhoSession(ctx context.Context, selection gateway.Selection, provider, m } // NewRhoSessionForSettings constructs a session with an invocation-scoped -// Eyrie engine, including custom gateways from an explicit settings file. +// Flux engine, including custom gateways from an explicit settings file. func NewRhoSessionForSettings(ctx context.Context, settings rhoconfig.Settings, selection gateway.Selection, provider, model, systemPrompt string, registry *tool.Registry) *Session { chat, label, deploy, err := BuildChatClientForSettings(ctx, settings, selection, provider) if err != nil { diff --git a/internal/engine/session_h3_h4_test.go b/internal/engine/session_h3_h4_test.go index 822cb1d8..bac18eac 100644 --- a/internal/engine/session_h3_h4_test.go +++ b/internal/engine/session_h3_h4_test.go @@ -93,7 +93,7 @@ func TestPersistenceService_RemoveLastExchangeAndCount(t *testing.T) { t.Parallel() ps := NewPersistenceService(nil) - ps.LoadMessages([]types.EyrieMessage{ + ps.LoadMessages([]types.FluxMessage{ {Role: "user", Content: "u1"}, {Role: "assistant", Content: "a1"}, {Role: "user", Content: "u2"}, diff --git a/internal/engine/session_mock_test.go b/internal/engine/session_mock_test.go index 4be6ee52..c8326576 100644 --- a/internal/engine/session_mock_test.go +++ b/internal/engine/session_mock_test.go @@ -116,7 +116,7 @@ func TestSession_LoadMessages(t *testing.T) { mc := newMockClient() s := newMockSession(mc) - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "msg1"}, {Role: "assistant", Content: "msg2"}, {Role: "user", Content: "msg3"}, diff --git a/internal/engine/stream.go b/internal/engine/stream.go index f5c7cf57..0b406b6f 100644 --- a/internal/engine/stream.go +++ b/internal/engine/stream.go @@ -124,7 +124,7 @@ func (s *Session) buildTurnOptions(tc turnContext) types.ChatOptions { s.Tools().Registry().PromoteForIntent(lastUserMsg) } if !smallTalk { - opts.Tools = s.Tools().Registry().EyrieTools() + opts.Tools = s.Tools().Registry().FluxTools() } } return opts @@ -291,7 +291,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Cache hit: short-circuit the LLM call if preResult.CacheHit && preResult.CachedResponse != "" { emit(StreamEvent{Type: "content", Content: preResult.CachedResponse}) - s.Persistence().AppendAssistantJournaled(types.EyrieMessage{Role: "assistant", Content: preResult.CachedResponse}) + s.Persistence().AppendAssistantJournaled(types.FluxMessage{Role: "assistant", Content: preResult.CachedResponse}) emit(StreamEvent{Type: "done"}) return } @@ -451,7 +451,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // rate limit, retry, and emergency compact internally; the // api.requests counter is incremented inside ChatService.Stream. // Rho records product-level latency; provider health and circuit - // breaking are owned by Eyrie's routed transport. + // breaking are owned by Flux's routed transport. apiStart := time.Now() managesResilience := clientManagesResilience(s.ChatLLM().Client()) result, err := s.ChatLLM().Stream(ctx, s.Persistence().RawMessages(), opts) @@ -474,13 +474,13 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { var textContent strings.Builder var toolCalls []types.ToolCall var stopReason string - var lastUsage *types.EyrieUsage + var lastUsage *types.FluxUsage var usageLedger streamUsageLedger resolvedProvider := strings.TrimSpace(s.ChatLLM().Provider()) resolvedModel := strings.TrimSpace(activeModel) // Compatibility clients retain Rho's historical stream retry and - // reasoning-only recovery. Eyrie facade clients already normalize and + // reasoning-only recovery. Flux facade clients already normalize and // recover provider streams, so Rho must consume their result exactly once. const maxStreamRetries = 2 var streamErr error @@ -663,7 +663,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { completionEst := estimateStreamCompletionTokens(textContent.String(), toolCalls) if inputTokens > 0 || completionEst > 0 { s.recordStreamUsage(ch, inputTokens, completionEst, resolvedProvider, resolvedModel, taskType, apiStart) - lastUsage = &types.EyrieUsage{ + lastUsage = &types.FluxUsage{ PromptTokens: inputTokens, CompletionTokens: completionEst, } @@ -682,7 +682,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { if j := s.Persistence().Journal(); j != nil { j.AppendRequestContext(resolvedProvider, resolvedModel, s.ContextWindowSize()) } - s.recordEyrieOperationObservation( + s.recordFluxOperationObservation( resolvedProvider, resolvedModel, stopReason, @@ -744,14 +744,14 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { oteltrace.EndSpanWithError(loopSpan, nil) } - // Compatibility-only max_tokens recovery. Eyrie's engine facade owns + // Compatibility-only max_tokens recovery. Flux's engine facade owns // continuation and exposes one normalized stream to Rho. Legacy clients // retain the historical synthetic turn so injected integrations do not // change behavior while they migrate to the facade. if !managesResilience && stopReason == "max_tokens" && len(toolCalls) == 0 && recoveryCount < maxRecoveryRetries { recoveryCount++ - s.Persistence().AppendAssistantJournaled(types.EyrieMessage{Role: "assistant", Content: textContent.String()}) - s.Persistence().AppendUserJournaled(types.EyrieMessage{Role: "user", Content: "Continue from where you left off."}) + s.Persistence().AppendAssistantJournaled(types.FluxMessage{Role: "assistant", Content: textContent.String()}) + s.Persistence().AppendUserJournaled(types.FluxMessage{Role: "user", Content: "Continue from where you left off."}) continue } @@ -774,7 +774,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { } } if textContent.Len() > 0 { - s.Persistence().AppendAssistantJournaled(types.EyrieMessage{Role: "assistant", Content: textContent.String()}) + s.Persistence().AppendAssistantJournaled(types.FluxMessage{Role: "assistant", Content: textContent.String()}) // Auto-remember corrections and learnings. Best-effort // fire-and-forget, bounded so a hung backend cannot leak. if s.MemorySvc().Memory() != nil && shouldRemember(textContent.String()) { @@ -931,7 +931,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { if assistContent == "" && len(toolCalls) > 0 { assistContent = " " // non-empty to satisfy APIs that reject empty content } - s.Persistence().AppendAssistantJournaled(types.EyrieMessage{ + s.Persistence().AppendAssistantJournaled(types.FluxMessage{ Role: "assistant", Content: assistContent, ToolUse: toolCalls, @@ -948,7 +948,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Network-facing tool output is untrusted: wrap it with an explicit // boundary so prompt-injection text is treated as data. resultContent = wrapExternalToolResult(r.tc.Name, resultContent) - msg := types.EyrieMessage{ + msg := types.FluxMessage{ Role: "user", Content: resultContent, ToolResults: []types.ToolResult{{ @@ -970,7 +970,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { steerCount := 0 if s.Persistence().Steering() != nil && s.Persistence().Steering().HasPending() { for _, steer := range s.Persistence().Steering().Drain() { - s.Persistence().AppendUserJournaled(types.EyrieMessage{ + s.Persistence().AppendUserJournaled(types.FluxMessage{ Role: "user", Content: "[User guidance during execution]: " + steer.Content, }) @@ -1084,7 +1084,7 @@ var smallTalkPhrases = []string{ // executed a tool. Once tools are in play, later turns keep the full prompt // and tool surface even if they read like small talk ("thanks"), so the // follow-up context is not lost. -func sessionHasToolUse(msgs []types.EyrieMessage) bool { +func sessionHasToolUse(msgs []types.FluxMessage) bool { for _, m := range msgs { if len(m.ToolResults) > 0 { return true diff --git a/internal/engine/stream_exhaustion.go b/internal/engine/stream_exhaustion.go index 1eca7aed..69637a00 100644 --- a/internal/engine/stream_exhaustion.go +++ b/internal/engine/stream_exhaustion.go @@ -46,7 +46,7 @@ func (s *Session) SynthesisForExhaustion(ctx context.Context, reason string) str callCtx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() - resp, err := s.ChatLLM().Chat(callCtx, []types.EyrieMessage{ + resp, err := s.ChatLLM().Chat(callCtx, []types.FluxMessage{ {Role: "user", Content: b.String()}, }, types.ChatOptions{ Provider: s.ChatLLM().Provider(), diff --git a/internal/engine/stream_exhaustion_test.go b/internal/engine/stream_exhaustion_test.go index a4d1ecfe..3cf50158 100644 --- a/internal/engine/stream_exhaustion_test.go +++ b/internal/engine/stream_exhaustion_test.go @@ -10,7 +10,7 @@ import ( func TestSynthesisForExhaustion(t *testing.T) { sess := NewSessionWithClient(NewMockClientForTest(), "test", "test-model", "", nil, false) - sess.Persistence().SetRawMessages([]types.EyrieMessage{ + sess.Persistence().SetRawMessages([]types.FluxMessage{ {Role: "user", Content: "fix the bug"}, {Role: "assistant", Content: "I found it"}, }) @@ -36,7 +36,7 @@ func TestSynthesisForExhaustionNoMessages(t *testing.T) { func TestSynthesisForExhaustionCancelled(t *testing.T) { sess := NewSessionWithClient(NewMockClientForTest(), "test", "test-model", "", nil, false) - sess.Persistence().SetRawMessages([]types.EyrieMessage{{Role: "user", Content: "hi"}}) + sess.Persistence().SetRawMessages([]types.FluxMessage{{Role: "user", Content: "hi"}}) ctx, cancel := context.WithCancel(context.Background()) cancel() if out := sess.SynthesisForExhaustion(ctx, "x"); out != "" { @@ -47,7 +47,7 @@ func TestSynthesisForExhaustionCancelled(t *testing.T) { func TestEmitExhaustionFallsBack(t *testing.T) { // Default (opt-in off): static fallback message even with a live LLM. sess := NewSessionWithClient(NewMockClientForTest(), "test", "test-model", "", nil, false) - sess.Persistence().SetRawMessages([]types.EyrieMessage{{Role: "user", Content: "hi"}}) + sess.Persistence().SetRawMessages([]types.FluxMessage{{Role: "user", Content: "hi"}}) ch := make(chan StreamEvent, 2) sess.emitExhaustion(context.Background(), ch, "reason") ev := <-ch @@ -63,7 +63,7 @@ func TestEmitExhaustionFallsBack(t *testing.T) { func TestEmitExhaustionSynthesizesWhenOptedIn(t *testing.T) { t.Setenv(gracefulExhaustionEnv, "1") sess := NewSessionWithClient(NewMockClientForTest(), "test", "test-model", "", nil, false) - sess.Persistence().SetRawMessages([]types.EyrieMessage{{Role: "user", Content: "hi"}}) + sess.Persistence().SetRawMessages([]types.FluxMessage{{Role: "user", Content: "hi"}}) ch := make(chan StreamEvent, 2) sess.emitExhaustion(context.Background(), ch, "turn limit reached") ev := <-ch diff --git a/internal/engine/stream_helpers.go b/internal/engine/stream_helpers.go index a9d742c1..314099cc 100644 --- a/internal/engine/stream_helpers.go +++ b/internal/engine/stream_helpers.go @@ -77,8 +77,8 @@ func streamRetryDelay(err error, attempt int) time.Duration { } // parseRetryDelayHint extracts a delay hint from an error message, returning 0 -// when no hint is present (mirroring eyrie's parseRetryDelay, but kept local so -// the engine package does not reach past the eyrie engine facade boundary). +// when no hint is present (mirroring flux's parseRetryDelay, but kept local so +// the engine package does not reach past the flux engine facade boundary). func parseRetryDelayHint(errMsg string) time.Duration { m := retryDelayRe.FindStringSubmatch(errMsg) if m == nil { diff --git a/internal/engine/stream_prompt_test.go b/internal/engine/stream_prompt_test.go index 8e7b6763..ef3c2f37 100644 --- a/internal/engine/stream_prompt_test.go +++ b/internal/engine/stream_prompt_test.go @@ -31,8 +31,8 @@ func TestIsSmallTalkPrompt(t *testing.T) { } func TestSessionHasToolUse(t *testing.T) { - plain := []types.EyrieMessage{{Role: "user", Content: "hi"}} - used := []types.EyrieMessage{ + plain := []types.FluxMessage{{Role: "user", Content: "hi"}} + used := []types.FluxMessage{ {Role: "user", Content: "read stream.go"}, {Role: "assistant", ToolUse: []types.ToolCall{{Name: "Read"}}}, {Role: "user", Content: "thanks", ToolResults: []types.ToolResult{{}}}, diff --git a/internal/engine/stream_usage.go b/internal/engine/stream_usage.go index 877ad8d2..19bf24bf 100644 --- a/internal/engine/stream_usage.go +++ b/internal/engine/stream_usage.go @@ -17,10 +17,10 @@ import ( // done event from being billed twice. Distinct usage events are preserved so // multi-request continuation streams still account for every provider call. type streamUsageLedger struct { - lastUsageEvent *types.EyrieUsage + lastUsageEvent *types.FluxUsage } -func (l *streamUsageLedger) shouldRecord(usage *types.EyrieUsage, terminal bool) bool { +func (l *streamUsageLedger) shouldRecord(usage *types.FluxUsage, terminal bool) bool { if usage == nil { return false } @@ -37,14 +37,14 @@ func (l *streamUsageLedger) reset() { l.lastUsageEvent = nil } -func equalStreamUsage(a, b *types.EyrieUsage) bool { +func equalStreamUsage(a, b *types.FluxUsage) bool { if a == nil || b == nil { return false } return *a == *b } -func cloneStreamUsage(usage *types.EyrieUsage) *types.EyrieUsage { +func cloneStreamUsage(usage *types.FluxUsage) *types.FluxUsage { if usage == nil { return nil } @@ -69,7 +69,7 @@ func updateResolvedRoute(provider, model string, route *types.ResolvedRoute) (st } // recordStreamUsage updates session billing/context counters and notifies the TUI. -// Provider and model are the concrete route selected by Eyrie, not merely the +// Provider and model are the concrete route selected by Flux, not merely the // user's preference, so fallback usage is attributed to the transport that ran. func (s *Session) recordStreamUsage(ch chan<- StreamEvent, prompt, completion int, provider, model, taskType string, apiStart time.Time) { if prompt <= 0 && completion <= 0 { diff --git a/internal/engine/stream_usage_test.go b/internal/engine/stream_usage_test.go index 3fbfe10d..72602ef9 100644 --- a/internal/engine/stream_usage_test.go +++ b/internal/engine/stream_usage_test.go @@ -9,7 +9,7 @@ import ( func TestStreamUsageLedgerRecordsDoneOnlyUsage(t *testing.T) { var ledger streamUsageLedger - terminal := &types.EyrieUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20} + terminal := &types.FluxUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20} if !ledger.shouldRecord(terminal, true) { t.Fatal("done-only usage was dropped") @@ -18,7 +18,7 @@ func TestStreamUsageLedgerRecordsDoneOnlyUsage(t *testing.T) { func TestStreamUsageLedgerDoesNotDoubleCountRepeatedDoneUsage(t *testing.T) { var ledger streamUsageLedger - usage := &types.EyrieUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20, CacheReadTokens: 4} + usage := &types.FluxUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20, CacheReadTokens: 4} if !ledger.shouldRecord(usage, false) { t.Fatal("usage event was dropped") @@ -30,8 +30,8 @@ func TestStreamUsageLedgerDoesNotDoubleCountRepeatedDoneUsage(t *testing.T) { func TestStreamUsageLedgerPreservesDistinctContinuationUsage(t *testing.T) { var ledger streamUsageLedger - first := &types.EyrieUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20} - second := &types.EyrieUsage{PromptTokens: 20, CompletionTokens: 5, TotalTokens: 25} + first := &types.FluxUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20} + second := &types.FluxUsage{PromptTokens: 20, CompletionTokens: 5, TotalTokens: 25} if !ledger.shouldRecord(first, false) || !ledger.shouldRecord(second, false) { t.Fatal("distinct continuation usage was dropped") diff --git a/internal/engine/structured_output.go b/internal/engine/structured_output.go index 6c1f264b..6ca3a6d0 100644 --- a/internal/engine/structured_output.go +++ b/internal/engine/structured_output.go @@ -11,7 +11,7 @@ import ( // structured_output.go implements JSON-schema-constrained responses (Task A). // -// The session-level option Session.OutputSchema is plumbed into eyrie's +// The session-level option Session.OutputSchema is plumbed into flux's // ChatOptions.ResponseFormat (json_schema) on the streaming path (see // stream.go). On the non-streaming ChatStructured path below, the response is // validated against the schema and, on mismatch, retried exactly once with a @@ -36,7 +36,7 @@ func (e *SchemaError) Error() string { return "schema validation failed: " + e.R // // schema must be a JSON Schema document (as a string). A blank schema disables // validation and behaves like an ordinary Chat call. -func (s *Session) ChatStructured(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions, schema string) (*types.EyrieResponse, error) { +func (s *Session) ChatStructured(ctx context.Context, msgs []types.FluxMessage, opts types.ChatOptions, schema string) (*types.FluxResponse, error) { chat := s.ChatLLM() if chat == nil { return nil, fmt.Errorf("session: no chat service configured") @@ -57,11 +57,11 @@ func (s *Session) ChatStructured(ctx context.Context, msgs []types.EyrieMessage, return resp, nil } else { // Retry once with a corrective instruction. - retryMsgs := append([]types.EyrieMessage(nil), msgs...) + retryMsgs := append([]types.FluxMessage(nil), msgs...) retryMsgs = append( retryMsgs, - types.EyrieMessage{Role: "assistant", Content: responseText(resp)}, - types.EyrieMessage{Role: "user", Content: fmt.Sprintf( + types.FluxMessage{Role: "assistant", Content: responseText(resp)}, + types.FluxMessage{Role: "user", Content: fmt.Sprintf( "Your previous response did not conform to the required JSON schema (%s). "+ "Respond again with ONLY valid JSON that matches the schema. Schema:\n%s", vErr.Error(), schema, @@ -79,7 +79,7 @@ func (s *Session) ChatStructured(ctx context.Context, msgs []types.EyrieMessage, } // responseText returns the textual content of a response, tolerating nil. -func responseText(resp *types.EyrieResponse) string { +func responseText(resp *types.FluxResponse) string { if resp == nil { return "" } diff --git a/internal/engine/structured_output_test.go b/internal/engine/structured_output_test.go index 1ff0d3a7..83e618b2 100644 --- a/internal/engine/structured_output_test.go +++ b/internal/engine/structured_output_test.go @@ -48,7 +48,7 @@ func TestChatStructured_NoSchema(t *testing.T) { s.SetTestClient(mc) resp, err := s.ChatStructured(context.Background(), - []types.EyrieMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}, "") + []types.FluxMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}, "") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -66,7 +66,7 @@ func TestChatStructured_ValidFirstTry(t *testing.T) { s.SetTestClient(mc) _, err := s.ChatStructured(context.Background(), - []types.EyrieMessage{{Role: "user", Content: "describe ada"}}, types.ChatOptions{}, personSchema) + []types.FluxMessage{{Role: "user", Content: "describe ada"}}, types.ChatOptions{}, personSchema) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -85,7 +85,7 @@ func TestChatStructured_RetryOnceThenSucceeds(t *testing.T) { s.SetTestClient(mc) resp, err := s.ChatStructured(context.Background(), - []types.EyrieMessage{{Role: "user", Content: "describe ada"}}, types.ChatOptions{}, personSchema) + []types.FluxMessage{{Role: "user", Content: "describe ada"}}, types.ChatOptions{}, personSchema) if err != nil { t.Fatalf("unexpected error after retry: %v", err) } @@ -107,7 +107,7 @@ func TestChatStructured_RetryOnceThenReturnsError(t *testing.T) { s.SetTestClient(mc) resp, err := s.ChatStructured(context.Background(), - []types.EyrieMessage{{Role: "user", Content: "describe"}}, types.ChatOptions{}, personSchema) + []types.FluxMessage{{Role: "user", Content: "describe"}}, types.ChatOptions{}, personSchema) if err == nil { t.Fatal("expected schema error after failed retry") } diff --git a/internal/engine/sub_service_wiring_test.go b/internal/engine/sub_service_wiring_test.go index 907dfed7..5a6a2d37 100644 --- a/internal/engine/sub_service_wiring_test.go +++ b/internal/engine/sub_service_wiring_test.go @@ -93,7 +93,7 @@ func TestSession_NewSessionWithClient_WiresAllSubServices(t *testing.T) { // actually goes through s.ChatLLM().Stream() rather than the legacy // ChatService.StreamChatContinue(). The mock client is injected via // SetTestClient, which also reattaches the ChatService, so the agent -// loop's call site must hit the mock and not the real eyrie client. +// loop's call site must hit the mock and not the real flux client. func TestSession_Stream_UsesChatService(t *testing.T) { mc := newMockClient(mockTextResponse("hi from service")) s := newMockSession(mc) diff --git a/internal/engine/subagent_synthesis.go b/internal/engine/subagent_synthesis.go index c95084d3..77401998 100644 --- a/internal/engine/subagent_synthesis.go +++ b/internal/engine/subagent_synthesis.go @@ -16,15 +16,15 @@ const SynthesisPrompt = "You have reached your turn limit. Provide a concise sum // The function builds messages from the conversation so far, appends a user // message with SynthesisPrompt, and calls the provider with Tools=nil (disabled) // to force a text-only response. -func SynthesizeSubAgent(ctx context.Context, llm LLMClient, model string, conversationSoFar []types.EyrieMessage) (string, error) { +func SynthesizeSubAgent(ctx context.Context, llm LLMClient, model string, conversationSoFar []types.FluxMessage) (string, error) { if llm == nil { return "", fmt.Errorf("subagent synthesis: LLM client is nil") } // Build messages: conversation so far + synthesis prompt. - msgs := make([]types.EyrieMessage, len(conversationSoFar)+1) + msgs := make([]types.FluxMessage, len(conversationSoFar)+1) copy(msgs, conversationSoFar) - msgs[len(msgs)-1] = types.EyrieMessage{ + msgs[len(msgs)-1] = types.FluxMessage{ Role: "user", Content: SynthesisPrompt, } diff --git a/internal/engine/subagent_synthesis_test.go b/internal/engine/subagent_synthesis_test.go index 497c2d21..8687ab34 100644 --- a/internal/engine/subagent_synthesis_test.go +++ b/internal/engine/subagent_synthesis_test.go @@ -11,7 +11,7 @@ import ( func TestSynthesizeSubAgent(t *testing.T) { t.Run("tools disabled and prompt appended", func(t *testing.T) { mock := &synthesisMockClient{response: "Here is my summary of findings."} - conversation := []types.EyrieMessage{ + conversation := []types.FluxMessage{ {Role: "user", Content: "Find all Go files with errors"}, {Role: "assistant", Content: "I'll search for Go files."}, } @@ -42,7 +42,7 @@ func TestSynthesizeSubAgent(t *testing.T) { }) t.Run("nil client returns error", func(t *testing.T) { - _, err := SynthesizeSubAgent(context.Background(), nil, "model", []types.EyrieMessage{{Role: "user", Content: "test"}}) + _, err := SynthesizeSubAgent(context.Background(), nil, "model", []types.FluxMessage{{Role: "user", Content: "test"}}) if err == nil { t.Fatal("expected error for nil client") } @@ -50,14 +50,14 @@ func TestSynthesizeSubAgent(t *testing.T) { t.Run("provider error propagated", func(t *testing.T) { mock := &synthesisMockClient{err: errors.New("provider unavailable")} - _, err := SynthesizeSubAgent(context.Background(), mock, "model", []types.EyrieMessage{{Role: "user", Content: "test"}}) + _, err := SynthesizeSubAgent(context.Background(), mock, "model", []types.FluxMessage{{Role: "user", Content: "test"}}) if err == nil { t.Fatal("expected error from provider") } }) t.Run("empty response returns error", func(t *testing.T) { - _, err := SynthesizeSubAgent(context.Background(), &synthesisMockClient{}, "model", []types.EyrieMessage{{Role: "user", Content: "test"}}) + _, err := SynthesizeSubAgent(context.Background(), &synthesisMockClient{}, "model", []types.FluxMessage{{Role: "user", Content: "test"}}) if err == nil { t.Fatal("expected error for empty response") } @@ -68,16 +68,16 @@ type synthesisMockClient struct { response string err error calls int - messages []types.EyrieMessage + messages []types.FluxMessage options types.ChatOptions } -func (m *synthesisMockClient) Chat(_ context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (m *synthesisMockClient) Chat(_ context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { m.calls++ - m.messages = append([]types.EyrieMessage(nil), messages...) + m.messages = append([]types.FluxMessage(nil), messages...) m.options = opts if m.err != nil { return nil, m.err } - return &types.EyrieResponse{Content: m.response, FinishReason: "end_turn"}, nil + return &types.FluxResponse{Content: m.response, FinishReason: "end_turn"}, nil } diff --git a/internal/engine/title.go b/internal/engine/title.go index 632775c6..120d3673 100644 --- a/internal/engine/title.go +++ b/internal/engine/title.go @@ -82,7 +82,7 @@ func (s *Session) generateTitleLLM(ctx context.Context) (string, error) { } // Fast streaming call for title generation - reqMsgs := []types.EyrieMessage{ + reqMsgs := []types.FluxMessage{ { Role: "user", Content: titlePrompt, diff --git a/internal/engine/token/token_budget.go b/internal/engine/token/token_budget.go index 7cd38e88..10586caf 100644 --- a/internal/engine/token/token_budget.go +++ b/internal/engine/token/token_budget.go @@ -14,7 +14,7 @@ import ( // // Research basis: Output tokens are 3-5x more expensive than input tokens. // Most tool-call turns only need 200-2000 tokens of output. -func DynamicMaxTokens(messages []types.EyrieMessage, contextSize int, taskType string) int { +func DynamicMaxTokens(messages []types.FluxMessage, contextSize int, taskType string) int { base := selectBaseTokens(taskType) // Check recent messages for tool-call-heavy pattern. @@ -69,7 +69,7 @@ func selectBaseTokens(taskType string) int { // isRecentToolHeavy returns true if the last N assistant messages all // contained tool calls, suggesting the agent is in a tool-use loop where // large output budgets are wasted. -func isRecentToolHeavy(messages []types.EyrieMessage) bool { +func isRecentToolHeavy(messages []types.FluxMessage) bool { const lookback = 3 toolTurns := 0 assistantSeen := 0 @@ -92,7 +92,7 @@ func isRecentToolHeavy(messages []types.EyrieMessage) bool { // isTextQuestion returns true if the last user message looks like a // question expecting a textual answer rather than a tool invocation. -func isTextQuestion(messages []types.EyrieMessage) bool { +func isTextQuestion(messages []types.FluxMessage) bool { // Find the last user message (skip tool results). var lastUserMsg string for i := len(messages) - 1; i >= 0; i-- { diff --git a/internal/engine/token/token_budget_test.go b/internal/engine/token/token_budget_test.go index 96a822e3..7c1140b2 100644 --- a/internal/engine/token/token_budget_test.go +++ b/internal/engine/token/token_budget_test.go @@ -8,7 +8,7 @@ import ( ) func TestDynamicMaxTokens_CodeGenTask(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "Implement a REST API server"}, } got := DynamicMaxTokens(msgs, 200000, "code") @@ -18,7 +18,7 @@ func TestDynamicMaxTokens_CodeGenTask(t *testing.T) { } func TestDynamicMaxTokens_TextQuestion(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "What is the difference between a mutex and a semaphore?"}, } got := DynamicMaxTokens(msgs, 200000, "code") @@ -30,7 +30,7 @@ func TestDynamicMaxTokens_TextQuestion(t *testing.T) { func TestDynamicMaxTokens_ToolHeavyPattern(t *testing.T) { // Simulate 3 consecutive assistant turns that all used tools. - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "read the files"}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{ID: "t1", Name: "Read"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "file1 content"}}}, @@ -48,7 +48,7 @@ func TestDynamicMaxTokens_ToolHeavyPattern(t *testing.T) { func TestDynamicMaxTokens_NotEnoughToolTurns(t *testing.T) { // Only 2 tool turns (below the lookback threshold of 3). - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "do something"}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{ID: "t1", Name: "Read"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "output"}}}, @@ -65,7 +65,7 @@ func TestDynamicMaxTokens_NotEnoughToolTurns(t *testing.T) { func TestDynamicMaxTokens_MixedToolAndText(t *testing.T) { // 3 assistant turns, but one is text-only -- not tool-heavy. - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "help me"}, {Role: "assistant", Content: "Sure, I can help."}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{ID: "t1", Name: "Read"}}}, @@ -83,7 +83,7 @@ func TestDynamicMaxTokens_MixedToolAndText(t *testing.T) { func TestDynamicMaxTokens_ContextBudgetLimit(t *testing.T) { // Build messages that consume most of the context. - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: strings.Repeat("word ", 5000)}, } // Small context window of 8000 tokens. @@ -98,7 +98,7 @@ func TestDynamicMaxTokens_ContextBudgetLimit(t *testing.T) { } func TestDynamicMaxTokens_VerySmallContext(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: strings.Repeat("word ", 2000)}, } got := DynamicMaxTokens(msgs, 2100, "code") @@ -109,7 +109,7 @@ func TestDynamicMaxTokens_VerySmallContext(t *testing.T) { } func TestDynamicMaxTokens_ZeroContextSize(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "generate code"}, } // contextSize=0 means unknown/unlimited -- should use base budget. @@ -120,7 +120,7 @@ func TestDynamicMaxTokens_ZeroContextSize(t *testing.T) { } func TestDynamicMaxTokens_ExplainTask(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "generate something"}, } got := DynamicMaxTokens(msgs, 200000, "explain") @@ -130,7 +130,7 @@ func TestDynamicMaxTokens_ExplainTask(t *testing.T) { } func TestDynamicMaxTokens_ToolTask(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "do it"}, } got := DynamicMaxTokens(msgs, 200000, "tool") @@ -140,7 +140,7 @@ func TestDynamicMaxTokens_ToolTask(t *testing.T) { } func TestDynamicMaxTokens_DefaultTask(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "do something"}, } got := DynamicMaxTokens(msgs, 200000, "") @@ -152,7 +152,7 @@ func TestDynamicMaxTokens_DefaultTask(t *testing.T) { func TestIsRecentToolHeavy(t *testing.T) { tests := []struct { name string - msgs []types.EyrieMessage + msgs []types.FluxMessage want bool }{ { @@ -162,7 +162,7 @@ func TestIsRecentToolHeavy(t *testing.T) { }, { name: "only user messages", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "user", Content: "world"}, }, @@ -170,7 +170,7 @@ func TestIsRecentToolHeavy(t *testing.T) { }, { name: "three tool turns", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "assistant", ToolUse: []types.ToolCall{{ID: "1", Name: "Read"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "1"}}}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "2", Name: "Edit"}}}, @@ -182,7 +182,7 @@ func TestIsRecentToolHeavy(t *testing.T) { }, { name: "two tool one text", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "assistant", Content: "let me think"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "1", Name: "Read"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "1"}}}, @@ -205,7 +205,7 @@ func TestIsRecentToolHeavy(t *testing.T) { func TestIsTextQuestion(t *testing.T) { tests := []struct { name string - msgs []types.EyrieMessage + msgs []types.FluxMessage want bool }{ { @@ -215,35 +215,35 @@ func TestIsTextQuestion(t *testing.T) { }, { name: "question mark", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "Is this working?"}, }, want: true, }, { name: "what prefix", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "What does this function do"}, }, want: true, }, { name: "explain prefix", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "Explain the architecture"}, }, want: true, }, { name: "imperative command", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "Implement the REST API"}, }, want: false, }, { name: "skips tool results", - msgs: []types.EyrieMessage{ + msgs: []types.FluxMessage{ {Role: "user", Content: "Why does this fail?"}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "error output"}}}, }, diff --git a/internal/engine/token/token_estimates.go b/internal/engine/token/token_estimates.go index c6b258f9..5a2cf6a0 100644 --- a/internal/engine/token/token_estimates.go +++ b/internal/engine/token/token_estimates.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/rho/internal/types" ) -func EstimateTokens(msgs []types.EyrieMessage) int { +func EstimateTokens(msgs []types.FluxMessage) int { total := 0 for _, m := range msgs { total += EstimateMessageTokens(m) @@ -15,7 +15,7 @@ func EstimateTokens(msgs []types.EyrieMessage) int { return total } -func EstimateMessageTokens(m types.EyrieMessage) int { +func EstimateMessageTokens(m types.FluxMessage) int { tokens := CountTokens(m.Content) for _, tc := range m.ToolUse { tokens += CountTokens(tc.Name) diff --git a/internal/engine/token_reexports.go b/internal/engine/token_reexports.go index e0f50a49..c970cde6 100644 --- a/internal/engine/token_reexports.go +++ b/internal/engine/token_reexports.go @@ -17,7 +17,7 @@ type ( func NewTokenPredictor() *TokenPredictor { return token.NewTokenPredictor() } func NewTokenReporter(sessionBudget int) *TokenReporter { return token.NewTokenReporter(sessionBudget) } -func DynamicMaxTokens(messages []types.EyrieMessage, contextSize int, taskType string) int { +func DynamicMaxTokens(messages []types.FluxMessage, contextSize int, taskType string) int { return token.DynamicMaxTokens(messages, contextSize, taskType) } func ClassifyTaskComplexity(task string) string { return token.ClassifyTaskComplexity(task) } @@ -33,5 +33,5 @@ func CountTokensFast(text string) int { return token.CountTokensFast(text) } func CompressForContext(text string, budget int) (string, int) { return token.CompressForContext(text, budget) } -func EstimateTokens(msgs []types.EyrieMessage) int { return token.EstimateTokens(msgs) } -func EstimateMessageTokens(m types.EyrieMessage) int { return token.EstimateMessageTokens(m) } +func EstimateTokens(msgs []types.FluxMessage) int { return token.EstimateTokens(msgs) } +func EstimateMessageTokens(m types.FluxMessage) int { return token.EstimateMessageTokens(m) } diff --git a/internal/engine/tool_catalog_shrink.go b/internal/engine/tool_catalog_shrink.go index 0a18412c..7daf21a4 100644 --- a/internal/engine/tool_catalog_shrink.go +++ b/internal/engine/tool_catalog_shrink.go @@ -34,19 +34,19 @@ func originalsDir() string { return filepath.Join(storage.StateDir(), "tool-catalog-originals") } -// shrinkEyrieTools compresses the rho tool list via the token engine's toolschema +// shrinkFluxTools compresses the rho tool list via the token engine's toolschema // compressor. The list is converted to the OpenAI function-catalog wire shape, // shrunk, and converted back; any name-set mismatch fails open to input. // When compression changed something, the original catalog is persisted under // the state dir keyed by content hash before the shrunk form is returned. -func shrinkEyrieTools(tools []types.EyrieTool) []types.EyrieTool { +func shrinkFluxTools(tools []types.FluxTool) []types.FluxTool { if len(tools) == 0 || !toolShrinkEnabled() { return tools } type wireTool struct { - Type string `json:"type"` - Function types.EyrieTool `json:"function"` + Type string `json:"type"` + Function types.FluxTool `json:"function"` } wire := make([]wireTool, len(tools)) for i := range tools { @@ -68,7 +68,7 @@ func shrinkEyrieTools(tools []types.EyrieTool) []types.EyrieTool { if len(shrunkWire) != len(tools) { return tools // structural drift: never risk it } - out := make([]types.EyrieTool, len(tools)) + out := make([]types.FluxTool, len(tools)) for i := range shrunkWire { if shrunkWire[i].Function.Name != tools[i].Name { slog.Debug("tool shrink name drift, failing open", "position", i) diff --git a/internal/engine/tool_catalog_shrink_test.go b/internal/engine/tool_catalog_shrink_test.go index c9e051c7..ffca2101 100644 --- a/internal/engine/tool_catalog_shrink_test.go +++ b/internal/engine/tool_catalog_shrink_test.go @@ -8,8 +8,8 @@ import ( "github.com/GrayCodeAI/rho/internal/types" ) -func bloatedTools() []types.EyrieTool { - return []types.EyrieTool{ +func bloatedTools() []types.FluxTool { + return []types.FluxTool{ { Name: "read_file", Description: strings.Repeat("Reads a file from disk quickly and safely. ", 30) + @@ -32,20 +32,20 @@ func bloatedTools() []types.EyrieTool { } } -func TestShrinkEyrieToolsDisabledByDefault(t *testing.T) { +func TestShrinkFluxToolsDisabledByDefault(t *testing.T) { t.Setenv("RHO_TOOL_SHRINK", "") in := bloatedTools() - out := shrinkEyrieTools(in) + out := shrinkFluxTools(in) if len(out) != len(in) || out[0].Description != in[0].Description { t.Fatal("shrink must be a no-op when disabled") } } -func TestShrinkEyrieToolsEnabledReducesAndPreservesNames(t *testing.T) { +func TestShrinkFluxToolsEnabledReducesAndPreservesNames(t *testing.T) { t.Setenv("RHO_TOOL_SHRINK", "1") t.Setenv("RHO_STATE_DIR", t.TempDir()) in := bloatedTools() - out := shrinkEyrieTools(in) + out := shrinkFluxTools(in) if len(out) != 2 { t.Fatalf("tool count changed: %d", len(out)) } @@ -82,7 +82,7 @@ func TestOriginalCatalogPersistedForRecovery(t *testing.T) { t.Setenv("RHO_TOOL_SHRINK", "1") t.Setenv("RHO_STATE_DIR", stateDir) in := bloatedTools() - _ = shrinkEyrieTools(in) + _ = shrinkFluxTools(in) matches, err := filepath.Glob(stateDir + "/tool-catalog-originals/*.json") if err != nil || len(matches) == 0 { t.Fatalf("original catalog not persisted: %v %v", matches, err) diff --git a/internal/engine/tool_service.go b/internal/engine/tool_service.go index 46c9c2d3..441a0f14 100644 --- a/internal/engine/tool_service.go +++ b/internal/engine/tool_service.go @@ -461,7 +461,7 @@ func (s *ToolService) ExecuteOne(ctx context.Context, tc types.ToolCall, overrid var commitChat func(context.Context, string) (string, error) if s.deps.chat != nil { commitChat = func(chatCtx context.Context, prompt string) (string, error) { - resp, err := s.deps.chat.Chat(chatCtx, []types.EyrieMessage{{Role: "user", Content: prompt}}, types.ChatOptions{Provider: s.deps.chat.Provider(), Model: s.deps.chat.Model(), MaxTokens: 256}) + resp, err := s.deps.chat.Chat(chatCtx, []types.FluxMessage{{Role: "user", Content: prompt}}, types.ChatOptions{Provider: s.deps.chat.Provider(), Model: s.deps.chat.Model(), MaxTokens: 256}) if err != nil { return "", err } diff --git a/internal/engine/trajectory.go b/internal/engine/trajectory.go index 28b3da69..94e53839 100644 --- a/internal/engine/trajectory.go +++ b/internal/engine/trajectory.go @@ -11,7 +11,7 @@ import ( // TrajectoryRun records a single attempt at completing a task. type TrajectoryRun struct { ID int - Messages []types.EyrieMessage + Messages []types.FluxMessage Success bool Summary string // distilled lessons from this run Tokens int @@ -123,7 +123,7 @@ func (td *TrajectoryDistiller) RunWithDistillation(ctx context.Context, prompt s // SummarizeTrajectory extracts a concise summary from a sequence of messages: // what was attempted, what failed, key decisions made, and files touched. -func SummarizeTrajectory(messages []types.EyrieMessage) string { +func SummarizeTrajectory(messages []types.FluxMessage) string { var b strings.Builder var attempted []string var failures []string diff --git a/internal/engine/trajectory_test.go b/internal/engine/trajectory_test.go index 70d29dec..350b6e29 100644 --- a/internal/engine/trajectory_test.go +++ b/internal/engine/trajectory_test.go @@ -8,7 +8,7 @@ import ( ) func TestSummarizeTrajectory_ToolUsage(t *testing.T) { - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ { Role: "assistant", Content: "I will edit the file.", @@ -46,7 +46,7 @@ func TestSummarizeTrajectory_ToolUsage(t *testing.T) { } func TestSummarizeTrajectory_NoActions(t *testing.T) { - messages := []types.EyrieMessage{ + messages := []types.FluxMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi there"}, } @@ -60,9 +60,9 @@ func TestSummarizeTrajectory_NoActions(t *testing.T) { func TestBestRun_PrefersSuccess(t *testing.T) { td := &TrajectoryDistiller{maxRuns: 3} runs := []TrajectoryRun{ - {ID: 1, Success: false, Messages: make([]types.EyrieMessage, 10)}, - {ID: 2, Success: true, Messages: make([]types.EyrieMessage, 5)}, - {ID: 3, Success: false, Messages: make([]types.EyrieMessage, 15)}, + {ID: 1, Success: false, Messages: make([]types.FluxMessage, 10)}, + {ID: 2, Success: true, Messages: make([]types.FluxMessage, 5)}, + {ID: 3, Success: false, Messages: make([]types.FluxMessage, 15)}, } best := td.BestRun(runs) @@ -77,9 +77,9 @@ func TestBestRun_PrefersSuccess(t *testing.T) { func TestBestRun_FallsBackToMostProgress(t *testing.T) { td := &TrajectoryDistiller{maxRuns: 3} runs := []TrajectoryRun{ - {ID: 1, Success: false, Messages: make([]types.EyrieMessage, 5)}, - {ID: 2, Success: false, Messages: make([]types.EyrieMessage, 12)}, - {ID: 3, Success: false, Messages: make([]types.EyrieMessage, 8)}, + {ID: 1, Success: false, Messages: make([]types.FluxMessage, 5)}, + {ID: 2, Success: false, Messages: make([]types.FluxMessage, 12)}, + {ID: 3, Success: false, Messages: make([]types.FluxMessage, 8)}, } best := td.BestRun(runs) diff --git a/internal/engine/vision.go b/internal/engine/vision.go index d775c77d..d3cfca6a 100644 --- a/internal/engine/vision.go +++ b/internal/engine/vision.go @@ -7,13 +7,13 @@ import ( ) // ModelSupportsVision reports whether the named model can accept image content -// blocks. eyrie carries image data on EyrieMessage.Images but does not expose a +// blocks. flux carries image data on FluxMessage.Images but does not expose a // per-model capability flag, so rho gates locally on the model identifier. // // The check is heuristic and intentionally conservative: an unknown model is // treated as non-vision so that we never send image blocks a provider will // reject. Known vision-capable families are matched by substring on the -// lower-cased model id (which is how eyrie/providers name them). +// lower-cased model id (which is how flux/providers name them). func ModelSupportsVision(model string) bool { m := strings.ToLower(strings.TrimSpace(model)) if m == "" { @@ -70,7 +70,7 @@ func (s *Session) SupportsVision() bool { // empty it defaults to image/png. // // When the active model supports vision the image is attached as a multimodal -// block (via EyrieMessage.Images, which eyrie expands into an image content +// block (via FluxMessage.Images, which flux expands into an image content // block). When it does not, the message degrades gracefully: only the text is // sent, annotated with a note so the model is aware an image was dropped, and // attached reports false. Callers can surface a warning to the user on false. @@ -93,7 +93,7 @@ func (s *Session) AddUserWithAttachment(content, imageBase64, mediaType string) if persist == nil { return false } - persist.AppendUserJournaled(types.EyrieMessage{ + persist.AppendUserJournaled(types.FluxMessage{ Role: "user", Content: content, Images: []string{"data:" + mediaType + ";base64," + imageBase64}, @@ -111,7 +111,7 @@ func (s *Session) AddUserWithAttachment(content, imageBase64, mediaType string) // AddUserWithDocumentText adds a user message that incorporates extracted // document text (e.g. from a PDF) inline as text. This is the graceful path for -// formats eyrie cannot carry as native image/document blocks: the extracted +// formats flux cannot carry as native image/document blocks: the extracted // text is fenced and labeled so the model can reason over it. // // label is a short human identifier for the source (e.g. the file name); it may diff --git a/internal/env/scrub.go b/internal/env/scrub.go index be5b7642..a2e7c3d7 100644 --- a/internal/env/scrub.go +++ b/internal/env/scrub.go @@ -8,9 +8,9 @@ import ( // ScrubSet is the canonical list of credential env vars that agent-launched // subprocesses (the Bash tool, background tasks, seatbelt wrapper) must never -// inherit. It mirrors the provider key names in Eyrie's provider profiles -// (eyrie/config/profiles.go). Keeping the list explicit here avoids -// importing eyrie internals and makes the boundary auditable. +// inherit. It mirrors the provider key names in Flux's provider profiles +// (flux/config/profiles.go). Keeping the list explicit here avoids +// importing flux internals and makes the boundary auditable. var ScrubSet = []string{ "ANTHROPIC_API_KEY", "AWS_ACCESS_KEY_ID", diff --git a/internal/feature/eval/toolmatrix.go b/internal/feature/eval/toolmatrix.go index f1d7a225..05bede2e 100644 --- a/internal/feature/eval/toolmatrix.go +++ b/internal/feature/eval/toolmatrix.go @@ -18,7 +18,7 @@ import ( // right tool and format the arguments correctly? Scored separately, and only // over cases where a trigger actually fired. // -// With 40+ tools and model-swapping via eyrie, "picked the wrong moment to call +// With 40+ tools and model-swapping via flux, "picked the wrong moment to call // a tool" and "called the right tool with bad args" are distinct regressions. // Scoring them apart makes which one regressed legible. diff --git a/internal/multiagent/worker.go b/internal/multiagent/worker.go index be78de94..9342a292 100644 --- a/internal/multiagent/worker.go +++ b/internal/multiagent/worker.go @@ -103,7 +103,7 @@ func EngineWorker(provider, model, systemPrompt string) WorkerFunc { defer func() { _ = writer.Close() }() // Persist the initial user prompt. - _ = writer.Write(types.EyrieMessage{Role: "user", Content: workerPrompt}) + _ = writer.Write(types.FluxMessage{Role: "user", Content: workerPrompt}) sess.AddUser(workerPrompt) events, err := sess.Stream(ctx) @@ -117,9 +117,9 @@ func EngineWorker(provider, model, systemPrompt string) WorkerFunc { switch ev.Type { case "content": response.WriteString(ev.Content) - _ = writer.Write(types.EyrieMessage{Role: "assistant", Content: ev.Content}) + _ = writer.Write(types.FluxMessage{Role: "assistant", Content: ev.Content}) case "tool_use": - _ = writer.Write(types.EyrieMessage{ + _ = writer.Write(types.FluxMessage{ Role: "assistant", Content: "", ToolUse: []types.ToolCall{{Name: ev.ToolName, ID: ev.ToolID}}, }) @@ -166,7 +166,7 @@ func checkExistingTranscript(path string) *Handoff { // incompleteTranscriptMessages returns the messages from an incomplete // transcript (one without a completion marker). Returns false if the transcript // is missing or complete. -func incompleteTranscriptMessages(path string) ([]types.EyrieMessage, bool) { +func incompleteTranscriptMessages(path string) ([]types.FluxMessage, bool) { exists, complete, err := IsTranscriptComplete(path) if err != nil || !exists || complete { return nil, false diff --git a/internal/multiagent/worker_transcript.go b/internal/multiagent/worker_transcript.go index bac22440..037cb401 100644 --- a/internal/multiagent/worker_transcript.go +++ b/internal/multiagent/worker_transcript.go @@ -55,16 +55,16 @@ func IsTranscriptComplete(path string) (bool, bool, error) { return found, false, nil } -// LoadTranscript reads a transcript file back into EyrieMessages. The final +// LoadTranscript reads a transcript file back into FluxMessages. The final // __complete record (if any) is returned separately as handoff+true. -func LoadTranscript(path string) ([]types.EyrieMessage, *Handoff, bool, error) { +func LoadTranscript(path string) ([]types.FluxMessage, *Handoff, bool, error) { f, err := os.Open(path) if err != nil { return nil, nil, false, err } defer func() { _ = f.Close() }() - var messages []types.EyrieMessage + var messages []types.FluxMessage var handoff *Handoff var complete bool @@ -86,7 +86,7 @@ func LoadTranscript(path string) ([]types.EyrieMessage, *Handoff, bool, error) { continue } - var msg types.EyrieMessage + var msg types.FluxMessage if err := json.Unmarshal(line, &msg); err != nil { // Skip malformed lines rather than failing the whole load. continue @@ -127,13 +127,13 @@ func (w *PersistWriter) Path() string { } // Write appends one message to the transcript. -func (w *PersistWriter) Write(msg types.EyrieMessage) error { +func (w *PersistWriter) Write(msg types.FluxMessage) error { w.mu.Lock() defer w.mu.Unlock() return w.writeLocked(msg) } -func (w *PersistWriter) writeLocked(msg types.EyrieMessage) error { +func (w *PersistWriter) writeLocked(msg types.FluxMessage) error { data, err := json.Marshal(msg) if err != nil { return fmt.Errorf("marshal transcript message: %w", err) diff --git a/internal/multiagent/worker_transcript_test.go b/internal/multiagent/worker_transcript_test.go index 04cf6caa..45f62ec9 100644 --- a/internal/multiagent/worker_transcript_test.go +++ b/internal/multiagent/worker_transcript_test.go @@ -17,7 +17,7 @@ func TestTranscriptRoundTrip(t *testing.T) { t.Fatalf("NewPersistWriter: %v", err) } - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "Start working on feature 1"}, {Role: "assistant", Content: "I'll explore the codebase first."}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{Name: "Bash", ID: "tc1"}}}, @@ -74,7 +74,7 @@ func TestTranscriptIncomplete(t *testing.T) { if err != nil { t.Fatalf("NewPersistWriter: %v", err) } - if err := w.Write(types.EyrieMessage{Role: "user", Content: "Start"}); err != nil { + if err := w.Write(types.FluxMessage{Role: "user", Content: "Start"}); err != nil { t.Fatalf("Write: %v", err) } if err := w.Close(); err != nil { diff --git a/internal/planning/model_test.go b/internal/planning/model_test.go index 4cdee8d7..61849042 100644 --- a/internal/planning/model_test.go +++ b/internal/planning/model_test.go @@ -14,7 +14,7 @@ type mockProvider struct { scores map[string]string } -func (m *mockProvider) Chat(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (m *mockProvider) Chat(ctx context.Context, msgs []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { user := "" for _, mm := range msgs { if mm.Role == "user" { @@ -22,15 +22,15 @@ func (m *mockProvider) Chat(ctx context.Context, msgs []types.EyrieMessage, opts } } if e, ok := m.expand[user]; ok { - return &types.EyrieResponse{Content: e}, nil + return &types.FluxResponse{Content: e}, nil } if s, ok := m.scores[user]; ok { - return &types.EyrieResponse{Content: s}, nil + return &types.FluxResponse{Content: s}, nil } - return &types.EyrieResponse{Content: "0.5"}, nil + return &types.FluxResponse{Content: "0.5"}, nil } -func (m *mockProvider) StreamChat(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { +func (m *mockProvider) StreamChat(ctx context.Context, msgs []types.FluxMessage, opts types.ChatOptions) (*types.StreamResult, error) { return nil, nil } func (m *mockProvider) Ping(ctx context.Context) error { return nil } diff --git a/internal/planning/search.go b/internal/planning/search.go index 8e8255a3..d302545c 100644 --- a/internal/planning/search.go +++ b/internal/planning/search.go @@ -95,7 +95,7 @@ type ModelExpander struct { // Expand returns candidate next states generated by the model. func (e *ModelExpander) Expand(state string) []string { - resp, err := e.Provider.Chat(context.Background(), []types.EyrieMessage{ + resp, err := e.Provider.Chat(context.Background(), []types.FluxMessage{ {Role: "system", Content: e.Prompt}, {Role: "user", Content: state}, }, types.ChatOptions{Model: e.Model, MaxTokens: 512}) @@ -115,7 +115,7 @@ type ModelScorer struct { // Score returns the model's 0..1 value for a state. func (s *ModelScorer) Score(state string) float64 { - resp, err := s.Provider.Chat(context.Background(), []types.EyrieMessage{ + resp, err := s.Provider.Chat(context.Background(), []types.FluxMessage{ {Role: "system", Content: s.Prompt}, {Role: "user", Content: state}, }, types.ChatOptions{Model: s.Model, MaxTokens: 16}) diff --git a/internal/provider/gateway/engine_client.go b/internal/provider/gateway/engine_client.go index b10fb72b..559797c0 100644 --- a/internal/provider/gateway/engine_client.go +++ b/internal/provider/gateway/engine_client.go @@ -1,9 +1,9 @@ -// Package gateway is Rho's single boundary to Eyrie's provider runtime. It is -// the only package that imports Eyrie; everything else speaks the rho-owned +// Package gateway is Rho's single boundary to Flux's provider runtime. It is +// the only package that imports Flux; everything else speaks the rho-owned // Provider interface and the internal/types DTOs. // -// rho = product face (UX/agent/sessions) · eyrie = provider engine -// One-way dependency only: eyrie never imports rho. See README ecosystems. +// rho = product face (UX/agent/sessions) · flux = provider engine +// One-way dependency only: flux never imports rho. See README ecosystems. package gateway import ( @@ -11,13 +11,13 @@ import ( "fmt" "log/slog" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" - "github.com/GrayCodeAI/eyrie/llm" + fluxengine "github.com/GrayCodeAI/flux/engine" + "github.com/GrayCodeAI/flux/llm" "github.com/GrayCodeAI/rho/internal/types" ) -// Provider is Rho's rho-owned view of the Eyrie engine: a composition of the -// role interfaces below. It wraps the concrete *eyrieengine.Engine (whose fields +// Provider is Rho's rho-owned view of the Flux engine: a composition of the +// role interfaces below. It wraps the concrete *fluxengine.Engine (whose fields // are unexported and therefore not directly mockable) so Rho tests can inject a // stub. Splitting into roles lets callers and stubs depend only on the facet they // use (e.g. the ChatClient path needs only Generator); Provider stays the full @@ -34,50 +34,50 @@ type Provider interface { // Generator is the chat transport facet: the only part the ChatClient path uses. type Generator interface { - Generate(ctx context.Context, req eyrieengine.GenerateRequest) (*eyrieengine.GenerateResponse, error) - Stream(ctx context.Context, req eyrieengine.GenerateRequest) (eyrieengine.EventStreamer, error) + Generate(ctx context.Context, req fluxengine.GenerateRequest) (*fluxengine.GenerateResponse, error) + Stream(ctx context.Context, req fluxengine.GenerateRequest) (fluxengine.EventStreamer, error) } // NativeCompactor is the provider-native-compaction facet. type NativeCompactor interface { SupportsNativeCompaction(ctx context.Context, provider, model string) bool - CompactNative(ctx context.Context, req eyrieengine.NativeCompactionRequest) (string, error) + CompactNative(ctx context.Context, req fluxengine.NativeCompactionRequest) (string, error) } // ModelCatalog is the model-discovery facet (used by routing + config). type ModelCatalog interface { - ListModels(ctx context.Context, providerID string, refresh bool) ([]eyrieengine.Model, error) - ListLiveModels(ctx context.Context, providerID string) ([]eyrieengine.Model, error) - ListPublicModels(ctx context.Context, providerID string) ([]eyrieengine.Model, error) - ModelInfo(ctx context.Context, modelID string) (eyrieengine.Model, bool, error) + ListModels(ctx context.Context, providerID string, refresh bool) ([]fluxengine.Model, error) + ListLiveModels(ctx context.Context, providerID string) ([]fluxengine.Model, error) + ListPublicModels(ctx context.Context, providerID string) ([]fluxengine.Model, error) + ModelInfo(ctx context.Context, modelID string) (fluxengine.Model, bool, error) ModelProviders(ctx context.Context) ([]string, error) DefaultModel(ctx context.Context, provider, fallback string) string - PreferredModel(ctx context.Context, provider string, class eyrieengine.ModelClass, fallback string) string - PreferredModels(ctx context.Context, primaryProvider string, class eyrieengine.ModelClass, limit int) []string - ModelClassOf(ctx context.Context, modelID string) eyrieengine.ModelClass + PreferredModel(ctx context.Context, provider string, class fluxengine.ModelClass, fallback string) string + PreferredModels(ctx context.Context, primaryProvider string, class fluxengine.ModelClass, limit int) []string + ModelClassOf(ctx context.Context, modelID string) fluxengine.ModelClass ProviderForModel(ctx context.Context, modelID string) string PrimaryModel(ctx context.Context) string ModelNames(ctx context.Context) []string - Catalog(ctx context.Context) (eyrieengine.CatalogSnapshot, error) + Catalog(ctx context.Context) (fluxengine.CatalogSnapshot, error) } // CredentialManager is the key/credential facet (config only). type CredentialManager interface { - SaveCredential(ctx context.Context, providerID, secret string) (eyrieengine.CredentialStatus, error) + SaveCredential(ctx context.Context, providerID, secret string) (fluxengine.CredentialStatus, error) RemoveCredential(ctx context.Context, providerID string) error - CredentialStatus(ctx context.Context, providerID string) (eyrieengine.CredentialStatus, error) + CredentialStatus(ctx context.Context, providerID string) (fluxengine.CredentialStatus, error) SaveCredentialEnv(ctx context.Context, envVar, secret string) error HasCredentialEnv(ctx context.Context, envVar string) bool CredentialEnvKeys(providerID string) []string - ResolveCredential(ctx context.Context, secret string) eyrieengine.CredentialResolution - CredentialProviders(context.Context) []eyrieengine.CredentialProvider - ApplyCredentials(ctx context.Context, providerID string) (eyrieengine.CatalogSnapshot, error) + ResolveCredential(ctx context.Context, secret string) fluxengine.CredentialResolution + CredentialProviders(context.Context) []fluxengine.CredentialProvider + ApplyCredentials(ctx context.Context, providerID string) (fluxengine.CatalogSnapshot, error) } // SelectionManager is the get/set selection facet (config only). type SelectionManager interface { - ActiveSelection(ctx context.Context) eyrieengine.Route - EffectiveSelection(ctx context.Context, opts eyrieengine.SelectionOptions) eyrieengine.Selection + ActiveSelection(ctx context.Context) fluxengine.Route + EffectiveSelection(ctx context.Context, opts fluxengine.SelectionOptions) fluxengine.Selection SetActiveProvider(ctx context.Context, provider string) error SetActiveModel(ctx context.Context, modelID string) error SetSelection(ctx context.Context, provider, modelID string) error @@ -86,59 +86,59 @@ type SelectionManager interface { // GatewayInspector is the gateway/deployment/catalog-state facet (config only). type GatewayInspector interface { - GatewayDefinitions() []eyrieengine.Gateway - Gateways(ctx context.Context) []eyrieengine.Gateway + GatewayDefinitions() []fluxengine.Gateway + Gateways(ctx context.Context) []fluxengine.Gateway GatewayRegion(providerID string) (label string, required bool) SetGatewayRegion(ctx context.Context, providerID, value string) error GatewayForModel(ctx context.Context, modelID string) string CanonicalModel(ctx context.Context, modelID string) string DeploymentRoutingEnabled(override *bool) bool DeploymentStatus(ctx context.Context, activeModel string) (string, error) - DeploymentSummary(ctx context.Context, activeModel string) (eyrieengine.DeploymentSummary, error) + DeploymentSummary(ctx context.Context, activeModel string) (fluxengine.DeploymentSummary, error) RoutingPreview(ctx context.Context, modelID string) (string, error) } // CatalogMaintenance is the refresh/preflight/security facet (config only). type CatalogMaintenance interface { - RefreshCatalog(ctx context.Context, providerID string) (eyrieengine.CatalogSnapshot, error) - CatalogHealth(ctx context.Context) eyrieengine.CatalogHealth - StatePaths() eyrieengine.StatePaths + RefreshCatalog(ctx context.Context, providerID string) (fluxengine.CatalogSnapshot, error) + CatalogHealth(ctx context.Context) fluxengine.CatalogHealth + StatePaths() fluxengine.StatePaths DefaultProviderFilter(ctx context.Context) string - PreflightWithOptions(ctx context.Context, opts eyrieengine.PreflightOptions) eyrieengine.PreflightReport - ProviderStateSecurityStatus() eyrieengine.ProviderStateSecurity + PreflightWithOptions(ctx context.Context, opts fluxengine.PreflightOptions) fluxengine.PreflightReport + ProviderStateSecurityStatus() fluxengine.ProviderStateSecurity } -// engineProvider is the production Provider: a thin wrapper over Eyrie's +// engineProvider is the production Provider: a thin wrapper over Flux's // concrete engine facade. type engineProvider struct { - eng *eyrieengine.Engine + eng *fluxengine.Engine } -func newEngineProvider(eng *eyrieengine.Engine) *engineProvider { +func newEngineProvider(eng *fluxengine.Engine) *engineProvider { return &engineProvider{eng: eng} } -func (p *engineProvider) Generate(ctx context.Context, req eyrieengine.GenerateRequest) (*eyrieengine.GenerateResponse, error) { +func (p *engineProvider) Generate(ctx context.Context, req fluxengine.GenerateRequest) (*fluxengine.GenerateResponse, error) { return p.eng.Generate(ctx, req) } -func (p *engineProvider) Stream(ctx context.Context, req eyrieengine.GenerateRequest) (eyrieengine.EventStreamer, error) { +func (p *engineProvider) Stream(ctx context.Context, req fluxengine.GenerateRequest) (fluxengine.EventStreamer, error) { return p.eng.Stream(ctx, req) } -func (p *engineProvider) ListModels(ctx context.Context, providerID string, refresh bool) ([]eyrieengine.Model, error) { +func (p *engineProvider) ListModels(ctx context.Context, providerID string, refresh bool) ([]fluxengine.Model, error) { return p.eng.ListModels(ctx, providerID, refresh) } -func (p *engineProvider) ListLiveModels(ctx context.Context, providerID string) ([]eyrieengine.Model, error) { +func (p *engineProvider) ListLiveModels(ctx context.Context, providerID string) ([]fluxengine.Model, error) { return p.eng.ListLiveModels(ctx, providerID) } -func (p *engineProvider) ListPublicModels(ctx context.Context, providerID string) ([]eyrieengine.Model, error) { +func (p *engineProvider) ListPublicModels(ctx context.Context, providerID string) ([]fluxengine.Model, error) { return p.eng.ListPublicModels(ctx, providerID) } -func (p *engineProvider) ModelInfo(ctx context.Context, modelID string) (eyrieengine.Model, bool, error) { +func (p *engineProvider) ModelInfo(ctx context.Context, modelID string) (fluxengine.Model, bool, error) { return p.eng.ModelInfo(ctx, modelID) } @@ -150,15 +150,15 @@ func (p *engineProvider) DefaultModel(ctx context.Context, provider, fallback st return p.eng.DefaultModel(ctx, provider, fallback) } -func (p *engineProvider) PreferredModel(ctx context.Context, provider string, class eyrieengine.ModelClass, fallback string) string { +func (p *engineProvider) PreferredModel(ctx context.Context, provider string, class fluxengine.ModelClass, fallback string) string { return p.eng.PreferredModel(ctx, provider, class, fallback) } -func (p *engineProvider) PreferredModels(ctx context.Context, primaryProvider string, class eyrieengine.ModelClass, limit int) []string { +func (p *engineProvider) PreferredModels(ctx context.Context, primaryProvider string, class fluxengine.ModelClass, limit int) []string { return p.eng.PreferredModels(ctx, primaryProvider, class, limit) } -func (p *engineProvider) ModelClassOf(ctx context.Context, modelID string) eyrieengine.ModelClass { +func (p *engineProvider) ModelClassOf(ctx context.Context, modelID string) fluxengine.ModelClass { return p.eng.ModelClassOf(ctx, modelID) } @@ -174,7 +174,7 @@ func (p *engineProvider) ModelNames(ctx context.Context) []string { return p.eng.ModelNames(ctx) } -func (p *engineProvider) StatePaths() eyrieengine.StatePaths { +func (p *engineProvider) StatePaths() fluxengine.StatePaths { return p.eng.StatePaths() } @@ -182,19 +182,19 @@ func (p *engineProvider) DefaultProviderFilter(ctx context.Context) string { return p.eng.DefaultProviderFilter(ctx) } -func (p *engineProvider) Catalog(ctx context.Context) (eyrieengine.CatalogSnapshot, error) { +func (p *engineProvider) Catalog(ctx context.Context) (fluxengine.CatalogSnapshot, error) { return p.eng.Catalog(ctx) } -func (p *engineProvider) RefreshCatalog(ctx context.Context, providerID string) (eyrieengine.CatalogSnapshot, error) { +func (p *engineProvider) RefreshCatalog(ctx context.Context, providerID string) (fluxengine.CatalogSnapshot, error) { return p.eng.RefreshCatalog(ctx, providerID) } -func (p *engineProvider) ApplyCredentials(ctx context.Context, providerID string) (eyrieengine.CatalogSnapshot, error) { +func (p *engineProvider) ApplyCredentials(ctx context.Context, providerID string) (fluxengine.CatalogSnapshot, error) { return p.eng.ApplyCredentials(ctx, providerID) } -func (p *engineProvider) SaveCredential(ctx context.Context, providerID, secret string) (eyrieengine.CredentialStatus, error) { +func (p *engineProvider) SaveCredential(ctx context.Context, providerID, secret string) (fluxengine.CredentialStatus, error) { return p.eng.SaveCredential(ctx, providerID, secret) } @@ -202,7 +202,7 @@ func (p *engineProvider) RemoveCredential(ctx context.Context, providerID string return p.eng.RemoveCredential(ctx, providerID) } -func (p *engineProvider) CredentialStatus(ctx context.Context, providerID string) (eyrieengine.CredentialStatus, error) { +func (p *engineProvider) CredentialStatus(ctx context.Context, providerID string) (fluxengine.CredentialStatus, error) { return p.eng.CredentialStatus(ctx, providerID) } @@ -218,19 +218,19 @@ func (p *engineProvider) CredentialEnvKeys(providerID string) []string { return p.eng.CredentialEnvKeys(providerID) } -func (p *engineProvider) ResolveCredential(ctx context.Context, secret string) eyrieengine.CredentialResolution { +func (p *engineProvider) ResolveCredential(ctx context.Context, secret string) fluxengine.CredentialResolution { return p.eng.ResolveCredential(ctx, secret) } -func (p *engineProvider) CredentialProviders(ctx context.Context) []eyrieengine.CredentialProvider { +func (p *engineProvider) CredentialProviders(ctx context.Context) []fluxengine.CredentialProvider { return p.eng.CredentialProviders(ctx) } -func (p *engineProvider) GatewayDefinitions() []eyrieengine.Gateway { +func (p *engineProvider) GatewayDefinitions() []fluxengine.Gateway { return p.eng.GatewayDefinitions() } -func (p *engineProvider) Gateways(ctx context.Context) []eyrieengine.Gateway { +func (p *engineProvider) Gateways(ctx context.Context) []fluxengine.Gateway { return p.eng.Gateways(ctx) } @@ -258,7 +258,7 @@ func (p *engineProvider) DeploymentStatus(ctx context.Context, activeModel strin return p.eng.DeploymentStatus(ctx, activeModel) } -func (p *engineProvider) DeploymentSummary(ctx context.Context, activeModel string) (eyrieengine.DeploymentSummary, error) { +func (p *engineProvider) DeploymentSummary(ctx context.Context, activeModel string) (fluxengine.DeploymentSummary, error) { return p.eng.DeploymentSummary(ctx, activeModel) } @@ -266,19 +266,19 @@ func (p *engineProvider) RoutingPreview(ctx context.Context, modelID string) (st return p.eng.RoutingPreview(ctx, modelID) } -func (p *engineProvider) CatalogHealth(ctx context.Context) eyrieengine.CatalogHealth { +func (p *engineProvider) CatalogHealth(ctx context.Context) fluxengine.CatalogHealth { return p.eng.CatalogHealth(ctx) } -func (p *engineProvider) PreflightWithOptions(ctx context.Context, opts eyrieengine.PreflightOptions) eyrieengine.PreflightReport { +func (p *engineProvider) PreflightWithOptions(ctx context.Context, opts fluxengine.PreflightOptions) fluxengine.PreflightReport { return p.eng.PreflightWithOptions(ctx, opts) } -func (p *engineProvider) ActiveSelection(ctx context.Context) eyrieengine.Route { +func (p *engineProvider) ActiveSelection(ctx context.Context) fluxengine.Route { return p.eng.ActiveSelection(ctx) } -func (p *engineProvider) EffectiveSelection(ctx context.Context, opts eyrieengine.SelectionOptions) eyrieengine.Selection { +func (p *engineProvider) EffectiveSelection(ctx context.Context, opts fluxengine.SelectionOptions) fluxengine.Selection { return p.eng.EffectiveSelection(ctx, opts) } @@ -298,7 +298,7 @@ func (p *engineProvider) ClearSelection(ctx context.Context) error { return p.eng.ClearSelection(ctx) } -func (p *engineProvider) ProviderStateSecurityStatus() eyrieengine.ProviderStateSecurity { +func (p *engineProvider) ProviderStateSecurityStatus() fluxengine.ProviderStateSecurity { return p.eng.ProviderStateSecurityStatus() } @@ -306,13 +306,13 @@ func (p *engineProvider) SupportsNativeCompaction(ctx context.Context, provider, return p.eng.SupportsNativeCompaction(ctx, provider, model) } -func (p *engineProvider) CompactNative(ctx context.Context, req eyrieengine.NativeCompactionRequest) (string, error) { +func (p *engineProvider) CompactNative(ctx context.Context, req fluxengine.NativeCompactionRequest) (string, error) { return p.eng.CompactNative(ctx, req) } // translateProvider bridges the rho-owned ChatClient port to the Generator and // NativeCompactor roles. It needs no other Provider facet. The type conversions -// here (internal/types <-> eyrieengine.*) are the single, centralized +// here (internal/types <-> fluxengine.*) are the single, centralized // translation point — Rho's conversation DTOs never leak past it. type translateProvider struct { generator Generator @@ -323,7 +323,7 @@ func newChatClientProvider(provider Provider) *translateProvider { return &translateProvider{generator: provider, compactor: provider} } -func (c *translateProvider) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (c *translateProvider) Chat(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions) (*types.FluxResponse, error) { response, err := c.generator.Generate(ctx, toEngineRequest(messages, opts, types.ContinuationConfig{})) if err != nil { return nil, err @@ -331,14 +331,14 @@ func (c *translateProvider) Chat(ctx context.Context, messages []types.EyrieMess return fromEngineResponse(response), nil } -func (c *translateProvider) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, continuation types.ContinuationConfig) (*types.StreamResult, error) { +func (c *translateProvider) StreamChatContinue(ctx context.Context, messages []types.FluxMessage, opts types.ChatOptions, continuation types.ContinuationConfig) (*types.StreamResult, error) { request := toEngineRequest(messages, opts, continuation) request.Requirements.Streaming = true stream, err := c.generator.Stream(ctx, request) if err != nil { return nil, err } - events := make(chan types.EyrieStreamEvent, 64) + events := make(chan types.FluxStreamEvent, 64) streamCtx, cancel := context.WithCancel(ctx) go func() { defer close(events) @@ -356,7 +356,7 @@ func (c *translateProvider) StreamChatContinue(ctx context.Context, messages []t } if err := stream.Err(); err != nil { select { - case events <- types.EyrieStreamEvent{Type: "error", Error: err.Error()}: + case events <- types.FluxStreamEvent{Type: "error", Error: err.Error()}: case <-streamCtx.Done(): } } @@ -369,7 +369,7 @@ func (c *translateProvider) StreamChatContinue(ctx context.Context, messages []t } // ManagesResilience tells Rho not to add provider retry, continuation, or -// protocol-recovery layers around Eyrie's routed transport. +// protocol-recovery layers around Flux's routed transport. func (c *translateProvider) ManagesResilience() bool { return true } // NativeCompaction reports whether the bound compactor supports provider-native @@ -382,43 +382,43 @@ func (c *translateProvider) NativeCompaction(ctx context.Context, provider, mode } // CompactNative performs provider-native compaction through the bound compactor. -func (c *translateProvider) CompactNative(ctx context.Context, req eyrieengine.NativeCompactionRequest) (string, error) { +func (c *translateProvider) CompactNative(ctx context.Context, req fluxengine.NativeCompactionRequest) (string, error) { if c.compactor == nil { return "", fmt.Errorf("gateway: no provider") } return c.compactor.CompactNative(ctx, req) } -func toEngineRequest(messages []types.EyrieMessage, opts types.ChatOptions, continuation types.ContinuationConfig) eyrieengine.GenerateRequest { +func toEngineRequest(messages []types.FluxMessage, opts types.ChatOptions, continuation types.ContinuationConfig) fluxengine.GenerateRequest { thinkingEnabled := opts.ThinkingEnabled if thinkingEnabled == nil { thinkingEnabled = opts.GLMThinkingEnabled } reasoningEnabled := thinkingEnabled != nil && *thinkingEnabled - request := eyrieengine.GenerateRequest{ + request := fluxengine.GenerateRequest{ Messages: toEngineMessages(messages), SystemPrompt: opts.System, Tools: toEngineTools(opts.Tools), - Requirements: eyrieengine.Requirements{ + Requirements: fluxengine.Requirements{ Streaming: opts.Stream, Tools: len(opts.Tools) > 0, Vision: messagesContainVision(messages), StructuredJSON: opts.ResponseFormat != nil || opts.OutputSchema != "", Reasoning: opts.ReasoningEffort != "" || opts.ThinkingBudgetTokens > 0 || opts.ThinkingMode != "" || reasoningEnabled, }, - Preference: eyrieengine.Preference{ + Preference: fluxengine.Preference{ PreferredProvider: opts.Provider, PreferredModelID: opts.Model, }, - Limits: eyrieengine.Limits{ + Limits: fluxengine.Limits{ MaxOutputTokens: opts.MaxTokens, MaxContinuations: continuation.MaxContinuations, MaxTotalOutputTokens: continuation.MaxTotalTokens, }, - Metadata: eyrieengine.Metadata{UserID: opts.MetadataUserID}, + Metadata: fluxengine.Metadata{UserID: opts.MetadataUserID}, Temperature: opts.Temperature, OutputSchema: firstNonEmpty(opts.OutputSchema, responseSchema(opts.ResponseFormat)), - Options: eyrieengine.GenerationOptions{ + Options: fluxengine.GenerationOptions{ EnableCaching: opts.EnableCaching, ReasoningEffort: opts.ReasoningEffort, ThinkingBudgetTokens: opts.ThinkingBudgetTokens, ThinkingMode: opts.ThinkingMode, ThinkingDisplay: opts.ThinkingDisplay, ThinkingEnabled: thinkingEnabled, GLMThinkingEnabled: thinkingEnabled, @@ -439,57 +439,57 @@ func toEngineRequest(messages []types.EyrieMessage, opts types.ChatOptions, cont // client all speak the canonical contract message type, so no per-field // conversion is needed. It is exposed for the session layer (e.g. native // compaction), which translates without reaching into the raw engine. -func ToEngineMessages(messages []types.EyrieMessage) []eyrieengine.Message { +func ToEngineMessages(messages []types.FluxMessage) []fluxengine.Message { return messages } -func toEngineMessages(messages []types.EyrieMessage) []eyrieengine.Message { +func toEngineMessages(messages []types.FluxMessage) []fluxengine.Message { return messages } -func toEngineTools(tools []types.EyrieTool) []eyrieengine.Tool { +func toEngineTools(tools []types.FluxTool) []fluxengine.Tool { return tools } -func toEngineToolChoice(choice *types.ToolChoiceOption) *eyrieengine.ToolChoice { +func toEngineToolChoice(choice *types.ToolChoiceOption) *fluxengine.ToolChoice { return choice } -func fromEngineResponse(response *eyrieengine.GenerateResponse) *types.EyrieResponse { +func fromEngineResponse(response *fluxengine.GenerateResponse) *types.FluxResponse { return response } -func fromEngineEvent(event eyrieengine.Event) (types.EyrieStreamEvent, bool) { - out := types.EyrieStreamEvent{ +func fromEngineEvent(event fluxengine.Event) (types.FluxStreamEvent, bool) { + out := types.FluxStreamEvent{ Content: event.Content, Thinking: event.Thinking, RequestID: event.RequestID, Usage: event.Usage, StopReason: event.StopReason, TTFTms: event.TTFTms, } switch event.Type { - case eyrieengine.EventRouteSelected: + case fluxengine.EventRouteSelected: out.Type = "route_selected" - case eyrieengine.EventRouteChanged: + case fluxengine.EventRouteChanged: out.Type = "route_changed" - case eyrieengine.EventContentDelta: + case fluxengine.EventContentDelta: out.Type = "content" - case eyrieengine.EventThinkingDelta: + case fluxengine.EventThinkingDelta: out.Type = "thinking" - case eyrieengine.EventToolCallStart, eyrieengine.EventToolCallDone: + case fluxengine.EventToolCallStart, fluxengine.EventToolCallDone: out.Type = "tool_call" - case eyrieengine.EventToolCallDelta: + case fluxengine.EventToolCallDelta: out.Type = "tool_input_delta" - case eyrieengine.EventUsage: + case fluxengine.EventUsage: out.Type = "usage" - case eyrieengine.EventTTFT: + case fluxengine.EventTTFT: out.Type = "ttft" out.TTFT = event.TTFTms - case eyrieengine.EventDone: + case fluxengine.EventDone: out.Type = "done" - case eyrieengine.EventContinuation: + case fluxengine.EventContinuation: out.Type = "continuation" - case eyrieengine.EventWarning: + case fluxengine.EventWarning: out.Type, out.Content = "warning", event.Warning default: - // An unrecognized engine event type means eyrie emits something this + // An unrecognized engine event type means flux emits something this // adapter has not been taught to translate. Forward it verbatim so no // data is silently dropped, but log it so the mapping gap is visible. slog.Warn("gateway: forwarding unrecognized engine event type", "type", event.Type) @@ -531,7 +531,7 @@ func cloneMetadata(in map[string]string) map[string]string { return out } -func messagesContainVision(messages []types.EyrieMessage) bool { +func messagesContainVision(messages []types.FluxMessage) bool { for _, message := range messages { if len(message.Images) > 0 { return true diff --git a/internal/provider/gateway/engine_client_test.go b/internal/provider/gateway/engine_client_test.go index 1d63f4cf..a711e487 100644 --- a/internal/provider/gateway/engine_client_test.go +++ b/internal/provider/gateway/engine_client_test.go @@ -3,7 +3,7 @@ package gateway import ( "testing" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" + fluxengine "github.com/GrayCodeAI/flux/engine" "github.com/GrayCodeAI/rho/internal/types" ) @@ -11,14 +11,14 @@ func TestEngineAdapterPreservesRhoRequestOptions(t *testing.T) { topP := 0.75 thinking := true request := toEngineRequest( - []types.EyrieMessage{{ + []types.FluxMessage{{ Role: "user", Content: "inspect", ContentParts: []types.ContentPart{{Type: "image_url", ImageURL: &types.ImageURLPart{URL: "data:image/png;base64,abc"}}}, }}, types.ChatOptions{ Provider: "openrouter", Model: "openrouter/auto", MaxTokens: 2048, - Tools: []types.EyrieTool{{Name: "read_file", Description: "read", Parameters: map[string]interface{}{"type": "object"}}}, + Tools: []types.FluxTool{{Name: "read_file", Description: "read", Parameters: map[string]interface{}{"type": "object"}}}, System: "system", EnableCaching: true, ReasoningEffort: "high", GLMThinkingEnabled: &thinking, TopP: &topP, ServiceTier: "priority", MetadataUserID: "rho-user-1", @@ -63,29 +63,29 @@ func TestEngineAdapterOnlyRequiresGLMReasoningWhenEnabled(t *testing.T) { func TestEngineAdapterNormalizesEventsForRhoLoop(t *testing.T) { tests := []struct { - in eyrieengine.Event + in fluxengine.Event wantType string emit bool provider string model string }{ { - in: eyrieengine.Event{Type: eyrieengine.EventRouteSelected, Route: &eyrieengine.Route{ + in: fluxengine.Event{Type: fluxengine.EventRouteSelected, Route: &fluxengine.Route{ Provider: "openai", Model: "openai/gpt-5", DeploymentRouting: true, }}, wantType: "route_selected", emit: true, provider: "openai", model: "openai/gpt-5", }, { - in: eyrieengine.Event{Type: eyrieengine.EventRouteChanged, Route: &eyrieengine.Route{ + in: fluxengine.Event{Type: fluxengine.EventRouteChanged, Route: &fluxengine.Route{ Provider: "anthropic", Model: "anthropic/claude-sonnet-4-6", DeploymentRouting: true, }}, wantType: "route_changed", emit: true, provider: "anthropic", model: "anthropic/claude-sonnet-4-6", }, - {in: eyrieengine.Event{Type: eyrieengine.EventContentDelta, Content: "x"}, wantType: "content", emit: true}, - {in: eyrieengine.Event{Type: eyrieengine.EventThinkingDelta}, wantType: "thinking", emit: true}, - {in: eyrieengine.Event{Type: eyrieengine.EventToolCallDone, ToolCall: &eyrieengine.ToolCall{Name: "read"}}, wantType: "tool_call", emit: true}, - {in: eyrieengine.Event{Type: eyrieengine.EventUsage, Usage: &eyrieengine.Usage{TotalTokens: 8}}, wantType: "usage", emit: true}, - {in: eyrieengine.Event{Type: eyrieengine.EventDone, StopReason: "end_turn", Usage: &eyrieengine.Usage{PromptTokens: 5, CompletionTokens: 3, TotalTokens: 8}}, wantType: "done", emit: true}, + {in: fluxengine.Event{Type: fluxengine.EventContentDelta, Content: "x"}, wantType: "content", emit: true}, + {in: fluxengine.Event{Type: fluxengine.EventThinkingDelta}, wantType: "thinking", emit: true}, + {in: fluxengine.Event{Type: fluxengine.EventToolCallDone, ToolCall: &fluxengine.ToolCall{Name: "read"}}, wantType: "tool_call", emit: true}, + {in: fluxengine.Event{Type: fluxengine.EventUsage, Usage: &fluxengine.Usage{TotalTokens: 8}}, wantType: "usage", emit: true}, + {in: fluxengine.Event{Type: fluxengine.EventDone, StopReason: "end_turn", Usage: &fluxengine.Usage{PromptTokens: 5, CompletionTokens: 3, TotalTokens: 8}}, wantType: "done", emit: true}, } for _, tt := range tests { got, emit := fromEngineEvent(tt.in) @@ -95,16 +95,16 @@ func TestEngineAdapterNormalizesEventsForRhoLoop(t *testing.T) { if tt.provider != "" && (got.Route == nil || got.Route.Provider != tt.provider || got.Route.Model != tt.model || !got.Route.DeploymentRouting) { t.Fatalf("event %q lost resolved route: %+v", tt.in.Type, got.Route) } - if tt.in.Type == eyrieengine.EventDone && (got.Usage == nil || got.Usage.PromptTokens != 5 || got.Usage.CompletionTokens != 3 || got.Usage.TotalTokens != 8) { + if tt.in.Type == fluxengine.EventDone && (got.Usage == nil || got.Usage.PromptTokens != 5 || got.Usage.CompletionTokens != 3 || got.Usage.TotalTokens != 8) { t.Fatalf("terminal usage lost: %+v", got.Usage) } } } func TestEngineAdapterPreservesResolvedRouteInBlockingResponse(t *testing.T) { - got := fromEngineResponse(&eyrieengine.GenerateResponse{ + got := fromEngineResponse(&fluxengine.GenerateResponse{ Content: "ok", - Route: &eyrieengine.Route{ + Route: &fluxengine.Route{ Provider: "openai", Model: "openai/gpt-5", DeploymentRouting: true, }, }) diff --git a/internal/provider/gateway/gateway.go b/internal/provider/gateway/gateway.go index 68d0d33f..7675c7a6 100644 --- a/internal/provider/gateway/gateway.go +++ b/internal/provider/gateway/gateway.go @@ -1,9 +1,9 @@ -// Package gateway is Rho's single boundary to Eyrie's provider runtime. It is -// the only package that imports Eyrie; everything else speaks the rho-owned +// Package gateway is Rho's single boundary to Flux's provider runtime. It is +// the only package that imports Flux; everything else speaks the rho-owned // Provider interface and the internal/types DTOs. // -// rho = product face (UX/agent/sessions) · eyrie = provider engine -// One-way dependency only: eyrie never imports rho. See README ecosystems. +// rho = product face (UX/agent/sessions) · flux = provider engine +// One-way dependency only: flux never imports rho. See README ecosystems. package gateway import ( @@ -11,21 +11,21 @@ import ( "log/slog" "sync" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" - "github.com/GrayCodeAI/eyrie/llm" + fluxengine "github.com/GrayCodeAI/flux/engine" + "github.com/GrayCodeAI/flux/llm" ) -// Gateway is Rho's single boundary to the Eyrie provider runtime. It embeds +// Gateway is Rho's single boundary to the Flux provider runtime. It embeds // Provider so every engine method is forwarded, and it is the only type that // constructs one (via New). All other Rho packages hold a *Gateway or speak -// the Provider interface — never an *eyrieengine.Engine. +// the Provider interface — never an *fluxengine.Engine. // -// Construction is centralized here: New is the only call to eyrieengine.New and +// Construction is centralized here: New is the only call to fluxengine.New and // the place Rho declares its identity to the credential store. -// Gateway is Rho's single boundary to the Eyrie provider runtime. It embeds the +// Gateway is Rho's single boundary to the Flux provider runtime. It embeds the // Provider roles so every engine method is forwarded, and it is the only type // that constructs one (via New). All other Rho packages hold a *Gateway or speak -// the Provider interface — never an *eyrieengine.Engine. *Gateway satisfies the +// the Provider interface — never an *fluxengine.Engine. *Gateway satisfies the // composite Provider interface. type Gateway struct { Generator @@ -37,27 +37,27 @@ type Gateway struct { CatalogMaintenance } -// declareRhoIdentity sets Eyrie's OS keychain service name to "rho" so existing -// credentials (filed under "rho") stay readable under Eyrie's now host-neutral +// declareRhoIdentity sets Flux's OS keychain service name to "rho" so existing +// credentials (filed under "rho") stay readable under Flux's now host-neutral // default. It is idempotent and runs exactly once. Called from New so the // identity is always declared before any credential read, no matter which New // path runs first. var declareRhoIdentity = sync.OnceFunc(func() { - eyrieengine.SetSecretStoreServiceName("rho") + fluxengine.SetSecretStoreServiceName("rho") }) -// New composes the Eyrie engine for one effective settings snapshot and wraps it -// as a Provider. It is the single composition root — every eyrieengine.New call +// New composes the Flux engine for one effective settings snapshot and wraps it +// as a Provider. It is the single composition root — every fluxengine.New call // in Rho flows through here. func New(ctx context.Context, providers []CustomProviderConfig) (*Gateway, error) { // Declare rho's identity to the credential store FIRST, before // constructing the engine, so no credential read ever happens under - // Eyrie's host-neutral default service name. The OnceFunc makes this + // Flux's host-neutral default service name. The OnceFunc makes this // safe to call from every construction path. declareRhoIdentity() gateways := customGatewaysFromSettings(providers) - eng, err := eyrieengine.New(eyrieengine.Options{CustomGateways: gateways}) + eng, err := fluxengine.New(fluxengine.Options{CustomGateways: gateways}) if err != nil { return nil, err } @@ -77,16 +77,16 @@ func New(ctx context.Context, providers []CustomProviderConfig) (*Gateway, error } // BuildCustomGateways maps Rho's OpenAI-compatible provider config onto -// Eyrie's CustomGateway spec. Shared by gateway.New, config.eyrie_engine, and +// Flux's CustomGateway spec. Shared by gateway.New, config.flux_engine, and // engine.session_factory so a new CustomProviderConfig field only needs wiring // in one place. -func BuildCustomGateways(providers []CustomProviderConfig) []eyrieengine.CustomGateway { - gateways := make([]eyrieengine.CustomGateway, 0, len(providers)) +func BuildCustomGateways(providers []CustomProviderConfig) []fluxengine.CustomGateway { + gateways := make([]fluxengine.CustomGateway, 0, len(providers)) for _, provider := range providers { if provider.Name == "" && provider.BaseURL == "" { continue } - gateways = append(gateways, eyrieengine.CustomGateway{ + gateways = append(gateways, fluxengine.CustomGateway{ ID: provider.Name, BaseURL: provider.BaseURL, CredentialEnv: provider.APIKeyEnv, DefaultModel: provider.Model, }) @@ -95,7 +95,7 @@ func BuildCustomGateways(providers []CustomProviderConfig) []eyrieengine.CustomG } // customGatewaysFromSettings is the internal alias kept for backward compat. -func customGatewaysFromSettings(providers []CustomProviderConfig) []eyrieengine.CustomGateway { +func customGatewaysFromSettings(providers []CustomProviderConfig) []fluxengine.CustomGateway { return BuildCustomGateways(providers) } @@ -109,7 +109,7 @@ type CustomProviderConfig struct { Model string } -// ModelInfo is Rho's product-facing view of Eyrie model metadata. +// ModelInfo is Rho's product-facing view of Flux model metadata. type ModelInfo struct { Name string `json:"name"` Provider string `json:"provider"` @@ -120,7 +120,7 @@ type ModelInfo struct { Recommended bool `json:"recommended,omitempty"` } -func fromEngineModel(model eyrieengine.Model) ModelInfo { +func fromEngineModel(model fluxengine.Model) ModelInfo { return ModelInfo{ Name: model.ID, Provider: model.ProviderID, ContextSize: model.ContextWindow, @@ -142,10 +142,10 @@ func (g *Gateway) MustSelectProvider() Provider { return g } -// NewFromEngine wraps an existing *eyrieengine.Engine as a Gateway. Tests that -// inject an Eyrie SecretStore (e.g. compaction-support detection) use it so the +// NewFromEngine wraps an existing *fluxengine.Engine as a Gateway. Tests that +// inject an Flux SecretStore (e.g. compaction-support detection) use it so the // rest of Rho still speaks the Gateway boundary. -func NewFromEngine(eng *eyrieengine.Engine) *Gateway { +func NewFromEngine(eng *fluxengine.Engine) *Gateway { if eng == nil { return nil } @@ -162,8 +162,8 @@ func NewFromEngine(eng *eyrieengine.Engine) *Gateway { } // --- Stateless package-level lookups ------------------------------------- -// These delegate Eyrie reads to one shared default gateway so rho-owned -// policy packages (routing, config) never import Eyrie themselves. eyrie's +// These delegate Flux reads to one shared default gateway so rho-owned +// policy packages (routing, config) never import Flux themselves. flux's // Engine reloads its catalog and provider config from disk on every method // call, so a single long-lived gateway returns identical freshness to // constructing one per call — this just avoids redundant construction. It @@ -277,7 +277,7 @@ func ProviderForModel(ctx context.Context, modelName string) string { return g.ProviderForModel(ctx, modelName) } -// PreferredModel returns Eyrie's tier-preferred model for a provider. +// PreferredModel returns Flux's tier-preferred model for a provider. func PreferredModel(ctx context.Context, provider string, class ModelClass, fallback string) string { g := defaultGateway(ctx) if g == nil { @@ -322,46 +322,46 @@ func ModelNames(ctx context.Context) []string { return g.ModelNames(ctx) } -// --- rho-owned mirror of Eyrie's ModelClass tier enum ------------------- +// --- rho-owned mirror of Flux's ModelClass tier enum ------------------- // Kept here (rather than importing neutral constants) so the boundary stays -// one-way; values match eyrieengine.ModelClass. -type ModelClass = eyrieengine.ModelClass +// one-way; values match fluxengine.ModelClass. +type ModelClass = fluxengine.ModelClass const ( ModelClassEconomical = llm.ModelClassEconomical ModelClassBalanced = llm.ModelClassBalanced ModelClassPremium = llm.ModelClassPremium - CheckFail = eyrieengine.CheckFail + CheckFail = fluxengine.CheckFail ) // NormalizeProviderID canonicalizes a host-facing provider/gateway id. func NormalizeProviderID(id string) string { - return eyrieengine.NormalizeProviderID(id) + return fluxengine.NormalizeProviderID(id) } -// --- Eyrie report/type re-exports config internals consume ---------------- -// These alias Eyrie types that a few config-only report paths return. They -// live in gateway (the single Eyrie importer) rather than config. +// --- Flux report/type re-exports config internals consume ---------------- +// These alias Flux types that a few config-only report paths return. They +// live in gateway (the single Flux importer) rather than config. type ( - PreflightReport = eyrieengine.PreflightReport - PreflightOptions = eyrieengine.PreflightOptions - ProviderStateSecurity = eyrieengine.ProviderStateSecurity - DeploymentSummary = eyrieengine.DeploymentSummary - CredentialStorageReport = eyrieengine.CredentialStorageReport - CredentialStatus = eyrieengine.CredentialStatus - CredentialResolution = eyrieengine.CredentialResolution - CredentialProvider = eyrieengine.CredentialProvider - GatewayDefs = eyrieengine.Gateway - CatalogSnapshot = eyrieengine.CatalogSnapshot - Model = eyrieengine.Model - StatePaths = eyrieengine.StatePaths - SelectionOptions = eyrieengine.SelectionOptions - Selection = eyrieengine.Selection - NativeCompactionRequest = eyrieengine.NativeCompactionRequest + PreflightReport = fluxengine.PreflightReport + PreflightOptions = fluxengine.PreflightOptions + ProviderStateSecurity = fluxengine.ProviderStateSecurity + DeploymentSummary = fluxengine.DeploymentSummary + CredentialStorageReport = fluxengine.CredentialStorageReport + CredentialStatus = fluxengine.CredentialStatus + CredentialResolution = fluxengine.CredentialResolution + CredentialProvider = fluxengine.CredentialProvider + GatewayDefs = fluxengine.Gateway + CatalogSnapshot = fluxengine.CatalogSnapshot + Model = fluxengine.Model + StatePaths = fluxengine.StatePaths + SelectionOptions = fluxengine.SelectionOptions + Selection = fluxengine.Selection + NativeCompactionRequest = fluxengine.NativeCompactionRequest ) -// Package-level Eyrie helpers that config delegates to (gateway stays the only importer). +// Package-level Flux helpers that config delegates to (gateway stays the only importer). func PreflightReportWithOptions(ctx context.Context, opts PreflightOptions) PreflightReport { return PreflightWithProviders(ctx, nil, opts) @@ -379,53 +379,53 @@ func PreflightWithProviders(ctx context.Context, providers []CustomProviderConfi } func FormatPreflight(report PreflightReport) string { - return eyrieengine.FormatPreflight(report) + return fluxengine.FormatPreflight(report) } func IsCatalogCacheRequired(err error) bool { - return eyrieengine.IsCatalogCacheRequired(err) + return fluxengine.IsCatalogCacheRequired(err) } -// RegisteredProviderCount exposes Eyrie's first-class provider count through -// Rho's single provider-runtime boundary. The count derives from Eyrie's -// provider registry, so adding a provider in Eyrie never requires a Rho edit. +// RegisteredProviderCount exposes Flux's first-class provider count through +// Rho's single provider-runtime boundary. The count derives from Flux's +// provider registry, so adding a provider in Flux never requires a Rho edit. func RegisteredProviderCount() int { - return eyrieengine.RegisteredGatewayCount() + return fluxengine.RegisteredGatewayCount() } -func SecretStoreName() string { return eyrieengine.SecretStoreName() } +func SecretStoreName() string { return fluxengine.SecretStoreName() } func CredentialStorage(ctx context.Context) CredentialStorageReport { - return eyrieengine.CredentialStorage(ctx) + return fluxengine.CredentialStorage(ctx) } func CredentialGuidance(providerID, secret string) string { - return eyrieengine.CredentialGuidance(providerID, secret) + return fluxengine.CredentialGuidance(providerID, secret) } func FormatSetupError(providerID string, err error) string { - return eyrieengine.FormatSetupError(providerID, err) + return fluxengine.FormatSetupError(providerID, err) } // ParseInlineToolCalls extracts inline tool-call markup from model output. -func ParseInlineToolCalls(content string) (string, []eyrieengine.ToolCall) { - return eyrieengine.ParseInlineToolCalls(content) +func ParseInlineToolCalls(content string) (string, []fluxengine.ToolCall) { + return fluxengine.ParseInlineToolCalls(content) } func DefaultThinkingDisabled(providerID string) bool { - return eyrieengine.DefaultThinkingDisabled(providerID) + return fluxengine.DefaultThinkingDisabled(providerID) } func ThinkingToggleSupported(providerID string) bool { - return eyrieengine.ThinkingToggleSupported(providerID) + return fluxengine.ThinkingToggleSupported(providerID) } func (g *Gateway) DefaultThinkingDisabled(providerID string) bool { - return eyrieengine.DefaultThinkingDisabled(providerID) + return fluxengine.DefaultThinkingDisabled(providerID) } func (g *Gateway) ThinkingToggleSupported(providerID string) bool { - return eyrieengine.ThinkingToggleSupported(providerID) + return fluxengine.ThinkingToggleSupported(providerID) } // --- Test fixtures ----------------------------------------------------- @@ -433,18 +433,18 @@ func (g *Gateway) ThinkingToggleSupported(providerID string) bool { // single gateway+engine boundary. These are thin aliases only. // SetDefaultStore replaces the process-wide credential store (for tests). -var SetDefaultStore = eyrieengine.SetDefaultStore +var SetDefaultStore = fluxengine.SetDefaultStore // DefaultStore returns the process-wide credential store (for tests). -var DefaultStore = eyrieengine.DefaultStore +var DefaultStore = fluxengine.DefaultStore // MapStore is the in-memory credential store for tests (alias). -type MapStore = eyrieengine.MapStore +type MapStore = fluxengine.MapStore // AccountForEnv returns the keychain account name for an env var. -func AccountForEnv(envVar string) string { return eyrieengine.AccountForEnv(envVar) } +func AccountForEnv(envVar string) string { return fluxengine.AccountForEnv(envVar) } // HasSecret reports whether a secret exists for an env var (for tests). func HasSecret(ctx context.Context, envKey string) bool { - return eyrieengine.HasSecret(ctx, envKey) + return fluxengine.HasSecret(ctx, envKey) } diff --git a/internal/provider/gateway/media_backends.go b/internal/provider/gateway/media_backends.go index aca98513..d91b7153 100644 --- a/internal/provider/gateway/media_backends.go +++ b/internal/provider/gateway/media_backends.go @@ -8,7 +8,7 @@ import ( "os" "path/filepath" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" + fluxengine "github.com/GrayCodeAI/flux/engine" "github.com/GrayCodeAI/rho/internal/stt" "github.com/GrayCodeAI/rho/internal/tool" ) @@ -27,24 +27,24 @@ const ( // facade's image-generation backend. Video generation has no router facade // yet, so it preserves the unwired fail-safe error. type routerMediaEngine struct { - eng *eyrieengine.Engine + eng *fluxengine.Engine apiKey string baseURL string model string } -func (e *routerMediaEngine) Name() string { return "eyrie" } +func (e *routerMediaEngine) Name() string { return "flux" } func (e *routerMediaEngine) GenerateImage(ctx context.Context, prompt, source string, opts tool.MediaOptions) ([]tool.MediaResult, error) { if source != "" { - return nil, fmt.Errorf("image editing via the eyrie backend is not supported; generate a new image instead") + return nil, fmt.Errorf("image editing via the flux backend is not supported; generate a new image instead") } n := opts.Count if n <= 0 { n = 1 } - res, err := e.eng.GenerateImage(ctx, eyrieengine.GenerateImageRequest{ - MediaOptions: eyrieengine.MediaOptions{APIKey: e.apiKey, BaseURL: e.baseURL}, + res, err := e.eng.GenerateImage(ctx, fluxengine.GenerateImageRequest{ + MediaOptions: fluxengine.MediaOptions{APIKey: e.apiKey, BaseURL: e.baseURL}, Prompt: prompt, Model: e.model, Size: mediaSize(opts), @@ -61,27 +61,27 @@ func (e *routerMediaEngine) GenerateImage(ctx context.Context, prompt, source st } func (e *routerMediaEngine) GenerateVideo(ctx context.Context, prompt, source string, opts tool.MediaOptions) ([]tool.MediaResult, error) { - return nil, fmt.Errorf("video generation via the eyrie backend is not wired; no video backend installed") + return nil, fmt.Errorf("video generation via the flux backend is not wired; no video backend installed") } // routerTranscriber implements stt.Transcriber against the router engine // facade's audio-transcription backend. type routerTranscriber struct { - eng *eyrieengine.Engine + eng *fluxengine.Engine apiKey string baseURL string model string } -func (t *routerTranscriber) Name() string { return "eyrie" } +func (t *routerTranscriber) Name() string { return "flux" } func (t *routerTranscriber) Transcribe(ctx context.Context, localPath, language string) (string, error) { audio, err := os.ReadFile(localPath) if err != nil { return "", fmt.Errorf("read audio for transcription: %w", err) } - return t.eng.Transcribe(ctx, eyrieengine.TranscribeRequest{ - MediaOptions: eyrieengine.MediaOptions{APIKey: t.apiKey, BaseURL: t.baseURL}, + return t.eng.Transcribe(ctx, fluxengine.TranscribeRequest{ + MediaOptions: fluxengine.MediaOptions{APIKey: t.apiKey, BaseURL: t.baseURL}, Audio: audio, FileName: filepath.Base(localPath), Model: t.model, @@ -93,7 +93,7 @@ func (t *routerTranscriber) Transcribe(ctx context.Context, localPath, language // tool/stt seams. It is a no-op unless the corresponding env gate is "1". // Called once from the gateway composition root (New) so all construction // paths wire identically. -func wireOptionalBackends(eng *eyrieengine.Engine) { +func wireOptionalBackends(eng *fluxengine.Engine) { if os.Getenv(envMediaGate) == "1" { tool.SetMediaEngine(&routerMediaEngine{ eng: eng, diff --git a/internal/provider/gateway/media_backends_stub.go b/internal/provider/gateway/media_backends_stub.go index 8d3194da..4620ab76 100644 --- a/internal/provider/gateway/media_backends_stub.go +++ b/internal/provider/gateway/media_backends_stub.go @@ -2,10 +2,10 @@ package gateway -import eyrieengine "github.com/GrayCodeAI/eyrie/engine" +import fluxengine "github.com/GrayCodeAI/flux/engine" // wireOptionalBackends is a no-op when the media_engine build tag is disabled. // The full implementation (media_backends.go) is only compiled with -// -tags media_engine and requires a eyrie checkout that exposes the +// -tags media_engine and requires a flux checkout that exposes the // GenerateImage/Transcribe engine facade (not present in the published v0.0.1). -func wireOptionalBackends(eng *eyrieengine.Engine) {} +func wireOptionalBackends(eng *fluxengine.Engine) {} diff --git a/internal/provider/gateway/provider_stub_test.go b/internal/provider/gateway/provider_stub_test.go index 9554de31..019ff2c8 100644 --- a/internal/provider/gateway/provider_stub_test.go +++ b/internal/provider/gateway/provider_stub_test.go @@ -4,98 +4,98 @@ import ( "context" "testing" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" + fluxengine "github.com/GrayCodeAI/flux/engine" "github.com/GrayCodeAI/rho/internal/types" ) // stubProvider proves the Provider interface is swappable: a test can inject a // deterministic fake and drive the ChatClient adapter without constructing any -// Eyrie engine. This is the swappable-Engine guarantee Phase 3 delivers. +// Flux engine. This is the swappable-Engine guarantee Phase 3 delivers. type stubProvider struct { - resp *eyrieengine.GenerateResponse + resp *fluxengine.GenerateResponse err error } -func (s *stubProvider) Resolve(context.Context, eyrieengine.SelectionRequest) (eyrieengine.Route, error) { - return eyrieengine.Route{}, nil +func (s *stubProvider) Resolve(context.Context, fluxengine.SelectionRequest) (fluxengine.Route, error) { + return fluxengine.Route{}, nil } -func (s *stubProvider) Generate(context.Context, eyrieengine.GenerateRequest) (*eyrieengine.GenerateResponse, error) { +func (s *stubProvider) Generate(context.Context, fluxengine.GenerateRequest) (*fluxengine.GenerateResponse, error) { return s.resp, s.err } -func (s *stubProvider) Stream(context.Context, eyrieengine.GenerateRequest) (eyrieengine.EventStreamer, error) { +func (s *stubProvider) Stream(context.Context, fluxengine.GenerateRequest) (fluxengine.EventStreamer, error) { return nil, nil } -func (s *stubProvider) ListModels(context.Context, string, bool) ([]eyrieengine.Model, error) { +func (s *stubProvider) ListModels(context.Context, string, bool) ([]fluxengine.Model, error) { return nil, nil } -func (s *stubProvider) ListLiveModels(context.Context, string) ([]eyrieengine.Model, error) { +func (s *stubProvider) ListLiveModels(context.Context, string) ([]fluxengine.Model, error) { return nil, nil } -func (s *stubProvider) ListPublicModels(context.Context, string) ([]eyrieengine.Model, error) { +func (s *stubProvider) ListPublicModels(context.Context, string) ([]fluxengine.Model, error) { return nil, nil } -func (s *stubProvider) ModelInfo(context.Context, string) (eyrieengine.Model, bool, error) { - return eyrieengine.Model{}, false, nil +func (s *stubProvider) ModelInfo(context.Context, string) (fluxengine.Model, bool, error) { + return fluxengine.Model{}, false, nil } func (s *stubProvider) ModelProviders(context.Context) ([]string, error) { return nil, nil } func (s *stubProvider) DefaultModel(context.Context, string, string) string { return "" } -func (s *stubProvider) PreferredModel(context.Context, string, eyrieengine.ModelClass, string) string { +func (s *stubProvider) PreferredModel(context.Context, string, fluxengine.ModelClass, string) string { return "" } -func (s *stubProvider) PreferredModels(context.Context, string, eyrieengine.ModelClass, int) []string { +func (s *stubProvider) PreferredModels(context.Context, string, fluxengine.ModelClass, int) []string { return nil } -func (s *stubProvider) ModelClassOf(context.Context, string) eyrieengine.ModelClass { +func (s *stubProvider) ModelClassOf(context.Context, string) fluxengine.ModelClass { return ModelClassEconomical } func (s *stubProvider) ProviderForModel(context.Context, string) string { return "" } func (s *stubProvider) PrimaryModel(context.Context) string { return "" } func (s *stubProvider) ModelNames(context.Context) []string { return nil } -func (s *stubProvider) StatePaths() eyrieengine.StatePaths { - return eyrieengine.StatePaths{} +func (s *stubProvider) StatePaths() fluxengine.StatePaths { + return fluxengine.StatePaths{} } func (s *stubProvider) DefaultProviderFilter(context.Context) string { return "" } -func (s *stubProvider) Catalog(context.Context) (eyrieengine.CatalogSnapshot, error) { - return eyrieengine.CatalogSnapshot{}, nil +func (s *stubProvider) Catalog(context.Context) (fluxengine.CatalogSnapshot, error) { + return fluxengine.CatalogSnapshot{}, nil } -func (s *stubProvider) RefreshCatalog(context.Context, string) (eyrieengine.CatalogSnapshot, error) { - return eyrieengine.CatalogSnapshot{}, nil +func (s *stubProvider) RefreshCatalog(context.Context, string) (fluxengine.CatalogSnapshot, error) { + return fluxengine.CatalogSnapshot{}, nil } -func (s *stubProvider) ApplyCredentials(context.Context, string) (eyrieengine.CatalogSnapshot, error) { - return eyrieengine.CatalogSnapshot{}, nil +func (s *stubProvider) ApplyCredentials(context.Context, string) (fluxengine.CatalogSnapshot, error) { + return fluxengine.CatalogSnapshot{}, nil } -func (s *stubProvider) SaveCredential(context.Context, string, string) (eyrieengine.CredentialStatus, error) { - return eyrieengine.CredentialStatus{}, nil +func (s *stubProvider) SaveCredential(context.Context, string, string) (fluxengine.CredentialStatus, error) { + return fluxengine.CredentialStatus{}, nil } func (s *stubProvider) RemoveCredential(context.Context, string) error { return nil } -func (s *stubProvider) CredentialStatus(context.Context, string) (eyrieengine.CredentialStatus, error) { - return eyrieengine.CredentialStatus{}, nil +func (s *stubProvider) CredentialStatus(context.Context, string) (fluxengine.CredentialStatus, error) { + return fluxengine.CredentialStatus{}, nil } func (s *stubProvider) SaveCredentialEnv(context.Context, string, string) error { return nil } func (s *stubProvider) HasCredentialEnv(context.Context, string) bool { return false } func (s *stubProvider) CredentialEnvKeys(string) []string { return nil } -func (s *stubProvider) ResolveCredential(context.Context, string) eyrieengine.CredentialResolution { - return eyrieengine.CredentialResolution{} +func (s *stubProvider) ResolveCredential(context.Context, string) fluxengine.CredentialResolution { + return fluxengine.CredentialResolution{} } -func (s *stubProvider) CredentialProviders(context.Context) []eyrieengine.CredentialProvider { +func (s *stubProvider) CredentialProviders(context.Context) []fluxengine.CredentialProvider { return nil } -func (s *stubProvider) GatewayDefinitions() []eyrieengine.Gateway { return nil } +func (s *stubProvider) GatewayDefinitions() []fluxengine.Gateway { return nil } -func (s *stubProvider) Gateways(context.Context) []eyrieengine.Gateway { return nil } +func (s *stubProvider) Gateways(context.Context) []fluxengine.Gateway { return nil } func (s *stubProvider) GatewayRegion(string) (string, bool) { return "", false } func (s *stubProvider) SetGatewayRegion(context.Context, string, string) error { return nil } func (s *stubProvider) GatewayForModel(context.Context, string) string { return "" } @@ -106,49 +106,49 @@ func (s *stubProvider) DeploymentStatus(context.Context, string) (string, error) return "", nil } -func (s *stubProvider) DeploymentSummary(context.Context, string) (eyrieengine.DeploymentSummary, error) { - return eyrieengine.DeploymentSummary{}, nil +func (s *stubProvider) DeploymentSummary(context.Context, string) (fluxengine.DeploymentSummary, error) { + return fluxengine.DeploymentSummary{}, nil } func (s *stubProvider) RoutingPreview(context.Context, string) (string, error) { return "", nil } -func (s *stubProvider) CatalogHealth(context.Context) eyrieengine.CatalogHealth { - return eyrieengine.CatalogHealth{} +func (s *stubProvider) CatalogHealth(context.Context) fluxengine.CatalogHealth { + return fluxengine.CatalogHealth{} } -func (s *stubProvider) Preflight(context.Context) eyrieengine.PreflightReport { - return eyrieengine.PreflightReport{} +func (s *stubProvider) Preflight(context.Context) fluxengine.PreflightReport { + return fluxengine.PreflightReport{} } -func (s *stubProvider) PreflightWithOptions(context.Context, eyrieengine.PreflightOptions) eyrieengine.PreflightReport { - return eyrieengine.PreflightReport{} +func (s *stubProvider) PreflightWithOptions(context.Context, fluxengine.PreflightOptions) fluxengine.PreflightReport { + return fluxengine.PreflightReport{} } -func (s *stubProvider) ActiveSelection(context.Context) eyrieengine.Route { - return eyrieengine.Route{} +func (s *stubProvider) ActiveSelection(context.Context) fluxengine.Route { + return fluxengine.Route{} } -func (s *stubProvider) EffectiveSelection(context.Context, eyrieengine.SelectionOptions) eyrieengine.Selection { - return eyrieengine.Selection{} +func (s *stubProvider) EffectiveSelection(context.Context, fluxengine.SelectionOptions) fluxengine.Selection { + return fluxengine.Selection{} } func (s *stubProvider) SetActiveProvider(context.Context, string) error { return nil } func (s *stubProvider) SetActiveModel(context.Context, string) error { return nil } func (s *stubProvider) SetSelection(context.Context, string, string) error { return nil } func (s *stubProvider) ClearSelection(context.Context) error { return nil } -func (s *stubProvider) ProviderStateSecurityStatus() eyrieengine.ProviderStateSecurity { - return eyrieengine.ProviderStateSecurity{} +func (s *stubProvider) ProviderStateSecurityStatus() fluxengine.ProviderStateSecurity { + return fluxengine.ProviderStateSecurity{} } func (s *stubProvider) SupportsNativeCompaction(context.Context, string, string) bool { return false } -func (s *stubProvider) CompactNative(context.Context, eyrieengine.NativeCompactionRequest) (string, error) { +func (s *stubProvider) CompactNative(context.Context, fluxengine.NativeCompactionRequest) (string, error) { return "", nil } func TestStubProviderDrivesChatClient(t *testing.T) { - stub := &stubProvider{resp: &eyrieengine.GenerateResponse{Content: "from stub", FinishReason: "end_turn"}} + stub := &stubProvider{resp: &fluxengine.GenerateResponse{Content: "from stub", FinishReason: "end_turn"}} gw := &Gateway{Generator: stub} client := gw.ChatClient() - got, err := client.Chat(context.Background(), []types.EyrieMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}) + got, err := client.Chat(context.Background(), []types.FluxMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/internal/provider/routing/catalog.go b/internal/provider/routing/catalog.go index bdbf840a..b7d2a8e4 100644 --- a/internal/provider/routing/catalog.go +++ b/internal/provider/routing/catalog.go @@ -1,6 +1,6 @@ // Package routing provides Rho-owned task routing and health policy. Model // discovery, pricing, provider ownership, and catalog policy are delegated to -// Eyrie's engine facade through the gateway package (the single Eyrie boundary). +// Flux's engine facade through the gateway package (the single Flux boundary). package routing import ( @@ -10,11 +10,11 @@ import ( "github.com/GrayCodeAI/rho/internal/provider/gateway" ) -// ModelInfo is Rho's product-facing view of Eyrie model metadata (the gateway -// layer owns the Eyrie conversation that produces it). +// ModelInfo is Rho's product-facing view of Flux model metadata (the gateway +// layer owns the Flux conversation that produces it). type ModelInfo = gateway.ModelInfo -// Find looks up a model by id or alias through Eyrie. +// Find looks up a model by id or alias through Flux. func Find(name string) (ModelInfo, bool) { return gateway.ModelInfoLookup(context.Background(), name) } diff --git a/internal/provider/routing/health_router.go b/internal/provider/routing/health_router.go index 879ff24f..a79836c1 100644 --- a/internal/provider/routing/health_router.go +++ b/internal/provider/routing/health_router.go @@ -34,7 +34,7 @@ func NewHealthRouter() *HealthRouter { return NewHealthRouterForProvider("") } -// NewHealthRouterForProvider creates a router using eyrie tier models for the provider. +// NewHealthRouterForProvider creates a router using flux tier models for the provider. func NewHealthRouterForProvider(provider string) *HealthRouter { return &HealthRouter{ tiers: DefaultHealthTiers(provider), diff --git a/internal/provider/routing/roles.go b/internal/provider/routing/roles.go index 29fbe9a1..0f0fb98f 100644 --- a/internal/provider/routing/roles.go +++ b/internal/provider/routing/roles.go @@ -26,7 +26,7 @@ type ModelRoles struct { Commit string `json:"commit,omitempty"` } -// DefaultRoles keeps Rho's workflow policy while asking Eyrie for the +// DefaultRoles keeps Rho's workflow policy while asking Flux for the // economical same-provider model used for commit/summarization work. func DefaultRoles(primaryModel string) ModelRoles { primaryModel = strings.TrimSpace(primaryModel) diff --git a/internal/recipe/providers.go b/internal/recipe/providers.go index ec6c9b7b..cbc22171 100644 --- a/internal/recipe/providers.go +++ b/internal/recipe/providers.go @@ -1,5 +1,5 @@ // Package recipe also provides declarative provider configuration. -// This file implements YAML-based provider definitions compatible with eyrie. +// This file implements YAML-based provider definitions compatible with flux. package recipe import ( diff --git a/internal/replaycache/replaycache.go b/internal/replaycache/replaycache.go index f51d5e43..4c1b350e 100644 --- a/internal/replaycache/replaycache.go +++ b/internal/replaycache/replaycache.go @@ -46,14 +46,14 @@ func Fingerprint(secrets ...string) string { // Key computes the cache key for a chat request. Messages are canonicalized // (role/content/tool fields, sorted map keys) so semantically identical // requests produce identical keys regardless of struct field order. -func Key(fingerprint, provider, model string, messages []types.EyrieMessage, maxTokens int) string { +func Key(fingerprint, provider, model string, messages []types.FluxMessage, maxTokens int) string { canonical := canonicalMessages(messages) payload := fmt.Sprintf("%s|%s|%s|%s|%d", fingerprint, provider, model, canonical, maxTokens) sum := sha256.Sum256([]byte(payload)) return hex.EncodeToString(sum[:]) } -func canonicalMessages(messages []types.EyrieMessage) string { +func canonicalMessages(messages []types.FluxMessage) string { var b strings.Builder for _, m := range messages { fmt.Fprintf(&b, "<%s>%s", m.Role, m.Content) @@ -80,12 +80,12 @@ func b2i(v bool) int { } // Get returns the cached complete response for key, or ok=false. -func (c *Cache) Get(key string) (*types.EyrieResponse, bool) { +func (c *Cache) Get(key string) (*types.FluxResponse, bool) { data, err := os.ReadFile(c.path("resp", key)) if err != nil { return nil, false } - var resp types.EyrieResponse + var resp types.FluxResponse if err := json.Unmarshal(data, &resp); err != nil { return nil, false } @@ -93,7 +93,7 @@ func (c *Cache) Get(key string) (*types.EyrieResponse, bool) { } // Put stores a complete response under key. -func (c *Cache) Put(key string, resp *types.EyrieResponse) error { +func (c *Cache) Put(key string, resp *types.FluxResponse) error { if resp == nil { return fmt.Errorf("replaycache: nil response") } @@ -105,12 +105,12 @@ func (c *Cache) Put(key string, resp *types.EyrieResponse) error { } // GetStream returns the cached stream events for key, or ok=false. -func (c *Cache) GetStream(key string) ([]types.EyrieStreamEvent, bool) { +func (c *Cache) GetStream(key string) ([]types.FluxStreamEvent, bool) { data, err := os.ReadFile(c.path("stream", key)) if err != nil { return nil, false } - var events []types.EyrieStreamEvent + var events []types.FluxStreamEvent if err := json.Unmarshal(data, &events); err != nil { return nil, false } @@ -119,7 +119,7 @@ func (c *Cache) GetStream(key string) ([]types.EyrieStreamEvent, bool) { // PutStream stores stream events under key so a later identical request can // replay the exact same sequence. -func (c *Cache) PutStream(key string, events []types.EyrieStreamEvent) error { +func (c *Cache) PutStream(key string, events []types.FluxStreamEvent) error { if len(events) == 0 { return fmt.Errorf("replaycache: no events") } diff --git a/internal/replaycache/replaycache_test.go b/internal/replaycache/replaycache_test.go index 1c670d09..e3140da9 100644 --- a/internal/replaycache/replaycache_test.go +++ b/internal/replaycache/replaycache_test.go @@ -10,7 +10,7 @@ import ( ) func TestKeyDeterministic(t *testing.T) { - msgs := []types.EyrieMessage{{Role: "user", Content: "hello"}} + msgs := []types.FluxMessage{{Role: "user", Content: "hello"}} k1 := Key("fp", "anthropic", "claude", msgs, 100) k2 := Key("fp", "anthropic", "claude", msgs, 100) if k1 != k2 { @@ -39,8 +39,8 @@ func TestFingerprintHidesSecrets(t *testing.T) { func TestPutGetResponseRoundTrip(t *testing.T) { c := New(t.TempDir()) - key := Key("fp", "p", "m", []types.EyrieMessage{{Role: "user", Content: "hi"}}, 10) - want := &types.EyrieResponse{Content: "cached answer", FinishReason: "end_turn"} + key := Key("fp", "p", "m", []types.FluxMessage{{Role: "user", Content: "hi"}}, 10) + want := &types.FluxResponse{Content: "cached answer", FinishReason: "end_turn"} if err := c.Put(key, want); err != nil { t.Fatal(err) } @@ -58,8 +58,8 @@ func TestPutGetResponseRoundTrip(t *testing.T) { func TestStreamRoundTrip(t *testing.T) { c := New(t.TempDir()) - key := Key("fp", "p", "m", []types.EyrieMessage{{Role: "user", Content: "s"}}, 10) - events := []types.EyrieStreamEvent{ + key := Key("fp", "p", "m", []types.FluxMessage{{Role: "user", Content: "s"}}, 10) + events := []types.FluxStreamEvent{ {Type: "content", Content: "hel"}, {Type: "content", Content: "lo"}, {Type: "done", StopReason: "end_turn"}, @@ -80,7 +80,7 @@ func TestFilesAreNotWorldReadable(t *testing.T) { dir := t.TempDir() c := New(dir) key := Key("fp", "p", "m", nil, 0) - if err := c.Put(key, &types.EyrieResponse{Content: "x"}); err != nil { + if err := c.Put(key, &types.FluxResponse{Content: "x"}); err != nil { t.Fatal(err) } matches, _ := filepath.Glob(filepath.Join(dir, "resp", "*", "*.json")) @@ -110,11 +110,11 @@ func TestPutStreamEmptyErrors(t *testing.T) { func TestEntriesCountsBothKinds(t *testing.T) { c := New(t.TempDir()) - k := Key("fp", "p", "m", []types.EyrieMessage{{Role: "user", Content: "e"}}, 5) - if err := c.Put(k, &types.EyrieResponse{Content: "r"}); err != nil { + k := Key("fp", "p", "m", []types.FluxMessage{{Role: "user", Content: "e"}}, 5) + if err := c.Put(k, &types.FluxResponse{Content: "r"}); err != nil { t.Fatal(err) } - if err := c.PutStream(k, []types.EyrieStreamEvent{{Type: "done"}}); err != nil { + if err := c.PutStream(k, []types.FluxStreamEvent{{Type: "done"}}); err != nil { t.Fatal(err) } if got := c.Entries(); got != 2 { @@ -124,7 +124,7 @@ func TestEntriesCountsBothKinds(t *testing.T) { func TestJSONStableAcrossMarshalOrder(t *testing.T) { // The canonical key must not depend on Go struct marshaling order. - msgs := []types.EyrieMessage{ + msgs := []types.FluxMessage{ {Role: "user", Content: "q"}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{Name: "Bash", Arguments: map[string]interface{}{"command": "ls"}}}, ToolResults: []types.ToolResult{{Content: "out", IsError: true}}}, } diff --git a/internal/rhoerr/classify.go b/internal/rhoerr/classify.go index 2512e9da..ac2316c6 100644 --- a/internal/rhoerr/classify.go +++ b/internal/rhoerr/classify.go @@ -133,7 +133,7 @@ func classify(err error) errorClass { // cost before fulfilling a request. When the account balance can't cover // that hold they return 403 with an insufficient_user_quota code rather // than a 401 — so this must be checked before the generic 403 branch below, - // otherwise the user is misled into "check your API key". eyrie already + // otherwise the user is misled into "check your API key". flux already // tags these as "billing/quota problem"; the Agnes body also carries the // Chinese pre-deduction phrasing (预扣费) and an insufficient_user_quota code. if strings.Contains(low, "insufficient_user_quota") || strings.Contains(low, "insufficient_quota") || diff --git a/internal/rhoerr/classify_extra_test.go b/internal/rhoerr/classify_extra_test.go index 982ccda7..344c64c1 100644 --- a/internal/rhoerr/classify_extra_test.go +++ b/internal/rhoerr/classify_extra_test.go @@ -100,7 +100,7 @@ func TestClassifyError_InsufficientCredits(t *testing.T) { // cover the maximum-token pre-authorization hold. That must surface as a // quota problem, NOT as "check your API key". func TestClassifyError_AgnesPreDeductionQuota(t *testing.T) { - err := errors.New("eyrie: agnes chat failed (HTTP 403) [request_id=202607300111184425982109LnnftLG]: " + + err := errors.New("flux: agnes chat failed (HTTP 403) [request_id=202607300111184425982109LnnftLG]: " + "billing/quota problem — check the provider account's balance and limits — " + "AgnesAI_error: 预扣费额度失败, 用户剩余额度: $0.000740, 需要预扣费额度: $0.002068") result := ClassifyError(err) @@ -116,7 +116,7 @@ func TestClassifyError_AgnesPreDeductionQuota(t *testing.T) { } } -// The eyrie layer tags quota holds as "billing/quota problem"; a bare hint +// The flux layer tags quota holds as "billing/quota problem"; a bare hint // (without the full Agnes body) must still be classified as a quota problem. func TestClassifyError_QuotaHintOnly(t *testing.T) { err := errors.New("billing/quota problem — check the provider account's balance and limits") diff --git a/internal/rhoerr/errors.go b/internal/rhoerr/errors.go index 7721bfeb..17b461b8 100644 --- a/internal/rhoerr/errors.go +++ b/internal/rhoerr/errors.go @@ -6,7 +6,7 @@ import "fmt" // It captures which bridge failed, the operation attempted, and the // underlying reason or wrapped error. type BridgeError struct { - Bridge string // e.g., "eyrie", "memory", "token" + Bridge string // e.g., "flux", "memory", "token" Op string // e.g., "Remember", "Recall", "Enable" Reason string // human-readable reason Err error // underlying error, if any diff --git a/internal/session/conversion.go b/internal/session/conversion.go index 6546edba..9cb15892 100644 --- a/internal/session/conversion.go +++ b/internal/session/conversion.go @@ -3,7 +3,7 @@ package session import "github.com/GrayCodeAI/rho/internal/types" // FromRuntimeMessages converts Rho runtime messages into persisted session messages. -func FromRuntimeMessages(in []types.EyrieMessage) []Message { +func FromRuntimeMessages(in []types.FluxMessage) []Message { if len(in) == 0 { return nil } @@ -34,13 +34,13 @@ func FromRuntimeToolResults(in []types.ToolResult) []ToolResult { } // ToRuntimeMessages converts persisted session messages back into Rho runtime messages. -func ToRuntimeMessages(in []Message) []types.EyrieMessage { +func ToRuntimeMessages(in []Message) []types.FluxMessage { if len(in) == 0 { return nil } - out := make([]types.EyrieMessage, len(in)) + out := make([]types.FluxMessage, len(in)) for i, msg := range in { - out[i] = types.EyrieMessage{ + out[i] = types.FluxMessage{ Role: msg.Role, Content: msg.Content, Thinking: msg.Thinking, diff --git a/internal/session/conversion_test.go b/internal/session/conversion_test.go index cc0d98aa..01e8c0ea 100644 --- a/internal/session/conversion_test.go +++ b/internal/session/conversion_test.go @@ -8,7 +8,7 @@ import ( ) func TestRuntimeMessageRoundTrip(t *testing.T) { - in := []types.EyrieMessage{ + in := []types.FluxMessage{ { Role: "assistant", Content: "working", diff --git a/internal/session/session.go b/internal/session/session.go index 371947b1..b36d9876 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -16,7 +16,7 @@ import ( "sync" "time" - contracts "github.com/GrayCodeAI/eyrie/tools" + contracts "github.com/GrayCodeAI/flux/tools" "github.com/GrayCodeAI/rho/internal/eventlog" "github.com/GrayCodeAI/rho/internal/eventlog/zstdz" "github.com/GrayCodeAI/rho/internal/storage" diff --git a/internal/storage/paths.go b/internal/storage/paths.go index 5582baf0..8f7505f1 100644 --- a/internal/storage/paths.go +++ b/internal/storage/paths.go @@ -9,13 +9,13 @@ import ( ) const ( - appName = "rho" - legacyAppName = "hawk" - envConfigDir = "RHO_CONFIG_DIR" - envEyrieConfigDir = "EYRIE_CONFIG_DIR" - envStateDir = "RHO_STATE_DIR" - envCacheDir = "RHO_CACHE_DIR" - projectIDHashLen = 12 + appName = "rho" + legacyAppName = "hawk" + envConfigDir = "RHO_CONFIG_DIR" + envFluxConfigDir = "FLUX_CONFIG_DIR" + envStateDir = "RHO_STATE_DIR" + envCacheDir = "RHO_CACHE_DIR" + projectIDHashLen = 12 ) // legacyEnvDir reads a legacy HAWK_* override (e.g. HAWK_STATE_DIR) so an @@ -83,12 +83,12 @@ func SettingsPath() string { } func ProviderConfigPath() string { - // Eyrie owns provider routing state and resolves it from EYRIE_CONFIG_DIR, + // Flux owns provider routing state and resolves it from FLUX_CONFIG_DIR, // defaulting to its own directory under the user config root. - if dir := cleanEnvDir(envEyrieConfigDir); dir != "" { + if dir := cleanEnvDir(envFluxConfigDir); dir != "" { return filepath.Join(dir, "provider.json") } - return filepath.Join(mustUserConfigDir(), "eyrie", "provider.json") + return filepath.Join(mustUserConfigDir(), "flux", "provider.json") } func SessionsDir() string { diff --git a/internal/storage/paths_extra_test.go b/internal/storage/paths_extra_test.go index 1d9a3eec..25f0f326 100644 --- a/internal/storage/paths_extra_test.go +++ b/internal/storage/paths_extra_test.go @@ -232,7 +232,7 @@ func TestConfigDir_Default(t *testing.T) { t.Setenv("HOME", home) t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config")) t.Setenv(envConfigDir, "") - t.Setenv(envEyrieConfigDir, "") + t.Setenv(envFluxConfigDir, "") t.Setenv("HAWK_CONFIG_DIR", "") got := ConfigDir() @@ -278,22 +278,22 @@ func TestSettingsPath(t *testing.T) { // --- ProviderConfigPath tests --- -func TestProviderConfigPath_EyrieOverride(t *testing.T) { - eyrieDir := filepath.Join(t.TempDir(), "eyrie") - t.Setenv(envEyrieConfigDir, eyrieDir) +func TestProviderConfigPath_FluxOverride(t *testing.T) { + fluxDir := filepath.Join(t.TempDir(), "flux") + t.Setenv(envFluxConfigDir, fluxDir) got := ProviderConfigPath() - want := filepath.Join(eyrieDir, "provider.json") + want := filepath.Join(fluxDir, "provider.json") if got != want { t.Errorf("ProviderConfigPath() = %q, want %q", got, want) } } -func TestProviderConfigPath_EyrieWhitespaceIgnored(t *testing.T) { - t.Setenv(envEyrieConfigDir, " ") // whitespace should be treated as empty +func TestProviderConfigPath_FluxWhitespaceIgnored(t *testing.T) { + t.Setenv(envFluxConfigDir, " ") // whitespace should be treated as empty got := ProviderConfigPath() - want := filepath.Join(mustUserConfigDir(), "eyrie", "provider.json") + want := filepath.Join(mustUserConfigDir(), "flux", "provider.json") if got != want { t.Errorf("ProviderConfigPath() = %q, want %q", got, want) } diff --git a/internal/storage/paths_test.go b/internal/storage/paths_test.go index d4b0cdd9..5f927d1f 100644 --- a/internal/storage/paths_test.go +++ b/internal/storage/paths_test.go @@ -26,26 +26,26 @@ func TestStorageDirsRespectOverrides(t *testing.T) { } } -func TestProviderConfigPathUsesEyrieOverrideWithoutMovingRhoSettings(t *testing.T) { +func TestProviderConfigPathUsesFluxOverrideWithoutMovingRhoSettings(t *testing.T) { rhoDir := filepath.Join(t.TempDir(), "rho") - eyrieDir := filepath.Join(t.TempDir(), "eyrie") + fluxDir := filepath.Join(t.TempDir(), "flux") t.Setenv(envConfigDir, rhoDir) - t.Setenv(envEyrieConfigDir, eyrieDir) + t.Setenv(envFluxConfigDir, fluxDir) - if got, want := ProviderConfigPath(), filepath.Join(eyrieDir, "provider.json"); got != want { - t.Fatalf("ProviderConfigPath() = %q, want EYRIE_CONFIG_DIR path %q", got, want) + if got, want := ProviderConfigPath(), filepath.Join(fluxDir, "provider.json"); got != want { + t.Fatalf("ProviderConfigPath() = %q, want FLUX_CONFIG_DIR path %q", got, want) } if got, want := SettingsPath(), filepath.Join(rhoDir, "settings.json"); got != want { t.Fatalf("SettingsPath() = %q, want RHO_CONFIG_DIR path %q", got, want) } } -func TestProviderConfigPathDefaultsToEyrieDir(t *testing.T) { +func TestProviderConfigPathDefaultsToFluxDir(t *testing.T) { t.Setenv(envConfigDir, filepath.Join(t.TempDir(), "rho")) - t.Setenv(envEyrieConfigDir, " ") + t.Setenv(envFluxConfigDir, " ") - if got, want := ProviderConfigPath(), filepath.Join(mustUserConfigDir(), "eyrie", "provider.json"); got != want { - t.Fatalf("ProviderConfigPath() = %q, want Eyrie default %q", got, want) + if got, want := ProviderConfigPath(), filepath.Join(mustUserConfigDir(), "flux", "provider.json"); got != want { + t.Fatalf("ProviderConfigPath() = %q, want Flux default %q", got, want) } } diff --git a/internal/storage/testdirs.go b/internal/storage/testdirs.go index b261e2f1..d491534b 100644 --- a/internal/storage/testdirs.go +++ b/internal/storage/testdirs.go @@ -9,7 +9,7 @@ import ( func SetTestDirs(t *testing.T, root string) { t.Helper() t.Setenv(envConfigDir, filepath.Join(root, "config")) - t.Setenv(envEyrieConfigDir, filepath.Join(root, "eyrie")) + t.Setenv(envFluxConfigDir, filepath.Join(root, "flux")) t.Setenv(envStateDir, filepath.Join(root, "state")) t.Setenv(envCacheDir, filepath.Join(root, "cache")) } diff --git a/internal/testaudit/audit_test.go b/internal/testaudit/audit_test.go index b5f5edae..95d0a3ac 100644 --- a/internal/testaudit/audit_test.go +++ b/internal/testaudit/audit_test.go @@ -184,9 +184,9 @@ func TestNoDirectOsGetenvInInternal(t *testing.T) { t.Logf("Total os.Getenv violations in internal/: %d (logged as tech debt)", violationCount) } -// TestNoDirectLowerEyrieImports verifies production Rho code uses only -// Eyrie's stable engine facade. Tests may import lower packages for fixtures. -func TestNoDirectLowerEyrieImports(t *testing.T) { +// TestNoDirectLowerFluxImports verifies production Rho code uses only +// Flux's stable engine facade. Tests may import lower packages for fixtures. +func TestNoDirectLowerFluxImports(t *testing.T) { root := repoRoot(t) paths := []string{ filepath.Join(root, "internal"), @@ -202,29 +202,29 @@ func TestNoDirectLowerEyrieImports(t *testing.T) { } for _, imp := range pf.File.Imports { path := strings.Trim(imp.Path.Value, `"`) - if !strings.HasPrefix(path, "github.com/GrayCodeAI/eyrie/") || - path == "github.com/GrayCodeAI/eyrie/engine" || - strings.HasPrefix(path, "github.com/GrayCodeAI/eyrie/engine/") { + if !strings.HasPrefix(path, "github.com/GrayCodeAI/flux/") || + path == "github.com/GrayCodeAI/flux/engine" || + strings.HasPrefix(path, "github.com/GrayCodeAI/flux/engine/") { continue } - // Rho uses the full vendored Eyrie API surface for provider, + // Rho uses the full vendored Flux API surface for provider, // graph, and tooling contracts that the engine facade does not // re-export. switch path { - case "github.com/GrayCodeAI/eyrie/llm", - "github.com/GrayCodeAI/eyrie/graph", - "github.com/GrayCodeAI/eyrie/tools": + case "github.com/GrayCodeAI/flux/llm", + "github.com/GrayCodeAI/flux/graph", + "github.com/GrayCodeAI/flux/tools": continue } pos := pf.FSet.Position(imp.Pos()) - t.Fatalf("forbidden lower-level Eyrie import %q at %s:%d; use github.com/GrayCodeAI/eyrie/engine", path, rel, pos.Line) + t.Fatalf("forbidden lower-level Flux import %q at %s:%d; use github.com/GrayCodeAI/flux/engine", path, rel, pos.Line) } } } } // TestNoLazyProviderConstructionInRho verifies Rho does not construct lazy -// provider transports directly. Provider/model transport resolution belongs in Eyrie. +// provider transports directly. Provider/model transport resolution belongs in Flux. func TestNoLazyProviderConstructionInRho(t *testing.T) { root := repoRoot(t) paths := []string{ @@ -249,7 +249,7 @@ func TestNoLazyProviderConstructionInRho(t *testing.T) { return true } pos := pf.FSet.Position(call.Pos()) - t.Fatalf("forbidden eyrie lazy provider construction at %s:%d; use the eyrie/engine facade", rel, pos.Line) + t.Fatalf("forbidden flux lazy provider construction at %s:%d; use the flux/engine facade", rel, pos.Line) return true }) } diff --git a/internal/testaudit/docs_audit_test.go b/internal/testaudit/docs_audit_test.go index e036feff..8cc7f169 100644 --- a/internal/testaudit/docs_audit_test.go +++ b/internal/testaudit/docs_audit_test.go @@ -22,7 +22,7 @@ func TestArchitectureDocsDoNotContainStaleContractsLanguage(t *testing.T) { forbiddenPhrases := []string{ "eagle` (to add)", "planned shared contracts layer", - "runtime still uses `eyrie/client` provider interfaces and config types", + "runtime still uses `flux/client` provider interfaces and config types", } for _, rel := range files { diff --git a/internal/testaudit/package_boundaries_test.go b/internal/testaudit/package_boundaries_test.go index 7c4ac428..9e4b15e5 100644 --- a/internal/testaudit/package_boundaries_test.go +++ b/internal/testaudit/package_boundaries_test.go @@ -13,13 +13,13 @@ import ( ) const ( - rhoModule = "github.com/GrayCodeAI/rho" - eyrieModule = "github.com/GrayCodeAI/eyrie" + rhoModule = "github.com/GrayCodeAI/rho" + fluxModule = "github.com/GrayCodeAI/flux" ) // supportEngines lists the sibling repos checked for boundary violations. -// Rho now depends only on eyrie. -var supportEngines = []string{"eyrie"} +// Rho now depends only on flux. +var supportEngines = []string{"flux"} type packageImport struct { file string @@ -34,34 +34,34 @@ type packageImport struct { func TestPackageDependencyGraph(t *testing.T) { root := repoRoot(t) - checkRhoEyrieFacade(t, root) + checkRhoFluxFacade(t, root) checkRhoInternalLayers(t, root) checkSupportRepositoryBoundaries(t, root) } -func checkRhoEyrieFacade(t *testing.T, root string) { +func checkRhoFluxFacade(t *testing.T, root string) { paths := []string{filepath.Join(root, "internal"), filepath.Join(root, "cmd")} var violations []string for _, path := range paths { for _, imp := range productionImports(t, root, path) { - if !strings.HasPrefix(imp.path, eyrieModule+"/") { + if !strings.HasPrefix(imp.path, fluxModule+"/") { continue } - if imp.path == eyrieModule+"/engine" || strings.HasPrefix(imp.path, eyrieModule+"/engine/") { + if imp.path == fluxModule+"/engine" || strings.HasPrefix(imp.path, fluxModule+"/engine/") { continue } - // Rho uses the full vendored Eyrie API surface for provider, graph, + // Rho uses the full vendored Flux API surface for provider, graph, // and tooling contracts that the engine facade does not re-export. switch imp.path { - case eyrieModule + "/llm", eyrieModule + "/graph", eyrieModule + "/tools": + case fluxModule + "/llm", fluxModule + "/graph", fluxModule + "/tools": continue } - violations = append(violations, formatImportViolation(root, imp, "use the eyrie/engine facade")) + violations = append(violations, formatImportViolation(root, imp, "use the flux/engine facade")) } } - assertNoPackageViolations(t, "Rho Eyrie facade", violations) + assertNoPackageViolations(t, "Rho Flux facade", violations) } func checkRhoInternalLayers(t *testing.T, root string) { diff --git a/internal/testutil/storage.go b/internal/testutil/storage.go index 38e800d5..248fd8d3 100644 --- a/internal/testutil/storage.go +++ b/internal/testutil/storage.go @@ -24,7 +24,7 @@ func InstallHermeticStorage() (func(), error) { if err != nil { return nil, err } - keys := []string{"HOME", "RHO_CONFIG_DIR", "RHO_STATE_DIR", "RHO_CACHE_DIR", "EYRIE_CONFIG_DIR"} + keys := []string{"HOME", "RHO_CONFIG_DIR", "RHO_STATE_DIR", "RHO_CACHE_DIR", "FLUX_CONFIG_DIR"} previous := make(map[string]string, len(keys)) wasSet := make(map[string]bool, len(keys)) for _, key := range keys { @@ -37,10 +37,10 @@ func InstallHermeticStorage() (func(), error) { return nil, err } for key, suffix := range map[string]string{ - "RHO_CONFIG_DIR": "config", - "RHO_STATE_DIR": "state", - "RHO_CACHE_DIR": "cache", - "EYRIE_CONFIG_DIR": "eyrie-config", + "RHO_CONFIG_DIR": "config", + "RHO_STATE_DIR": "state", + "RHO_CACHE_DIR": "cache", + "FLUX_CONFIG_DIR": "flux-config", } { if _, ok := os.LookupEnv(key); !ok { if err := os.Setenv(key, filepath.Join(root, suffix)); err != nil { diff --git a/internal/tool/batch_exec.go b/internal/tool/batch_exec.go index f9cc0e75..97365539 100644 --- a/internal/tool/batch_exec.go +++ b/internal/tool/batch_exec.go @@ -16,7 +16,7 @@ import ( // BatchExecTool submits prompts to the provider-native Message Batches API // for 50%-cost async execution. Designed for CI/scripted workloads where the // result is not needed immediately. Uses fixed HTTP calls (no shell) against -// the provider's REST endpoint; no eyrie/client import (boundary-guarded). +// the provider's REST endpoint; no flux/client import (boundary-guarded). type BatchExecTool struct{} func (BatchExecTool) Name() string { return "BatchExec" } @@ -226,8 +226,8 @@ func isBatchTerminal(s string) bool { return batchTerminalStates[strings.ToLower // batchWait polls until the batch reaches a terminal state, with exponential // backoff + jitter capped at 30s, honoring Retry-After on 429/5xx, bounded by -// timeoutSeconds (default 600). Mirrors eyrie's WaitUntilDone but inlined to -// stay boundary-compliant (no eyrie/client import). +// timeoutSeconds (default 600). Mirrors flux's WaitUntilDone but inlined to +// stay boundary-compliant (no flux/client import). func batchWait(ctx context.Context, apiKey, batchID string, timeoutSeconds, pollIntervalSec int) (string, error) { if timeoutSeconds <= 0 { timeoutSeconds = 600 diff --git a/internal/tool/media_generation.go b/internal/tool/media_generation.go index b5145f00..a3a021e3 100644 --- a/internal/tool/media_generation.go +++ b/internal/tool/media_generation.go @@ -17,7 +17,7 @@ import ( // MediaEngine is the pluggable backend that actually generates image/video // assets. rho does not bundle a media provider; a host (or a provider-backed -// eyrie integration) wires one in via SetMediaEngine. The tool and the +// flux integration) wires one in via SetMediaEngine. The tool and the // local-persistence contract are provider-agnostic, so generation, saving, and // result reporting all work the same regardless of backend. type MediaEngine interface { diff --git a/internal/tool/registry_lazy.go b/internal/tool/registry_lazy.go index 639b3ce9..e6208126 100644 --- a/internal/tool/registry_lazy.go +++ b/internal/tool/registry_lazy.go @@ -1,12 +1,12 @@ package tool // Lazy model-surface helpers: tools can be registered for execution while -// staying hidden from EyrieTools until promoted (ToolSearch select, mode, etc.). +// staying hidden from FluxTools until promoted (ToolSearch select, mode, etc.). // EnableLazyModelSurface restricts model-visible tools to essentialNames. // All other primary tools remain executable via Get (and discoverable via // ToolSearch when AvailableTools includes them) but are omitted from -// EyrieTools until PromoteModelTool. +// FluxTools until PromoteModelTool. func (r *Registry) EnableLazyModelSurface(essentialNames []string) { if r == nil { return @@ -88,7 +88,7 @@ func (r *Registry) ModelVisibleNames() []string { return out } -// IsModelVisible reports whether name is included in EyrieTools. +// IsModelVisible reports whether name is included in FluxTools. func (r *Registry) IsModelVisible(name string) bool { if r == nil { return false diff --git a/internal/tool/safety.go b/internal/tool/safety.go index 75227e52..8bc9e446 100644 --- a/internal/tool/safety.go +++ b/internal/tool/safety.go @@ -353,7 +353,7 @@ func expandCommandPathVariables(command string) string { }{ {name: "HOME", value: home.MustDir()}, {name: "RHO_CONFIG_DIR", value: strings.TrimSpace(env.Getenv("RHO_CONFIG_DIR"))}, - {name: "EYRIE_CONFIG_DIR", value: strings.TrimSpace(env.Getenv("EYRIE_CONFIG_DIR"))}, + {name: "FLUX_CONFIG_DIR", value: strings.TrimSpace(env.Getenv("FLUX_CONFIG_DIR"))}, } { if item.value == "" { continue @@ -373,11 +373,11 @@ func expandCommandPathVariables(command string) string { // the file tools. func CommandReferencesSensitivePath(command string) string { // Do not try to emulate every shell parameter-expansion form here. An - // EYRIE_CONFIG_DIR reference is itself sensitive because the variable + // FLUX_CONFIG_DIR reference is itself sensitive because the variable // identifies the credential-bearing provider-state directory. This also - // closes modifier forms such as ${EYRIE_CONFIG_DIR%/}/provider.json. - if strings.Contains(command, "EYRIE_CONFIG_DIR") { - return "command references EYRIE_CONFIG_DIR, blocked for security" + // closes modifier forms such as ${FLUX_CONFIG_DIR%/}/provider.json. + if strings.Contains(command, "FLUX_CONFIG_DIR") { + return "command references FLUX_CONFIG_DIR, blocked for security" } if !strings.ContainsAny(command, "/.") { return "" @@ -408,7 +408,7 @@ func CommandReferencesSensitivePath(command string) string { } } // Keep Bash aligned with the Read/Edit/Write path policy, including a - // provider.json rooted at EYRIE_CONFIG_DIR. ResolvePath also handles + // provider.json rooted at FLUX_CONFIG_DIR. ResolvePath also handles // relative custom config directories and symlinked parents. if reason := IsSensitivePath(tok); reason != "" { return "command references a sensitive path: " + reason diff --git a/internal/tool/safety_test.go b/internal/tool/safety_test.go index 5f630f7f..eea3ee35 100644 --- a/internal/tool/safety_test.go +++ b/internal/tool/safety_test.go @@ -268,24 +268,24 @@ func TestIsSensitivePath_RhoConfigDir(t *testing.T) { } } -func TestIsSensitivePath_EyrieConfigDir(t *testing.T) { +func TestIsSensitivePath_FluxConfigDir(t *testing.T) { rhoDir := filepath.Join(t.TempDir(), "rho") - eyrieDir := filepath.Join(t.TempDir(), "eyrie") + fluxDir := filepath.Join(t.TempDir(), "flux") t.Setenv("RHO_CONFIG_DIR", rhoDir) - t.Setenv("EYRIE_CONFIG_DIR", eyrieDir) + t.Setenv("FLUX_CONFIG_DIR", fluxDir) - if reason := IsSensitivePath(filepath.Join(eyrieDir, "provider.json")); reason == "" { - t.Fatal("expected EYRIE_CONFIG_DIR/provider.json to be blocked") + if reason := IsSensitivePath(filepath.Join(fluxDir, "provider.json")); reason == "" { + t.Fatal("expected FLUX_CONFIG_DIR/provider.json to be blocked") } if reason := IsSensitivePath(filepath.Join(rhoDir, "settings.json")); reason != "" { t.Fatalf("expected Rho settings path to remain allowed, got %q", reason) } } -func TestFileToolsBlockEyrieProviderConfig(t *testing.T) { - eyrieDir := filepath.Join(t.TempDir(), "eyrie") - t.Setenv("EYRIE_CONFIG_DIR", eyrieDir) - providerPath := filepath.Join(eyrieDir, "provider.json") +func TestFileToolsBlockFluxProviderConfig(t *testing.T) { + fluxDir := filepath.Join(t.TempDir(), "flux") + t.Setenv("FLUX_CONFIG_DIR", fluxDir) + providerPath := filepath.Join(fluxDir, "provider.json") readInput, _ := json.Marshal(map[string]string{"path": providerPath}) editInput, _ := json.Marshal(map[string]string{ @@ -341,12 +341,12 @@ func TestIsSensitivePath_RhoConfigDirEnv(t *testing.T) { // symlinks before opening (M13): reading through a symlink that points at a // sensitive target is blocked, while a symlink to an ordinary file works. func TestFileRead_BlocksSymlinkToSensitiveFile(t *testing.T) { - eyrieDir := filepath.Join(t.TempDir(), "eyrie") - if err := os.MkdirAll(eyrieDir, 0o755); err != nil { + fluxDir := filepath.Join(t.TempDir(), "flux") + if err := os.MkdirAll(fluxDir, 0o755); err != nil { t.Fatal(err) } - t.Setenv("EYRIE_CONFIG_DIR", eyrieDir) - providerPath := filepath.Join(eyrieDir, "provider.json") + t.Setenv("FLUX_CONFIG_DIR", fluxDir) + providerPath := filepath.Join(fluxDir, "provider.json") if err := os.WriteFile(providerPath, []byte(`{"key":"x"}`), 0o600); err != nil { t.Fatal(err) } @@ -861,17 +861,17 @@ func TestCommandReferencesSensitivePath(t *testing.T) { } } -func TestCommandReferencesSensitivePath_EyrieConfigDir(t *testing.T) { - eyrieDir := filepath.Join(t.TempDir(), "eyrie config with spaces") - t.Setenv("EYRIE_CONFIG_DIR", eyrieDir) +func TestCommandReferencesSensitivePath_FluxConfigDir(t *testing.T) { + fluxDir := filepath.Join(t.TempDir(), "flux config with spaces") + t.Setenv("FLUX_CONFIG_DIR", fluxDir) commands := []string{ - `cat "` + filepath.Join(eyrieDir, "provider.json") + `"`, - `cat "$EYRIE_CONFIG_DIR/provider.json"`, - `cat "${EYRIE_CONFIG_DIR}/provider.json"`, - `cat "${EYRIE_CONFIG_DIR%/}/provider.json"`, - `printf '%s\n' "$EYRIE_CONFIG_DIR"`, - "cat " + strings.ReplaceAll(filepath.Join(eyrieDir, "provider.json"), " ", `\ `), + `cat "` + filepath.Join(fluxDir, "provider.json") + `"`, + `cat "$FLUX_CONFIG_DIR/provider.json"`, + `cat "${FLUX_CONFIG_DIR}/provider.json"`, + `cat "${FLUX_CONFIG_DIR%/}/provider.json"`, + `printf '%s\n' "$FLUX_CONFIG_DIR"`, + "cat " + strings.ReplaceAll(filepath.Join(fluxDir, "provider.json"), " ", `\ `), } for _, command := range commands { if reason := CommandReferencesSensitivePath(command); reason == "" { diff --git a/internal/tool/search_x.go b/internal/tool/search_x.go index 86d6ebfd..83debf69 100644 --- a/internal/tool/search_x.go +++ b/internal/tool/search_x.go @@ -16,7 +16,7 @@ import ( // xAI-compatible chat-completions endpoint with server-side X search enabled, // returning the model's summarized answer. Adopted from grok-cli's search_x // (server-side search tooling rather than a bespoke crawler). Inline HTTP, -// boundary-compliant (no eyrie/client import), httptest-testable. +// boundary-compliant (no flux/client import), httptest-testable. type SearchXTool struct{} func (SearchXTool) Name() string { return "SearchX" } diff --git a/internal/tool/tool.go b/internal/tool/tool.go index d896244e..976d2e4f 100644 --- a/internal/tool/tool.go +++ b/internal/tool/tool.go @@ -36,7 +36,7 @@ type AliasedTool interface { // schema. It replaces the error-prone hand-written map[string]interface{} // JSON-schema literals that every tool used to embed in Parameters(). A // ToolSchema converts to the wire format via ToJSONSchema(), so the external -// EyrieTool.Parameters contract (map[string]interface{}) is unchanged. +// FluxTool.Parameters contract (map[string]interface{}) is unchanged. // // Tools that don't implement SchemaProvider keep working exactly as before — // Parameters() is still the source of truth and validation stays permissive. @@ -70,7 +70,7 @@ type SchemaProvider interface { } // ToJSONSchema converts the typed schema to the wire-format -// map[string]interface{} expected by EyrieTool.Parameters. +// map[string]interface{} expected by FluxTool.Parameters. func (s ToolSchema) ToJSONSchema() map[string]interface{} { props := make(map[string]interface{}, len(s.Properties)) for name, p := range s.Properties { @@ -285,7 +285,7 @@ type Registry struct { mu sync.RWMutex tools map[string]Tool primary []Tool - // modelVisible, when non-nil, restricts EyrieTools to the listed primary + // modelVisible, when non-nil, restricts FluxTools to the listed primary // names (lazy model surface). Get/Execute still reach every registered tool. modelVisible map[string]bool } @@ -344,17 +344,17 @@ func (r *Registry) Filter(allow []string) *Registry { return NewRegistry(filtered...) } -// EyrieTools converts model-visible tools to Rho runtime tool definitions. +// FluxTools converts model-visible tools to Rho runtime tool definitions. // When lazy model surface is enabled, only promoted/essential tools are listed. -func (r *Registry) EyrieTools() []types.EyrieTool { +func (r *Registry) FluxTools() []types.FluxTool { r.mu.RLock() defer r.mu.RUnlock() - out := make([]types.EyrieTool, 0, len(r.primary)) + out := make([]types.FluxTool, 0, len(r.primary)) for _, t := range r.primary { if r.modelVisible != nil && !r.modelVisible[t.Name()] { continue } - out = append(out, types.EyrieTool{ + out = append(out, types.FluxTool{ Name: t.Name(), Description: t.Description(), Parameters: t.Parameters(), diff --git a/internal/tool/tool_metadata_test.go b/internal/tool/tool_metadata_test.go index 535a1c18..2c4dff56 100644 --- a/internal/tool/tool_metadata_test.go +++ b/internal/tool/tool_metadata_test.go @@ -118,17 +118,17 @@ func TestRegistry_WithTools(t *testing.T) { } } -func TestRegistry_EyrieTools_WithTools(t *testing.T) { +func TestRegistry_FluxTools_WithTools(t *testing.T) { t.Parallel() registry := NewRegistry(&BashTool{}, &FileReadTool{}) - eyrieTools := registry.EyrieTools() + fluxTools := registry.FluxTools() - if len(eyrieTools) != 2 { - t.Errorf("EyrieTools() returned %d, want 2", len(eyrieTools)) + if len(fluxTools) != 2 { + t.Errorf("FluxTools() returned %d, want 2", len(fluxTools)) } - for _, et := range eyrieTools { + for _, et := range fluxTools { if et.Name == "" { - t.Error("EyrieTool.Name should not be empty") + t.Error("FluxTool.Name should not be empty") } } } diff --git a/internal/tool/tool_test.go b/internal/tool/tool_test.go index 3a9d6f18..00239b7f 100644 --- a/internal/tool/tool_test.go +++ b/internal/tool/tool_test.go @@ -421,7 +421,7 @@ func TestRegistryExposesArchiveNamesAndAcceptsAliases(t *testing.T) { } var exposed []string - for _, t := range r.EyrieTools() { + for _, t := range r.FluxTools() { exposed = append(exposed, t.Name) } for _, alias := range []string{"bash", "file_read", "file_write", "file_edit", "ls"} { diff --git a/internal/types/client.go b/internal/types/client.go index c05e7b81..d4811f39 100644 --- a/internal/types/client.go +++ b/internal/types/client.go @@ -3,11 +3,11 @@ package types import ( "context" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" ) // ContentPart is a provider-neutral multimodal message part. Rho owns this -// conversation shape; the Eyrie engine adapter translates it at the transport +// conversation shape; the Flux engine adapter translates it at the transport // boundary. It aliases the canonical contract type. type ContentPart = llm.ContentPart @@ -19,16 +19,16 @@ type InputAudioPart = llm.InputAudioPart // ChatProvider is Rho's transport-provider interface. type ChatProvider interface { - Chat(ctx context.Context, messages []EyrieMessage, opts ChatOptions) (*EyrieResponse, error) - StreamChat(ctx context.Context, messages []EyrieMessage, opts ChatOptions) (*StreamResult, error) + Chat(ctx context.Context, messages []FluxMessage, opts ChatOptions) (*FluxResponse, error) + StreamChat(ctx context.Context, messages []FluxMessage, opts ChatOptions) (*StreamResult, error) Ping(ctx context.Context) error Name() string } // ChatClient is the session-level agent-loop client interface. type ChatClient interface { - Chat(ctx context.Context, messages []EyrieMessage, opts ChatOptions) (*EyrieResponse, error) - StreamChatContinue(ctx context.Context, messages []EyrieMessage, opts ChatOptions, cfg ContinuationConfig) (*StreamResult, error) + Chat(ctx context.Context, messages []FluxMessage, opts ChatOptions) (*FluxResponse, error) + StreamChatContinue(ctx context.Context, messages []FluxMessage, opts ChatOptions, cfg ContinuationConfig) (*StreamResult, error) } // ResponseFormat specifies the desired output format for a Rho runtime request. @@ -37,8 +37,8 @@ type ResponseFormat = llm.ResponseFormat // ToolChoiceOption controls how the model uses tools. type ToolChoiceOption = llm.ToolChoiceOption -// EyrieTool is Rho's runtime tool definition DTO. -type EyrieTool = llm.EyrieTool +// FluxTool is Rho's runtime tool definition DTO. +type FluxTool = llm.FluxTool // ChatOptions holds Rho-owned request options for an engine chat call. type ChatOptions = llm.ChatOptions @@ -46,7 +46,7 @@ type ChatOptions = llm.ChatOptions // ContinuationConfig controls output continuation behavior for Rho runtime calls. type ContinuationConfig = llm.ContinuationConfig -// DefaultContinuationConfig is Rho's agent-loop continuation policy. Eyrie +// DefaultContinuationConfig is Rho's agent-loop continuation policy. Flux // receives these limits through its engine request rather than owning the // product policy. func DefaultContinuationConfig() ContinuationConfig { @@ -59,25 +59,25 @@ type ToolCall = llm.ToolCall // ToolResult is Rho's runtime tool result DTO. type ToolResult = llm.ToolResult -// EyrieUsage tracks token usage for Rho runtime responses and streams. -type EyrieUsage = llm.EyrieUsage +// FluxUsage tracks token usage for Rho runtime responses and streams. +type FluxUsage = llm.FluxUsage // ResolvedRoute is Rho's view of the concrete provider/model route selected -// by the provider engine. Keeping this projection Rho-owned prevents Eyrie's +// by the provider engine. Keeping this projection Rho-owned prevents Flux's // transport DTOs from leaking into product state and observability payloads. type ResolvedRoute = llm.ResolvedRoute -// EyrieResponse is Rho's runtime chat response DTO. -type EyrieResponse = llm.EyrieResponse +// FluxResponse is Rho's runtime chat response DTO. +type FluxResponse = llm.FluxResponse -// EyrieStreamEvent is Rho's runtime stream event DTO. -type EyrieStreamEvent = llm.EyrieStreamEvent +// FluxStreamEvent is Rho's runtime stream event DTO. +type FluxStreamEvent = llm.FluxStreamEvent // StreamResult wraps a Rho-owned streaming response with cleanup. It aliases // the canonical contract type; its Close() method and canonical constructor -// (NewStreamResult) live in github.com/GrayCodeAI/eyrie/llm. +// (NewStreamResult) live in github.com/GrayCodeAI/flux/llm. type StreamResult = llm.StreamResult -// EyrieMessage is Rho's runtime conversation DTO. +// FluxMessage is Rho's runtime conversation DTO. // It intentionally mirrors the engine boundary shape while remaining Rho-owned. -type EyrieMessage = llm.EyrieMessage +type FluxMessage = llm.FluxMessage diff --git a/internal/types/client_test.go b/internal/types/client_test.go index ab5e3d11..3a67ed02 100644 --- a/internal/types/client_test.go +++ b/internal/types/client_test.go @@ -5,11 +5,11 @@ import ( "encoding/json" "testing" - "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/flux/llm" ) func TestContentPartJSONContract(t *testing.T) { - in := EyrieMessage{ + in := FluxMessage{ Role: "user", ContentParts: []ContentPart{ {Type: "text", Text: "hello"}, @@ -22,7 +22,7 @@ func TestContentPartJSONContract(t *testing.T) { if err != nil { t.Fatalf("Marshal() error = %v", err) } - var got EyrieMessage + var got FluxMessage if err := json.Unmarshal(raw, &got); err != nil { t.Fatalf("Unmarshal() error = %v", err) } diff --git a/lefthook.yml b/lefthook.yml index 816cbfb9..95f830d1 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -112,11 +112,11 @@ pre-push: fi govulncheck ./... - boundary-eyrie-client: - run: bash scripts/check-eyrie-client-imports.sh + boundary-flux-client: + run: bash scripts/check-flux-client-imports.sh - boundary-eyrie-engine: - run: bash scripts/check-eyrie-engine-boundary.sh + boundary-flux-engine: + run: bash scripts/check-flux-engine-boundary.sh boundary-support-repo: run: bash scripts/check-support-repo-coupling.sh diff --git a/scripts/check-eyrie-client-imports.sh b/scripts/check-flux-client-imports.sh similarity index 60% rename from scripts/check-eyrie-client-imports.sh rename to scripts/check-flux-client-imports.sh index 7759f377..2968723e 100755 --- a/scripts/check-eyrie-client-imports.sh +++ b/scripts/check-flux-client-imports.sh @@ -6,22 +6,22 @@ cd "$ROOT_DIR" if command -v rg >/dev/null 2>&1; then violations="$( - rg -n '"github\.com/GrayCodeAI/eyrie/client(?:/[^\"]*)?"' \ + rg -n '"github\.com/GrayCodeAI/flux/client(?:/[^\"]*)?"' \ --glob '*.go' --glob '!*_test.go' . || true )" else violations="$( grep -RInE --include='*.go' --exclude='*_test.go' \ - '"github\.com/GrayCodeAI/eyrie/client(/[^\"]*)?"' . || true + '"github\.com/GrayCodeAI/flux/client(/[^\"]*)?"' . || true )" fi if [[ -n "${violations}" ]]; then - echo "forbidden direct imports of github.com/GrayCodeAI/eyrie/client found:" + echo "forbidden direct imports of github.com/GrayCodeAI/flux/client found:" echo "${violations}" echo - echo "Rho production code must go through github.com/GrayCodeAI/eyrie/engine" + echo "Rho production code must go through github.com/GrayCodeAI/flux/engine" exit 1 fi -echo "eyrie/client boundary guard passed (zero production imports)" +echo "flux/client boundary guard passed (zero production imports)" diff --git a/scripts/check-eyrie-engine-boundary.sh b/scripts/check-flux-engine-boundary.sh similarity index 68% rename from scripts/check-eyrie-engine-boundary.sh rename to scripts/check-flux-engine-boundary.sh index 07dd99c0..213abbc8 100755 --- a/scripts/check-eyrie-engine-boundary.sh +++ b/scripts/check-flux-engine-boundary.sh @@ -5,26 +5,26 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" if command -v rg >/dev/null 2>&1; then - eyrie_imports="$( - rg -n '"github\.com/GrayCodeAI/eyrie/[^\"]+"' \ + flux_imports="$( + rg -n '"github\.com/GrayCodeAI/flux/[^\"]+"' \ --glob '*.go' --glob '!*_test.go' . || true )" else - eyrie_imports="$( + flux_imports="$( grep -RInE --include='*.go' --exclude='*_test.go' \ - '"github\.com/GrayCodeAI/eyrie/[^\"]+"' . || true + '"github\.com/GrayCodeAI/flux/[^\"]+"' . || true )" fi # Host contract surface is exactly four packages: engine (facade), llm (DTOs # and the Provider port), graph (portable graph vocabulary), tools (tool-call -# contracts). See eyrie/README.md "Ecosystem Boundaries". -violations="$(printf '%s\n' "$eyrie_imports" | grep -vE '"github\.com/GrayCodeAI/eyrie/(engine|llm|graph|tools)(/|\")' || true)" +# contracts). See flux/README.md "Ecosystem Boundaries". +violations="$(printf '%s\n' "$flux_imports" | grep -vE '"github\.com/GrayCodeAI/flux/(engine|llm|graph|tools)(/|\")' || true)" if [[ -n "$violations" ]]; then - echo "direct production imports below the eyrie/engine facade found:" + echo "direct production imports below the flux/engine facade found:" echo "$violations" echo - echo "route every Rho production integration through github.com/GrayCodeAI/eyrie/engine" + echo "route every Rho production integration through github.com/GrayCodeAI/flux/engine" exit 1 fi @@ -40,4 +40,4 @@ if [[ -n "$credential_symbols" ]]; then exit 1 fi -echo "eyrie engine boundary passed (zero lower-level production imports)" +echo "flux engine boundary passed (zero lower-level production imports)" diff --git a/scripts/check-module-release-parity.sh b/scripts/check-module-release-parity.sh index 93c5d03e..34ee2661 100755 --- a/scripts/check-module-release-parity.sh +++ b/scripts/check-module-release-parity.sh @@ -14,8 +14,8 @@ while IFS= read -r repo; do done < <("${ROOT_DIR}/scripts/ecosystem-manifest.sh" list workspace) failed=0 -# Directory and module path differ when a repo is renamed (e.g. eyrie -# hosts module github.com/GrayCodeAI/eyrie), so resolve via the manifest. +# Directory and module path differ when a repo is renamed (e.g. flux +# hosts module github.com/GrayCodeAI/flux), so resolve via the manifest. module_for_repo() { awk -v dir="$1" ' /^ - directory:/ { cur=$0; sub(/^ - directory:[[:space:]]*/, "", cur); next } diff --git a/testdata/compatibility-matrix.json b/testdata/compatibility-matrix.json index f44158d3..10c778c0 100644 --- a/testdata/compatibility-matrix.json +++ b/testdata/compatibility-matrix.json @@ -5,13 +5,13 @@ "updated": "2026-09-14", "components": [ "rho", - "eyrie" + "flux" ], "dependencies": { "rho": [ - "eyrie" + "flux" ], - "eyrie": [] + "flux": [] }, "matrices": [ { @@ -19,7 +19,7 @@ "description": "Latest tested-together stable combination across the eco. This is what new users get when installing.", "components": { "rho": "0.0.1", - "eyrie": "0.0.1" + "flux": "0.0.1" } }, { @@ -27,7 +27,7 @@ "description": "Combination that the upcoming release will ship — kept up to date by release-please as components advance. Tested in CI on every PR.", "components": { "rho": "main", - "eyrie": "main" + "flux": "main" } } ] diff --git a/testdata/golden/help_root.txt b/testdata/golden/help_root.txt index e6384c4c..caf53804 100644 --- a/testdata/golden/help_root.txt +++ b/testdata/golden/help_root.txt @@ -1,6 +1,6 @@ rho is an AI coding agent that reads, writes, and runs code in your terminal. -It connects to 28 first-class LLM providers through eyrie, executes tools (file I/O, shell, +It connects to 28 first-class LLM providers through flux, executes tools (file I/O, shell, git, web search), and manages sessions — all from a keyboard-driven TUI or headless mode for scripts and CI. @@ -66,7 +66,7 @@ Configure: features List and manage feature flags governance Inspect and validate the governance policy ceiling mcp Show MCP configuration; run or register rho as an MCP server - models Deployment-aware model catalog (via eyrie) + models Deployment-aware model catalog (via flux) permissions List and manage exact permission rules plugin Manage plugins rules Detect, import, and export AI coding rules between tool formats @@ -80,7 +80,7 @@ Diagnose: bug-report Print a redacted diagnostic report for bug reports cost [Experimental] Analyze and optimize LLM API spend doctor Run local diagnostics - ecosystem Show eyrie and token-pipeline integration status + ecosystem Show flux and token-pipeline integration status path Developer path readiness (setup, security, ecosystem) preflight Check local readiness; use --live to verify the selected provider securitylog Inspect the tamper-evident security event log @@ -123,7 +123,7 @@ Flags: --max-budget-usd float maximum estimated API spend in USD --max-turns int maximum number of agentic turns in non-interactive mode --mcp stringArray MCP server command - -m, --model string model to use (from eyrie catalog; see /models) + -m, --model string model to use (from flux catalog; see /models) --no-auto-catalog-refresh disable automatic catalog refresh when cache is missing, empty, or stale --no-session-persistence disable session persistence in print mode --output-fields string comma-separated field whitelist for --output-format json (e.g. "result,session_id") @@ -135,7 +135,7 @@ Flags: -q, --quiet suppress non-essential output (spinners, progress, decoration); machine-parseable output only --record string record interactive REPL output to an fxtape file (fx --record parity) --recover scan for interrupted sessions and offer to resume - --refresh-catalog refresh the eyrie model catalog before starting + --refresh-catalog refresh the flux model catalog before starting --repl start interactive REPL mode (like aider) for multi-turn conversation without TUI --repo-map inject an AST-ranked repository map (Aider-style) into the system prompt -r, --resume string resume a saved session by ID