diff --git a/.env.example b/.env.example index c8c98f50..49f8e7f1 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,6 @@ GRAYCODE_DAEMON_API_KEY= # The following are NOT read by graycode-cli — setting them has no effect: # GRAYCODE_DAEMON_PORT / GRAYCODE_DAEMON_HOST — daemon binds 127.0.0.1:4590; # override with the --host / --port flags (see `graycode daemon start --help`) -# EYRIE_API_KEY / EYRIE_BASE_URL, HARRIER_API_KEY / HARRIER_ADDR — +# GRAYCODE_ROUTER_API_KEY / GRAYCODE_ROUTER_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-graycode-router/action.yml similarity index 100% rename from .github/actions/checkout-eyrie/action.yml rename to .github/actions/checkout-graycode-router/action.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db807c47..2eabb8e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,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-graycode-router 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 @@ -77,10 +77,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: graycode-router client boundary guard + run: bash ./scripts/check-graycode-router-client-imports.sh + - name: graycode-router engine facade boundary guard + run: bash ./scripts/check-graycode-router-engine-boundary.sh # ------------------------------------------------------------------------- # 2. Module hygiene — tidy, verify Graycode plus the sibling Go modules via go.work. @@ -98,7 +98,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref == 'feat/ecosystem-wiring-pr' && 'feat/ecosystem-wiring' || github.head_ref || github.ref_name }} - name: Generate ecosystem workspace @@ -149,7 +149,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -172,7 +172,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -191,7 +191,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -216,7 +216,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -245,7 +245,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -315,7 +315,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -423,7 +423,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c @@ -481,7 +481,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -526,7 +526,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -553,7 +553,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-eyrie + - uses: ./.github/actions/checkout-graycode-router 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..e72f7236 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-graycode-router 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 c84872ba..ec7818f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,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-graycode-router with: ref: ${{ github.ref_name }} diff --git a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl index d8602b10..3d16ff81 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 (graycode-router, harrier, shrike, swift, kestrel, merlin). # Engines are peers: they may depend on falcon, # but never on graycode/internal/* or another engine. # ============================================================================= @@ -81,7 +81,7 @@ echo "ecosystem boundary guard passed" # only — never on a support engine directly, and never on graycode/internal. # ============================================================================= # -# FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(eyrie|harrier|shrike|swift|kestrel|merlin)(/|")' +# FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(graycode-router|harrier|shrike|swift|kestrel|merlin)(/|")' # FORBIDDEN_INTERNAL='github\.com/GrayCodeAI/graycode-cli/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 acf7509b..91a93868 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 (graycode itself clones eyrie this way via - # ./.github/actions/checkout-eyrie). Omit for repos with none. + # graycode-eco repos at build time (graycode itself clones graycode-router this way via + # ./.github/actions/checkout-graycode-router). 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 de5ba689..8daafae7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ alwaysApply: false # Extending graycode graycode 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 (`graycode-router`, `shrike`, `harrier`, `swift`, `kestrel`, `merlin`). This document describes how to extend graycode with custom tools, skills, hooks, and integrations. @@ -192,8 +192,8 @@ Legacy `graycode/shared/types` has been removed. Cross-repo severity and finding ### Architecture note: provider ownership Implement provider protocols, adapters, catalog metadata, credential mappings, and -provider contract tests in `../graycode-router` (the eyrie engine's repo) first. Graycode consumes providers only -through Eyrie's stable engine facade; Graycode changes should be limited to host UX +provider contract tests in `../graycode-router` (the graycode-router engine's repo) first. Graycode consumes providers only +through GraycodeRouter's stable engine facade; Graycode 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 **graycode** (97743 symbols, 322940 relat ### Workspace workflow (sibling repos) -graycode depends on ecosystem repos (`eyrie`, etc.) as independent sibling repos in the `graycode-eco` workspace. Graycode's `go.work` lists them as `../`, so local changes in any sibling are automatically picked up by graycode. Each sibling is its own git repo, versioned and released independently. +graycode depends on ecosystem repos (`graycode-router`, etc.) as independent sibling repos in the `graycode-eco` workspace. Graycode's `go.work` lists them as `../`, so local changes in any sibling are automatically picked up by graycode. Each sibling is its own git repo, versioned and released independently. 1. Edit + test in `../` — run its tests, run `make test` in graycode 2. Push from the sibling: `git push origin ` diff --git a/CHANGELOG.md b/CHANGELOG.md index aba41a96..d479daa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,13 +23,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.2.0] — 2026-07-13 ### Changed -- **Graycode/Eyrie production boundary completed**: Graycode owns the product face, - sessions, tools, permissions, and public schemas while Eyrie v0.2.1 owns +- **Graycode/GraycodeRouter production boundary completed**: Graycode owns the product face, + sessions, tools, permissions, and public schemas while GraycodeRouter v0.2.1 owns credentials, catalog resolution, provider transport, resilience, and usage - telemetry behind the stable `eyrie/engine` facade. + telemetry behind the stable `graycode-router/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 GraycodeRouter 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. @@ -42,8 +42,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/graycode/main.go`, `cmd/daemon.go`, `flake.nix`, `.github/workflows/release.yml`, and the `update`/daemon test suites, aligning graycode - with the rest of the GrayCodeAI ecosystem (`eyrie`, `shrike`, `harrier`, `kestrel`, `merlin`). -- **Architecture boundary hardening**: Graycode 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 (`graycode-router`, `shrike`, `harrier`, `kestrel`, `merlin`). +- **Architecture boundary hardening**: Graycode now owns runtime request/response DTOs, transport config/provider seams, and review/verification product-boundary contracts, with `graycode-router/client` usage restricted to internal adapters and guarded in CI. - **`shared/types` removed**: Graycode no longer ships the old shared type path, and local boundary checks now block any attempt to reintroduce it. ### Added @@ -231,5 +231,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 +- graycode-router wired as LLM provider dependency - GitHub Actions CI diff --git a/Makefile b/Makefile index 75a936b8..5c6a6272 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 contracts-guard 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 contracts-guard ecosystem-guard graycode-router-client-guard graycode-router-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) @@ -118,11 +118,11 @@ contracts-guard: ## Fail on any legacy imports of removed graycode/shared/types. ecosystem-guard: ## Fail if external ecosystem repos import graycode/internal or removed graycode/shared/types. bash ./scripts/check-ecosystem-boundaries.sh -eyrie-client-guard: ## Fail on any production eyrie/client import. - bash ./scripts/check-eyrie-client-imports.sh +graycode-router-client-guard: ## Fail on any production graycode-router/client import. + bash ./scripts/check-graycode-router-client-imports.sh -eyrie-engine-guard: ## Require all production Eyrie imports to use the stable engine facade. - bash ./scripts/check-eyrie-engine-boundary.sh +graycode-router-engine-guard: ## Require all production GraycodeRouter imports to use the stable engine facade. + bash ./scripts/check-graycode-router-engine-boundary.sh peer-guard: ## Fail if support engines import each other instead of depending only on Graycode contracts. bash ./scripts/check-support-repo-coupling.sh @@ -133,7 +133,7 @@ internal-layers-guard: ## Enforce one-way dependencies across stable Graycode in package-boundaries-guard: ## Enforce AST/package-graph boundaries with file/line diagnostics. bash ./scripts/check-package-boundaries.sh -boundaries: manifest-guard check-replace contracts-guard 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 contracts-guard ecosystem-guard graycode-router-client-guard graycode-router-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 d2252969..b15fd0b3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ graycode is an AI-powered coding agent that lives in your terminal. It reads you **Developer path:** one machine, keychain credentials, local memory. Run `graycode path` to check readiness. -- **Model-agnostic** — supports 28 first-class providers through [eyrie](https://github.com/GrayCodeAI/graycode-router), including Anthropic, OpenAI, Gemini, Fireworks AI, Concentrate AI (pay-as-you-go), DeepSeek, and Ollama +- **Model-agnostic** — supports 28 first-class providers through [graycode-router](https://github.com/GrayCodeAI/graycode-router), 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 - **Docker-only execution** — agent commands run in an isolated container and @@ -275,8 +275,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 | `graycode 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 — graycode 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 `graycode-router/client` (`ImageClient`), wired by the host (boundary-guarded — graycode routes through the graycode-router 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 `graycode-router/client` (`AudioClient`), wired by the host | | Git-tree file snapshots | `internal/gitsnapshot` | Content-addressed tree capture/diff/preview/restore | | Turn-boundary rewind | `internal/filestate` | Per-prompt before/after snapshots with durable store | | Continual harness | `internal/intelligence/harness` | Versioned, evidence-backed refinement of supplemental prompts/memories/skills/subagents with rollback | @@ -341,7 +341,7 @@ graycode exec --agent reviewer "review last commit" # Custom persona ```bash graycode path # Developer path readiness (setup + security + sandbox) -graycode doctor # Full health report (eyrie + harrier + shrike panel) +graycode doctor # Full health report (graycode-router + harrier + shrike panel) graycode ecosystem # Ecosystem panel only graycode harrier # Persistent memory graph graycode harrier search # Search harrier memories @@ -352,7 +352,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, harrier, and shrike connect 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 graycode-router, harrier, and shrike connect 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`, `/harrier`, `/harrier search `, `/memory` (AGENTS.md). @@ -395,12 +395,12 @@ graycode works with any LLM provider. **Developer path:** paste keys in `/config | 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/graycode-router). +Provider routing, model resolution, and retries are handled by [graycode-router](https://github.com/GrayCodeAI/graycode-router). For deployment-aware routing, set `"deployment_routing": true` in `.graycode/settings.json` or export `GRAYCODE_DEPLOYMENT_ROUTING=true`. Graycode will route canonical model IDs through -Eyrie's deployment catalog, so new models can be exposed by refreshing the catalog +GraycodeRouter's deployment catalog, so new models can be exposed by refreshing the catalog instead of changing Graycode. In chat, run `/refresh-model-catalog` to fetch the latest -deployment-aware catalog into `~/.eyrie/model_catalog.json`. +deployment-aware catalog into `~/.graycode-router/model_catalog.json`. ## Architecture @@ -436,7 +436,7 @@ graycode/ Ecosystem sibling repos (independent Git repos in the `graycode-eco` parent folder): -├── eyrie/ # LLM provider runtime +├── graycode-router/ # LLM provider runtime ``` ### Ecosystem @@ -445,7 +445,7 @@ graycode is the main CLI/product and integrates these GrayCodeAI repositories in three runtime layers plus optional tooling/platform services: - **Primary product:** **graycode** is the only end-user product surface in this ecosystem. -- **Support engines mounted by Graycode:** **eyrie**, **harrier**, **shrike**, **swift**, **kestrel**, **merlin**. Graycode imports or shells into these engines behind its own command surface. +- **Support engines mounted by Graycode:** **graycode-router**, **harrier**, **shrike**, **swift**, **kestrel**, **merlin**. Graycode imports or shells into these engines behind its own command surface. - **Shared foundations:** **falcon** provides shared MCP server scaffolding. - **API consumers/extensions:** **graycode-skills** provides Graycode skills @@ -482,7 +482,7 @@ You may keep a **personal** parent **`go.work`** that lists alternate clones on | Component | Repository | Purpose | |---|---|---| | **graycode** | This repo | AI coding agent | -| **eyrie** | [GrayCodeAI/graycode-router](https://github.com/GrayCodeAI/graycode-router) | LLM provider runtime | +| **graycode-router** | [GrayCodeAI/graycode-router](https://github.com/GrayCodeAI/graycode-router) | LLM provider runtime | For the consolidated repo map and the current-vs-proposed architecture diagrams, see [docs/architecture/graycode-current-vs-proposed.md](docs/architecture/graycode-current-vs-proposed.md). For execution-graph ownership, automatic capture seams, export/sync commands, diff --git a/SECURITY.md b/SECURITY.md index 17b0d5bf..cc0835aa 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 `graycode-router/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 569f1db2..86a4ac57 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 GraycodeRouter'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 ac968889..9ecee297 100644 --- a/cmd/chat.go +++ b/cmd/chat.go @@ -298,7 +298,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco go func() { providerName := effectiveProvider entries, _ := graycodeconfig.ListEngineModels(context.Background(), providerName, false) - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromGraycodeRouter(entries) if len(opts) > 0 { modelCacheMu.Lock() modelCache[providerName] = opts @@ -713,7 +713,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. graycode-router 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 ab7aa9a4..354b4460 100644 --- a/cmd/chat_commands.go +++ b/cmd/chat_commands.go @@ -257,7 +257,7 @@ var slashDescriptions = map[string]string{ "/version": "Show graycode version", "/vim": "Toggle vim mode", "/welcome": "Re-print the welcome header", - "/ecosystem": "Show eyrie, harrier, and shrike integration status", + "/ecosystem": "Show graycode-router, harrier, and shrike integration status", "/path": "Developer path readiness (setup, security, sandbox)", "/harrier": "Show harrier memory (use /harrier search to search)", "/cron": "Show scheduled jobs", diff --git a/cmd/chat_commands_config.go b/cmd/chat_commands_config.go index 2f8fe626..c121b61d 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) _ = graycodeconfig.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 graycode-router (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 graycode-router (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 48bb2a4a..23015359 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 (graycode-router 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 944f6325..dd33b424 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.GraycodeRouterTools()), 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 6c9b8765..6e84a4f7 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.GraycodeRouterTools() { 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 1310c1c6..36844308 100644 --- a/cmd/chat_config_deployment.go +++ b/cmd/chat_config_deployment.go @@ -78,10 +78,10 @@ func saveCredentialAsync(inference graycodeconfig.CredentialInference, secret st } graycodeconfig.InvalidateConfigUICache() graycodeconfig.RefreshConfigCredSnapshot(ctx) - result, err := graycodeconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) + result, err := graycodeconfig.ApplyGraycodeRouterCredentialsForProvider(ctx, inference.ProviderID) if err != nil && graycodeconfig.IsCatalogCacheRequired(err) { if refreshErr := graycodeconfig.RefreshCatalogAfterCredentials(ctx, nil); refreshErr == nil { - result, err = graycodeconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) + result, err = graycodeconfig.ApplyGraycodeRouterCredentialsForProvider(ctx, inference.ProviderID) } else { err = fmt.Errorf("%w; automatic catalog refresh failed: %w", err, refreshErr) } @@ -102,7 +102,7 @@ func saveCredentialAsync(inference graycodeconfig.CredentialInference, secret st deploymentID: inference.DeploymentID, } } - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromGraycodeRouter(entries) return configApplyCredentialsMsg{ summary: graycodeconfig.FormatApplyCredentialsSummary(result), providerID: inference.ProviderID, diff --git a/cmd/chat_config_models.go b/cmd/chat_config_models.go index 26818871..2ba2328c 100644 --- a/cmd/chat_config_models.go +++ b/cmd/chat_config_models.go @@ -11,7 +11,7 @@ import ( "github.com/GrayCodeAI/graycode-cli/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 graycode-router, 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 := configModelOptionsFromGraycodeRouter(entries) if len(opts) > 0 { modelCacheMu.Lock() modelCache[provider] = opts @@ -84,7 +84,7 @@ func fetchModelsAsync(provider string) tea.Cmd { } } -func configModelOptionsFromEyrie(entries []graycodeconfig.EngineModel) []configModelOption { +func configModelOptionsFromGraycodeRouter(entries []graycodeconfig.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 := configModelOptionsFromGraycodeRouter(entries) modelCacheMu.Lock() modelCache[provider] = opts modelCacheMu.Unlock() @@ -252,7 +252,7 @@ func loadConfigModelOptions(provider string) []configModelOption { modelCacheMu.RUnlock() entries, err := graycodeconfig.ListEngineModels(context.Background(), provider, false) if err == nil && len(entries) > 0 { - opts := configModelOptionsFromEyrie(entries) + opts := configModelOptionsFromGraycodeRouter(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 9f034ed5..95ae657d 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([]graycodeconfig.EngineModel{{ + opts := configModelOptionsFromGraycodeRouter([]graycodeconfig.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 af340265..0c1ea488 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 · graycode-router 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 bf496252..7ddb41d4 100644 --- a/cmd/chat_config_save_flow_test.go +++ b/cmd/chat_config_save_flow_test.go @@ -8,9 +8,9 @@ import ( "testing" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/eyrie/catalog" - "github.com/GrayCodeAI/eyrie/credentials" graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-router/catalog" + "github.com/GrayCodeAI/graycode-router/credentials" ) func TestFinishConfigEntry_APIKeyPaste_SavesBeforeProbe(t *testing.T) { @@ -134,7 +134,7 @@ func configSaveTestTransport(status int) http.RoundTripper { }) } -// roundTripFunc is defined in eyrie/runtime tests; duplicate here for graycode cmd tests. +// roundTripFunc is defined in graycode-router/runtime tests; duplicate here for graycode 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 graycode-router 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 graycode-router catalog fixtures pin the claude-fable-5 model state. + t.Skip("requires specific graycode-router model catalog state (claude-fable-5)") graycodeconfig.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 graycode-router 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 graycode-router catalog fixtures pin the auth-failure model state. + t.Skip("requires specific graycode-router model catalog state") graycodeconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) diff --git a/cmd/chat_config_ui.go b/cmd/chat_config_ui.go index f4d6f974..e107c0cb 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, "GraycodeRouter:"), 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..a26650a9 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", "graycode-router: 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 d765d7ab..27337ebe 100644 --- a/cmd/chat_model.go +++ b/cmd/chat_model.go @@ -252,7 +252,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 graycode-router 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 ce2999d5..4970e755 100644 --- a/cmd/chat_print.go +++ b/cmd/chat_print.go @@ -138,7 +138,7 @@ func runPrint(text string) error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveEyrieSession(sessionID, sess) + saveGraycodeRouterSession(sessionID, sess) } return nil } @@ -154,7 +154,7 @@ func runPrint(text string) error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveEyrieSession(sessionID, sess) + saveGraycodeRouterSession(sessionID, sess) } return nil } @@ -246,7 +246,7 @@ func writePrintEvent(sessionID, eventType, content, toolName string) { fmt.Println(string(data)) } -func saveEyrieSession(id string, sess *engine.Session) { +func saveGraycodeRouterSession(id string, sess *engine.Session) { raw := sess.RawMessages() if len(raw) == 0 { return @@ -426,7 +426,7 @@ func runRepl() error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveEyrieSession(sessionID, sess) + saveGraycodeRouterSession(sessionID, sess) } } } diff --git a/cmd/chat_subcommand_mode.go b/cmd/chat_subcommand_mode.go index be716abf..cba5da01 100644 --- a/cmd/chat_subcommand_mode.go +++ b/cmd/chat_subcommand_mode.go @@ -45,7 +45,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().GraycodeRouterTools()) } m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf( "Work mode: %s (plan | act | review)\nShell mode: %s (auto | shell | agent)\nIsolation: %s\nTrust: %s\nAuto-commit: %s\nTools visible: %d\n\n/start · /isolation · /trust · /branch-agent · /auto-commit", diff --git a/cmd/chat_subcommand_model.go b/cmd/chat_subcommand_model.go index aa6b2196..8a9bfd0b 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 graycode-router (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 4c2daa96..a07063d9 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.GraycodeRouterTools()) } work := m.session.WorkMode() if work == "" { diff --git a/cmd/chat_turn.go b/cmd/chat_turn.go index 048e991c..1dbe0d04 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 graycode-router'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 5b74ae97..cce02727 100644 --- a/cmd/chat_welcome.go +++ b/cmd/chat_welcome.go @@ -372,7 +372,7 @@ func toolListSummary(registry *tool.Registry) string { if registry == nil { return "No tools enabled." } - tools := registry.EyrieTools() + tools := registry.GraycodeRouterTools() registered := len(registry.PrimaryTools()) if len(tools) == 0 { return "No tools enabled." @@ -421,7 +421,7 @@ func configCommandSummary(settings graycodeconfig.Settings) string { _ = settings providerName := displayConfigValue(graycodeconfig.ActiveProvider(context.Background())) modelName := displayConfigValue(graycodeconfig.ActiveModel(context.Background())) - return fmt.Sprintf(`Setup (eyrie) + return fmt.Sprintf(`Setup (graycode-router) /config → paste API key (OS keychain) + pick model /path → verify readiness in TUI @@ -432,7 +432,7 @@ Current: model: %s keys: %s -Model catalog and routing live in eyrie — graycode is the UI only.`, providerName, modelName, configuredKeyList()) +Model catalog and routing live in graycode-router — graycode is the UI only.`, providerName, modelName, configuredKeyList()) } func apiKeyConfigSummary() string { diff --git a/cmd/completions_test.go b/cmd/completions_test.go index 2dfb805f..ba26c6ef 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/graycode-router/catalog/registry" ) func TestNewCompletionGenerator(t *testing.T) { diff --git a/cmd/control_plane_hints.go b/cmd/control_plane_hints.go index ca00ee6a..68ad8c11 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().GraycodeRouterTools()) } 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 cfe65d9f..bce69d3d 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 GraycodeRouter's authoritative local preflight into GET /v1/ready. A session // factory only proves Graycode can attempt construction; readiness additionally - // requires Eyrie's provider state, catalog, credentials, and model selection. + // requires GraycodeRouter'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 GraycodeRouter'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 GraycodeRouter check. // -// This never performs a paid/live model call — Eyrie preflight only inspects +// This never performs a paid/live model call — GraycodeRouter 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, graycodeconfig.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, "GraycodeRouter 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("GraycodeRouter %s: %s", check.Name, detail) } } if ctx.Err() != nil { - return false, "Eyrie preflight timed out" + return false, "GraycodeRouter preflight timed out" } - return false, "Eyrie preflight is not ready" + return false, "GraycodeRouter preflight is not ready" } } diff --git a/cmd/daemon_ready_test.go b/cmd/daemon_ready_test.go index b3ac3cd9..66e45f11 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_FailedGraycodeRouterPreflight verifies a factory alone does not +// make the daemon ready when GraycodeRouter's authoritative preflight is incomplete. +func TestDaemonReadyProbe_FailedGraycodeRouterPreflight(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } probe := daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { return graycodeconfig.EnginePreflight{Ready: false} }) ok, reason := probe() if ok { - t.Fatal("failed Eyrie preflight must report not ready") + t.Fatal("failed GraycodeRouter preflight must report not ready") } if reason == "" { - t.Fatal("failed Eyrie preflight must include a reason") + t.Fatal("failed GraycodeRouter preflight must include a reason") } } -func TestDaemonReadyProbe_ReadyEyriePreflight(t *testing.T) { +func TestDaemonReadyProbe_ReadyGraycodeRouterPreflight(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } probe := daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { return graycodeconfig.EnginePreflight{Ready: true} }) ok, reason := probe() if !ok || reason != "" { - t.Fatalf("ready Eyrie preflight = (%v, %q), want (true, empty)", ok, reason) + t.Fatalf("ready GraycodeRouter 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 GraycodeRouter 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 746abb01..f47fef38 100644 --- a/cmd/diagnostics.go +++ b/cmd/diagnostics.go @@ -53,7 +53,7 @@ func doctorReport(settings graycodeconfig.Settings) string { // Ecosystem versions b.WriteString("\nEcosystem versions:\n") for _, component := range []struct{ directory, product string }{ - {directory: "eyrie", product: "Eyrie"}, + {directory: "graycode-router", product: "GraycodeRouter"}, {directory: "harrier", product: "Harrier"}, {directory: "shrike", product: "Shrike"}, {directory: "kestrel", product: "Kestrel"}, diff --git a/cmd/diagnostics_test.go b/cmd/diagnostics_test.go index 48d2ff40..7aca806f 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 · harrier · shrike)") { + if !strings.Contains(report, "Ecosystem (graycode-router · harrier · shrike)") { 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("GRAYCODE_ROUTER_CONFIG_DIR", t.TempDir()) graycodeconfig.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("GRAYCODE_ROUTER_CONFIG_DIR", t.TempDir()) graycodeconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) t.Cleanup(func() { diff --git a/cmd/dx.go b/cmd/dx.go index 02300ac2..ea87949a 100644 --- a/cmd/dx.go +++ b/cmd/dx.go @@ -69,7 +69,7 @@ func doctorOutput(settings graycodeconfig.Settings) string { b.WriteString(fmt.Sprintf(" Provider: %s\n", effectiveProvider)) b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(graycodeconfig.ActiveProvider(context.Background())))) - // Model configured (eyrie provider.json) + // Model configured (graycode-router provider.json) effectiveModel := strings.TrimSpace(graycodeconfig.ActiveModel(context.Background())) if effectiveModel == "" { effectiveModel = "(not configured)" diff --git a/cmd/ecosystem.go b/cmd/ecosystem.go index d3977373..3baab0c9 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, harrier, and shrike integration status", - Long: "Print the ecosystem panel summarizing LLM provider (eyrie), memory graph (harrier), and token pipeline (shrike). Same block as the top of graycode doctor.", + Short: "Show graycode-router, harrier, and shrike integration status", + Long: "Print the ecosystem panel summarizing LLM provider (graycode-router), memory graph (harrier), and token pipeline (shrike). Same block as the top of graycode 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 6603d664..f17cbe33 100644 --- a/cmd/ecosystem_test.go +++ b/cmd/ecosystem_test.go @@ -14,7 +14,7 @@ func TestEcosystemCmdRuns(t *testing.T) { provider = "auto" } out := graycodeconfig.FormatEcosystemPanel(t.Context(), provider, model) - if !strings.Contains(out, "Ecosystem (eyrie · harrier · shrike)") { + if !strings.Contains(out, "Ecosystem (graycode-router · harrier · shrike)") { 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 3b59cb4f..5827c2d9 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 := graycodeconfig.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 graycode-router 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 ee759769..44ad7a30 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", "graycode-router: 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 5d2d0721..61d0bde7 100644 --- a/cmd/eval.go +++ b/cmd/eval.go @@ -101,7 +101,7 @@ func runEvalLoop(cmd *cobra.Command, _ []string) error { settings := graycodeconfig.LoadGlobalSettings() ctx := context.Background() - gw, err := graycodeconfig.NewEyrieEngineForSettings(settings) + gw, err := graycodeconfig.NewGraycodeRouterEngineForSettings(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 460b2fb5..489ae143 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.GraycodeRouterTools() // 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.GraycodeRouterMessage{ {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 267f8321..0c06bbeb 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -165,7 +165,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 +// graycode-router 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 7c58496c..118168c5 100644 --- a/cmd/manpage.go +++ b/cmd/manpage.go @@ -39,7 +39,7 @@ func GenerateManPage() string { b.WriteString("\n") // Name - b.WriteString(".SH NAME\ngraycode \\- AI coding agent powered by eyrie\n") + b.WriteString(".SH NAME\ngraycode \\- AI coding agent powered by graycode-router\n") // Synopsis b.WriteString(".SH SYNOPSIS\n") @@ -48,7 +48,7 @@ func GenerateManPage() string { // Description b.WriteString(".SH DESCRIPTION\n") b.WriteString("graycode 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 graycode-router, 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 500ad44b..a458c3c9 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 graycode for models, pricing, and deployment routing. + Short: "Deployment-aware model catalog (via graycode-router)", + Long: `Manage the graycode-router model catalog used by graycode 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 ~/.graycode-router/model_catalog.json (override with GRAYCODE_ROUTER_MODEL_CATALOG_PATH). Graycode refreshes the catalog automatically on startup when the cache is missing, empty, or stale (disable with --no-auto-catalog-refresh or GRAYCODE_AUTO_REFRESH_CATALOG=0). Use 'graycode models refresh' for a manual refresh or full discover report.`, } @@ -30,7 +30,7 @@ Use 'graycode 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 (graycode-router remote + live provider APIs) into ~/.graycode-router/model_catalog.json", RunE: func(cmd *cobra.Command, _ []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -92,7 +92,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 graycode-router catalog cache (or live provider API)", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -138,7 +138,7 @@ var modelsListCmd = &cobra.Command{ } // modelListJSONEntry is Graycode's versioned command-output contract. Keep this -// separate from Eyrie's host-facing Model DTO so engine-only fields can evolve +// separate from GraycodeRouter's host-facing Model DTO so engine-only fields can evolve // without breaking users that consume `graycode 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 90cdf8a1..c0703feb 100644 --- a/cmd/ocg_live_test.go +++ b/cmd/ocg_live_test.go @@ -15,10 +15,10 @@ import ( "testing" "time" - eyriecfg "github.com/GrayCodeAI/eyrie/config" - "github.com/GrayCodeAI/eyrie/credentials" - "github.com/GrayCodeAI/eyrie/setup" "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" + graycoderoutercfg "github.com/GrayCodeAI/graycode-router/config" + "github.com/GrayCodeAI/graycode-router/credentials" + "github.com/GrayCodeAI/graycode-router/setup" ) func TestLiveOpenCodeGoMiniMaxM3FullGraycodePath(t *testing.T) { @@ -38,9 +38,9 @@ func TestLiveOpenCodeGoMiniMaxM3FullGraycodePath(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.GraycodeRouterTools()), len(systemPrompt)) - adapter := setup.ConfiguredDeploymentAdapters(eyriecfg.LoadProviderConfig(""))["opencodego"] + adapter := setup.ConfiguredDeploymentAdapters(graycoderoutercfg.LoadProviderConfig(""))["opencodego"] t.Logf("adapter_type=%T", adapter.Provider) sess := newGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) diff --git a/cmd/options_welcome_test.go b/cmd/options_welcome_test.go index ed959c14..4db87cc3 100644 --- a/cmd/options_welcome_test.go +++ b/cmd/options_welcome_test.go @@ -18,7 +18,7 @@ func isolateCredentialHome(t *testing.T) { _ = os.MkdirAll(graycodeDir, 0o700) t.Setenv("HOME", home) t.Setenv("GRAYCODE_CONFIG_DIR", graycodeDir) - t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", filepath.Join(home, "graycode-router")) } func TestEffectiveModelAndProvider_ClearsWithoutCredentials(t *testing.T) { diff --git a/cmd/path.go b/cmd/path.go index 5b8bb79b..608f569d 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -19,7 +19,7 @@ var pathCmd = &cobra.Command{ Short: "Developer path readiness (setup, security, sandbox, ecosystem)", Long: `Check whether graycode is configured on the developer path: API keys in OS secret store, model selected, no secrets on disk, -mandatory Docker isolation, and eyrie/harrier/shrike integration. +mandatory Docker isolation, and graycode-router/harrier/shrike integration. Built for individual developers first — teams and enterprise later. diff --git a/cmd/preflight_test.go b/cmd/preflight_test.go index 37cee90d..f6df23fd 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/graycode-router import path. type preflightReportShape struct { Ready bool `json:"ready"` Checks []preflightCheckShape `json:"checks"` diff --git a/cmd/review_analyze.go b/cmd/review_analyze.go index 0d0a5706..513f02d2 100644 --- a/cmd/review_analyze.go +++ b/cmd/review_analyze.go @@ -124,7 +124,7 @@ func runReviewAnalyze(_ *cobra.Command, args []string) error { return nil } - // Build the Kestrel bridge through Graycode's Eyrie engine boundary. + // Build the Kestrel bridge through Graycode's GraycodeRouter engine boundary. ctx := context.Background() selection := graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{ ProviderOverride: strings.TrimSpace(provider), diff --git a/cmd/review_run.go b/cmd/review_run.go index 2b7e0700..8bfd30b4 100644 --- a/cmd/review_run.go +++ b/cmd/review_run.go @@ -96,7 +96,7 @@ func runReviewRun(_ *cobra.Command, args []string) error { return nil } - // Build the Kestrel bridge through Graycode's Eyrie engine boundary. + // Build the Kestrel bridge through Graycode's GraycodeRouter engine boundary. ctx := context.Background() selection := graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{ ProviderOverride: strings.TrimSpace(provider), diff --git a/cmd/root.go b/cmd/root.go index a7ad6b1e..2a6aaebb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -92,10 +92,10 @@ func registeredProviderCount() int { var rootCmd = &cobra.Command{ Use: "graycode [prompt]", - Short: "AI coding agent powered by eyrie", + Short: "AI coding agent powered by graycode-router", Long: fmt.Sprintf(`graycode 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 graycode-router, 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. @@ -148,7 +148,7 @@ Run graycode and use /config to set up your first provider.`, registeredProvider if printMode || promptFlag != "" || inputFormat == "stream-json" || replFlag || watchFlag { // Credential migration is deferred until a path that actually // uses credentials: `graycode path`, `graycode version`, auto-skill and - // other cold commands no longer construct the eyrie engine + // other cold commands no longer construct the graycode-router engine // (M17 — was ~1.8s on every root command). logMigrateProviderSecretsError(logger.Default(), graycodeconfig.MigrateProviderSecrets()) if promptFlag == "" && !replFlag && !watchFlag { @@ -212,13 +212,13 @@ Run graycode and use /config to set up your first provider.`, registeredProvider return fmt.Errorf("cannot start CLI: folder not trusted (%s)\nProject-scoped hooks, MCP servers, and custom specialists are blocked.\nRun 'graycode trust add' to trust this folder before starting graycode", tr.Path) } - // Launch TUI — use /config to set API keys; eyrie supplies providers and models + // Launch TUI — use /config to set API keys; graycode-router 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 graycode-router 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", or "stream-json"`) @@ -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 graycode-router 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 68cba775..d1268b57 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(graycodeconfig.ActiveGateway(ctx)), strings.TrimSpace(graycodeconfig.ActiveModel(ctx)) } -// syncSessionFromPersistedSelection copies explicit eyrie provider.json +// syncSessionFromPersistedSelection copies explicit graycode-router provider.json // selection into the live session when the session fields are empty. // It intentionally avoids runtime defaults so Graycode can preserve the // "gateway selected, model still missing" setup state. diff --git a/cmd/tips.go b/cmd/tips.go index e5454c8b..4f0765b2 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; /harrier for graph memory; /ecosystem for eyrie/harrier/shrike status.", Category: "context"}, + {ID: "slash-memory", Text: "Use /memory for AGENTS.md; /harrier for graph memory; /ecosystem for graycode-router/harrier/shrike 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/DEVELOPER-PATH.md b/docs/DEVELOPER-PATH.md index d34571b2..8ce518ac 100644 --- a/docs/DEVELOPER-PATH.md +++ b/docs/DEVELOPER-PATH.md @@ -8,8 +8,8 @@ For Graycode, the developer path is the minimum local setup required to chat, ed - A provider credential stored in the OS secret store - A model selected in Graycode 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 graycode-router +- No plaintext API keys left in GraycodeRouter's configured `provider.json` or legacy env files - Safe defaults for Bash execution and filesystem access - Optional but healthy ecosystem integrations like harrier memory @@ -63,7 +63,7 @@ validation or `graycode models list --live`. ### 3. Select a model -Pick a model in `/config`. Graycode stores the selected model in settings and uses eyrie for provider routing and catalog resolution. +Pick a model in `/config`. Graycode stores the selected model in settings and uses graycode-router for provider routing and catalog resolution. If the catalog is missing or empty: @@ -75,11 +75,11 @@ graycode models refresh `graycode path` treats these as important security conditions: -- Eyrie's resolved `provider.json` must not contain secret fields +- GraycodeRouter's resolved `provider.json` must not contain secret fields - legacy `~/.graycode/env` or `~/.graycode/.env` files should be migrated away - sensitive files like provider config and SSH paths should be blocked from agent reads -Eyrie resolves provider state from `EYRIE_CONFIG_DIR` first, then +GraycodeRouter resolves provider state from `GRAYCODE_ROUTER_CONFIG_DIR` first, then `GRAYCODE_CONFIG_DIR` for compatibility, then the platform user-config directory. Graycode protects that resolved path even when it is customized or symlinked. @@ -105,7 +105,7 @@ graycode path --strict `graycode path` also verifies the core support layer behind Graycode: -- `eyrie` for provider routing and local preflight readiness +- `graycode-router` for provider routing and local preflight readiness - `shrike` for token estimation and compression - `harrier` for optional persistent memory diff --git a/docs/DYNAMIC-MODELS.md b/docs/DYNAMIC-MODELS.md index 4a131d15..25109894 100644 --- a/docs/DYNAMIC-MODELS.md +++ b/docs/DYNAMIC-MODELS.md @@ -2,9 +2,9 @@ ## Ownership and flow -Graycode is the product face; Eyrie is the provider engine. Graycode owns the CLI/TUI, +Graycode is the product face; GraycodeRouter is the provider engine. Graycode owns the CLI/TUI, model-picker presentation, user intent, and output compatibility. The -`eyrie/engine` facade alone owns provider registry details, credentials, +`graycode-router/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 + graycode-router/engine.Engine catalog + credentials + routing | stable host DTOs and methods @@ -21,14 +21,14 @@ provider APIs / remote catalog / local cache /config | models | conversation ``` -Production Graycode packages must not import Eyrie packages below -`github.com/GrayCodeAI/eyrie/engine`. Shell and AST guards enforce a +Production Graycode packages must not import GraycodeRouter packages below +`github.com/GrayCodeAI/graycode-router/engine`. Shell and AST guards enforce a zero-exception boundary. ## Graycode composition boundary `internal/config` is Graycode's control-plane composition root. It creates an -Eyrie engine and projects engine models into Graycode UI and command contracts: +GraycodeRouter engine and projects engine models into Graycode 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 Graycode's `internal/engine` adapter. The adapter translates Graycode-owned message, tool, usage, and stream DTOs to the -Eyrie engine facade; conversation history, WAL, resume, approvals, and tool +GraycodeRouter engine facade; conversation history, WAL, resume, approvals, and tool execution remain Graycode-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 GraycodeRouter engine combines its provider registry, provider-scoped live +discovery, and the cache at `~/.graycode-router/model_catalog.json` by default. Override +the cache with `GRAYCODE_ROUTER_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 `graycode models list --json` is a Graycode-owned compatibility contract, not a direct -serialization of Eyrie's evolving `engine.Model` DTO. Its stable fields are: +serialization of GraycodeRouter'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 Graycode compatibility row instead of `null`. Graycode 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 GraycodeRouter 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 GraycodeRouter's engine facade. +2. Add GraycodeRouter tests for cache and live discovery, credential status, selection, and generation/streaming. -3. Commit and verify standalone Eyrie. +3. Commit and verify standalone GraycodeRouter. 4. Advance Graycode's `../graycode-router` sibling checkout to that exact commit, then update - Graycode's module version when the Eyrie revision is published. + Graycode's module version when the GraycodeRouter revision is published. 5. Verify both the workspace (`go.work`) and published-module (`GOWORK=off`) build modes. @@ -114,6 +114,6 @@ Graycode-owned presentation field—not for provider-specific mechanics. graycode models refresh graycode models status graycode preflight -make eyrie-engine-guard +make graycode-router-engine-guard go test ./cmd ./internal/config ./internal/engine -count=1 ``` diff --git a/docs/ECOSYSTEM-CONFIG.md b/docs/ECOSYSTEM-CONFIG.md index 722d6232..4ac2910b 100644 --- a/docs/ECOSYSTEM-CONFIG.md +++ b/docs/ECOSYSTEM-CONFIG.md @@ -1,7 +1,7 @@ # graycode-eco Unified Config-as-Code Status: Draft / shared spec -Applies to: graycode, eyrie, harrier, shrike, swift +Applies to: graycode, graycode-router, harrier, shrike, swift This document specifies a **single, unified configuration schema** for the graycode-eco ecosystem: one declarative file (`graycode-eco.yaml`, with an equivalent @@ -11,7 +11,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 (graycode: `config.json` + `GRAYCODE_*`/`GRAYCODE_*` env; eyrie: +flags, and config files (graycode: `config.json` + `GRAYCODE_*`/`GRAYCODE_*` env; graycode-router: 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 @@ -27,7 +27,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 graycode + eyrie). A repo only reads its sections. + `model`/`providers` shared by graycode + graycode-router). 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`.) @@ -49,7 +49,7 @@ runtime precedence above): ```yaml version: 1 -# ─── Shared: model + providers (graycode + eyrie) ─────────────────────────────── +# ─── Shared: model + providers (graycode + graycode-router) ─────────────────────────────── model: default: anthropic/claude-sonnet-4-5 # provider/model the agent uses small_fast: anthropic/claude-haiku # cheap model for trivial steps @@ -66,16 +66,16 @@ providers: api_key_env: GEMINI_API_KEY model: gemini-2.0-flash -# ─── eyrie: gateway / runtime ─────────────────────────────────────────────── +# ─── graycode-router: gateway / runtime ─────────────────────────────────────────────── gateway: - base_url: http://localhost:8080 # eyrie endpoint graycode talks to - api_key_env: EYRIE_API_KEY + base_url: http://localhost:8080 # graycode-router endpoint graycode talks to + api_key_env: GRAYCODE_ROUTER_API_KEY allow_insecure_public_api: false - deployment_routing: "" # EYRIE_DEPLOYMENT_ROUTING + deployment_routing: "" # GRAYCODE_ROUTER_DEPLOYMENT_ROUTING model_catalog: - path_env: EYRIE_MODEL_CATALOG_PATH - url_env: EYRIE_MODEL_CATALOG_URL - refresh: EYRIE_MODEL_CATALOG_REFRESH + path_env: GRAYCODE_ROUTER_MODEL_CATALOG_PATH + url_env: GRAYCODE_ROUTER_MODEL_CATALOG_URL + refresh: GRAYCODE_ROUTER_MODEL_CATALOG_REFRESH # ─── harrier: memory ─────────────────────────────────────────────────────────── memory: @@ -129,30 +129,30 @@ telemetry: The authoritative mapping. "Mechanism today" is what already implements the setting; the unified key is rendered down to it. -### Shared: model / providers (graycode + eyrie) +### Shared: model / providers (graycode + graycode-router) | Unified key | Repo | Mechanism today | |---------------------------------|-------------|--------------------------------------------------| | `model.default` | graycode | `GRAYCODE_MODEL` env / `config.json` | | `model.small_fast` | graycode | `GRAYCODE_SMALL_FAST_MODEL` env | -| `providers[].api_key_env` | eyrie/graycode | `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` | graycode-router/graycode | `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` | graycode-router | `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` / `OPENAI_API_BASE`, `OLLAMA_BASE_URL`, `FIREWORKS_BASE_URL` | +| `providers[].model` (openai) | graycode-router | `OPENAI_MODEL` env | +| `providers[].model` (gemini) | graycode-router | `GEMINI_MODEL` env | +| `providers[].model` (anthropic) | graycode-router | `ANTHROPIC_MODEL` env | -### eyrie: gateway / runtime +### graycode-router: gateway / runtime -| Unified key | Mechanism today (eyrie) | +| Unified key | Mechanism today (graycode-router) | |--------------------------------------|--------------------------------------| -| `gateway.base_url` | `EYRIE_BASE_URL` (graycode→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 `GRAYCODE_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` / `GRAYCODE_AUTO_REFRESH_CATALOG` / `GRAYCODE_CATALOG_REFRESH_ALWAYS` | -| `gateway` config dir | `GRAYCODE_CONFIG_DIR` (default `~/.eyrie`) | +| `gateway.base_url` | `GRAYCODE_ROUTER_BASE_URL` (graycode→graycode-router link) | +| `gateway.api_key_env` | `GRAYCODE_ROUTER_API_KEY` | +| `gateway.allow_insecure_public_api` | `GRAYCODE_ROUTER_ALLOW_INSECURE_PUBLIC_API` | +| `gateway.deployment_routing` | `GRAYCODE_ROUTER_DEPLOYMENT_ROUTING` (also `GRAYCODE_DEPLOYMENT_ROUTING`) | +| `gateway.model_catalog.path_env` | `GRAYCODE_ROUTER_MODEL_CATALOG_PATH` | +| `gateway.model_catalog.url_env` | `GRAYCODE_ROUTER_MODEL_CATALOG_URL` | +| `gateway.model_catalog.refresh` | `GRAYCODE_ROUTER_MODEL_CATALOG_REFRESH` / `GRAYCODE_AUTO_REFRESH_CATALOG` / `GRAYCODE_CATALOG_REFRESH_ALWAYS` | +| `gateway` config dir | `GRAYCODE_CONFIG_DIR` (default `~/.graycode-router`) | ### harrier: memory @@ -208,7 +208,7 @@ few env vars. The unified file is designed to be **resolved** into the existing mechanisms: -- **env-based repos** (graycode, eyrie, shrike, swift): export the mapped env var for +- **env-based repos** (graycode, graycode-router, 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 @@ -230,7 +230,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": "GRAYCODE_ROUTER_API_KEY" }, "memory": { "addr": "127.0.0.1:3456", "data_dir": "~/.harrier" }, "compression": { "enabled": true, "db_path": "~/.shrike/usage.db" }, "swift": { "telemetry_optout": false }, @@ -242,5 +242,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 graycode-router's `internal/observability` package as the reference implementation. diff --git a/docs/ECOSYSTEM-WIRING.md b/docs/ECOSYSTEM-WIRING.md index c055b666..22097bc7 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] --> Graycode[Graycode composition root] - Graycode --> Eyrie[Eyrie facade] + Graycode --> GraycodeRouter[GraycodeRouter facade] Graycode --> Harrier[Harrier / Harrier facade] Graycode --> Shrike[Shrike / Shrike facade] Graycode --> Swift[Swift / Swift facade] Graycode --> Kestrel[Kestrel / Kestrel facade] Graycode --> Merlin[Merlin / Merlin facade] Graycode --> Eagle[Eagle neutral contracts] - Eyrie --> Eagle + GraycodeRouter --> Eagle Harrier --> Eagle Shrike --> Eagle Swift --> Eagle @@ -88,7 +88,7 @@ flowchart TB | Boundary | Owner | Rule | |---|---|---| | Product orchestration | Graycode | Engines never orchestrate Graycode or one another. | -| Provider runtime | Eyrie | Graycode imports its supported `engine` facade. | +| Provider runtime | GraycodeRouter | Graycode imports its supported `engine` facade. | | Shared data contracts | Eagle | Neutral types only; no product behavior. | | Portable local graph | Source engine/Graycode | Emit bounded `graycode.graph/v1` facts without raw secrets or prompts. | | Daemon HTTP API | Graycode | `graycode/api/openapi.yaml` is authoritative. | @@ -137,15 +137,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 Graycode against the older Eyrie +The compatible Eagle-migrated GraycodeRouter source is currently ahead of GraycodeRouter's +published `origin/main`. Do not merge or release Graycode against the older GraycodeRouter pseudo-version: it still exposes the retired `graycode-core-contracts` types and is not type-compatible with Graycode's Eagle boundary. -1. Merge and publish the Eyrie ecosystem-wiring branch first. +1. Merge and publish the GraycodeRouter 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 Graycode's Eyrie requirement, run `GOWORK=off go mod tidy`, and remove any + `go list -m github.com/GrayCodeAI/graycode-router@`. +3. Update Graycode's GraycodeRouter requirement, run `GOWORK=off go mod tidy`, and remove any transition excludes no longer required by the published engine graphs. 4. Require Graycode's `public-modules` and `release-parity` CI jobs to pass before merging or tagging Graycode. diff --git a/docs/IMPLEMENTATION-ROADMAP.md b/docs/IMPLEMENTATION-ROADMAP.md index 5dbb0b75..6f81cdee 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 (graycode-router) **Effort:** Medium (1-2 weeks) **Priority:** LOW @@ -562,7 +562,7 @@ func main() { --- -### 10. Add Completion Endpoint to eyrie +### 10. Add Completion Endpoint to graycode-router **Effort:** Medium (1 week) **Priority:** LOW @@ -628,7 +628,7 @@ Current architecture status: see `docs/architecture/graycode-architecture-baseli --- -### **eyrie** (LLM Runtime) +### **graycode-router** (LLM Runtime) | Phase | Improvement | Effort | Impact | Status | |-------|--------------|--------|--------|--------| diff --git a/docs/OTEL-CONVENTIONS.md b/docs/OTEL-CONVENTIONS.md index 5fd41e99..418f27ce 100644 --- a/docs/OTEL-CONVENTIONS.md +++ b/docs/OTEL-CONVENTIONS.md @@ -1,7 +1,7 @@ # graycode-eco OpenTelemetry Semantic Conventions for AI Agent Spans Status: Draft / shared spec -Applies to: graycode, eyrie, harrier, shrike, swift +Applies to: graycode, graycode-router, harrier, shrike, swift This document defines the **ecosystem-wide** OpenTelemetry (OTel) semantic conventions that every graycode-eco repo should follow when emitting spans for AI @@ -19,23 +19,23 @@ concerns that the GenAI spec does not yet standardize. ## Reference ownership -Eyrie owns provider-call instrumentation behind its `eyrie/engine` facade. +GraycodeRouter owns provider-call instrumentation behind its `graycode-router/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; Graycode must not import or compose it +decorator is an GraycodeRouter implementation detail; Graycode must not import or compose it directly. -- `eyrie/internal/observability/observability.go` provides a stdlib-only, +- `graycode-router/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 +- `graycode-router/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 Graycode, propagate swift context through the Engine call and -use the attribute keys in this document. Eyrie wraps provider operations; +use the attribute keys in this document. GraycodeRouter wraps provider operations; Graycode wraps product turns and tools. Harrier, Shrike, and Swift instrument only their own operations. @@ -52,7 +52,7 @@ own operations. 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 +graycode-router'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. @@ -60,10 +60,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 `graycode-router/internal/observability/genai_semconv.go` (constant name in parentheses). -| Attribute key | Go const (`eyrie` observability) | Type | Required | Meaning | +| Attribute key | Go const (`graycode-router` 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`. | @@ -79,13 +79,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. graycode-router'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 + graycode-router'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 graycode-router'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. @@ -94,7 +94,7 @@ parentheses). ## Legacy attribute mapping -eyrie's older stdlib constants (`AttrLLMProvider`, `AttrLLMModel`, +graycode-router'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: @@ -111,12 +111,12 @@ Mapping: ## Per-repo guidance -- **eyrie** — owns provider/model/usage spans behind `eyrie/engine`; align +- **graycode-router** — owns provider/model/usage spans behind `graycode-router/engine`; align attribute keys to `gen_ai.*` over time. - **graycode** — daemon/orchestrator. Already has OTel hooks (`GRAYCODE_ENABLE_TELEMETRY`, `GRAYCODE_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. + graycode-router via context so provider spans inherit the same IDs. - **harrier** — memory service. Add `embeddings` spans with `gen_ai.system` + `gen_ai.request.model` from its embeddings config; tag retrieval spans with `session.id`. @@ -129,5 +129,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 graycode-router and update this table in the same change. diff --git a/docs/SECURITY-DEVELOPER.md b/docs/SECURITY-DEVELOPER.md index 6f1a0c7a..05a4e336 100644 --- a/docs/SECURITY-DEVELOPER.md +++ b/docs/SECURITY-DEVELOPER.md @@ -1,13 +1,13 @@ # Graycode developer security model -This document describes how graycode 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 graycode and graycode-router 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). - Graycode 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. -- Graycode talks to eyrie without putting keys in JSON or chat messages. +- GraycodeRouter's `provider.json` holds routing and deployment metadata only — never secrets on disk. +- Graycode talks to graycode-router without putting keys in JSON or chat messages. - Agent commands run inside mandatory Docker isolation; file tools cannot read credential paths. @@ -15,9 +15,9 @@ This document describes how graycode and eyrie handle API keys and agent isolati | Write | Read | Remove | |-------|------|--------| -| `/config` paste flow → `eyrie/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `graycode credentials remove` | +| `/config` paste flow → `graycode-router/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `graycode credentials remove` | -On startup, Graycode asks the Eyrie engine facade to migrate legacy +On startup, Graycode asks the GraycodeRouter engine facade to migrate legacy `~/.graycode/env` / `~/.graycode/.env` values into the secret store and delete those files. It also imports recognized historical secret fields from `provider.json` before atomically rewriting that file with metadata only. A @@ -32,10 +32,10 @@ Check status: `graycode credentials status`, `graycode path`, or `graycode prefl User pastes API key in /config | v -Graycode /config -> Eyrie engine credential service (OS secret store) +Graycode /config -> GraycodeRouter engine credential service (OS secret store) | v -Eyrie engine discover/apply (credentials from store, not JSON body) +GraycodeRouter engine discover/apply (credentials from store, not JSON body) | v SetupUI JSON (display_name + canonical_id per model) @@ -46,13 +46,13 @@ User picks model -> settings.json (canonical id only) Remove a stored key: `/config key remove` (interactive picker). -## Graycode to Eyrie +## Graycode to GraycodeRouter -- **Control plane**: Graycode calls only `eyrie/engine`; no lower Eyrie package is a +- **Control plane**: Graycode calls only `graycode-router/engine`; no lower GraycodeRouter 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**: Graycode sends model intent, messages, and tool definitions; Eyrie +- **Chat**: Graycode sends model intent, messages, and tool definitions; GraycodeRouter resolves the gateway and reads secrets internally. ## Agent isolation @@ -80,7 +80,7 @@ required for users, and neither provisioning path enables host execution. ### Blocked for agents -- **Read** tool: legacy Graycode env files, Eyrie's configured `provider.json`, +- **Read** tool: legacy Graycode env files, GraycodeRouter's configured `provider.json`, `~/.ssh/*`, etc. - **Bash**: `printenv`, `env`, reading graycode env paths, echoing `*_API_KEY` variables. @@ -88,22 +88,22 @@ required for users, and neither provisioning path enables host execution. - **Legacy env files**: startup migration imports `~/.graycode/env` and `~/.graycode/.env` into the OS secret store, then deletes the plaintext files. -- **provider.json secrets**: Eyrie transactionally imports recognized top-level +- **provider.json secrets**: GraycodeRouter transactionally imports recognized top-level and deployment credentials, atomically writes sanitized metadata, and uses a temporary `provider.json.pre-secret-migrate.bak` only during the transaction. -- **All subsequent writes**: the Eyrie engine applies the same sanitization and +- **All subsequent writes**: the GraycodeRouter engine applies the same sanitization and atomic-write path, so migrated secret fields cannot be reintroduced. ## Provider state path -Eyrie owns the provider-state path. Resolution order is: +GraycodeRouter owns the provider-state path. Resolution order is: -1. `EYRIE_CONFIG_DIR/provider.json` +1. `GRAYCODE_ROUTER_CONFIG_DIR/provider.json` 2. `GRAYCODE_CONFIG_DIR/provider.json` (compatibility fallback) 3. the platform user-config directory under `graycode/provider.json` Graycode'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 `GRAYCODE_ROUTER_CONFIG_DIR`; protection is not limited to the historical default provider-state location. ## Environment variables @@ -113,13 +113,13 @@ Non-secret overrides only (graycode does not load provider API keys from env): | Variable | Meaning | |----------|---------| | `GRAYCODE_CONFIG_DIR` | Override graycode config directory | -| `EYRIE_CONFIG_DIR` | Override Eyrie provider-state directory; takes precedence for `provider.json` | +| `GRAYCODE_ROUTER_CONFIG_DIR` | Override GraycodeRouter 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 -- Graycode: `internal/config/eyrie_engine.go`, `internal/tool/safety.go`, +- Graycode: `internal/config/graycode-router_engine.go`, `internal/tool/safety.go`, `internal/storage/paths.go`, `cmd/credentials.go` -- Eyrie public host boundary: `engine/` +- GraycodeRouter 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 bcdfec5b..52cdfa01 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -90,7 +90,7 @@ and finding contracts now live in `eagle/types`. | Service | Role | Connection | |---------|------|------------| -| bird **eyrie** | LLM provider runtime | `:8080` — all LLM calls routed here | +| bird **graycode-router** | LLM provider runtime | `:8080` — all LLM calls routed here | | brain **harrier** | Persistent memory | `:3456` — session context, recall | | eye **kestrel** | Code review | Library — diff-based review | | search **merlin** | Security audit | Library — website scanning | @@ -107,7 +107,7 @@ and finding contracts now live in `eagle/types`. All three SDKs share types from **eagle** and consume the daemon REST API (:4590). -> lightbulb **graycode never talks to LLM APIs directly** — all calls go through eyrie. +> lightbulb **graycode never talks to LLM APIs directly** — all calls go through graycode-router. --- diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 26069b43..aa2fa6ad 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -34,7 +34,7 @@ Final target shape: - `graycode` is the orchestrator and only primary product surface - six peer support engines sit below Graycode: - `eyrie`, `harrier`, `shrike`, `swift`, `kestrel`, `merlin` + `graycode-router`, `harrier`, `shrike`, `swift`, `kestrel`, `merlin` - `eagle` and `falcon` sit below those engines as shared foundations - SDKs and community skills sit above Graycode as consumers of Graycode public surfaces - `graycode-platform` stays outside the Graycode runtime module graph and exposes 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 c36460f8..fb0295c4 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. Graycode is a multi-repo Go platform: product (`graycode`) plus peer -engines (`eyrie`, `harrier`, `shrike`, `swift`, `kestrel`, `merlin`), foundation +engines (`graycode-router`, `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. **Graycode remains multi-repo.** Map Grok capabilities onto existing owners: - product surface → `graycode` - shared DTOs → `eagle` - - LLM routing/stream → `eyrie` + - LLM routing/stream → `graycode-router` - 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 8eedf121..642cfa45 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 · harrier · shrike · swift · kestrel · merlin + graycode-router · harrier · shrike · swift · kestrel · merlin ``` ## Pieces @@ -53,7 +53,7 @@ Single entry for subagents + background tasks: ### 3. 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 `GraycodeRouterTools` - `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 659c0f4c..7f963298 100644 --- a/docs/architecture/ecosystem-architecture.md +++ b/docs/architecture/ecosystem-architecture.md @@ -11,7 +11,7 @@ repository, product, or runtime module. | `graycode` | Main CLI, daemon, orchestration, policy, and public API | Main product and integration root | | `eagle` | Neutral cross-repository contracts | Go dependency of Graycode and selected engines | | `falcon` | Shared MCP transport and handler scaffolding | Go dependency of MCP-serving engines | -| `eyrie` | Provider credentials, catalog, routing, transport, streaming | Graycode consumes `eyrie/engine` | +| `graycode-router` | Provider credentials, catalog, routing, transport, streaming | Graycode consumes `graycode-router/engine` | | `harrier` | Harrier memory and retrieval engine | Graycode integration | | `shrike` | Shrike token budgeting and context compression | Graycode integration | | `swift` | Swift provenance and replay engine | Graycode consumes `swift/cli` | @@ -32,7 +32,7 @@ Shrike, `swift` is Swift, `kestrel` is Kestrel, and `merlin` is Merlin. ```text sparrow / robin / wren ── HTTP/OpenAPI ──> graycode <── skill surface ── starling │ - ├── eyrie/engine + ├── graycode-router/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -78,7 +78,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 GraycodeRouter revision, remove its transitional legacy contract dependency from the standalone graph, and decide whether Graycode'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 164d8aa1..ad74ce42 100644 --- a/docs/architecture/ecosystem-design.md +++ b/docs/architecture/ecosystem-design.md @@ -18,7 +18,7 @@ graycode-eco/ ├── graycode # product, CLI, daemon, orchestration, policy ├── eagle # shared neutral contracts ├── falcon # shared MCP transport/handler kit -├── eyrie # provider runtime (product label: Eyrie) +├── graycode-router # provider runtime (product label: GraycodeRouter) ├── harrier # memory engine (product label: Harrier) ├── shrike # token/context engine (product label: Shrike) ├── swift # provenance engine (product label: Swift) @@ -37,7 +37,7 @@ graycode-eco/ ```text sparrow / robin / wren ── Graycode daemon API ──> graycode <── skill API ── starling │ - ├── eyrie/engine + ├── graycode-router/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -45,7 +45,7 @@ sparrow / robin / wren ── Graycode daemon API ──> graycode <── skill ├── merlin (Merlin) └── eagle -eyrie / harrier / shrike / swift / kestrel / merlin ──> eagle (as needed) +graycode-router / harrier / shrike / swift / kestrel / merlin ──> eagle (as needed) harrier / kestrel / merlin ──> falcon ──> mark3labs/mcp-go ``` @@ -69,7 +69,7 @@ CI must test both workspace mode and `GOWORK=off` module mode. ```text Graycode local runtime - ├── Eyrie provider generation + ├── GraycodeRouter provider generation ├── Harrier memory and retrieval ├── Shrike token budgeting/compression ├── Swift swift/provenance @@ -129,7 +129,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 Graycode's published -Eyrie pin still has a transitive `graycode-core-contracts` dependency. That is a +The current local GraycodeRouter checkout is Eagle-compatible, but Graycode's published +GraycodeRouter pin still has a transitive `graycode-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 eb82dde9..636cebe3 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 +GraycodeRouter Harrier Shrike models memory tokens | | | +----------+----------+ @@ -65,7 +65,7 @@ bash owl/scripts/sync-ecosystem.sh - Keep Eagle imports in Graycode and engines limited to shared contracts. - Keep Falcon imports limited to MCP-serving components. -- Keep Graycode's Eyrie integration behind `eyrie/engine`. +- Keep Graycode's GraycodeRouter integration behind `graycode-router/engine`. - Keep Graycode'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 Graycode integration @@ -76,21 +76,21 @@ Acceptance checks: ```text bash graycode/scripts/check-ecosystem-boundaries.sh bash graycode/scripts/check-support-repo-coupling.sh -bash graycode/scripts/check-eyrie-engine-boundary.sh -bash graycode/scripts/check-eyrie-client-imports.sh +bash graycode/scripts/check-graycode-router-engine-boundary.sh +bash graycode/scripts/check-graycode-router-client-imports.sh bash graycode/scripts/check-shared-types-imports.sh bash graycode/scripts/check-no-replace-directives.sh ``` ## Phase 3: Published-module cutover — pending external release -The local Eyrie source already uses Eagle contracts. Graycode's current published -Eyrie pseudo-version still declares the retired compatibility contract -transitively, so this phase requires publishing the compatible Eyrie revision. +The local GraycodeRouter source already uses Eagle contracts. Graycode's current published +GraycodeRouter pseudo-version still declares the retired compatibility contract +transitively, so this phase requires publishing the compatible GraycodeRouter revision. -1. Publish the current Eagle-compatible Eyrie revision. +1. Publish the current Eagle-compatible GraycodeRouter revision. 2. Resolve its canonical Go pseudo-version from the published commit. -3. Update Graycode's Eyrie requirement to that version. +3. Update Graycode's GraycodeRouter requirement to that version. 4. Run `GOWORK=off go mod tidy` in Graycode. 5. Confirm the retired compatibility module is absent from Graycode's module graph. 6. Remove any obsolete transition excludes. @@ -149,7 +149,7 @@ The architecture is complete when: - all 15 repositories match the canonical manifest; - Graycode 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 GraycodeRouter revision no longer brings the retired compatibility contract into Graycode'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 1f13520f..a3f8e00e 100644 --- a/docs/architecture/execution-graph.md +++ b/docs/architecture/execution-graph.md @@ -16,7 +16,7 @@ runtime owners Harrier retrieval + Graycode code index Merlin + Kestrel quality projections Shrike runtime projection - Eyrie operations projection + GraycodeRouter operations projection Swift checkpoints | v @@ -48,7 +48,7 @@ eagle/graph | Context compression operations | Shrike | Compression statistics are automatically captured during persisted-session context compaction | | Response redaction quality | Shrike | Aggregate Shrike-only secret matches are automatically captured after persisted-session response redaction | | Token usage and budget decisions | Shrike | Deduplicated provider usage updates Shrike's 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 | GraycodeRouter | Route and normalized usage operations are automatically captured after each persisted-session model turn | | Observation history | `internal/graphjournal` | Append-only, privacy-safe runtime evidence | | Swift sessions and checkpoints | Swift | Exact start-time metadata correlation plus stable external references | @@ -108,7 +108,7 @@ Nodes: facts; - `shrike/compression/`, `shrike/usage/`, `shrike/budget/`, and `shrike/redaction/` — operations, policy, and quality facts; -- `eyrie/route/` and `eyrie/generation/` — model route and +- `graycode-router/route/` and `graycode-router/generation/` — model route and normalized generation operations; - `swift/checkpoint/` — externally owned Swift checkpoint reference. @@ -234,7 +234,7 @@ The central tool-execution seam automatically records: - Shrike-only secret matches removed from persisted-session responses. - Shrike usage summaries and budget decisions emitted from the central, deduplicated provider-accounting seam. -- Eyrie model route and usage reported for persisted-session turns. +- GraycodeRouter model route and usage reported for persisted-session turns. Mission execution now also persists a portable graph artifact for local mission runs. Plain `graycode mission` runs emit mission and feature execution nodes; the diff --git a/docs/architecture/graycode-architecture-baseline.md b/docs/architecture/graycode-architecture-baseline.md index 6d2e5c7d..42ee1171 100644 --- a/docs/architecture/graycode-architecture-baseline.md +++ b/docs/architecture/graycode-architecture-baseline.md @@ -33,7 +33,7 @@ users / SDKs / skills / daemon clients v graycode / | \ - eyrie harrier shrike + graycode-router harrier shrike swift kestrel merlin | v @@ -44,8 +44,8 @@ The graph is intentionally directional: - Graycode 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`. +- GraycodeRouter owns provider protocols, routing, credentials, catalogs, and provider + execution behind `graycode-router/engine`. - Harrier, Shrike, Swift, Kestrel, and Merlin are support engines and must not import Graycode internals or one another. - Core contracts contain stable cross-repository vocabulary and DTOs, not @@ -56,14 +56,14 @@ The graph is intentionally directional: ### Complete or enforced -- Graycode production code uses Eyrie through the `eyrie/engine` facade. +- Graycode production code uses GraycodeRouter through the `graycode-router/engine` facade. - Kestrel and Merlin are integrated through Graycode bridge packages. - Support-engine sibling imports and imports of Graycode internals are guarded. - The AST/package-graph guard reports production boundary violations with file/line diagnostics across Graycode 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`; GraycodeRouter 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 @@ -90,7 +90,7 @@ The graph is intentionally directional: - Graycode'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 GraycodeRouter boundary. - `PersistenceService` is the in-memory runtime owner for transcript/context state and checkpoint metadata. The active durable session path remains @@ -117,11 +117,11 @@ The `graycode-eco` parent workspace and Graycode's published module pins provide local integration without removing independent ownership and release boundaries. -### ADR-B02 — Preserve the Eyrie boundary +### ADR-B02 — Preserve the GraycodeRouter boundary Provider implementation, catalog metadata, credential mapping, and protocol -adapters remain owned by Eyrie. Graycode may own product policy and user-facing -selection, but production provider access remains through `eyrie/engine`. +adapters remain owned by GraycodeRouter. Graycode may own product policy and user-facing +selection, but production provider access remains through `graycode-router/engine`. ### ADR-B03 — Complete internal consolidation before adding new seams diff --git a/docs/architecture/graycode-architecture-v1-definition-of-done.md b/docs/architecture/graycode-architecture-v1-definition-of-done.md index 660217c4..f237498b 100644 --- a/docs/architecture/graycode-architecture-v1-definition-of-done.md +++ b/docs/architecture/graycode-architecture-v1-definition-of-done.md @@ -37,7 +37,7 @@ Status note: - [x] no support repo imports `graycode/internal/*` - [x] no support repo imports removed `graycode/shared/types` - [x] no SDK/skills repo references support engines as primary dependencies -- [x] Graycode production code imports Eyrie only through `eyrie/engine` +- [x] Graycode production code imports GraycodeRouter only through `graycode-router/engine` - [x] Graycode's graph/projection imports are documented as explicit integration surfaces and do not create engine-to-engine dependencies @@ -56,18 +56,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] `graycode-router`, `harrier`, and `swift` remain contract-free unless they gain a true cross-repo type ### Graycode 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] Graycode owns runtime DTOs in `internal/types` and translates them to `eyrie/engine` in `internal/engine` +- [x] Graycode owns runtime DTOs in `internal/types` and translates them to `graycode-router/engine` in `internal/engine` - [x] `graycode swift ...` remains a Graycode-mounted subcommand, not a competing product surface ### Enforcement -- [x] Graycode CI runs ecosystem, shared-types, eyrie-client, and peer-coupling guards +- [x] Graycode CI runs ecosystem, shared-types, graycode-router-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 @@ -111,7 +111,7 @@ Remove compatibility shims only when: From `graycode`: ```bash -make ecosystem-guard contracts-guard eyrie-client-guard eyrie-engine-guard peer-guard +make ecosystem-guard contracts-guard graycode-router-client-guard graycode-router-engine-guard peer-guard go test ./internal/testaudit/... -count=1 go test ./... -count=1 ``` diff --git a/docs/architecture/graycode-contract-spec.md b/docs/architecture/graycode-contract-spec.md index 198127db..a0015a4e 100644 --- a/docs/architecture/graycode-contract-spec.md +++ b/docs/architecture/graycode-contract-spec.md @@ -106,7 +106,7 @@ list in sync with the code (it is the inventory the dependency rules assume). - Graycode session persistence has started migrating to provider-neutral tool contracts - Graycode review storage and merlin/review bridge paths now consume neutral review/verify contracts - Graycode runtime conversation DTOs and the `ChatClient` port are Graycode-owned and - translated to the stable `eyrie/engine` contract at the integration edge + translated to the stable `graycode-router/engine` contract at the integration edge ## Versioning rule diff --git a/docs/architecture/graycode-current-vs-proposed.md b/docs/architecture/graycode-current-vs-proposed.md index 131e3a8b..b8539d82 100644 --- a/docs/architecture/graycode-current-vs-proposed.md +++ b/docs/architecture/graycode-current-vs-proposed.md @@ -11,7 +11,7 @@ The repository directory and product-name distinction is intentional: | Directory/repository | Product or role | |---|---| | `graycode` | Graycode product and orchestration root | -| `eyrie` | Eyrie provider runtime | +| `graycode-router` | GraycodeRouter provider runtime | | `harrier` | Harrier memory engine | | `shrike` | Shrike token/context engine | | `swift` | Swift provenance engine | @@ -37,7 +37,7 @@ graycode-eco/ ├── graycode # product / composition root ├── eagle # neutral contracts foundation ├── falcon # MCP foundation -├── eyrie # Eyrie provider engine +├── graycode-router # GraycodeRouter provider engine ├── harrier # Harrier memory engine ├── shrike # Shrike context engine ├── swift # Swift engine @@ -60,7 +60,7 @@ published versions pinned in each `go.mod`. ```text sparrow / robin / wren ── HTTP/OpenAPI ──> graycode <── skill surface ── starling │ - ├── eyrie/engine + ├── graycode-router/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -97,7 +97,7 @@ contract parity checks, and Graycode-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 Graycode's standalone pin; +1. publish the Eagle-migrated GraycodeRouter revision and update Graycode's standalone pin; 2. remove the transitional `graycode-core-contracts` dependency from the published module graph; 3. decide whether graph/projection packages should remain explicit Graycode @@ -108,7 +108,7 @@ reorganization: ## Allowed dependency edges ```text -graycode -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle +graycode -> graycode-router / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # only for shared contracts harrier / kestrel / merlin -> falcon sparrow / robin / wren -> Graycode public HTTP/OpenAPI surface diff --git a/docs/architecture/graycode-dependency-rules.md b/docs/architecture/graycode-dependency-rules.md index 7c5bedc6..e225cf03 100644 --- a/docs/architecture/graycode-dependency-rules.md +++ b/docs/architecture/graycode-dependency-rules.md @@ -9,7 +9,7 @@ product labels are shown in parentheses where they differ. ```text sparrow / robin / wren ── Graycode daemon API ──> graycode <── skill API ── starling │ - ├── eyrie/engine + ├── graycode-router/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 GraycodeRouter Graycode 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/graycode-router/engine`. This is enforced by shell and AST/package-graph guards. ### 4. Graycode schemas stay Graycode-owned Graycode conversation persistence and CLI/JSON output are explicit projections, -not aliases or direct serialization of Eyrie DTOs. +not aliases or direct serialization of GraycodeRouter DTOs. ### 5. Graph integrations are explicit surfaces @@ -86,13 +86,13 @@ execution. ### 7. Engine configuration is instance-scoped -Graycode supplies effective gateway settings while constructing an Eyrie Engine. -Product code must not mutate Eyrie process-global gateway state. +Graycode supplies effective gateway settings while constructing an GraycodeRouter Engine. +Product code must not mutate GraycodeRouter process-global gateway state. ## Enforcement Graycode CI runs manifest validation, no-local-replace checks, Eagle parity, -support-repository coupling, Eyrie facade checks, internal-layer checks, and +support-repository coupling, GraycodeRouter 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 Graycode's public daemon contract. diff --git a/docs/architecture/graycode-ecosystem-summary.md b/docs/architecture/graycode-ecosystem-summary.md index db75949b..ffe058e5 100644 --- a/docs/architecture/graycode-ecosystem-summary.md +++ b/docs/architecture/graycode-ecosystem-summary.md @@ -14,7 +14,7 @@ API consumers/extensions (main CLI and daemon) | Support engines - eyrie harrier shrike swift kestrel merlin + graycode-router harrier shrike swift kestrel merlin | Foundations eagle falcon @@ -30,7 +30,7 @@ Product labels map to directories as follows: `harrier`/Harrier, `shrike`/Shrike ## Dependency direction ```text -graycode -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle +graycode -> graycode-router / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # when shared contracts are needed harrier / kestrel / merlin -> falcon sparrow / robin / wren -> Graycode daemon API @@ -44,7 +44,7 @@ skills-to-engine, and any Go-module dependency on GrayCode Platform. ```text Graycode main CLI - ├── Eyrie provider execution + ├── GraycodeRouter provider execution ├── Harrier memory ├── Shrike token/context management ├── Swift swift/provenance @@ -65,7 +65,7 @@ Graycode's cloud usage path is fail-open; graph synchronization is explicit. | Repository | Role | Direct dependency rule | |---|---|---| | `graycode` | Main CLI, daemon, orchestration, policy | Integrates engines and contracts | -| `eyrie` | Provider runtime | Uses Eagle contracts; exposes `engine` | +| `graycode-router` | 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 | @@ -85,6 +85,6 @@ Graycode'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 GraycodeRouter revision, remove the transitional `graycode-core-contracts` dependency from the standalone graph, and decide whether Graycode's graph/projection packages need additional engine-owned facades. diff --git a/docs/architecture/graycode-harness.md b/docs/architecture/graycode-harness.md index 37de59d6..ca2503bd 100644 --- a/docs/architecture/graycode-harness.md +++ b/docs/architecture/graycode-harness.md @@ -4,7 +4,7 @@ Graycode treats the agent runtime as a product boundary around provider output. ## Tool-Call Path -1. Eyrie normalizes provider protocol responses. +1. GraycodeRouter normalizes provider protocol responses. 2. Graycode 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; Graycode owns host-level UX, permissions, persistence, +GraycodeRouter's verification package; Graycode owns host-level UX, permissions, persistence, and review contracts. ## Security Rule diff --git a/docs/architecture/graycode-product-architecture.md b/docs/architecture/graycode-product-architecture.md index 88b5c1d3..2cfb32d7 100644 --- a/docs/architecture/graycode-product-architecture.md +++ b/docs/architecture/graycode-product-architecture.md @@ -10,8 +10,8 @@ repositories. It is not a monorepo or a runtime product. Graycode is the only primary product surface in the graycode-eco ecosystem. The support repos exist to power Graycode, not to compete with it as standalone products. For model execution specifically: **Graycode is the face and composition layer; -Eyrie is the engine.** Graycode owns the conversation and product experience while -the `eyrie/engine` facade owns the complete provider path from credential and +GraycodeRouter is the engine.** Graycode owns the conversation and product experience while +the `graycode-router/engine` facade owns the complete provider path from credential and catalog state through model selection and normalized generation/streaming. ## Goals @@ -26,7 +26,7 @@ catalog state through model selection and normalized generation/streaming. ## Target repo set - `graycode` -- `eyrie` +- `graycode-router` - `harrier` - `shrike` - `swift` @@ -57,7 +57,7 @@ Users / SDKs / Skills | | v v core execution trust / quality - eyrie harrier shrike swift kestrel merlin + graycode-router harrier shrike swift kestrel merlin \ | / \ | / \ | / \ | / +--+--+---------------------+--+--+ @@ -72,7 +72,7 @@ Current implementation in the workspace: ```text graycode - -> eyrie + -> graycode-router -> harrier -> shrike -> swift @@ -97,7 +97,7 @@ SDKs / Skills / future integrations +------------+------------+------------+------------+------------+------------+ | | | | | | | v v v v v v v - Eyrie Harrier Shrike Swift Kestrel Merlin public APIs + GraycodeRouter Harrier Shrike Swift Kestrel Merlin public APIs \ | | | | / +-----------+------------+------------+------------+-----------+ | @@ -137,8 +137,8 @@ Graycode does not own: ## Engine responsibilities -### `eyrie` -- stable host control and generation facade (`eyrie/engine`) +### `graycode-router` +- stable host control and generation facade (`graycode-router/engine`) - credential storage and safe credential status - catalog discovery and model metadata - concrete provider/deployment selection @@ -181,20 +181,20 @@ Graycode does not own: 1. User invokes `graycode`. 2. Graycode loads product settings, policy, and workspace state, then creates an - Eyrie Engine with effective per-instance custom gateway settings. + GraycodeRouter Engine with effective per-instance custom gateway settings. 3. Graycode creates or resumes a session. 4. Graycode asks `shrike` for context assembly. 5. Graycode asks `harrier` for relevant memory. -6. Graycode routes provider execution through `eyrie`. +6. Graycode routes provider execution through `graycode-router`. 7. Graycode invokes tools and records actions through `swift`. 8. Graycode invokes `kestrel` when review should run. 9. Graycode invokes `merlin` when verification should run. 10. Graycode persists results and returns output to the user. At step 6, Graycode passes intent and Graycode-owned conversation DTOs through its -adapter. Eyrie loads provider/catalog/credential state, resolves the gateway, +adapter. GraycodeRouter loads provider/catalog/credential state, resolves the gateway, and returns normalized events. No production Graycode package imports a lower -Eyrie package, and no Eyrie engine DTO is used as Graycode's persistent or CLI +GraycodeRouter package, and no GraycodeRouter engine DTO is used as Graycode's persistent or CLI schema. ## Implementation phases @@ -227,8 +227,8 @@ Status: Status: - completed for the local runtime boundary - Graycode owns runtime DTOs and review/verify product-boundary contracts -- Graycode'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 +- Graycode's `ChatClient` anti-corruption port translates only to `graycode-router/engine` +- all lower-level GraycodeRouter production imports are forbidden by shell guards and meta-audit tests ### Phase 5 - align SDKs and skills to Graycode public interfaces only diff --git a/docs/architecture/graycode-provider-abstraction.md b/docs/architecture/graycode-provider-abstraction.md index 1d006050..7bddf41c 100644 --- a/docs/architecture/graycode-provider-abstraction.md +++ b/docs/architecture/graycode-provider-abstraction.md @@ -8,8 +8,8 @@ That means Graycode should support multiple providers without leaking provider-s ## Design principle -Provider-specific code lives behind Eyrie's stable `eyrie/engine` host facade. -Graycode is the face and composition root; Eyrie is the engine. +Provider-specific code lives behind GraycodeRouter's stable `graycode-router/engine` host facade. +Graycode is the face and composition root; GraycodeRouter is the engine. Graycode decides: @@ -17,7 +17,7 @@ Graycode decides: - the semantic intent (`fast`, `balanced`, `reasoning`, `economical`) - whether an exact user-selected model may fall back -`eyrie` handles: +`graycode-router` handles: - capability-to-model resolution - provider and deployment selection @@ -43,7 +43,7 @@ Graycode decides: ## Graycode-facing abstraction Graycode depends on its small `ChatClient` product port and adapts it only to -`eyrie/engine`, never to a vendor-specific or lower-level Eyrie client. +`graycode-router/engine`, never to a vendor-specific or lower-level GraycodeRouter 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 Graycode orchestration -- keep provider/deployment routing, health, retry, and fallback inside Eyrie -- Graycode 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 GraycodeRouter +- Graycode production integrations use `github.com/GrayCodeAI/graycode-router/engine` +- direct imports of lower GraycodeRouter packages are forbidden and CI-enforced - custom gateway settings enter as `engine.Options.CustomGateways` and are isolated per Engine instance - Graycode-owned JSON, session, and conversation schemas are explicit projections; diff --git a/docs/architecture/graycode-repo-roles.md b/docs/architecture/graycode-repo-roles.md index 5f41f91a..10604357 100644 --- a/docs/architecture/graycode-repo-roles.md +++ b/docs/architecture/graycode-repo-roles.md @@ -22,11 +22,11 @@ separate end-user products. ## Support engines -### `eyrie` -Graycode provider engine. Its public host boundary is `eyrie/engine`, which owns +### `graycode-router` +Graycode provider engine. Its public host boundary is `graycode-router/engine`, which owns credentials, provider state, catalog discovery, model/deployment selection, transport, resilience, and normalized generation/streaming. Graycode production -code has zero imports of Eyrie's lower-level packages. +code has zero imports of GraycodeRouter's lower-level packages. ### `harrier` (Harrier) Graycode memory engine. @@ -45,7 +45,7 @@ Graycode verification engine. All six support engines are peers: -- `eyrie` +- `graycode-router` - `harrier` (Harrier) - `shrike` (Shrike) - `swift` (Swift) diff --git a/docs/architecture/plan.md b/docs/architecture/plan.md index cdfb06d2..6c9b239d 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) +- **graycode-router:** LLM provider engine behind `graycode-router/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/spec.md b/docs/architecture/spec.md index 706fecf4..7966b25b 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: graycode, eagle, falcon, eyrie, harrier (Harrier), shrike (Shrike), swift (Swift), kestrel (Kestrel), and merlin (Merlin) | +| Parent `go.work` | Resolves the nine local Go siblings: graycode, eagle, falcon, graycode-router, 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, GraycodeRouter composition, state migration | | `session/` | SQLite persistence, search, export, replay | | `hooks/` | Event-driven plugin system | | `mcp/` | Model Context Protocol client/server | @@ -99,12 +99,12 @@ Graycode CLI/TUI + conversation + tools Graycode-owned ports/DTOs | v - eyrie/engine + graycode-router/engine credentials -> catalog -> routing -> generate/stream ``` -No production Graycode package may import a lower Eyrie package. Custom gateways -are supplied per Engine instance, and Eyrie DTOs are not Graycode persistence or +No production Graycode package may import a lower GraycodeRouter package. Custom gateways +are supplied per Engine instance, and GraycodeRouter DTOs are not Graycode 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 `graycode-router/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 6cd0b041..56cee929 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -12,11 +12,11 @@ Platform/provider capability metadata is separate: [`platform-capabilities.json` ```jsonc { - "components": ["graycode", "eyrie", ...], // canonical eco roster - "dependencies": { "graycode": ["eyrie", ...] }, // who depends on who + "components": ["graycode", "graycode-router", ...], // canonical eco roster + "dependencies": { "graycode": ["graycode-router", ...] }, // who depends on who "matrices": [ - { "name": "stable", "components": { "graycode": "0.1.0", "eyrie": "0.1.0", ... } }, - { "name": "next", "components": { "graycode": "main", "eyrie": "main", ... } } + { "name": "stable", "components": { "graycode": "0.1.0", "graycode-router": "0.1.0", ... } }, + { "name": "next", "components": { "graycode": "main", "graycode-router": "main", ... } } ] } ``` @@ -59,7 +59,7 @@ It runs on: ## Why bother -- **Bug reports become triageable.** "I ran graycode 0.4 with eyrie 0.2" — you +- **Bug reports become triageable.** "I ran graycode 0.4 with graycode-router 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 f6fbfbe7..6dfdbcc5 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 (graycode, eyrie, harrier, shrike, swift) + a new gallery web property + a unified docs site. +**Scope:** Multi-month effort spanning all 5 repos (graycode, graycode-router, 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.** `graycode/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.** `graycode/internal/plugin/malware_check.go:19` blocks `eval()`, pipe-to-shell, reverse shells; `graycode/internal/plugin/audit.go` flags hidden-Unicode / homoglyph attacks (`graycode 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 (`graycode/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 (`graycode/docs/`, `graycode-router/docs/`, `harrier/`, `shrike/docs/`, `swift/docs/`). --- @@ -81,7 +81,7 @@ The gap is therefore **consolidation and elevation**, not invention: (a) a *stan │ graycode 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/{graycode,eyrie,...} │ +│ cmd/skills_cmd.go (+ ext cmd)│ │ - /docs/{graycode,graycode-router,...} │ │ internal/plugin/manifest_v2 │ │ - generated from registry.v2 │ └───────────────────────────────┘ └──────────────────────────────┘ ``` @@ -203,7 +203,7 @@ browser → graycodeskills.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 graycode, eyrie, + pull README.md / ARCHITECTURE.md / docs/** from each of graycode, graycode-router, 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:** `graycode 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/{graycode,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/{graycode,graycode-router,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** `graycode ext update` (diff installed version vs index, re-verify, atomic replace) and `graycode 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** `graycode 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 graycode'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: graycode-router 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 `graycode-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 graycode-router/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.minGraycodeVersion` exists (`manifest_v2.go:20`), but do we also need per-asset compat (e.g., an MCP server needing a specific transport graycode 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 `~/.graycode/`. What are the canonical install dirs for each asset kind? 6. **Domain & hosting:** confirm `graycodeskills.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** | `graycode ext update`, `graycode ext verify`, atomic replace | **2 ew** | | **P2 — publisher self-service** | `graycode 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** | graycode-router/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/GRAYCODE-CLOUD-SAAS.md b/docs/design/GRAYCODE-CLOUD-SAAS.md index 3eb45086..c7c435ae 100644 --- a/docs/design/GRAYCODE-CLOUD-SAAS.md +++ b/docs/design/GRAYCODE-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 `/graycode/internal/auth/auth.go`" (`TOP20_COMPARISON.md:72`). -The eyrie side of the house has the parallel gaps: a LiteLLM-compatible proxy +The graycode-router 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`). Graycode Cloud and eyrie multi-tenancy share +budgets (`TOP20_COMPARISON.md:95-96`). Graycode Cloud and graycode-router 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 - Graycode's `eyrie/engine` integration; the plane calls a Graycode agent worker, which - composes Eyrie. The plane does not re-implement routing, caching, or provider +- **Not** an LLM gateway rewrite. GraycodeRouter remains the provider engine behind + Graycode's `graycode-router/engine` integration; the plane calls a Graycode agent worker, which + composes GraycodeRouter. 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 │─────▶│ graycode-router │ │ │ │ (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. Graycode Cloud owns this authority; Eyrie +hard-limit enforcement, and invoicing. Graycode Cloud owns this authority; GraycodeRouter supplies normalized model and token usage through Graycode'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 → graycode-router: 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 Graycode's adapter | Reuse model/token dimensions as metering input. Do not import lower Eyrie packages or trust client-computed prices for billing. | +| GraycodeRouter normalized usage | `graycode-router/engine` response and stream usage DTOs through Graycode's adapter | Reuse model/token dimensions as metering input. Do not import lower GraycodeRouter 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 `forwardToGraycode` (`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** — graycode-router 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 graycode-router 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 via `internal/system/cron/cron.go` (`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 graycode-router). Stripe SDK is permissive. | | Database | **Buy** managed Postgres | Standard. | -| Metering enforcement | **Build in Graycode Cloud** on normalized Engine usage | Billing authority must be server-owned and versioned; lower Eyrie packages remain engine implementation details. | +| Metering enforcement | **Build in Graycode Cloud** on normalized Engine usage | Billing authority must be server-owned and versioned; lower GraycodeRouter 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 graycode-router `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 graycode-router 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 +graycode-router 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/ecosystem-message-flow.md b/docs/ecosystem-message-flow.md index 67140077..0d672ec5 100644 --- a/docs/ecosystem-message-flow.md +++ b/docs/ecosystem-message-flow.md @@ -1,4 +1,4 @@ -# Ecosystem message flow (eyrie · harrier · shrike) +# Ecosystem message flow (graycode-router · harrier · shrike) How one user message travels through graycode and the GrayCodeAI ecosystem libraries. @@ -26,7 +26,7 @@ User prompt (TUI or graycode exec) │ ▼ ┌───────────────────┐ ┌─────────────┐ -│ Graycode ChatClient │────►│ eyrie/engine│ catalog, credentials, routing +│ Graycode ChatClient │────►│ graycode-router/engine│ catalog, credentials, routing │ port + adapter │ │ generate/ │────► provider API └─────────┬─────────┘ │ stream │ └─────────────┘ @@ -42,7 +42,7 @@ User prompt (TUI or graycode exec) ▼ (when context grows) ┌───────────────────┐ │ shrike Compress │ fast path before LLM summarization -│ + eyrie compact │ +│ + graycode-router compact │ └───────────────────┘ ``` @@ -50,8 +50,8 @@ User prompt (TUI or graycode exec) ### 1. Session start (`graycode` or `graycode exec`) -- **eyrie**: The Graycode composition root creates an `eyrie/engine.Engine` with - Eyrie-owned state paths, an injected secret store, and per-engine custom +- **graycode-router**: The Graycode composition root creates an `graycode-router/engine.Engine` with + GraycodeRouter-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 Graycode's `ChatClient` port. - **harrier**: `configureSession` creates `HarrierBridge` → opens `~/.harrier/data/harrier.db`. If missing, graycode runs without persistent memory. @@ -68,8 +68,8 @@ User prompt (TUI or graycode exec) Each turn: 1. **harrier** — recall memories matching the latest user message (token budget ~2000). -2. **eyrie** — Graycode's adapter calls engine generate/stream with Graycode-owned tool - definitions; Eyrie normalizes provider events and tool requests. +2. **graycode-router** — Graycode's adapter calls engine generate/stream with Graycode-owned tool + definitions; GraycodeRouter normalizes provider events and tool requests. 3. Tools run with `HarrierBridge` in context for `CoreMemory*` tools. 4. **harrier** — sleeptime consolidation, skill distillation, auto-remember after turns. @@ -78,7 +78,7 @@ Each turn: When messages exceed limits (`internal/engine/compact.go`): 1. **shrike** — `shrike.Compress()` tries a fast compression path for summaries. -2. **eyrie** — if shrike reduction is insufficient, graycode calls the LLM to summarize, then keeps recent messages. +2. **graycode-router** — if shrike reduction is insufficient, graycode calls the LLM to summarize, then keeps recent messages. ### 5. Token accounting @@ -87,7 +87,7 @@ When messages exceed limits (`internal/engine/compact.go`): ## Verify locally ```bash -graycode doctor # ecosystem panel + eyrie preflight + harrier status +graycode doctor # ecosystem panel + graycode-router preflight + harrier status graycode harrier # merlin memory graph ./scripts/smoke-graycode.sh # build + quick tests ``` @@ -96,15 +96,15 @@ graycode harrier # merlin memory graph | Module | Role in graycode | Required? | |--------|----------------|-----------| -| **eyrie** | LLM APIs, catalog, credentials, routing | Yes | +| **graycode-router** | LLM APIs, catalog, credentials, routing | Yes | | **harrier** | SQLite memory graph at `~/.harrier/data/` | No (degrades gracefully) | | **shrike** | Token estimate + context compression | Yes (embedded, no config) | -The support repositories are independent sibling checkouts: `eyrie`, +The support repositories are independent sibling checkouts: `graycode-router`, `harrier` (Harrier), and `shrike` (Shrike), with the parent `go.work` wiring the local Go workspace. -Production Graycode code imports Eyrie only through `eyrie/engine`. Conversation +Production Graycode code imports GraycodeRouter only through `graycode-router/engine`. Conversation history, WAL/resume, permissions, and tool execution remain in Graycode; provider credentials, discovery, selection, transport, resilience, and normalized -streaming remain in Eyrie. +streaming remain in GraycodeRouter. diff --git a/docs/monitoring-guide.md b/docs/monitoring-guide.md index f381f925..5ece59f1 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, GraycodeRouter 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-GRAYCODE-ECO-PORT-PLAN.md b/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md index 0f01bb2d..be3de082 100644 --- a/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md +++ b/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md @@ -21,7 +21,7 @@ 2. **Go multi-repo stays** — map Grok crates into graycode / engines / contracts / cloud / skills. 3. **Prefer wire-first** — Graycode 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 **graycode-router**, 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) │ ├─► graycode product CLI/TUI/engine/tools/hooks/plugins/ACP ├─► eagle pure DTOs (tool/spawn/hooks/policy) - ├─► eyrie LLM routing/stream/retry/catalog/auth credentials + ├─► graycode-router 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 | graycode observability | Partial | M | Privacy opt-in | | `xai-grok-telemetry` | 14k | Events + OTEL + Mixpanel + Sentry | graycode observability | Partial | L | **Skip Mixpanel default**; keep OTEL | | `xai-hunk-tracker` | 13k | Agent vs external hunks | graycode | 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 | **graycode-router** | Done* | — | Different shape; graycode-router owns | +| `xai-grok-sampler` | 11k | HTTP stream + retry | **graycode-router** | Done* | — | Do not replace graycode-router | | `xai-grok-update` | 11k | Auto-update | graycode | Partial | M | | | `xai-grok-mcp` | 10k | MCP client (oauth, wire) | graycode `mcp` | Partial | L | | | `xai-codebase-graph` | 9.7k | Tree-sitter graph | graycode codegraph/repomap | Partial | L | | @@ -144,16 +144,16 @@ grok-eco/grok-build (one Rust workspace) | `xai-agent-lifecycle` | 0.6k | Lifecycle hooks data | graycode hooks/engine | Partial | S | | | `xai-gix-status` | 0.6k | Fast git status | graycode git tools | Partial | S | | | `xai-grok-paths` | 0.6k | AbsPath types | graycode | 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 + graycode-router | Partial | S | | | `xai-grok-announcements` | 0.4k | Release announcements | graycode tips/notify | Port | S | | -| `xai-grok-auth` | 0.4k | Auth seam | eyrie + graycode auth | Partial | M | Browser OAuth optional | +| `xai-grok-auth` | 0.4k | Auth seam | graycode-router + graycode 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 | graycode observability | N/A | XS | | -| `xai-grok-env` | 0.2k | Backend env presets | eyrie/graycode | Partial | XS | | +| `xai-grok-env` | 0.2k | Backend env presets | graycode-router/graycode | Partial | XS | | | `xai-prompt-queue` | 0.2k | Prompt queue types | graycode | Port | S | | | `xai-mixpanel` | 0.1k | Mixpanel client | — | **Skip** | — | Privacy; OTEL opt-in | | `xai-grok-version` | 0.1k | Version | graycode VERSION | Done | — | | -| `xai-grok-models` | 0.1k | Default model IDs | eyrie catalog | Done* | — | | +| `xai-grok-models` | 0.1k | Default model IDs | graycode-router 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 | graycode tool | Partial | L | | | `xai-tool-types` | 3.6k | Spawn/task types | **contracts** | Port | M | **P0** | | `xai-grok-compaction` | 6.8k | Compaction engine | graycode engine + shrike | Partial | L | | -| `xai-circuit-breaker` | 2.2k | HTTP breaker | eyrie/resilience | Partial | S | | +| `xai-circuit-breaker` | 2.2k | HTTP breaker | graycode-router/resilience | Partial | S | | | `xai-tracing` | 0.8k | Tracing | graycode OTEL | Partial | S | | | `xai-test-utils` | 0.4k | Hermetic git tests | graycode testutil | Partial | S | | | `xai-interjection-core` | 0.3k | Interjection messaging | graycode | Port | S | Mid-turn user inject | @@ -269,7 +269,7 @@ Grok slash modules (port checklist → graycode `/` commands or CLI): | 08 skills | same | | 09 plugins | same | | 10 hooks | same | -| 11 custom models | eyrie + graycode | +| 11 custom models | graycode-router + graycode | | 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` +### `graycode-router` 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 graycode-router with xai-grok-sampler | Multi-provider better | | ratatui widgets | Bubble Tea stack | | `xai-proto-build` | Rust build | | Closed contribution policy | Graycode 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/graycode-router. 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 3440d632..45ed69a8 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:** `graycode` -**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/Graycode Cloud; outside the Graycode Go runtime graph) +**Supporting repositories:** `eagle`, `graycode-router`, `harrier` (Harrier), `shrike` (Shrike), `swift` (Swift), `kestrel` (Kestrel), `merlin` (Merlin), `falcon`, `starling`, `sparrow`, `robin`, `wren`, `owl`, and `graycode-platform` (web/BFF/Graycode Cloud; outside the Graycode Go runtime graph) --- @@ -13,13 +13,13 @@ ### Goal -Bring Graycode 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 Graycode’s multi-repo Go platform advantages (eyrie multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). +Bring Graycode 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 Graycode’s multi-repo Go platform advantages (graycode-router multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). ### Non-goals - Rewrite Graycode in Rust or monorepo-collapse engines into graycode. - Vendor lock-in to a single LLM host auth model. -- Replace eyrie/harrier/shrike/swift with Grok-shaped internal crates. +- Replace graycode-router/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 + graycode-router harrier shrike swift kestrel/merlin ``` **New core package (proposed):** `graycode/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`), `graycode`, optional `eyrie` +**Repos:** `graycode-platform/apps/worker` (deployed as `graycode-cloud`), `graycode`, optional `graycode-router` | Piece | Detail | |-------|--------| @@ -768,7 +768,7 @@ Build on existing enterprise policyInput (model/capability lists). | Foreign import | swift | graycode | optional | | Memory dream UX | harrier | graycode | no | | Token | shrike | — | no change | -| Providers | eyrie | graycode | no change | +| Providers | graycode-router | graycode | no change | | Review engines | kestrel/merlin | graycode | 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 graycode-router? | 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 0f2d06a5..b70e9479 100644 --- a/docs/plans/YEAR-0-ACTIVE.md +++ b/docs/plans/YEAR-0-ACTIVE.md @@ -15,7 +15,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 graycode monorepo | -| Wire existing modes/budgets first | Rebuild eyrie/harrier/shrike as Grok clones | +| Wire existing modes/budgets first | Rebuild graycode-router/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 8659d540..fd2507a3 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 graycode | | `mcp-server`, `codex-mcp`, `rmcp-client`, `connectors` | `internal/mcp` client+server, sibling `falcon` scaffolding | Keep graycode | | `skills`, `plugin`, `hooks` | community skill registry + structural validator, plugins, expanded lifecycle hook events | Keep graycode | -| `login`, `keyring-store`, `aws-auth` | eyrie credential store in OS keychain across 28 providers | Keep graycode (broader) | -| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `eyrie` adapters, catalog, cascade routing | Keep graycode (much broader) | +| `login`, `keyring-store`, `aws-auth` | graycode-router credential store in OS keychain across 28 providers | Keep graycode (broader) | +| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `graycode-router` adapters, catalog, cascade routing | Keep graycode (much broader) | | `memories`, `agent-graph-store`, `context-fragments` | sibling `harrier` (Harrier) graph memory; eventlog/graphjournal projections | Keep graycode | | `apply-patch`, `file-search`, `file-watcher`, `git-utils` | edit tools, codegraph, git tooling, watcher hooks | Keep graycode | | `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 1bc57c4b..11ad0195 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 Graycode's stronger Harrier memory, Shrike token controls, Swift replay, Kestrel -review, Merlin auditing, or Eyrie provider runtime. +review, Merlin auditing, or GraycodeRouter 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: GraycodeRouter owns provider protocols and routing. - BaseAI remote pipes: incompatible with Graycode's local authority and durable event model. - BaseAI `lowdb` JSON memory: weaker than Harrier and Graycode 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 GraycodeRouter repository remains a separate repository change and must be published through its own feature branch and PR before updating Graycode's module pin. diff --git a/docs/plans/dsh-harness-port-plan.md b/docs/plans/dsh-harness-port-plan.md index fc3f3699..9450971a 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 GraycodeRouter 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 9a281bb1..4e145e4c 100644 --- a/docs/plans/fix-critical-and-high-review.md +++ b/docs/plans/fix-critical-and-high-review.md @@ -77,7 +77,7 @@ These were documented in the original plan as out-of-scope-for-this-PR. They rem ## Context -A deep code review of `eyrie` and `graycode` (companion plan at +A deep code review of `graycode-router` and `graycode` (companion plan at `../graycode-router/docs/plans/fix-critical-and-high-review.md`) surfaced 7 critical and 9 high items. This plan covers **all graycode 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 graycode-router: `//nolint:errcheck` on type-assertion that can panic. - M1–M20 medium items. - L-tier quick wins (e.g. `cosmenticFlags` typo, `cmd/.graycode/` 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 **graycode PR 4 (H6 — Session +- **graycode-router PR 4 (C2 — Vertex fix)** and **graycode PR 4 (H6 — Session decomposition)** are independent. -- **eyrie PR 8 (H4 — EyrieError)** is a prerequisite for any future - graycode-side `errors.As(err, &eyrieErr)` use (currently none). No +- **graycode-router PR 8 (H4 — GraycodeRouterError)** is a prerequisite for any future + graycode-side `errors.As(err, &graycode-routerErr)` 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 7c2c2930..422a40b6 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 Graycode | +| Provider runtime | sibling `graycode-router` | Do not add provider logic to Graycode | ## 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 `graycode-router` or `falcon`. ### Required behavior @@ -422,7 +422,7 @@ graycode verify For sibling-repository changes, run that repository's own tests and boundary checks before updating the Graycode pointer. Do not modify provider protocols or adapters in Graycode; -those belong in the Eyrie repository. +those belong in the GraycodeRouter repository. ## Delivery Sequence diff --git a/docs/plans/goose-adoption-plan.md b/docs/plans/goose-adoption-plan.md index e3b747ba..e0824262 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 graycode found that most of its runtime concepts already have -a native graycode implementation (providers via eyrie, sessions, MCP, ACP, skills, +a native graycode implementation (providers via graycode-router, sessions, MCP, ACP, skills, native sandboxing, permissions). The genuinely novel, graycode-relevant ideas are adopted here in Go, without copying Rust code or weakening graycode's native sandboxing model. @@ -17,7 +17,7 @@ sandboxing model. | Goose package/concept | Graycode implementation | Decision | |---|---|---| -| Provider abstraction (~36) | sibling `eyrie` (28 built-in + 75+ live) | Keep graycode | +| Provider abstraction (~36) | sibling `graycode-router` (28 built-in + 75+ live) | Keep graycode | | Sessions (SQLite WAL) | `internal/session` JSONL+WAL+zstd + sibling `swift` (Swift) | Keep graycode | | MCP (client+server) | `internal/mcp` + sibling `falcon` | Keep graycode | | ACP | `internal/acp` | Keep graycode | @@ -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 `graycode-router`. ### 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 graycode-router's local-model path. - Recipe security scanner / cron recipes: graycode already has schedule/cron. diff --git a/docs/plans/graycode-contracts-migration-backlog.md b/docs/plans/graycode-contracts-migration-backlog.md index 9a7dc8a3..a1297c2f 100644 --- a/docs/plans/graycode-contracts-migration-backlog.md +++ b/docs/plans/graycode-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 Graycode-owned runtime DTO - added explicit Graycode-owned runtime DTOs; the final boundary translates them - through `eyrie/engine` + through `graycode-router/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-graycode-router-client-imports.sh` - wired both guards into `Makefile` and CI -- wired the Eyrie engine-boundary guards into `Makefile` and CI +- wired the GraycodeRouter 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 Graycode, 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`, `graycode-router`, `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 Graycode match the merged contract changes -### 3. Remove Graycode production dependency on lower Eyrie packages +### 3. Remove Graycode production dependency on lower GraycodeRouter packages Current state: - session persistence uses neutral tool contracts -- Graycode now owns the runtime message DTO in `internal/types.EyrieMessage` +- Graycode now owns the runtime message DTO in `internal/types.GraycodeRouterMessage` - Graycode now owns runtime tool call/result DTOs in `internal/types` - Graycode now owns runtime response/usage/stream DTOs in `internal/types` - Graycode now owns runtime chat options, response format, tool choice, continuation config, and tool definition DTOs in `internal/types` - Graycode now owns the transport-provider seam in `internal/types.ChatProvider` - Graycode 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 `graycode-router/engine` +- production imports of every lower GraycodeRouter package are zero - cmd/session restore paths now go through centralized `session.ToRuntimeMessages` and `session.FromRuntimeMessages` Decision: -- completed: Graycode owns the product DTO/port seam and Eyrie owns the engine, +- completed: Graycode owns the product DTO/port seam and GraycodeRouter 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 Graycode-owned transport adapters ### PR 3 -- completed: removed all lower-level Eyrie imports from Graycode production code and +- completed: removed all lower-level GraycodeRouter imports from Graycode production code and replaced the compatibility allowlist with a zero-exception guard ### PR 4 diff --git a/docs/plans/minimax-adoption-plan.md b/docs/plans/minimax-adoption-plan.md index 97639023..18418441 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 Graycode ecosystem (Graycode plus independent repositories: Eyrie, Eagle, +repos into the Graycode ecosystem (Graycode plus independent repositories: GraycodeRouter, 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 `graycode-router` | 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 | graycode / `falcon` | MCP v2 no-network test pattern + image-source normalization (patterns only) | | 4 | minimax_search | graycode tooling | Jina page→Markdown browse extractor + evidence-extraction prompt (pattern) | | 5 | Mini-Agent | graycode 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. GraycodeRouter provider-conformance metrics + CI wiring (highest value) -### Verified current state (eyrie repository) +### Verified current state (graycode-router repository) -- `eyrie/verify/verify.go` — a complete behavioral conformance harness +- `graycode-router/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: +- `graycode-router/verify/cases.go` — `CanonicalCases()` with 3 cases: `basic-chat`, `deterministic-answer`, `tool-call`. -- `eyrie/verify/metrics.go` — `ToolCallF1()` already implemented. +- `graycode-router/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`) +- graycode-router 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`** +`graycode-router/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 `graycode-router/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`** +`graycode-router/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 graycode-router 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 `graycode-router/Makefile` and +`graycode-router/.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) +- `graycode-router/verify/schema.go` (new) +- `graycode-router/verify/schema_test.go` (new) +- `graycode-router/verify/verify.go` (add MatchRate) +- `graycode-router/verify/verify_test.go` (add tests) +- `graycode-router/cmd/verify/main.go` (new) +- `graycode-router/Makefile` (verify target) +- `graycode-router/.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. GraycodeRouter verify metrics + schema validation + tests (highest value) +2. GraycodeRouter verify CLI + Makefile + CI wiring 3. graycode image-source normalization helper + tests 4. graycode 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] GraycodeRouter schema-accuracy validation and tool-call match-rate metrics. +- [x] GraycodeRouter verification CLI, deterministic Makefile target, and CI job. - [x] Graycode image-source normalization for data URIs, URLs, local files, and raw base64. - [x] Graycode Jina Reader page-to-Markdown client with opt-in configuration and tests. - [x] Graycode cancellation cleanup for incomplete assistant tool-use/tool-result turns. - [x] Confirmed graycode'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 GraycodeRouter 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 673286c2..18c023b3 100644 --- a/docs/plans/pi-adoption-plan.md +++ b/docs/plans/pi-adoption-plan.md @@ -34,14 +34,14 @@ The audit found that graycode already provides the foundation for most Pi featur | Pi package | Graycode implementation | Current decision | |---|---|---| -| `pi-ai` (multi-provider) | sibling `eyrie` client + engine facade + catalog + router + credentials | Keep graycode (broader) | +| `pi-ai` (multi-provider) | sibling `graycode-router` client + engine facade + catalog + router + credentials | Keep graycode (broader) | | `pi-agent-core` (agent loop) | `internal/engine` | Keep graycode | | `pi-coding-agent` (CLI) | `cmd` TUI + CLI + daemon | Keep graycode | | `pi-session-backends` (storage) | `internal/session` JSONL + zstd + WAL + SQLite index, sibling `swift` (Swift) | Keep graycode | | `pi-server` (RPC) | `internal/daemon` + `internal/acp` + `internal/mcp` | Keep graycode (broader) | | Permissions/sandbox | `internal/engine/safety` + `internal/sandbox` (seatbelt/landlock/seccomp/ACL/netproxy) | Keep graycode (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`, graycode-router `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` + `graycode-router/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 graycode-router 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 graycode and eyrie test suites so CI +4. Wire the conformance harness into the graycode and graycode-router 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 graycode and eyrie CI. +- The conformance harness runs green in graycode and graycode-router 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 graycode and eyrie CI. +- Cross-repo telemetry conformance in graycode and graycode-router CI. ### Security tests @@ -310,7 +310,7 @@ updating the Graycode pointer. - [x] Define the typed span/attribute schema. - [x] Implement the conformance harness. -- [x] Wire graycode and eyrie CI. +- [x] Wire graycode and graycode-router 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 63b654e4..c2fff1b9 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 Graycode feature branch. ## Guardrails Qwen Code is Apache-2.0 TypeScript software with a Gemini-shaped core. Graycode -will independently reimplement behavioral contracts in Go, preserve the Eyrie +will independently reimplement behavioral contracts in Go, preserve the GraycodeRouter provider boundary, and retain Graycode's event-sourced sessions and OS sandbox. No Qwen source or dependency is vendored. ## Existing Graycode Capabilities Graycode already has durable sessions, event logging, WAL/recovery, branching, -review contracts, provider routing in Eyrie, MCP integration, skills, memory, +review contracts, provider routing in GraycodeRouter, 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/platform-capabilities.json b/docs/platform-capabilities.json index 46de595d..d87aa7c0 100644 --- a/docs/platform-capabilities.json +++ b/docs/platform-capabilities.json @@ -51,13 +51,13 @@ "features": ["tools", "resources", "prompts", "sampling"] }, "ecosystem_compatibility": { - "graycode+eyrie": "full", + "graycode+graycode-router": "full", "graycode+shrike": "full", "graycode+kestrel": "full", "graycode+merlin": "full", "graycode+harrier": "full", "graycode+swift": "full", - "eyrie+shrike": "token_compression", + "graycode-router+shrike": "token_compression", "kestrel+merlin": "security_review" } } diff --git a/docs/session-decomposition.md b/docs/session-decomposition.md index 11dd9ea8..ab49559a 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.GraycodeRouterTool, activeModel string, maxTokens int) types.ChatOptions` +- `Chat(ctx, msgs, opts) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage)` — 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.GraycodeRouterMessage)` - `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.GraycodeRouterMessage, errOutput string) (*Reflection, error)` +- `RecordDecision(turn int, toolNames string, msgs []types.GraycodeRouterMessage)` - `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.GraycodeRouterMessage) []types.GraycodeRouterMessage` **File:** `graycode/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.GraycodeRouterMessage`, `RawMessages()`, `MessageCount()`, `LoadMessages([]types.GraycodeRouterMessage)` - `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)` @@ -223,7 +223,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().GraycodeRouterTools(), activeModel, maxTok) result, err := s.Chat.Stream(ctx, s.Persistence.Messages(), opts) if err != nil { /* error path */ } defer result.Close() @@ -257,7 +257,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.GraycodeRouterMessage` 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 a8b473fb..8c3d1cc2 100644 --- a/docs/troubleshooting-guide.md +++ b/docs/troubleshooting-guide.md @@ -73,7 +73,7 @@ graycode 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 GraycodeRouter'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 `GRAYCODE_MODEL` or provider credentials. -- Eyrie catalog not initialized — the nine Go modules are independent sibling +- GraycodeRouter catalog not initialized — the nine Go modules are independent sibling repositories; from a full parent workspace, run `make setup` in graycode 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 GraycodeRouter 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 182e0c05..72190201 100644 --- a/docs/user-guide/02-authentication.md +++ b/docs/user-guide/02-authentication.md @@ -1,6 +1,6 @@ # Authentication -Graycode supports several authentication methods, including API key configuration through the TUI and multi-provider support via Eyrie. +Graycode supports several authentication methods, including API key configuration through the TUI and multi-provider support via GraycodeRouter. --- @@ -49,7 +49,7 @@ base URL is `https://api.fireworks.ai/inference/v1`. See the official ## Provider Configuration -Graycode uses Eyrie for provider routing, health checks, and retry logic. To configure providers: +Graycode uses GraycodeRouter 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 GRAYCODE_DEPLOYMENT_ROUTING=true ``` -Graycode will route canonical model IDs through Eyrie's deployment catalog. Refresh the catalog with: +Graycode will route canonical model IDs through GraycodeRouter's deployment catalog. Refresh the catalog with: ``` /refresh-model-catalog @@ -185,7 +185,7 @@ Check credential status at any time: graycode credentials status ``` -This verifies keychain entries and validates Eyrie's provider status. +This verifies keychain entries and validates GraycodeRouter's provider status. --- @@ -203,7 +203,7 @@ During a session, the active method handles all refreshes. ## Multi-Provider Support -Graycode works with any LLM provider through Eyrie's adapter system: +Graycode works with any LLM provider through GraycodeRouter's adapter system: | Provider | Status | |----------|--------| diff --git a/docs/user-guide/04-slash-commands.md b/docs/user-guide/04-slash-commands.md index be019145..0d98d955 100644 --- a/docs/user-guide/04-slash-commands.md +++ b/docs/user-guide/04-slash-commands.md @@ -359,7 +359,7 @@ Quick ready-to-chat check. ### `/ecosystem` -Show ecosystem component status (Eyrie, harrier, shrike). +Show ecosystem component status (GraycodeRouter, harrier, shrike). ``` /ecosystem @@ -455,7 +455,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 GraycodeRouter. ``` /refresh-model-catalog diff --git a/docs/user-guide/11-custom-models.md b/docs/user-guide/11-custom-models.md index 80084c82..57ced321 100644 --- a/docs/user-guide/11-custom-models.md +++ b/docs/user-guide/11-custom-models.md @@ -1,6 +1,6 @@ # Custom Models -Graycode connects to custom model endpoints through Eyrie for alternative providers, self-hosted models, and overriding built-in settings. +Graycode connects to custom model endpoints through GraycodeRouter 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 GraycodeRouter's model catalog: ```bash export GRAYCODE_DEPLOYMENT_ROUTING=true diff --git a/docs/user-guide/23-dashboard.md b/docs/user-guide/23-dashboard.md index e31a9168..91eab4f2 100644 --- a/docs/user-guide/23-dashboard.md +++ b/docs/user-guide/23-dashboard.md @@ -9,7 +9,7 @@ The Graycode dashboard provides system status and monitoring information. In the TUI: ``` -/ecosystem # Ecosystem status (Eyrie, harrier, shrike) +/ecosystem # Ecosystem status (GraycodeRouter, harrier, shrike) /path # Developer path readiness /preflight # Quick health check ``` @@ -22,7 +22,7 @@ Shows the status of all Graycode components: | Component | Status | |-----------|--------| -| Eyrie (providers) | Ready / Error | +| GraycodeRouter (providers) | Ready / Error | | harrier (memory) | Connected / Offline | | shrike (tokenizer) | Available | | swift (session capture) | Ready | diff --git a/docs/versioning.md b/docs/versioning.md index 82a66501..251ad8db 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 (`sparrow`, `eyrie`, `kestrel`, `merlin`, `shrike`) +### Go libraries (`sparrow`, `graycode-router`, `kestrel`, `merlin`, `shrike`) - `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 a83f9a2c..8dd0bea5 100644 --- a/ecosystem.yaml +++ b/ecosystem.yaml @@ -21,12 +21,12 @@ repositories: - directory: graycode-router github_repo: graycode-router - product_name: Eyrie + product_name: Graycode Router kind: engine language: go - module: github.com/GrayCodeAI/eyrie + module: github.com/GrayCodeAI/graycode-router workspace: true - facade: github.com/GrayCodeAI/eyrie/engine + facade: github.com/GrayCodeAI/graycode-router/engine - directory: graycode-skills github_repo: graycode-skills diff --git a/flake.nix b/flake.nix index 574034ff..4b5c56af 100644 --- a/flake.nix +++ b/flake.nix @@ -1,12 +1,12 @@ { - description = "Graycode - AI coding agent powered by eyrie"; + description = "Graycode - AI coding agent powered by graycode-router"; 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; }; + graycode-router = { url = "github:GrayCodeAI/graycode-router"; 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, graycode-router, 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/graycode-router" = graycode-router; "github.com/GrayCodeAI/merlin" = merlin; "github.com/GrayCodeAI/kestrel" = kestrel; "github.com/GrayCodeAI/shrike" = shrike; @@ -55,7 +55,7 @@ graycode = pkgs.buildGoModule rec { pname = "graycode"; - version = "0.1.0"; + version = "0.0.1"; src = ./.; diff --git a/go.mod b/go.mod index 8dae49f5..60152f53 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.2.3-0.20260902140659-6bc3068cd48a + github.com/GrayCodeAI/graycode-router v0.0.1 github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 github.com/GrayCodeAI/merlin v0.0.0-20260902154444-0f4b9f7326cb diff --git a/go.sum b/go.sum index 08eb5214..e4b80bfc 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8 github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/GrayCodeAI/eyrie v0.2.3-0.20260902140659-6bc3068cd48a h1:6TnQbyidnPMBBfHBhIldTWzYQZx+bparWuWm1ft2q+s= -github.com/GrayCodeAI/eyrie v0.2.3-0.20260902140659-6bc3068cd48a/go.mod h1:gphUZ6Vcml7zyXfKU+704HZcxtHbJLFqgLDzke/rvFc= +github.com/GrayCodeAI/graycode-router v0.0.1 h1:+yfIteR3uNRpzLlcBuQQkEOvMK5b7oG/B9rRtil1Q5I= +github.com/GrayCodeAI/graycode-router v0.0.1/go.mod h1:7G4UtNEv1l943s6yg+uBBqkUt5b9It2X3yOkh9dMGzE= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 h1:X5lmXWlBdPVyk7lMAX7M0FiRr9o16Sq7yIjfN3mnXGU= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7/go.mod h1:JBkYX6kUbWCr1fsK//hxQ4Ro1VvlxtB+bSMR0/N5RRY= github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 h1:MrmWb0coJ3Uaxr/FtDSH1FaL/lqs663QXUh8ds+w8Og= diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 2b9ff69b..14a13183 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 graycode-router layer) return s.getFile(account) } diff --git a/internal/bench/bench_test.go b/internal/bench/bench_test.go index 4bfcb120..3e794a23 100644 --- a/internal/bench/bench_test.go +++ b/internal/bench/bench_test.go @@ -43,15 +43,15 @@ var sWEbenchSmokeTasks = []taskFixture{ // so a benchmark can assert the agent attempted them. type stubChatClient struct { t testing.TB - events []types.EyrieStreamEvent + events []types.GraycodeRouterStreamEvent } -func (m *stubChatClient) Chat(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions) (*types.EyrieResponse, error) { - return &types.EyrieResponse{Content: "stub", FinishReason: "end_turn"}, nil +func (m *stubChatClient) Chat(_ context.Context, _ []types.GraycodeRouterMessage, _ types.ChatOptions) (*types.GraycodeRouterResponse, error) { + return &types.GraycodeRouterResponse{Content: "stub", FinishReason: "end_turn"}, nil } -func (m *stubChatClient) StreamChatContinue(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { - ch := make(chan types.EyrieStreamEvent, len(m.events)+1) +func (m *stubChatClient) StreamChatContinue(_ context.Context, _ []types.GraycodeRouterMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { + ch := make(chan types.GraycodeRouterStreamEvent, len(m.events)+1) for _, e := range m.events { ch <- e } @@ -61,7 +61,7 @@ func (m *stubChatClient) StreamChatContinue(_ context.Context, _ []types.EyrieMe // runTask drives engine.Session.Stream for a single fixture and returns the // flattened event sequence + whether the loop terminated cleanly. -func runTask(t *testing.B, fix taskFixture, events []types.EyrieStreamEvent) (terminated bool, got []engine.StreamEvent) { +func runTask(t *testing.B, fix taskFixture, events []types.GraycodeRouterStreamEvent) (terminated bool, got []engine.StreamEvent) { svc := &stubChatClient{t: t, events: events} s := engine.NewSessionWithClient(svc, "bench", "bench-model", "bench system", nil, false) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) @@ -93,18 +93,18 @@ func TestBenchmark_SWE_benchHeadless(t *testing.T) { for _, fix := range sWEbenchSmokeTasks { // The stub emits a content + done event, modelling a provider that // answers without tools (the no-tools path) or a read+answer path. - events := []types.EyrieStreamEvent{ + events := []types.GraycodeRouterStreamEvent{ {Type: "content", Content: "understood"}, } if len(fix.MustUse) > 0 { // Simulate a tool-use turn followed by completion. events = nil for _, name := range fix.MustUse { - events = append(events, types.EyrieStreamEvent{Type: "tool_use", Content: name}) + events = append(events, types.GraycodeRouterStreamEvent{Type: "tool_use", Content: name}) } - events = append(events, types.EyrieStreamEvent{Type: "content", Content: "done"}) + events = append(events, types.GraycodeRouterStreamEvent{Type: "content", Content: "done"}) } - events = append(events, types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}) + events = append(events, types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}) terminated, got := runTask(b, fix, events) if !terminated { diff --git a/internal/bridge/kestrel/bridge.go b/internal/bridge/kestrel/bridge.go index c34ef95f..99408409 100644 --- a/internal/bridge/kestrel/bridge.go +++ b/internal/bridge/kestrel/bridge.go @@ -16,25 +16,25 @@ import ( kestrelreview "github.com/GrayCodeAI/kestrel/review" ) -// EyrieAdapter implements kestrel's Provider interface using graycode's eyrie client. +// GraycodeRouterAdapter implements kestrel's Provider interface using graycode's graycode-router client. // It translates between kestrel.Message/kestrel.ChatOpts and Graycode runtime DTOs. -type EyrieAdapter struct { +type GraycodeRouterAdapter struct { client types.ChatProvider provider string } -// NewEyrieAdapter creates an adapter that satisfies kestrel.Provider using -// the given eyrie client and provider name (e.g. "anthropic", "openai"). -func NewEyrieAdapter(c types.ChatProvider, provider string) *EyrieAdapter { - return &EyrieAdapter{client: c, provider: provider} +// NewGraycodeRouterAdapter creates an adapter that satisfies kestrel.Provider using +// the given graycode-router client and provider name (e.g. "anthropic", "openai"). +func NewGraycodeRouterAdapter(c types.ChatProvider, provider string) *GraycodeRouterAdapter { + return &GraycodeRouterAdapter{client: c, provider: provider} } -// Chat translates a kestrel LLM request into an eyrie call and returns the +// Chat translates a kestrel LLM request into an graycode-router call and returns the // result in kestrel's Response format. -func (a *EyrieAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, opts kestrelLib.ChatOpts) (*kestrelLib.Response, error) { - eyrieMessages := make([]types.EyrieMessage, len(messages)) +func (a *GraycodeRouterAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, opts kestrelLib.ChatOpts) (*kestrelLib.Response, error) { + graycodeRouterMessages := make([]types.GraycodeRouterMessage, len(messages)) for i, m := range messages { - eyrieMessages[i] = types.EyrieMessage{ + graycodeRouterMessages[i] = types.GraycodeRouterMessage{ Role: m.Role, Content: m.Content, } @@ -46,7 +46,7 @@ func (a *EyrieAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, temp = &t } - eyrieOpts := types.ChatOptions{ + graycodeRouterOpts := types.ChatOptions{ Provider: a.provider, Model: opts.Model, MaxTokens: opts.MaxTokens, @@ -54,7 +54,7 @@ func (a *EyrieAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, System: opts.System, } - resp, err := a.client.Chat(ctx, eyrieMessages, eyrieOpts) + resp, err := a.client.Chat(ctx, graycodeRouterMessages, graycodeRouterOpts) if err != nil { return nil, err } @@ -74,7 +74,7 @@ func (a *EyrieAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, // If initialization fails, all operations degrade gracefully and return // empty results rather than errors. type Bridge struct { - adapter *EyrieAdapter + adapter *GraycodeRouterAdapter reviewer *kestrelLib.Reviewer opts []kestrelLib.Option mu sync.Mutex @@ -103,7 +103,7 @@ func (b *Bridge) init(c types.ChatProvider, provider string, opts ...kestrelLib. if c == nil { return } - b.adapter = NewEyrieAdapter(c, provider) + b.adapter = NewGraycodeRouterAdapter(c, provider) // Prepend the provider option so callers don't have to. b.opts = append([]kestrelLib.Option{kestrelLib.WithProvider(b.adapter)}, opts...) b.reviewer = kestrelLib.NewReviewer(b.opts...) diff --git a/internal/bridge/kestrel/bridge_test.go b/internal/bridge/kestrel/bridge_test.go index ea4e86a4..324f1a24 100644 --- a/internal/bridge/kestrel/bridge_test.go +++ b/internal/bridge/kestrel/bridge_test.go @@ -83,11 +83,11 @@ func TestBridge_ImproveNotReady(t *testing.T) { } } -func TestEyrieAdapter_Chat(t *testing.T) { - // Test that EyrieAdapter correctly translates kestrel messages. - // We can't easily test with a real eyrie client, but we verify the adapter +func TestGraycodeRouterAdapter_Chat(t *testing.T) { + // Test that GraycodeRouterAdapter correctly translates kestrel messages. + // We can't easily test with a real graycode-router client, but we verify the adapter // struct is properly constructed. - adapter := NewEyrieAdapter(nil, "openai") + adapter := NewGraycodeRouterAdapter(nil, "openai") if adapter.provider != "openai" { t.Fatalf("expected provider openai, got %s", adapter.provider) } diff --git a/internal/catalogtest/install.go b/internal/catalogtest/install.go index 7532e245..9ae5f25f 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 GRAYCODE_ROUTER_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("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", globalPath) }) return func() { - _ = os.Unsetenv("EYRIE_MODEL_CATALOG_PATH") + _ = os.Unsetenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH") } } -// Install sets EYRIE_MODEL_CATALOG_PATH for a single test (per-test temp file). +// Install sets GRAYCODE_ROUTER_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("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", path) } diff --git a/internal/codegraph/algorithms_cgo_more.go b/internal/codegraph/algorithms_cgo_more.go index 7cac4b01..9c2cac85 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 graycode, eyrie, shrike, harrier, etc. +// Useful for finding relationships between graycode, graycode-router, shrike, harrier, etc. 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 graycode calls a symbol in eyrie, this traces that cross-repo dependency. +// If a symbol in graycode calls a symbol in graycode-router, 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, graycode calling eyrie functions. +// For example, graycode calling graycode-router 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 94a3adab..c1e71a30 100644 --- a/internal/config/catalog_api.go +++ b/internal/config/catalog_api.go @@ -5,8 +5,8 @@ import ( "sort" "strings" - llm "github.com/GrayCodeAI/eyrie/llm" gw "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + llm "github.com/GrayCodeAI/graycode-router/llm" ) type GatewayStatus struct { @@ -22,19 +22,19 @@ type GatewayStatus struct { DNSHost string } -// IsCatalogCacheRequired reports whether an Eyrie operation failed because +// IsCatalogCacheRequired reports whether an GraycodeRouter 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 GraycodeRouter's canonical first-class provider count. func RegisteredProviderCount() int { return gw.RegisteredProviderCount() } func AllCatalogProviders() []string { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return nil } @@ -57,7 +57,7 @@ func AllCatalogProviders() []string { } func AllSetupGateways() []string { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() if err != nil { return } @@ -213,7 +213,7 @@ func SyncSelectionWithCredentials(ctx context.Context) { } func DefaultModelForProvider(provider string) string { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 := NewGraycodeRouterEngineForSettings(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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() 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 := NewGraycodeRouterEngineForSettings(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 := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() if err != nil { return nil } @@ -298,7 +298,7 @@ func AllCanonicalModelIDs() []string { } func ProviderIDForDeployment(deploymentID string) string { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return "" } @@ -311,7 +311,7 @@ func ProviderIDForDeployment(deploymentID string) string { } func PrimaryAPIKeyEnvForDeployment(deploymentID string) string { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() if err != nil { return gw.GatewayDefs{}, false } diff --git a/internal/config/catalog_compat_test.go b/internal/config/catalog_compat_test.go index 8ff45f9a..b7f6ab5a 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/graycode-router/catalog" + graycoderouterconfig "github.com/GrayCodeAI/graycode-router/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 graycoderouterconfig.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 71b579f3..32428a93 100644 --- a/internal/config/catalog_gateways_test.go +++ b/internal/config/catalog_gateways_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - "github.com/GrayCodeAI/eyrie/catalog" - "github.com/GrayCodeAI/eyrie/credentials" "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" + "github.com/GrayCodeAI/graycode-router/catalog" + "github.com/GrayCodeAI/graycode-router/credentials" ) func TestIsCatalogCacheRequired(t *testing.T) { @@ -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 graycode-router 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 GraycodeRouter checkout + // but not yet in a published graycode-router 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 graycode-router 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_MatchesGraycodeRouterList(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 0c566a37..92735cde 100644 --- a/internal/config/catalog_health.go +++ b/internal/config/catalog_health.go @@ -16,7 +16,7 @@ var ( const catalogHealthCacheTTL = 15 * time.Second -// CatalogHealth summarizes the on-disk eyrie model catalog for doctor / status output. +// CatalogHealth summarizes the on-disk graycode-router model catalog for doctor / status output. type CatalogHealth struct { CachePath string `json:"cache_path"` Exists bool `json:"exists"` @@ -31,7 +31,7 @@ type CatalogHealth struct { Error string `json:"error,omitempty"` } -// CatalogHealthReport inspects ~/.eyrie/model_catalog.json (or EYRIE_MODEL_CATALOG_PATH). +// CatalogHealthReport inspects ~/.graycode-router/model_catalog.json (or GRAYCODE_ROUTER_MODEL_CATALOG_PATH). func CatalogHealthReport(ctx context.Context) CatalogHealth { path := CatalogCachePathForDisplay() catalogHealthMu.Lock() @@ -59,7 +59,7 @@ func InvalidateCatalogHealthCache() { } func catalogHealthReportUncached(ctx context.Context) CatalogHealth { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return CatalogHealth{Error: err.Error()} } @@ -79,7 +79,7 @@ func catalogHealthReportUncached(ctx context.Context) CatalogHealth { // FormatCatalogHealth returns human-readable catalog status for graycode doctor. func FormatCatalogHealth(h CatalogHealth) string { var b strings.Builder - b.WriteString("Model catalog (eyrie):\n") + b.WriteString("Model catalog (graycode-router):\n") b.WriteString(fmt.Sprintf(" path: %s\n", h.CachePath)) if h.Error != "" { b.WriteString(fmt.Sprintf(" status: %s\n", h.Error)) @@ -126,7 +126,7 @@ func EnsureCatalogAvailable(ctx context.Context) error { // CatalogCachePathForDisplay returns the path users should care about. func CatalogCachePathForDisplay() string { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err == nil { return engine.StatePaths().Catalog } diff --git a/internal/config/catalog_health_test.go b/internal/config/catalog_health_test.go index 4fc2e5f3..a6e6215f 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("GRAYCODE_ROUTER_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("GRAYCODE_ROUTER_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 94b75341..f2c3d885 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 graycode-router 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 graycode-router 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 graycode-router 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 48b37cda..3cb8e3f5 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("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", custom) if got := graycodeconfig.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 12fc773d..40ce0af7 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("GRAYCODE_ROUTER_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 13577d58..af036f88 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("GRAYCODE_CONFIG_DIR", configDir) - t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", filepath.Join(home, "graycode-router")) if err := os.MkdirAll(configDir, 0o755); err != nil { t.Fatal(err) } @@ -180,7 +180,7 @@ func TestLoadSettingsUsesUserConfigOnly(t *testing.T) { settings := LoadSettings() if got := ActiveModel(context.TODO()); got != "openai/gpt-4o" { - t.Fatalf("expected global model in eyrie, got %q (settings.model=%q)", got, settings.Model) + t.Fatalf("expected global model in graycode-router, got %q (settings.model=%q)", got, settings.Model) } if settings.Model != "" { t.Fatalf("model must not remain in graycode settings.json, got %q", settings.Model) @@ -243,7 +243,7 @@ func TestLoadSettingsPreservesRejectedLegacySelection(t *testing.T) { configDir := filepath.Join(home, "config") t.Setenv("HOME", home) t.Setenv("GRAYCODE_CONFIG_DIR", configDir) - t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", filepath.Join(home, "graycode-router")) if err := os.MkdirAll(configDir, 0o700); err != nil { t.Fatal(err) } @@ -264,7 +264,7 @@ func TestLoadSettingsPreservesRejectedLegacySelection(t *testing.T) { t.Fatalf("rejected legacy selection was removed from disk: %s", data) } if got := ActiveModel(context.Background()); got != "" { - t.Fatalf("rejected legacy model reached Eyrie state: %q", got) + t.Fatalf("rejected legacy model reached GraycodeRouter state: %q", got) } } diff --git a/internal/config/credentials_store.go b/internal/config/credentials_store.go index 17659779..6df75968 100644 --- a/internal/config/credentials_store.go +++ b/internal/config/credentials_store.go @@ -8,14 +8,14 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) -// PersistAPIKey saves a provider API key via eyrie (OS secret store). +// PersistAPIKey saves a provider API key via graycode-router (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 := newGraycodeRouterEngine() if err != nil { return err } @@ -26,7 +26,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 graycode-router provider match for a pasted API key. type CredentialInference struct { ProviderID string DeploymentID string @@ -34,7 +34,7 @@ type CredentialInference struct { DisplayName string } -// CredentialProviderOption is one eyrie provider row for /config pickers. +// CredentialProviderOption is one graycode-router provider row for /config pickers. type CredentialProviderOption struct { ProviderID string DeploymentID string @@ -45,7 +45,7 @@ type CredentialProviderOption struct { Rank int } -// CredentialResolveResult is eyrie paste-key resolution (format check + full provider list; no prefix inference). +// CredentialResolveResult is graycode-router paste-key resolution (format check + full provider list; no prefix inference). type CredentialResolveResult struct { FormatOK bool FormatError string @@ -53,9 +53,9 @@ type CredentialResolveResult struct { ProbeDisambiguationUsed bool } -// ResolveCredential validates format and lists all providers from eyrie registry. +// ResolveCredential validates format and lists all providers from graycode-router registry. func ResolveCredential(ctx context.Context, secret string) CredentialResolveResult { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return CredentialResolveResult{FormatError: err.Error()} } @@ -90,9 +90,9 @@ func InferenceFromOption(opt CredentialProviderOption) CredentialInference { } } -// SaveCredential validates, probes, and stores via eyrie keychain. +// SaveCredential validates, probes, and stores via graycode-router keychain. func SaveCredential(ctx context.Context, inference CredentialInference, secret string) error { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return err } @@ -105,7 +105,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 := newGraycodeRouterEngine() if err != nil { return false } @@ -116,7 +116,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 := newGraycodeRouterEngine() if err != nil { return "", false } @@ -164,7 +164,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 := newGraycodeRouterEngine() if err != nil { return nil, err } @@ -188,7 +188,7 @@ func MaskCredentialForProvider(ctx context.Context, provider string) string { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err == nil { status, statusErr := engine.CredentialStatus(ctx, provider) if statusErr == nil && status.Masked != "" { @@ -213,7 +213,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 := newGraycodeRouterEngine() if err != nil { return CredentialInference{}, err } @@ -234,7 +234,7 @@ func LocalCredentialInference(providerID string) (CredentialInference, error) { return CredentialInferenceForProvider(providerID) } -// FormatConfigProviderError maps eyrie setup errors to user-facing /config hints. +// FormatConfigProviderError maps graycode-router 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..2fa470ad 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 GraycodeRouter runtime. func DeploymentRoutingEnabled(s Settings) bool { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() return err == nil && engine.DeploymentRoutingEnabled(s.DeploymentRouting) } diff --git a/internal/config/deployment_status.go b/internal/config/deployment_status.go index 128a5f48..a4c6a4eb 100644 --- a/internal/config/deployment_status.go +++ b/internal/config/deployment_status.go @@ -12,7 +12,7 @@ func ResolveCanonicalModel(model string) string { // DeploymentStatusReport returns graycode deployment routing diagnostics. func DeploymentStatusReport(ctx context.Context, activeModel string) (string, error) { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 := NewGraycodeRouterEngineForSettings(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 (graycode-router routing JSON preview). func RoutingPreviewJSON(ctx context.Context, model string) (string, error) { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 := NewGraycodeRouterEngineForSettings(settings) if err != nil { return "", err } diff --git a/internal/config/developer_path.go b/internal/config/developer_path.go index 1742d8f9..5617a20e 100644 --- a/internal/config/developer_path.go +++ b/internal/config/developer_path.go @@ -184,7 +184,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: "graycode-router", Status: PathPass, Detail: "Preflight ready to chat", Blocking: true, }) @@ -197,7 +197,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { } } checks = append(checks, PathCheck{ - Section: "Ecosystem", Name: "eyrie", Status: status, + Section: "Ecosystem", Name: "graycode-router", Status: status, Detail: "Preflight not ready — see graycode preflight", FixHint: "Complete /config (credentials + model)", Blocking: true, @@ -262,7 +262,7 @@ func developerPathNextStep(r DeveloperPathReport, setup SetupState) string { func FormatDeveloperPathReport(ctx context.Context) string { r := EvaluateDeveloperPath(ctx) var b strings.Builder - b.WriteString("Developer path (graycode · eyrie · shrike · harrier)\n\n") + b.WriteString("Developer path (graycode · graycode-router · shrike · harrier)\n\n") status := "NEEDS SETUP" switch { diff --git a/internal/config/ecosystem_report.go b/internal/config/ecosystem_report.go index 61eacb4b..e841d603 100644 --- a/internal/config/ecosystem_report.go +++ b/internal/config/ecosystem_report.go @@ -11,12 +11,12 @@ import ( // EcosystemReport is the structured view of the ecosystem panel. type EcosystemReport struct { - Eyre EcosystemEyrie `json:"eyrie"` - Harrier EcosystemHarrier `json:"harrier"` - Shrike EcosystemShrike `json:"shrike"` + Eyre EcosystemGraycodeRouter `json:"graycode-router"` + Harrier EcosystemHarrier `json:"harrier"` + Shrike EcosystemShrike `json:"shrike"` } -type EcosystemEyrie struct { +type EcosystemGraycodeRouter struct { CatalogExists bool `json:"catalog_exists"` ModelCount int `json:"model_count,omitempty"` Ready bool `json:"ready"` @@ -39,7 +39,7 @@ type EcosystemShrike struct { func BuildEcosystemReport(ctx context.Context, provider, model string) EcosystemReport { var r EcosystemReport - // eyrie + // graycode-router cat := CatalogHealthReport(ctx) r.Eyre.CatalogExists = cat.Exists r.Eyre.ModelCount = cat.Models @@ -68,36 +68,36 @@ func BuildEcosystemReport(ctx context.Context, provider, model string) Ecosystem return r } -// FormatEcosystemPanel summarizes eyrie, harrier, and shrike integration for doctor and status output. +// FormatEcosystemPanel summarizes graycode-router, harrier, and shrike integration for doctor and status output. func FormatEcosystemPanel(ctx context.Context, provider, model string) string { var b strings.Builder - b.WriteString("Ecosystem (eyrie · harrier · shrike):\n") + b.WriteString("Ecosystem (graycode-router · harrier · shrike):\n") - // eyrie — LLM provider layer + // graycode-router — LLM provider layer cat := CatalogHealthReport(ctx) - eyrieLine := " eyrie: " + graycodeRouterLine := " graycode-router: " if cat.Exists { - eyrieLine += fmt.Sprintf("catalog %d models", cat.Models) + graycodeRouterLine += fmt.Sprintf("catalog %d models", cat.Models) } else { - eyrieLine += "catalog missing (run graycode models refresh)" + graycodeRouterLine += "catalog missing (run graycode models refresh)" } pre := EnginePreflightReport(ctx) if pre.Ready { - eyrieLine += " · locally ready" + graycodeRouterLine += " · locally ready" } else { - eyrieLine += " · setup incomplete" + graycodeRouterLine += " · setup incomplete" } if strings.TrimSpace(provider) != "" && provider != "auto" { - eyrieLine += fmt.Sprintf(" · provider %s", provider) + graycodeRouterLine += fmt.Sprintf(" · provider %s", provider) } if dep, err := EngineDeploymentSummary(ctx, model); err == nil { if dep.RoutingStages > 0 { - eyrieLine += fmt.Sprintf(" · routing %s (%d stages)", dep.RoutingSource, dep.RoutingStages) + graycodeRouterLine += fmt.Sprintf(" · routing %s (%d stages)", dep.RoutingSource, dep.RoutingStages) } else { - eyrieLine += fmt.Sprintf(" · routing %s", dep.RoutingSource) + graycodeRouterLine += fmt.Sprintf(" · routing %s", dep.RoutingSource) } } - b.WriteString(eyrieLine + "\n") + b.WriteString(graycodeRouterLine + "\n") // harrier — persistent memory graph bridge := memory.NewHarrierBridge() diff --git a/internal/config/ecosystem_report_test.go b/internal/config/ecosystem_report_test.go index 09e8a6d1..2455a491 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 · harrier · shrike):", - "eyrie:", + "Ecosystem (graycode-router · harrier · shrike):", + "graycode-router:", "harrier:", "shrike:", } { diff --git a/internal/config/eyrie_apply.go b/internal/config/graycode_router_apply.go similarity index 72% rename from internal/config/eyrie_apply.go rename to internal/config/graycode_router_apply.go index df52c20f..f0ac5e5f 100644 --- a/internal/config/eyrie_apply.go +++ b/internal/config/graycode_router_apply.go @@ -8,19 +8,19 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) -// ApplyCredentialsResult is Graycode's UI-safe view of an Eyrie catalog/routing -// application. It intentionally excludes Eyrie setup/config implementation +// ApplyCredentialsResult is Graycode's UI-safe view of an GraycodeRouter catalog/routing +// application. It intentionally excludes GraycodeRouter setup/config implementation // types from the product boundary. type ApplyCredentialsResult struct { Catalog gateway.CatalogSnapshot } -// ApplyEyrieCredentialsForProvider refreshes live models and writes sanitized +// ApplyGraycodeRouterCredentialsForProvider refreshes live models and writes sanitized // deployment routing after /config saves a key. -func ApplyEyrieCredentialsForProvider(ctx context.Context, providerID string) (*ApplyCredentialsResult, error) { +func ApplyGraycodeRouterCredentialsForProvider(ctx context.Context, providerID string) (*ApplyCredentialsResult, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 +// ApplyGraycodeRouterCredentials refreshes all configured providers and writes // sanitized deployment routing. -func ApplyEyrieCredentials(ctx context.Context) (*ApplyCredentialsResult, error) { +func ApplyGraycodeRouterCredentials(ctx context.Context) (*ApplyCredentialsResult, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 := newGraycodeRouterEngine() if err != nil { return "", err } diff --git a/internal/config/eyrie_engine.go b/internal/config/graycode_router_engine.go similarity index 82% rename from internal/config/eyrie_engine.go rename to internal/config/graycode_router_engine.go index d579a7e2..96ca9ecb 100644 --- a/internal/config/eyrie_engine.go +++ b/internal/config/graycode_router_engine.go @@ -12,16 +12,16 @@ type ( EnginePreflightOptions = gateway.PreflightOptions ) -func newEyrieEngine() (*gateway.Gateway, error) { +func newGraycodeRouterEngine() (*gateway.Gateway, error) { return gateway.New(context.Background(), globalCustomProviders()) } -func NewEyrieEngine() (*gateway.Gateway, error) { return newEyrieEngine() } +func NewGraycodeRouterEngine() (*gateway.Gateway, error) { return newGraycodeRouterEngine() } -// NewEyrieEngineForSettings composes a fresh gateway for one effective +// NewGraycodeRouterEngineForSettings composes a fresh gateway for one effective // Graycode settings snapshot. It performs no package-global registration and does // not mutate provider environment variables. -func NewEyrieEngineForSettings(settings Settings) (*gateway.Gateway, error) { +func NewGraycodeRouterEngineForSettings(settings Settings) (*gateway.Gateway, error) { return gateway.New(context.Background(), gatewayCustomGateways(settings.CustomProviders)) } @@ -84,7 +84,7 @@ func FormatEnginePreflight(report EnginePreflight) string { } func EngineGatewayRegion(ctx context.Context, providerID string) (string, bool) { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { return "", false } @@ -92,7 +92,7 @@ func EngineGatewayRegion(ctx context.Context, providerID string) (string, bool) } func SetEngineGatewayRegion(ctx context.Context, providerID, region string) error { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { return err } @@ -100,7 +100,7 @@ func SetEngineGatewayRegion(ctx context.Context, providerID, region string) erro } func CanonicalModelID(ctx context.Context, modelID string) string { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { return modelID } @@ -108,7 +108,7 @@ func CanonicalModelID(ctx context.Context, modelID string) string { } func HasCredentialEnv(ctx context.Context, envVar string) bool { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() return err == nil && gw.HasCredentialEnv(ctx, envVar) } @@ -117,15 +117,15 @@ func CredentialGuidance(providerID, secret string) string { } func ProviderStateSecurityStatus() gateway.ProviderStateSecurity { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { - return gateway.ProviderStateSecurity{Error: err.Error(), Detail: "Eyrie engine initialization failed"} + return gateway.ProviderStateSecurity{Error: err.Error(), Detail: "GraycodeRouter engine initialization failed"} } return gw.ProviderStateSecurityStatus() } func MigrateEngineProviderSecrets() error { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { return err } @@ -133,22 +133,22 @@ func MigrateEngineProviderSecrets() error { } func EngineDeploymentSummary(ctx context.Context, model string) (gateway.DeploymentSummary, error) { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { return gateway.DeploymentSummary{}, err } return gw.DeploymentSummary(ctx, model) } -// newEyrieEngine is Graycode's default composition root for Eyrie's stable host +// newGraycodeRouterEngine is Graycode's default composition root for GraycodeRouter's stable host // facade. Command paths that support --settings must use -// NewEyrieEngineForSettings instead of relying on this global-settings default. +// NewGraycodeRouterEngineForSettings instead of relying on this global-settings default. -// ListEngineModels returns model-picker rows through Eyrie's stable facade. +// ListEngineModels returns model-picker rows through GraycodeRouter'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 := newGraycodeRouterEngine() if err != nil { return nil, err } @@ -156,7 +156,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 := NewGraycodeRouterEngineForSettings(settings) if err != nil { return nil, err } @@ -164,7 +164,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 := NewGraycodeRouterEngineForSettings(settings) if err != nil { return nil, err } @@ -172,7 +172,7 @@ func ListLiveEngineModelsWithSettings(ctx context.Context, settings Settings, pr } func ListPublicEngineModels(ctx context.Context, providerID string) ([]EngineModel, error) { - gw, err := newEyrieEngine() + gw, err := newGraycodeRouterEngine() if err != nil { return nil, err } diff --git a/internal/config/eyrie_engine_test.go b/internal/config/graycode_router_engine_test.go similarity index 86% rename from internal/config/eyrie_engine_test.go rename to internal/config/graycode_router_engine_test.go index 809d955b..e64de101 100644 --- a/internal/config/eyrie_engine_test.go +++ b/internal/config/graycode_router_engine_test.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) -func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { +func TestNewGraycodeRouterEngineForSettingsIsInvocationScoped(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 := NewGraycodeRouterEngineForSettings(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 := NewGraycodeRouterEngineForSettings(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 := NewGraycodeRouterEngineForSettings(Settings{}) if err != nil { t.Fatal(err) } diff --git a/internal/config/eyrie_selection.go b/internal/config/graycode_router_selection.go similarity index 78% rename from internal/config/eyrie_selection.go rename to internal/config/graycode_router_selection.go index c7a99f84..3271967c 100644 --- a/internal/config/eyrie_selection.go +++ b/internal/config/graycode_router_selection.go @@ -13,9 +13,9 @@ type ( ) // EffectiveSelection resolves persisted selection and optional host overrides -// through Eyrie's host-neutral engine contract. +// through GraycodeRouter's host-neutral engine contract. func EffectiveSelection(ctx context.Context, opts SelectionOptions) Selection { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return Selection{} } @@ -23,60 +23,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 := NewGraycodeRouterEngineForSettings(settings) if err != nil { return Selection{} } return engine.EffectiveSelection(ctx, opts) } -// ActiveModel returns the selected model from eyrie provider.json (not graycode settings). +// ActiveModel returns the selected model from graycode-router provider.json (not graycode settings). func ActiveModel(ctx context.Context) string { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return "" } return engine.ActiveSelection(ctx).Model } -// ActiveProvider returns the selected provider from eyrie provider.json. +// ActiveProvider returns the selected provider from graycode-router provider.json. func ActiveProvider(ctx context.Context) string { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 GraycodeRouter runtime. func ActiveProviderID(provider string) string { return gateway.NormalizeProviderID(provider) } -// SetActiveModel persists model selection to eyrie provider.json. +// SetActiveModel persists model selection to graycode-router provider.json. func SetActiveModel(ctx context.Context, modelID string) error { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return err } return engine.SetActiveModel(ctx, modelID) } -// SetActiveProvider persists provider selection to eyrie provider.json. +// SetActiveProvider persists provider selection to graycode-router provider.json. func SetActiveProvider(ctx context.Context, provider string) error { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return err } @@ -90,14 +90,14 @@ func SetActiveSelection(ctx context.Context, provider, modelID string) error { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return err } return engine.SetSelection(ctx, provider, modelID) } -// migrateStoredModelProvider moves model/provider from ~/.graycode/settings.json into eyrie once. +// migrateStoredModelProvider moves model/provider from ~/.graycode/settings.json into graycode-router once. func migrateStoredModelProvider(s *Settings) { if s == nil { return @@ -109,7 +109,7 @@ func migrateStoredModelProvider(s *Settings) { activeProvider := strings.TrimSpace(ActiveProvider(ctx)) changed := false - // Existing Eyrie state is authoritative. Otherwise migrate a stored pair + // Existing GraycodeRouter state is authoritative. Otherwise migrate a stored pair // in one validated write so a rejected model cannot strand only the // provider in the destination or silently erase the user's source value. if activeModel != "" { diff --git a/internal/config/milestone_verify_test.go b/internal/config/milestone_verify_test.go index 3eeb12a6..c4f56e1c 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/graycode-router/catalog" + graycoderoutercfg "github.com/GrayCodeAI/graycode-router/config" + "github.com/GrayCodeAI/graycode-router/credentials" ) // isolateMilestoneTest uses a temp HOME and GRAYCODE_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("GRAYCODE_CONFIG_DIR", graycodeDir) - t.Setenv("EYRIE_CONFIG_DIR", graycodeDir) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", graycodeDir) return graycodeDir } @@ -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 := graycoderoutercfg.SyncProviderConfigFromCatalog(compiled, env) + path, err := graycoderoutercfg.GetProviderConfigPath() if err != nil { t.Fatalf("GetProviderConfigPath: %v", err) } - if err := eyriecfg.SaveProviderConfig(cfg, path); err != nil { + if err := graycoderoutercfg.SaveProviderConfig(cfg, path); err != nil { t.Fatal(err) } assertProviderJSONFileHasNoSecrets(t, path) @@ -138,12 +138,12 @@ func TestVerify_EvaluateSetupFlow(t *testing.T) { } providerPath := filepath.Join(os.Getenv("HOME"), ".graycode", "provider.json") - cfg := &eyriecfg.ProviderConfig{ + cfg := &graycoderoutercfg.ProviderConfig{ ActiveProvider: "anthropic", ActiveModel: "claude-sonnet-4-20250514", AnthropicModel: "claude-sonnet-4-20250514", } - if err := eyriecfg.SaveProviderConfig(cfg, providerPath); err != nil { + if err := graycoderoutercfg.SaveProviderConfig(cfg, providerPath); err != nil { t.Fatal(err) } st = EvaluateSetup(ctx) @@ -171,7 +171,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 graycoderoutercfg.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..08ba55e3 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 graycode-router provider to list models for when the UI +// has no explicit filter. Host prefs (settings) win; otherwise graycode-router routing/deployments decide. func DefaultModelProviderFilter(ctx context.Context) string { if p := ActiveGateway(ctx); p != "" { return p } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() if err != nil { return "" } diff --git a/internal/config/security_test.go b/internal/config/security_test.go index 53f264ec..dc717335 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("GRAYCODE_CONFIG_DIR", dir) - t.Setenv("EYRIE_CONFIG_DIR", dir) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", dir) s := Settings{ MaxBudgetUSD: 10.0, } diff --git a/internal/config/settings.go b/internal/config/settings.go index 6c44df79..3cb5f459 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -27,12 +27,12 @@ func fetchModelsViaRuntime(ctx context.Context, provider string) ([]EngineModel, } // Settings holds graycode configuration. -// Graycode: no API keys stored here. Secrets come from the OS secret store via eyrie. +// Graycode: no API keys stored here. Secrets come from the OS secret store via graycode-router. 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 are retained only for one-time migration into eyrie provider.json. + // Model and Provider are retained only for one-time migration into graycode-router provider.json. // Graycode does not persist model/provider here; use SetActiveModel / SetActiveProvider. Model string `json:"model,omitempty"` Provider string `json:"provider,omitempty"` @@ -702,7 +702,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 := newGraycodeRouterEngine() if err != nil { return "empty" } @@ -738,7 +738,7 @@ func providerCredentialEnvAliases(provider string) []string { primary := strings.TrimSpace(ProviderAPIKeyEnv(provider)) seen := map[string]bool{} var out []string - engine, _ := newEyrieEngine() + engine, _ := newGraycodeRouterEngine() var aliases []string if engine != nil { aliases = engine.CredentialEnvKeys(provider) @@ -755,10 +755,10 @@ func providerCredentialEnvAliases(provider string) []string { } // ───────────────────────────────────────────────────────────── -// Live model catalog fetch from eyrie +// Live model catalog fetch from graycode-router // ───────────────────────────────────────────────────────────── -// FetchModelsForProvider returns models from the eyrie catalog (dynamic; no graycode hardcoded lists). +// FetchModelsForProvider returns models from the graycode-router catalog (dynamic; no graycode hardcoded lists). // RefreshModelCatalogV1 is the explicit network refresh boundary. func FetchModelsForProvider(provider string) ([]EngineModel, error) { provider = gateway.NormalizeProviderID(provider) @@ -788,7 +788,7 @@ func FetchModelsForProvider(provider string) ([]EngineModel, error) { }}, nil } } - return nil, fmt.Errorf("no models found for provider %s in eyrie catalog (check API keys; graycode will refresh automatically on next start)", provider) + return nil, fmt.Errorf("no models found for provider %s in graycode-router catalog (check API keys; graycode will refresh automatically on next start)", provider) } // FetchModelsForProviderWithSettings resolves cached models using one @@ -798,7 +798,7 @@ func FetchModelsForProviderWithSettings(ctx context.Context, settings Settings, if provider == "" { return nil, fmt.Errorf("no provider specified") } - engine, engineErr := NewEyrieEngineForSettings(settings) + engine, engineErr := NewGraycodeRouterEngineForSettings(settings) if engineErr != nil { return nil, engineErr } @@ -812,18 +812,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 graycode-router catalog", provider) } func refreshModelCatalog(ctx context.Context, _ bool) (gateway.CatalogSnapshot, error) { - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() 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 graycode-router 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() @@ -840,7 +840,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 := NewGraycodeRouterEngineForSettings(settings) if err != nil { return "", err } diff --git a/internal/config/settings_status_test.go b/internal/config/settings_status_test.go index 7523a18e..a4636e51 100644 --- a/internal/config/settings_status_test.go +++ b/internal/config/settings_status_test.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) -func TestEnvKeyStatusUsesEyrieCredentialStatus(t *testing.T) { +func TestEnvKeyStatusUsesGraycodeRouterCredentialStatus(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..c6594cc5 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 graycode-router deployment has credentials. func HasConfiguredDeployment(ctx context.Context) bool { if ctx == nil { ctx = context.Background() } - engine, err := newEyrieEngine() + engine, err := newGraycodeRouterEngine() return err == nil && engine.EffectiveSelection(ctx, SelectionOptions{}).HasConfiguredDeployment } -// HasSelectedModel reports whether eyrie provider.json has a selected model. +// HasSelectedModel reports whether graycode-router 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 916ba342..de7719ee 100644 --- a/internal/config/setup_status_test.go +++ b/internal/config/setup_status_test.go @@ -5,9 +5,9 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/eyrie/catalog" - "github.com/GrayCodeAI/eyrie/credentials" "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/graycode-router/catalog" + "github.com/GrayCodeAI/graycode-router/credentials" ) func TestHasConfiguredDeployment_FromStore(t *testing.T) { diff --git a/internal/config/ui_cache.go b/internal/config/ui_cache.go index bf013cdd..8b531223 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 := newGraycodeRouterEngine() 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 2829cde0..95b09c77 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 GraycodeRouter. Do not hardcode/validate here. - // Validate model selection (stored in eyrie provider.json) + // Validate model selection (stored in graycode-router 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 32850017..887ab321 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 TestValidateSettingsProviderDelegatedToGraycodeRouter(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 graycode-router provider)") } } diff --git a/internal/config/xiaomi_setup_test.go b/internal/config/xiaomi_setup_test.go index 21cac067..d8b4b30d 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" + graycoderoutercfg "github.com/GrayCodeAI/graycode-router/config" ) func TestSetGatewayRegion_XiaomiClearsStaleBaseHost(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) t.Setenv("GRAYCODE_CONFIG_DIR", dir) - t.Setenv("EYRIE_CONFIG_DIR", dir) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", dir) t.Setenv("XIAOMI_MIMO_TOKEN_PLAN_BASE_URL", "https://caller-owned.example.test/v1") - cfg := &eyriecfg.ProviderConfig{ + cfg := &graycoderoutercfg.ProviderConfig{ Version: "1", XiaomiMimoTokenPlanRegion: "cn", XiaomiMimoTokenPlanBaseURL: "https://token-plan-cn.xiaomimimo.com/v1", } - if err := eyriecfg.SaveProviderConfig(cfg, ""); err != nil { + if err := graycoderoutercfg.SaveProviderConfig(cfg, ""); err != nil { t.Fatal(err) } if err := SetGatewayRegion(ProviderXiaomiTokenPlan, "sgp"); err != nil { t.Fatal(err) } - loaded := eyriecfg.LoadProviderConfig("") + loaded := graycoderoutercfg.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("GRAYCODE_CONFIG_DIR", dir) - t.Setenv("EYRIE_CONFIG_DIR", dir) + t.Setenv("GRAYCODE_ROUTER_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 := graycoderoutercfg.SaveProviderConfig(&graycoderoutercfg.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 3acd03e9..2b8f19fe 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 GraycodeRouter'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 GraycodeRouter's catalog, credentials, and model selection are ready. readyMu sync.RWMutex readyFn func() (bool, string) @@ -441,7 +441,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 GraycodeRouter 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) { @@ -454,7 +454,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, "GraycodeRouter readiness probe not configured" } func (s *Server) routes() { diff --git a/internal/daemon/daemon_test.go b/internal/daemon/daemon_test.go index e7807a2f..2265262e 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 GraycodeRouter 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 4dcdc6b6..695f436c 100644 --- a/internal/daemon/routes_sessions_test.go +++ b/internal/daemon/routes_sessions_test.go @@ -6,9 +6,9 @@ import ( "net/http" "testing" - contracts "github.com/GrayCodeAI/eyrie/tools" "github.com/GrayCodeAI/graycode-cli/internal/session" "github.com/GrayCodeAI/graycode-cli/internal/testutil" + contracts "github.com/GrayCodeAI/graycode-router/tools" ) // saveTestSession isolates session storage to a temp dir and persists a diff --git a/internal/engine/architect.go b/internal/engine/architect.go index bec83136..0bed7902 100644 --- a/internal/engine/architect.go +++ b/internal/engine/architect.go @@ -37,7 +37,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 graycode-router. type ArchitectMessage struct { Role string // "system", "user", "assistant" Content string diff --git a/internal/engine/async/engine_test.go b/internal/engine/async/engine_test.go index bd6d1e8a..3b14bc90 100644 --- a/internal/engine/async/engine_test.go +++ b/internal/engine/async/engine_test.go @@ -14,23 +14,23 @@ import ( // fakeClient is a scripted ChatClient for exercising the engine loop. type fakeClient struct { mu sync.Mutex - script []types.EyrieStreamEvent + script []types.GraycodeRouterStreamEvent streamStarted chan struct{} streamCtx context.Context } -func newFakeClient(events ...types.EyrieStreamEvent) *fakeClient { +func newFakeClient(events ...types.GraycodeRouterStreamEvent) *fakeClient { return &fakeClient{ script: events, streamStarted: make(chan struct{}), } } -func (f *fakeClient) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { - return &types.EyrieResponse{Content: "mock", FinishReason: "end_turn"}, nil +func (f *fakeClient) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { + return &types.GraycodeRouterResponse{Content: "mock", FinishReason: "end_turn"}, nil } -func (f *fakeClient) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { +func (f *fakeClient) StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { f.mu.Lock() f.streamCtx = ctx f.mu.Unlock() @@ -40,7 +40,7 @@ func (f *fakeClient) StreamChatContinue(ctx context.Context, messages []types.Ey close(f.streamStarted) } - ch := make(chan types.EyrieStreamEvent, 16) + ch := make(chan types.GraycodeRouterStreamEvent, 16) go func() { defer close(ch) for _, evt := range f.script { @@ -63,11 +63,11 @@ type cancelTurns struct { call int } -func (c *cancelTurns) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { - return &types.EyrieResponse{Content: "mock", FinishReason: "end_turn"}, nil +func (c *cancelTurns) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { + return &types.GraycodeRouterResponse{Content: "mock", FinishReason: "end_turn"}, nil } -func (c *cancelTurns) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { +func (c *cancelTurns) StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { select { case <-c.streamStarted: default: @@ -78,8 +78,8 @@ func (c *cancelTurns) StreamChatContinue(ctx context.Context, messages []types.E first := c.call == 1 c.mu.Unlock() - ch := make(chan types.EyrieStreamEvent, 4) - ch <- types.EyrieStreamEvent{Type: "content", Content: "thinking..."} + ch := make(chan types.GraycodeRouterStreamEvent, 4) + ch <- types.GraycodeRouterStreamEvent{Type: "content", Content: "thinking..."} if first { go func() { <-ctx.Done() @@ -118,9 +118,9 @@ func drainEvents(t *testing.T, ch <-chan *Event) []*Event { func TestEngineTurnEmitsTerminalDone(t *testing.T) { client := newFakeClient( - types.EyrieStreamEvent{Type: "content", Content: "hello"}, - types.EyrieStreamEvent{Type: "usage", Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}}, - types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}, + types.GraycodeRouterStreamEvent{Type: "content", Content: "hello"}, + types.GraycodeRouterStreamEvent{Type: "usage", Usage: &types.GraycodeRouterUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}}, + types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}, ) e := New(newTestSession(t, client)) e.Start(context.Background()) @@ -151,7 +151,7 @@ func TestEngineTurnEmitsTerminalDone(t *testing.T) { } func TestEngineStopThenStartProcessesAgain(t *testing.T) { - client := newFakeClient(types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}) + client := newFakeClient(types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}) e := New(newTestSession(t, client)) e.Start(context.Background()) @@ -211,7 +211,7 @@ func TestEngineCancelAbortsTurnAndKeepsRunning(t *testing.T) { func TestEngineStreamErrorEmitsErrorThenDone(t *testing.T) { client := newFakeClient( - types.EyrieStreamEvent{Type: "error", Error: "provider exploded"}, + types.GraycodeRouterStreamEvent{Type: "error", Error: "provider exploded"}, ) e := New(newTestSession(t, client)) e.Start(context.Background()) @@ -238,7 +238,7 @@ func TestEngineStreamErrorEmitsErrorThenDone(t *testing.T) { } func TestEngineReplyToReceivesTerminalEvent(t *testing.T) { - client := newFakeClient(types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}) + client := newFakeClient(types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}) e := New(newTestSession(t, client)) e.Start(context.Background()) defer e.Stop() diff --git a/internal/engine/branching/cascade.go b/internal/engine/branching/cascade.go index 38bf2d86..eade670f 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 graycode-router 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 graycode-router 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 f3a5673d..c5ea314c 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 graycode-router'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..185df71d 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 graycode-router-side. diff --git a/internal/engine/cache_gate_test.go b/internal/engine/cache_gate_test.go index 7653498b..fcb52f62 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.GraycodeRouterTool{ {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 0efa6196..be8c2708 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 graycode-router-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.GraycodeRouterTool, 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.GraycodeRouterTool) 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 30b6b1f7..2efbf18c 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.GraycodeRouterTool{{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 9e8e19c6..6a3c9874 100644 --- a/internal/engine/chat_provider.go +++ b/internal/engine/chat_provider.go @@ -11,7 +11,7 @@ import ( // BuildChatProvider adapts Graycode'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 GraycodeRouter'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("graycode-router 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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, 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 427d34fd..7bf5fe02 100644 --- a/internal/engine/chat_provider_test.go +++ b/internal/engine/chat_provider_test.go @@ -5,8 +5,8 @@ import ( "errors" "testing" - "github.com/GrayCodeAI/eyrie/llm" "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/graycode-router/llm" ) type recordingChatClient struct { @@ -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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { c.chatOptions = opts - return &types.EyrieResponse{Content: "ok"}, nil + return &types.GraycodeRouterResponse{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.GraycodeRouterMessage, opts types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { c.streamOptions = opts - events := make(chan types.EyrieStreamEvent) + events := make(chan types.GraycodeRouterStreamEvent) close(events) return llm.NewStreamResult(events, "", nil), nil } diff --git a/internal/engine/chat_replay.go b/internal/engine/chat_replay.go index ad7fe678..228b8e51 100644 --- a/internal/engine/chat_replay.go +++ b/internal/engine/chat_replay.go @@ -19,14 +19,14 @@ const replayCacheDirEnv = "GRAYCODE_REPLAY_CACHE_DIR" const replayFingerprintEnv = "GRAYCODE_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.GraycodeRouterMessage) 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 // GRAYCODE_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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, 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 6895f803..640466b1 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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, error) { *c.n++ if c.err != nil { return nil, c.err } - return &types.EyrieResponse{Content: c.resp}, nil + return &types.GraycodeRouterResponse{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.GraycodeRouterMessage, 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.GraycodeRouterStreamEvent, 1) + ch <- types.GraycodeRouterStreamEvent{Type: "done"} return &types.StreamResult{Events: ch}, nil } diff --git a/internal/engine/chat_service.go b/internal/engine/chat_service.go index 2aac5360..402dbaec 100644 --- a/internal/engine/chat_service.go +++ b/internal/engine/chat_service.go @@ -7,16 +7,16 @@ import ( "sync" "time" - "github.com/GrayCodeAI/eyrie/engine" "github.com/GrayCodeAI/graycode-cli/internal/observability/metrics" "github.com/GrayCodeAI/graycode-cli/internal/resilience/ratelimit" "github.com/GrayCodeAI/graycode-cli/internal/resilience/retry" "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/graycode-router/engine" ) // 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 +// GraycodeRouter client, provider/model identity, and compatibility-only rate/retry +// controls. Production facade clients own resilience inside GraycodeRouter. 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 graycode-router 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 graycode-router/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 graycode-router'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 graycode-router 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.GraycodeRouterTool) 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 (GRAYCODE_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 = shrinkGraycodeRouterTools(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 GraycodeRouterStreamEvent 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 +// GraycodeRouter 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 Graycode'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.GraycodeRouterMessage, 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.GraycodeRouterMessage) []types.GraycodeRouterMessage { if len(messages) <= emergencyCompactMin { return messages } - out := make([]types.EyrieMessage, 0, emergencyCompactWindow+1) + out := make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, 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 0a33a776..8952940f 100644 --- a/internal/engine/chat_service_test.go +++ b/internal/engine/chat_service_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/GrayCodeAI/eyrie/llm" "github.com/GrayCodeAI/graycode-cli/internal/resilience/retry" "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/graycode-router/llm" ) // TestChatService_BuildOptions checks that BuildOptions correctly @@ -137,7 +137,7 @@ func TestChatService_ChatDelegatesToClient(t *testing.T) { }) resp, err := svc.Chat( context.Background(), - []types.EyrieMessage{{Role: "user", Content: "hi"}}, + []types.GraycodeRouterMessage{{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.GraycodeRouterMessage, _ types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, _ 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.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, 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.GraycodeRouterMessage{{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.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, 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.GraycodeRouterStreamEvent) 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.GraycodeRouterMessage started bool } -func (*overflowThenCaptureClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { +func (*overflowThenCaptureClient) Chat(context.Context, []types.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, _ 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.GraycodeRouterMessage(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.GraycodeRouterMessage + messages = append(messages, types.GraycodeRouterMessage{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.GraycodeRouterMessage{Role: "user", Content: "message-" + itoaForTest(i)}) } client := &overflowThenCaptureClient{} diff --git a/internal/engine/client_interface.go b/internal/engine/client_interface.go index c3519835..9bdc9a44 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/graycode-cli/internal/types" + "github.com/GrayCodeAI/graycode-router/llm" ) // 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 GraycodeRouter 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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) + StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, 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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { + return &types.GraycodeRouterResponse{ Content: "mock test response", FinishReason: "end_turn", - Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}, + Usage: &types.GraycodeRouterUsage{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.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { + ch := make(chan types.GraycodeRouterStreamEvent, 5) + ch <- types.GraycodeRouterStreamEvent{Type: "content", Content: "mock streamed response"} + ch <- types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn", Usage: &types.GraycodeRouterUsage{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 5d514b56..a6f8bc13 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.GraycodeRouterMessage, 0, len(tail)+2) + keep = append(keep, types.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage) 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.GraycodeRouterMessage compactPrompt := BuildIncrementalCompactPrompt(prior) - summaryMsgs = append(summaryMsgs, types.EyrieMessage{ + summaryMsgs = append(summaryMsgs, types.GraycodeRouterMessage{ Role: "user", Content: compactPrompt + "\n\nConversation:\n", }) @@ -267,7 +267,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 graycode-router'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 61bd0df8..7049f46e 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.GraycodeRouterMessage{}) 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.GraycodeRouterMessage{}) if result { t.Error("expected false for empty message") } diff --git a/internal/engine/compact/api.go b/internal/engine/compact/api.go index 1f1ed3bb..cc6d93da 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.GraycodeRouterMessage, cfg APICompactConfig) []types.GraycodeRouterMessage { 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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 0423945f..003b83ee 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.GraycodeRouterMessage) { 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 1c29f6c4..13e40d59 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.GraycodeRouterMessage{ { 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.GraycodeRouterMessage{ {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 08b87ffb..b4b98172 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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 c1b89493..40593d24 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.GraycodeRouterMessage, cfg MicroCompactConfig) []types.GraycodeRouterMessage { 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.GraycodeRouterMessage, 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage, msgs []types.GraycodeRouterMessage) 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.GraycodeRouterMessage, 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 bdbea298..f21bfdd2 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.GraycodeRouterMessage) 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 36b1a93f..e349d65e 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) []types.GraycodeRouterMessage { + result := make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage) bool { if m.Role != "user" { return false } diff --git a/internal/engine/compact/split_test.go b/internal/engine/compact/split_test.go index a9516079..86c25828 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.GraycodeRouterMessage{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.GraycodeRouterMessage{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.GraycodeRouterMessage{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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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 652eadb6..ff00189b 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.GraycodeRouterMessage 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) 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 3349141b..34c7e143 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage startIdx int wantIdx int }{ @@ -42,7 +42,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "no tool pairs", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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 c26590d1..7adfb8ac 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 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 18c2836d..44c65a1d 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/graycode-cli/internal/types" ) -func segTestMessages() []types.EyrieMessage { - return []types.EyrieMessage{ +func segTestMessages() []types.GraycodeRouterMessage { + return []types.GraycodeRouterMessage{ {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 17d8d541..6e3214d3 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, tokenCount, threshold int) bool { return tokenCount >= threshold } diff --git a/internal/engine/compact_clear.go b/internal/engine/compact_clear.go index 0e4cc6ae..7a4c8d26 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.GraycodeRouterMessage, estimatedTokens, threshold int) ([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) []types.GraycodeRouterMessage { + out := make([]types.GraycodeRouterMessage, 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 e5245e2e..36b82568 100644 --- a/internal/engine/compact_clear_test.go +++ b/internal/engine/compact_clear_test.go @@ -6,12 +6,12 @@ import ( "github.com/GrayCodeAI/graycode-cli/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.GraycodeRouterMessage { + return types.GraycodeRouterMessage{Role: role, ToolResults: trs} } func TestClearOldToolResultsNoOpWhenBelowThreshold(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.GraycodeRouterMessage{ 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 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.GraycodeRouterMessage{ 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 cce6f3f4..a7877f65 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.GraycodeRouterMessage, 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 e9ea2341..1f4ae67e 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/graycode-cli/internal/types" + "github.com/GrayCodeAI/graycode-router/llm" ) // ProviderNativeCompactStrategy delegates provider-specific compaction to -// Eyrie. Graycode owns when conversation state is compacted and how the resulting -// summary is inserted; Eyrie owns credentials and provider transport details. +// GraycodeRouter. Graycode owns when conversation state is compacted and how the resulting +// summary is inserted; GraycodeRouter 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.GraycodeRouterMessage, 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.GraycodeRouterMessage(nil), messagesBefore[len(messagesBefore)-keepEnd:]...) + messages := append([]types.GraycodeRouterMessage{{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 91f1c318..bf1a8f96 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.GraycodeRouterMessage) 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.GraycodeRouterMessage, startIdx int) int { return compact.AdjustIndexToPreserveAPIInvariants(msgs, startIdx) } -func HasTextContent(m types.EyrieMessage) bool { return compact.HasTextContent(m) } +func HasTextContent(m types.GraycodeRouterMessage) bool { return compact.HasTextContent(m) } diff --git a/internal/engine/compact_regression_test.go b/internal/engine/compact_regression_test.go index 51e9bbe7..cd9e1d44 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.GraycodeRouterMessage { + msgs := make([]types.GraycodeRouterMessage, total) for i := range msgs { - msgs[i] = types.EyrieMessage{Role: "user", Content: "filler"} + msgs[i] = types.GraycodeRouterMessage{Role: "user", Content: "filler"} } - msgs[toolUseAt] = types.EyrieMessage{ + msgs[toolUseAt] = types.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{Role: "assistant", Content: "tail starts with assistant"} + msgs[31] = types.GraycodeRouterMessage{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.GraycodeRouterMessage{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.GraycodeRouterMessage{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.GraycodeRouterMessage(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.GraycodeRouterMessage, 40) for i := range msgs { if i%2 == 0 { - msgs[i] = types.EyrieMessage{Role: "user", Content: "short"} + msgs[i] = types.GraycodeRouterMessage{Role: "user", Content: "short"} } else { - msgs[i] = types.EyrieMessage{Role: "assistant", Content: "short reply"} + msgs[i] = types.GraycodeRouterMessage{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.GraycodeRouterMessage, 0, 500) for i := 0; i < 500; i++ { - msgs = append(msgs, types.EyrieMessage{Role: "user", Content: strings.Repeat("content ", 100)}) + msgs = append(msgs, types.GraycodeRouterMessage{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 b86fec71..7cfcbde4 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.GraycodeRouterMessage, 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 := toGraycodeRouterMessages(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.GraycodeRouterMessage, 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 toGraycodeRouterMessages(msgs []relevanceprune.Message) []types.GraycodeRouterMessage { + out := make([]types.GraycodeRouterMessage, len(msgs)) for i, m := range msgs { - out[i] = types.EyrieMessage{Role: m.Role, Content: m.Content} + out[i] = types.GraycodeRouterMessage{Role: m.Role, Content: m.Content} } return out } -func hasErrorResult(m types.EyrieMessage) bool { +func hasErrorResult(m types.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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 187015fd..917b7f2d 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.GraycodeRouterMessage 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.GraycodeRouterMessage{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.GraycodeRouterMessage{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.GraycodeRouterMessage{ {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 a3f56f25..e8223d21 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 0, len(kept)+2) + result = append(result, types.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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 6ee1fb1f..ae75cc29 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.GraycodeRouterMessage) 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.GraycodeRouterMessage(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.GraycodeRouterMessage, 0, len(remaining)+2) + keep = append(keep, types.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage) string { if len(messages) == 0 { return "" } - var summaryMsgs []types.EyrieMessage + var summaryMsgs []types.GraycodeRouterMessage 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage) 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.GraycodeRouterMessage + summaryMsgs = append(summaryMsgs, types.GraycodeRouterMessage{ 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 77400d10..4056cf90 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 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 a32943af..4144c5e9 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.GraycodeRouterMessage{{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.GraycodeRouterMessage, 10) for i := range big { - big[i] = types.EyrieMessage{Role: "user", Content: strings.Repeat("x", 10000)} + big[i] = types.GraycodeRouterMessage{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.GraycodeRouterMessage { + msgs := make([]types.GraycodeRouterMessage, n) for i := range msgs { if i%2 == 0 { - msgs[i] = types.EyrieMessage{Role: "user", Content: strings.Repeat("message ", 50)} + msgs[i] = types.GraycodeRouterMessage{Role: "user", Content: strings.Repeat("message ", 50)} } else { - msgs[i] = types.EyrieMessage{Role: "assistant", Content: strings.Repeat("response ", 50)} + msgs[i] = types.GraycodeRouterMessage{Role: "assistant", Content: strings.Repeat("response ", 50)} } } return msgs diff --git a/internal/engine/context_compaction.go b/internal/engine/context_compaction.go index 645fa88d..b5756b70 100644 --- a/internal/engine/context_compaction.go +++ b/internal/engine/context_compaction.go @@ -134,7 +134,7 @@ func (s *Session) checkpointManager() *session.CheckpointManager { return p.CheckpointManager() } -func rawToSessionMessages(raw []types.EyrieMessage) []session.Message { +func rawToSessionMessages(raw []types.GraycodeRouterMessage) []session.Message { return session.FromRuntimeMessages(raw) } diff --git a/internal/engine/context_compaction_test.go b/internal/engine/context_compaction_test.go index e658b3b4..012c14c9 100644 --- a/internal/engine/context_compaction_test.go +++ b/internal/engine/context_compaction_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" - "github.com/GrayCodeAI/eyrie/credentials" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-router/credentials" + graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" ) func TestContextUsedTokens_PrefersAPI(t *testing.T) { @@ -21,21 +21,21 @@ func TestContextUsedTokens_PrefersAPI(t *testing.T) { } } -func TestNativeCompactionSupportUsesEyrieCredentialStore(t *testing.T) { +func TestNativeCompactionSupportUsesGraycodeRouterCredentialStore(t *testing.T) { ctx := context.Background() store := &credentials.MapStore{} - runtime, err := eyrieengine.New(eyrieengine.Options{SecretStore: store}) + runtime, err := graycoderouterengine.New(graycoderouterengine.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 GraycodeRouter 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 GraycodeRouter's injected credential store") } } diff --git a/internal/engine/context_governor_test.go b/internal/engine/context_governor_test.go index f6052b39..abc84add 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.GraycodeRouterMessage{ {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 eb193b75..a4fb8a0a 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.GraycodeRouterMessage // 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.GraycodeRouterMessage) { // Copy messages to avoid mutation from caller - snapshot := make([]types.EyrieMessage, len(msgs)) + snapshot := make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage { if dp == nil { return nil } // Return a copy to prevent mutation - restored := make([]types.EyrieMessage, len(dp.Messages)) + restored := make([]types.GraycodeRouterMessage, 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 c840d15f..6a71b3d3 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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 d2a2f4fb..1ca443ab 100644 --- a/internal/engine/control_plane_test.go +++ b/internal/engine/control_plane_test.go @@ -71,19 +71,19 @@ func TestWorkModePlanFiltersToolsAndBash(t *testing.T) { } } -func TestLazyEyrieToolsAndPromote(t *testing.T) { +func TestLazyGraycodeRouterToolsAndPromote(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) + graycodeRouter := reg.GraycodeRouterTools() + if len(graycodeRouter) != 1 || graycodeRouter[0].Name != "Read" { + t.Fatalf("GraycodeRouterTools = %#v, want only Read", graycodeRouter) } 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)) + graycodeRouter = reg.GraycodeRouterTools() + if len(graycodeRouter) != 2 { + t.Fatalf("after promote GraycodeRouterTools len = %d", len(graycodeRouter)) } } diff --git a/internal/engine/conversation_adapter.go b/internal/engine/conversation_adapter.go index c01c097d..bd5c2c2b 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 graycode-router's // conversation management into graycode'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 ecd995b8..e4a4eba5 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.GraycodeRouterMessage) []types.GraycodeRouterMessage { if len(msgs) < 3 { return msgs } - result := make([]types.EyrieMessage, 0, len(msgs)) + result := make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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 ce712407..cef35b3c 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {Role: "user", Content: "hi"}, } result := CollapseRepeatedMessages(msgs) diff --git a/internal/engine/engine.go b/internal/engine/engine.go index 0d76f7c8..30778009 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.GraycodeRouterMessage, 0, len(raw)-(cutEnd-cutStart)+1) keep = append(keep, raw[:cutStart]...) - keep = append(keep, types.EyrieMessage{ + keep = append(keep, types.GraycodeRouterMessage{ 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 37d8f02b..9e39825a 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.GraycodeRouterResponse{ 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.GraycodeRouterUsage{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 d3d9b433..5998f9ea 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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 e5b63f7e..65764264 100644 --- a/internal/engine/engine_token_helpers.go +++ b/internal/engine/engine_token_helpers.go @@ -6,7 +6,7 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/types" ) -func isRecentToolHeavy(messages []types.EyrieMessage) bool { +func isRecentToolHeavy(messages []types.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) string { if isRecentToolHeavy(messages) { return "tool" } diff --git a/internal/engine/execution_graph_observations.go b/internal/engine/execution_graph_observations.go index fa99b14d..adea312e 100644 --- a/internal/engine/execution_graph_observations.go +++ b/internal/engine/execution_graph_observations.go @@ -7,13 +7,13 @@ import ( "strings" "time" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" - eyriegraph "github.com/GrayCodeAI/eyrie/graph" graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" policycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" "github.com/GrayCodeAI/graycode-cli/internal/engine/token" "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" "github.com/GrayCodeAI/graycode-cli/internal/types" + graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" + graycoderoutergraph "github.com/GrayCodeAI/graycode-router/graph" shrikegraph "github.com/GrayCodeAI/shrike/graph" ) @@ -291,10 +291,10 @@ func (s *Session) shrikeUsageCanProceed() (bool, string) { return tracker.CanProceed() } -func (s *Session) recordEyrieOperationObservation( +func (s *Session) recordGraycodeRouterOperationObservation( provider, model, finishReason, content string, toolCallCount int, - usage *types.EyrieUsage, + usage *types.GraycodeRouterUsage, ) { sessionID := s.executionGraphSessionID() if sessionID == "" || usage == nil { @@ -310,19 +310,19 @@ func (s *Session) recordEyrieOperationObservation( } observedAt := time.Now().UTC() route := types.ResolvedRoute{Provider: provider, Model: model} - export, err := eyrieengine.BuildOperationsGraph(eyrieengine.OperationsGraphInput{ + export, err := graycoderouterengine.BuildOperationsGraph(graycoderouterengine.OperationsGraphInput{ Route: &route, Usage: usage, FinishReason: finishReason, Content: content, ToolCallCount: toolCallCount, ObservedAt: observedAt, - Scope: eyriegraph.Scope{RepositoryID: repositoryID}, + Scope: graycoderoutergraph.Scope{RepositoryID: repositoryID}, CorrelationID: sessionID, }) if err == nil { err = graphjournal.AppendRuntimeGraph( - sessionID, "", "model-generation", "eyrie", + sessionID, "", "model-generation", "graycode-router", toContractNodes(export.Nodes), toContractEdges(export.Edges), toContractEvents(export.Events), observedAt, ) } @@ -334,11 +334,11 @@ func (s *Session) recordEyrieOperationObservation( } } -// The following helpers convert Eyrie's vendored graph contract types into +// The following helpers convert GraycodeRouter's vendored graph contract types into // Graycode'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 []graycoderoutergraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { out[i] = toContractNode(n) @@ -346,7 +346,7 @@ func toContractNodes(nodes []eyriegraph.Node) []graphcontracts.Node { return out } -func toContractNode(n eyriegraph.Node) graphcontracts.Node { +func toContractNode(n graycoderoutergraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), @@ -358,7 +358,7 @@ func toContractNode(n eyriegraph.Node) graphcontracts.Node { } } -func toContractEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { +func toContractEdges(edges []graycoderoutergraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { out[i] = toContractEdge(e) @@ -366,7 +366,7 @@ func toContractEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { return out } -func toContractEdge(e eyriegraph.Edge) graphcontracts.Edge { +func toContractEdge(e graycoderoutergraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), @@ -380,7 +380,7 @@ func toContractEdge(e eyriegraph.Edge) graphcontracts.Edge { } } -func toContractEvents(events []eyriegraph.Event) []graphcontracts.Event { +func toContractEvents(events []graycoderoutergraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { out[i] = toContractEvent(ev) @@ -388,7 +388,7 @@ func toContractEvents(events []eyriegraph.Event) []graphcontracts.Event { return out } -func toContractEvent(ev eyriegraph.Event) graphcontracts.Event { +func toContractEvent(ev graycoderoutergraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), @@ -402,15 +402,15 @@ func toContractEvent(ev eyriegraph.Event) graphcontracts.Event { } } -func toContractRef(r eyriegraph.Ref) graphcontracts.Ref { +func toContractRef(r graycoderoutergraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func toContractScope(s eyriegraph.Scope) graphcontracts.Scope { +func toContractScope(s graycoderoutergraph.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 graycoderoutergraph.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 f2be5932..ad8c76c1 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 TestGraycodeRouterOperationObservationIsPrivacySafe(t *testing.T) { t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) - sess.SetPersistID("eyrie-runtime-session") - sess.recordEyrieOperationObservation( + sess.SetPersistID("graycode-router-runtime-session") + sess.recordGraycodeRouterOperationObservation( "private-provider", "private/model", "stop", "private generated content", 2, - &types.EyrieUsage{PromptTokens: 100, CompletionTokens: 20, TotalTokens: 120}, + &types.GraycodeRouterUsage{PromptTokens: 100, CompletionTokens: 20, TotalTokens: 120}, ) - entries, err := graphjournal.Load("eyrie-runtime-session") + entries, err := graphjournal.Load("graycode-router-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("GraycodeRouter graph observation leaked %q", secret) } } } diff --git a/internal/engine/history/file_mentions.go b/internal/engine/history/file_mentions.go index 3183939b..5c40f3b5 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.GraycodeRouterMessage) 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 06eb660a..65e8eb31 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.GraycodeRouterMessage{} // 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.GraycodeRouterMessage{ {Role: "user", Content: "I'm working on src/auth.go"}, } diff --git a/internal/engine/integration.go b/internal/engine/integration.go index ac8b2d64..2d3bf54f 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) *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.GraycodeRouterMessage) 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.GraycodeRouterMessage) int { total := 0 for _, m := range messages { total += EstimateStringTokens(m.Content) diff --git a/internal/engine/journal.go b/internal/engine/journal.go index 0cd29e4a..1e6a54a4 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.GraycodeRouterMessage) { 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.GraycodeRouterMessage) { 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.GraycodeRouterMessage { 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.GraycodeRouterMessage, 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.GraycodeRouterMessage 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.GraycodeRouterMessage) 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.GraycodeRouterMessage { + out := types.GraycodeRouterMessage{ 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.GraycodeRouterMessage) types.GraycodeRouterMessage { + clones := cloneMessages([]types.GraycodeRouterMessage{m}) return clones[0] } diff --git a/internal/engine/journal_test.go b/internal/engine/journal_test.go index 71b2463f..d4b1df37 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.GraycodeRouterMessage{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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{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.GraycodeRouterMessage{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 8866c620..a6804e1c 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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 72ac91dd..664e0f82 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.GraycodeRouterMessage, 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 cdd43d61..5ce848f4 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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, 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.GraycodeRouterMessage{{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.GraycodeRouterMessage, 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 6293c95a..bf311b51 100644 --- a/internal/engine/memory_service.go +++ b/internal/engine/memory_service.go @@ -130,7 +130,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.GraycodeRouterMessage, success bool) { if s == nil { return } diff --git a/internal/engine/mock_client_test.go b/internal/engine/mock_client_test.go index e341a222..6b822ac1 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.GraycodeRouterResponse idx int calls []mockCall } type mockCall struct { method string - messages []types.EyrieMessage + messages []types.GraycodeRouterMessage } -func newMockClient(responses ...*types.EyrieResponse) *mockClient { +func newMockClient(responses ...*types.GraycodeRouterResponse) *mockClient { return &mockClient{responses: responses} } -func mockTextResponse(text string) *types.EyrieResponse { - return &types.EyrieResponse{ +func mockTextResponse(text string) *types.GraycodeRouterResponse { + return &types.GraycodeRouterResponse{ Content: text, FinishReason: "end_turn", - Usage: &types.EyrieUsage{PromptTokens: 50, CompletionTokens: 20, TotalTokens: 70}, + Usage: &types.GraycodeRouterUsage{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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, 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.GraycodeRouterStreamEvent, 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.GraycodeRouterStreamEvent{Type: "content", Content: content} + ch <- types.GraycodeRouterStreamEvent{ Type: "done", StopReason: finishReason, - Usage: &types.EyrieUsage{PromptTokens: 50, CompletionTokens: 20, TotalTokens: 70}, + Usage: &types.GraycodeRouterUsage{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 64a88854..dd55e5ee 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.GraycodeRouterMessage{ {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 f30e4a0f..011fd2cf 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.GraycodeRouterMessage // 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.GraycodeRouterMessage { 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.GraycodeRouterMessage) { 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.GraycodeRouterMessage { 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.GraycodeRouterMessage { 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.GraycodeRouterMessage{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.GraycodeRouterMessage) 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) { 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.GraycodeRouterMessage, 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.GraycodeRouterMessage, 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.GraycodeRouterMessage{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.GraycodeRouterMessage{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 graycode-router 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage) { 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.GraycodeRouterMessage) []types.GraycodeRouterMessage { if in == nil { return nil } - out := make([]types.EyrieMessage, len(in)) + out := make([]types.GraycodeRouterMessage, 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 da434b93..e75a01c2 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{{ 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.GraycodeRouterMessage{{ 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.GraycodeRouterMessage{{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 c1b33fe1..02aaad17 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.GraycodeRouterMessage{ Role: "assistant", Content: " ", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}, }) - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ 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.GraycodeRouterMessage{Role: "assistant", Content: "done"}) // Then a new turn started and was cancelled mid-flight: - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.GraycodeRouterMessage{ Role: "user", Content: "orphan result", ToolResults: []types.ToolResult{{ToolUseID: "x9", Content: "r"}}, }) - p.AppendAssistantJournaled(types.EyrieMessage{ + p.AppendAssistantJournaled(types.GraycodeRouterMessage{ 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.GraycodeRouterMessage{ Role: "assistant", Content: " ", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}, {ID: "t2", Name: "Read"}}, }) - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.GraycodeRouterMessage{ Role: "user", Content: "out1", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "out1"}}, diff --git a/internal/engine/prompt/prompt_extra_test.go b/internal/engine/prompt/prompt_extra_test.go index 91095132..fc7ce139 100644 --- a/internal/engine/prompt/prompt_extra_test.go +++ b/internal/engine/prompt/prompt_extra_test.go @@ -13,11 +13,11 @@ type mockLLMClient struct { err error } -func (m *mockLLMClient) Chat(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { +func (m *mockLLMClient) Chat(ctx context.Context, msgs []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { if m.err != nil { return nil, m.err } - return &types.EyrieResponse{Content: m.response}, nil + return &types.GraycodeRouterResponse{Content: m.response}, nil } func TestFormatFloat(t *testing.T) { diff --git a/internal/engine/prompt/prompt_optimizer.go b/internal/engine/prompt/prompt_optimizer.go index d5506434..abcad88b 100644 --- a/internal/engine/prompt/prompt_optimizer.go +++ b/internal/engine/prompt/prompt_optimizer.go @@ -15,7 +15,7 @@ import ( // LLMClient is the minimal interface needed for prompt optimization. type LLMClient interface { - Chat(ctx context.Context, msgs []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) + Chat(ctx context.Context, msgs []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) } type PromptParameter struct { @@ -142,7 +142,7 @@ func OptimizePrompt(ctx context.Context, llm LLMClient, model string, po *Prompt } prompt := ComputeGradientPrompt(paramName, p.Value, feedback, nil) - msgs := []types.EyrieMessage{{Role: "user", Content: prompt}} + msgs := []types.GraycodeRouterMessage{{Role: "user", Content: prompt}} resp, err := llm.Chat(ctx, msgs, types.ChatOptions{Model: model}) if err != nil { return "", err diff --git a/internal/engine/provider_chat_client.go b/internal/engine/provider_chat_client.go index dc201493..6a549751 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. +// GraycodeRouter transport setup failures at the first chat call. func NewUnavailableChatClient(err error) ChatClient { if err == nil { err = errors.New("graycode: 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.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage, 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 b7004938..fbbfd2fd 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.GraycodeRouterMessage, _ types.ChatOptions) (*types.GraycodeRouterResponse, error) { m.calls++ if m.err != nil { return nil, m.err } - return &types.EyrieResponse{Content: m.response}, nil + return &types.GraycodeRouterResponse{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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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 26de7dce..b634f973 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/graycode-cli/internal/types" + "github.com/GrayCodeAI/graycode-router/llm" ) type resilienceBoundaryClient struct { mu sync.Mutex - streams [][]types.EyrieStreamEvent - chatResponses []*types.EyrieResponse + streams [][]types.GraycodeRouterStreamEvent + chatResponses []*types.GraycodeRouterResponse streamCalls int chatCalls int - messages [][]types.EyrieMessage + messages [][]types.GraycodeRouterMessage } 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.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, error) { c.mu.Lock() defer c.mu.Unlock() c.chatCalls++ if len(c.chatResponses) == 0 { - return &types.EyrieResponse{}, nil + return &types.GraycodeRouterResponse{}, 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.GraycodeRouterMessage, _ 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.GraycodeRouterStreamEvent{{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.GraycodeRouterStreamEvent, 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.GraycodeRouterMessage { 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.GraycodeRouterMessage) []types.GraycodeRouterMessage { + out := make([]types.GraycodeRouterMessage, 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.GraycodeRouterStreamEvent{{Type: "error", Error: "temporary connection reset by peer"}} + success := []types.GraycodeRouterStreamEvent{ {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.GraycodeRouterStreamEvent{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.GraycodeRouterStreamEvent{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.GraycodeRouterStreamEvent{ {Type: "thinking", Thinking: "internal reasoning"}, {Type: "done", StopReason: "end_turn"}, } - fallback := &types.EyrieResponse{Content: "compatibility reply", FinishReason: "end_turn"} + fallback := &types.GraycodeRouterResponse{Content: "compatibility reply", FinishReason: "end_turn"} t.Run("engine facade does not trigger Graycode protocol recovery", func(t *testing.T) { base := &resilienceBoundaryClient{ - streams: [][]types.EyrieStreamEvent{thinkingOnly}, - chatResponses: []*types.EyrieResponse{fallback}, + streams: [][]types.GraycodeRouterStreamEvent{thinkingOnly}, + chatResponses: []*types.GraycodeRouterResponse{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.GraycodeRouterStreamEvent{thinkingOnly}, + chatResponses: []*types.GraycodeRouterResponse{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.GraycodeRouterStreamEvent{ {Type: "content", Content: "partial"}, {Type: "done", StopReason: "max_tokens"}, } - finished := []types.EyrieStreamEvent{ + finished := []types.GraycodeRouterStreamEvent{ {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.GraycodeRouterStreamEvent{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.GraycodeRouterStreamEvent{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_KeepsGraycodeToolAuthorizationAndMutation(t *testing.T) { - base := &resilienceBoundaryClient{streams: [][]types.EyrieStreamEvent{ + base := &resilienceBoundaryClient{streams: [][]types.GraycodeRouterStreamEvent{ { {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 45601fad..fe163de9 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.GraycodeRouterMessage { + return []types.GraycodeRouterMessage{ {Role: "user", Content: prompt}, } } diff --git a/internal/engine/session.go b/internal/engine/session.go index 4d9778e9..a2b81049 100644 --- a/internal/engine/session.go +++ b/internal/engine/session.go @@ -41,7 +41,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 graycode-router. // The mu RWMutex protects the remaining session metadata for concurrent // access. Transcript and system-context state are owned by PersistenceService. // @@ -160,7 +160,7 @@ type Session struct { isolation IsolationProfile } -// NewSession creates a conversation session through Eyrie's engine facade. +// NewSession creates a conversation session through GraycodeRouter's engine facade. func NewSession(provider, model, systemPrompt string, registry *tool.Registry) *Session { return NewGraycodeSession(context.Background(), gateway.Selection{ Provider: provider, @@ -683,10 +683,10 @@ func (s *Session) ForkConversation(nodeID string) (string, error) { if err != nil { return "", err } - msgs := make([]types.EyrieMessage, 0, len(history)) + msgs := make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage{Role: node.Role, Content: node.Content}) } } p.SetRawMessages(msgs) @@ -710,10 +710,10 @@ func (s *Session) SwitchBranch(nodeID string) error { if err != nil { return err } - msgs := make([]types.EyrieMessage, 0, len(history)) + msgs := make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage{Role: node.Role, Content: node.Content}) } } p.SetRawMessages(msgs) @@ -1011,7 +1011,7 @@ func (s *Session) Cwd() string { // effective policy change. Unchanged requests add nothing. System prompt is // unchanged across mode switches (KV-cache stability). func (s *Session) EnsureSandboxPolicyStatement() string { - if s == nil || s.tools == nil || s.tools.Registry() == nil || len(s.tools.Registry().EyrieTools()) == 0 { + if s == nil || s.tools == nil || s.tools.Registry() == nil || len(s.tools.Registry().GraycodeRouterTools()) == 0 { return "" } @@ -1044,7 +1044,7 @@ func (s *Session) EnsureSandboxPolicyStatement() string { if stmt != last { if p := s.Persistence(); p != nil { - p.AppendUserJournaled(types.EyrieMessage{Role: "user", Content: stmt}) + p.AppendUserJournaled(types.GraycodeRouterMessage{Role: "user", Content: stmt}) } s.mu.Lock() s.lastSandboxStatement = stmt @@ -1110,7 +1110,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.GraycodeRouterMessage{Role: "user", Content: msg}) } s.mu.Lock() s.lastSkillCatalogDigest = digest @@ -1127,7 +1127,7 @@ func (s *Session) CostValue() *Cost { return &s.Cost } -func (s *Session) LoadMessages(msgs []types.EyrieMessage) { +func (s *Session) LoadMessages(msgs []types.GraycodeRouterMessage) { s.Persistence().SetRawMessages(msgs) } @@ -1141,7 +1141,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.GraycodeRouterMessage { if p := s.Persistence(); p != nil { return p.RawMessages() } @@ -1150,7 +1150,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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { if s.ChatLLM() == nil { return nil, fmt.Errorf("session: no LLM client configured") } @@ -1179,7 +1179,7 @@ func (s *Session) Schedule() *schedule.Manager { Priority: 1, }) } else { - p.AppendUserJournaled(types.EyrieMessage{ + p.AppendUserJournaled(types.GraycodeRouterMessage{ Role: "user", Content: content, }) diff --git a/internal/engine/session_factory.go b/internal/engine/session_factory.go index f2c973bf..b321cfb0 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("graycode-router 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("graycode-router 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 graycodeconfig.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("graycode-router transport: %w", err) } return buildChatClientWithRuntime(ctx, modelRuntime, selection, legacyProvider) } @@ -96,7 +96,7 @@ func NewGraycodeSession(ctx context.Context, selection gateway.Selection, provid } // NewGraycodeSessionForSettings constructs a session with an invocation-scoped -// Eyrie engine, including custom gateways from an explicit settings file. +// GraycodeRouter engine, including custom gateways from an explicit settings file. func NewGraycodeSessionForSettings(ctx context.Context, settings graycodeconfig.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 508de3a2..ba5c61b9 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.GraycodeRouterMessage{ {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 187fd0d2..d766d0c3 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.GraycodeRouterMessage{ {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 8ce25b0c..51bcf1be 100644 --- a/internal/engine/stream.go +++ b/internal/engine/stream.go @@ -125,7 +125,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().GraycodeRouterTools() } } return opts @@ -293,7 +293,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.GraycodeRouterMessage{Role: "assistant", Content: preResult.CachedResponse}) emit(StreamEvent{Type: "done"}) return } @@ -467,7 +467,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. // Graycode records product-level latency; provider health and circuit - // breaking are owned by Eyrie's routed transport. + // breaking are owned by GraycodeRouter's routed transport. apiStart := time.Now() managesResilience := clientManagesResilience(s.ChatLLM().Client()) result, err := s.ChatLLM().Stream(ctx, s.Persistence().RawMessages(), opts) @@ -490,13 +490,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.GraycodeRouterUsage var usageLedger streamUsageLedger resolvedProvider := strings.TrimSpace(s.ChatLLM().Provider()) resolvedModel := strings.TrimSpace(activeModel) // Compatibility clients retain Graycode's historical stream retry and - // reasoning-only recovery. Eyrie facade clients already normalize and + // reasoning-only recovery. GraycodeRouter facade clients already normalize and // recover provider streams, so Graycode must consume their result exactly once. const maxStreamRetries = 2 var streamErr error @@ -679,7 +679,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.GraycodeRouterUsage{ PromptTokens: inputTokens, CompletionTokens: completionEst, } @@ -698,7 +698,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.recordGraycodeRouterOperationObservation( resolvedProvider, resolvedModel, stopReason, @@ -760,14 +760,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. GraycodeRouter's engine facade owns // continuation and exposes one normalized stream to Graycode. 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.GraycodeRouterMessage{Role: "assistant", Content: textContent.String()}) + s.Persistence().AppendUserJournaled(types.GraycodeRouterMessage{Role: "user", Content: "Continue from where you left off."}) continue } @@ -790,7 +790,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.GraycodeRouterMessage{Role: "assistant", Content: textContent.String()}) // Auto-remember corrections and learnings. Best-effort // fire-and-forget: the memory backend's Remember does not yet // accept a context, so this goroutine cannot be cancelled mid-call. @@ -807,7 +807,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Sleeptime: background memory consolidation if s.MemorySvc().Sleeptime() != nil && s.MemorySvc().Sleeptime().ShouldRun() && s.MemorySvc().Harrier() != nil && s.MemorySvc().Harrier().Ready() { // Snapshot messages to avoid data race with main loop appending - msgs := make([]types.EyrieMessage, len(s.Persistence().RawMessages())) + msgs := make([]types.GraycodeRouterMessage, len(s.Persistence().RawMessages())) copy(msgs, s.Persistence().RawMessages()) go func() { var transcript []string @@ -822,7 +822,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Use timeout context to prevent goroutine leak if LLM hangs sCtx, sCancel := context.WithTimeout(context.WithoutCancel(ctx), 2*time.Minute) defer sCancel() - resp, err := s.ChatLLM().Chat(sCtx, []types.EyrieMessage{ + resp, err := s.ChatLLM().Chat(sCtx, []types.GraycodeRouterMessage{ {Role: "user", Content: prompt}, }, types.ChatOptions{Provider: s.ChatLLM().Provider(), Model: s.ChatLLM().Model(), MaxTokens: 2048}) if err != nil || resp == nil { @@ -836,7 +836,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Skill distillation: extract reusable skill from multi-turn tasks if s.MemorySvc().SkillDistiller() != nil && toolTurns >= 5 && s.MemorySvc().Harrier() != nil && s.MemorySvc().Harrier().Ready() { // Snapshot messages to avoid data race with main loop appending - msgs := make([]types.EyrieMessage, len(s.Persistence().RawMessages())) + msgs := make([]types.GraycodeRouterMessage, len(s.Persistence().RawMessages())) copy(msgs, s.Persistence().RawMessages()) // Snapshot the tool/file sets too, so the goroutine never // reads the live maps while the main loop writes them on a @@ -861,7 +861,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Use timeout context to prevent goroutine leak if LLM hangs dCtx, dCancel := context.WithTimeout(context.WithoutCancel(ctx), 2*time.Minute) defer dCancel() - resp, err := s.ChatLLM().Chat(dCtx, []types.EyrieMessage{ + resp, err := s.ChatLLM().Chat(dCtx, []types.GraycodeRouterMessage{ {Role: "user", Content: prompt}, }, types.ChatOptions{Provider: s.ChatLLM().Provider(), Model: s.ChatLLM().Model(), MaxTokens: 2048}) if err != nil || resp == nil { @@ -1021,7 +1021,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.GraycodeRouterMessage{ Role: "assistant", Content: assistContent, ToolUse: toolCalls, @@ -1035,7 +1035,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { resultContent = "(no output)" } resultContent = s.redactToolResult(resultContent) - msg := types.EyrieMessage{ + msg := types.GraycodeRouterMessage{ Role: "user", Content: resultContent, ToolResults: []types.ToolResult{{ @@ -1057,7 +1057,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.GraycodeRouterMessage{ Role: "user", Content: "[User guidance during execution]: " + steer.Content, }) @@ -1171,7 +1171,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.GraycodeRouterMessage) 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 f9c9ffe0..951d7e5f 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.GraycodeRouterMessage{ {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 2ef1a48a..8a3ee960 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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 1c82c9dc..121be300 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 graycode-router's parseRetryDelay, but kept local so +// the engine package does not reach past the graycode-router 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 33bfa05a..893096d8 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.GraycodeRouterMessage{{Role: "user", Content: "hi"}} + used := []types.GraycodeRouterMessage{ {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 7561c529..08e0c7ec 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.GraycodeRouterUsage } -func (l *streamUsageLedger) shouldRecord(usage *types.EyrieUsage, terminal bool) bool { +func (l *streamUsageLedger) shouldRecord(usage *types.GraycodeRouterUsage, 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.GraycodeRouterUsage) bool { if a == nil || b == nil { return false } return *a == *b } -func cloneStreamUsage(usage *types.EyrieUsage) *types.EyrieUsage { +func cloneStreamUsage(usage *types.GraycodeRouterUsage) *types.GraycodeRouterUsage { 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 GraycodeRouter, 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 0a49966e..7059acf1 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.GraycodeRouterUsage{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.GraycodeRouterUsage{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.GraycodeRouterUsage{PromptTokens: 13, CompletionTokens: 7, TotalTokens: 20} + second := &types.GraycodeRouterUsage{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 deb6a3cf..5e9956f5 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 graycode-router'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.GraycodeRouterMessage, opts types.ChatOptions, schema string) (*types.GraycodeRouterResponse, 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.GraycodeRouterMessage(nil), msgs...) retryMsgs = append( retryMsgs, - types.EyrieMessage{Role: "assistant", Content: responseText(resp)}, - types.EyrieMessage{Role: "user", Content: fmt.Sprintf( + types.GraycodeRouterMessage{Role: "assistant", Content: responseText(resp)}, + types.GraycodeRouterMessage{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.GraycodeRouterResponse) string { if resp == nil { return "" } diff --git a/internal/engine/structured_output_test.go b/internal/engine/structured_output_test.go index 2b03777c..d93ca446 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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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 1eb650e8..baab8577 100644 --- a/internal/engine/sub_service_wiring_test.go +++ b/internal/engine/sub_service_wiring_test.go @@ -100,7 +100,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 graycode-router 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 2af7279e..153a8036 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.GraycodeRouterMessage) (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.GraycodeRouterMessage, len(conversationSoFar)+1) copy(msgs, conversationSoFar) - msgs[len(msgs)-1] = types.EyrieMessage{ + msgs[len(msgs)-1] = types.GraycodeRouterMessage{ Role: "user", Content: SynthesisPrompt, } diff --git a/internal/engine/subagent_synthesis_test.go b/internal/engine/subagent_synthesis_test.go index 6f729ca6..605c2888 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{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.GraycodeRouterMessage 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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { m.calls++ - m.messages = append([]types.EyrieMessage(nil), messages...) + m.messages = append([]types.GraycodeRouterMessage(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.GraycodeRouterResponse{Content: m.response, FinishReason: "end_turn"}, nil } diff --git a/internal/engine/title.go b/internal/engine/title.go index c8277117..59963ca1 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.GraycodeRouterMessage{ { Role: "user", Content: titlePrompt, diff --git a/internal/engine/token/token_budget.go b/internal/engine/token/token_budget.go index 1302ea95..83a3f5be 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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 e6da81fe..ba59f0f9 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage want bool }{ { @@ -162,7 +162,7 @@ func TestIsRecentToolHeavy(t *testing.T) { }, { name: "only user messages", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{ {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.GraycodeRouterMessage want bool }{ { @@ -215,35 +215,35 @@ func TestIsTextQuestion(t *testing.T) { }, { name: "question mark", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {Role: "user", Content: "Is this working?"}, }, want: true, }, { name: "what prefix", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {Role: "user", Content: "What does this function do"}, }, want: true, }, { name: "explain prefix", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {Role: "user", Content: "Explain the architecture"}, }, want: true, }, { name: "imperative command", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {Role: "user", Content: "Implement the REST API"}, }, want: false, }, { name: "skips tool results", - msgs: []types.EyrieMessage{ + msgs: []types.GraycodeRouterMessage{ {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 915242a0..95ab4433 100644 --- a/internal/engine/token/token_estimates.go +++ b/internal/engine/token/token_estimates.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/types" ) -func EstimateTokens(msgs []types.EyrieMessage) int { +func EstimateTokens(msgs []types.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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 882aa32c..4c75039d 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) int { return token.EstimateTokens(msgs) } +func EstimateMessageTokens(m types.GraycodeRouterMessage) int { return token.EstimateMessageTokens(m) } diff --git a/internal/engine/tool_catalog_shrink.go b/internal/engine/tool_catalog_shrink.go index 5b923e1b..d52ec3fd 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 graycode tool list via shrike's toolschema +// shrinkGraycodeRouterTools compresses the graycode tool list via shrike'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 shrinkGraycodeRouterTools(tools []types.GraycodeRouterTool) []types.GraycodeRouterTool { 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.GraycodeRouterTool `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.GraycodeRouterTool, 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 1ac5a021..6ea42e74 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/graycode-cli/internal/types" ) -func bloatedTools() []types.EyrieTool { - return []types.EyrieTool{ +func bloatedTools() []types.GraycodeRouterTool { + return []types.GraycodeRouterTool{ { 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 TestShrinkGraycodeRouterToolsDisabledByDefault(t *testing.T) { t.Setenv("GRAYCODE_TOOL_SHRINK", "") in := bloatedTools() - out := shrinkEyrieTools(in) + out := shrinkGraycodeRouterTools(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 TestShrinkGraycodeRouterToolsEnabledReducesAndPreservesNames(t *testing.T) { t.Setenv("GRAYCODE_TOOL_SHRINK", "1") t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) in := bloatedTools() - out := shrinkEyrieTools(in) + out := shrinkGraycodeRouterTools(in) if len(out) != 2 { t.Fatalf("tool count changed: %d", len(out)) } @@ -82,7 +82,7 @@ func TestOriginalCatalogPersistedForRecovery(t *testing.T) { t.Setenv("GRAYCODE_TOOL_SHRINK", "1") t.Setenv("GRAYCODE_STATE_DIR", stateDir) in := bloatedTools() - _ = shrinkEyrieTools(in) + _ = shrinkGraycodeRouterTools(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 ece037f2..87d8e6ee 100644 --- a/internal/engine/tool_service.go +++ b/internal/engine/tool_service.go @@ -454,7 +454,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.GraycodeRouterMessage{{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 9b5622e1..38678a38 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.GraycodeRouterMessage 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.GraycodeRouterMessage) string { var b strings.Builder var attempted []string var failures []string diff --git a/internal/engine/trajectory/trajectory.go b/internal/engine/trajectory/trajectory.go index 1ac63dd6..91594ba4 100644 --- a/internal/engine/trajectory/trajectory.go +++ b/internal/engine/trajectory/trajectory.go @@ -16,7 +16,7 @@ const bytesPerToken = 4 // estTokens estimates the token cost of a message from its text plus tool // payload sizes. -func estTokens(m types.EyrieMessage) int { +func estTokens(m types.GraycodeRouterMessage) int { n := len(m.Content) for _, tu := range m.ToolUse { if s, ok := tu.Arguments["_raw"]; ok { @@ -45,7 +45,7 @@ func estTokens(m types.EyrieMessage) int { // // Returns (msgs, false) unchanged when already under budget or there is no // compressible middle. -func CompressTrajectory(msgs []types.EyrieMessage, targetTokens, protectFirst, protectLast int) ([]types.EyrieMessage, bool) { +func CompressTrajectory(msgs []types.GraycodeRouterMessage, targetTokens, protectFirst, protectLast int) ([]types.GraycodeRouterMessage, bool) { if len(msgs) == 0 || targetTokens <= 0 { return msgs, false } @@ -72,9 +72,9 @@ func CompressTrajectory(msgs []types.EyrieMessage, targetTokens, protectFirst, p middle := msgs[protectFirst : len(msgs)-protectLast] summary := summarizeMiddle(middle) - out := make([]types.EyrieMessage, 0, len(head)+len(tail)+1) + out := make([]types.GraycodeRouterMessage, 0, len(head)+len(tail)+1) out = append(out, head...) - out = append(out, types.EyrieMessage{ + out = append(out, types.GraycodeRouterMessage{ Role: "user", Content: "[compressed middle: " + summary + "]", }) @@ -85,7 +85,7 @@ func CompressTrajectory(msgs []types.EyrieMessage, targetTokens, protectFirst, p // summarizeMiddle reduces the middle region to a compact one-line digest. It // counts turns and tool calls and captures the last user request, which is the // most task-relevant signal for a human checkpoint. -func summarizeMiddle(middle []types.EyrieMessage) string { +func summarizeMiddle(middle []types.GraycodeRouterMessage) string { turns := len(middle) toolCalls := 0 lastUser := "" diff --git a/internal/engine/trajectory/trajectory_test.go b/internal/engine/trajectory/trajectory_test.go index 01fe3469..d16ab7de 100644 --- a/internal/engine/trajectory/trajectory_test.go +++ b/internal/engine/trajectory/trajectory_test.go @@ -7,8 +7,8 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/types" ) -func bigToolMsg(payload string) types.EyrieMessage { - return types.EyrieMessage{ +func bigToolMsg(payload string) types.GraycodeRouterMessage { + return types.GraycodeRouterMessage{ Role: "assistant", ToolUse: []types.ToolCall{{Name: "Bash", Arguments: map[string]interface{}{"command": payload}}}, ToolResults: []types.ToolResult{{Content: payload}}, @@ -16,7 +16,7 @@ func bigToolMsg(payload string) types.EyrieMessage { } func TestUnderBudgetUnchanged(t *testing.T) { - msgs := []types.EyrieMessage{{Role: "user", Content: "hi"}} + msgs := []types.GraycodeRouterMessage{{Role: "user", Content: "hi"}} out, changed := CompressTrajectory(msgs, 10000, 2, 2) if changed || len(out) != 1 { t.Fatal("small trajectory must be unchanged") @@ -24,7 +24,7 @@ func TestUnderBudgetUnchanged(t *testing.T) { } func TestProtectHeadTailCompressMiddle(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.GraycodeRouterMessage{ {Role: "user", Content: "start"}, bigToolMsg(strings.Repeat("a", 4000)), // head turn with tool call {Role: "user", Content: strings.Repeat("b", 3000)}, @@ -59,7 +59,7 @@ func TestProtectHeadTailCompressMiddle(t *testing.T) { } func TestNoMiddleWhenProtectCoversAll(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.GraycodeRouterMessage{ {Role: "user", Content: "a"}, {Role: "user", Content: strings.Repeat("b", 5000)}, {Role: "user", Content: "c"}, @@ -71,7 +71,7 @@ func TestNoMiddleWhenProtectCoversAll(t *testing.T) { } func TestSummaryCapturesLatestRequest(t *testing.T) { - msgs := []types.EyrieMessage{ + msgs := []types.GraycodeRouterMessage{ {Role: "user", Content: "old"}, {Role: "user", Content: "the real request here"}, {Role: "assistant", Content: strings.Repeat("x", 3000)}, diff --git a/internal/engine/trajectory_test.go b/internal/engine/trajectory_test.go index 86411bd6..81a7d78c 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.GraycodeRouterMessage{ { 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage, 10)}, + {ID: 2, Success: true, Messages: make([]types.GraycodeRouterMessage, 5)}, + {ID: 3, Success: false, Messages: make([]types.GraycodeRouterMessage, 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.GraycodeRouterMessage, 5)}, + {ID: 2, Success: false, Messages: make([]types.GraycodeRouterMessage, 12)}, + {ID: 3, Success: false, Messages: make([]types.GraycodeRouterMessage, 8)}, } best := td.BestRun(runs) diff --git a/internal/engine/vision.go b/internal/engine/vision.go index 7067c741..923c4599 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. graycode-router carries image data on GraycodeRouterMessage.Images but does not expose a // per-model capability flag, so graycode 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 graycode-router/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 GraycodeRouterMessage.Images, which graycode-router 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.GraycodeRouterMessage{ 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 graycode-router 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 36fee81c..6ec16b04 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 GraycodeRouter's provider profiles +// (graycode-router/config/profiles.go). Keeping the list explicit here avoids +// importing graycode-router 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..b5c677f7 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 graycode-router, "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/graycodeerr/classify.go b/internal/graycodeerr/classify.go index 4a44623b..f4de6549 100644 --- a/internal/graycodeerr/classify.go +++ b/internal/graycodeerr/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". graycode-router 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/graycodeerr/classify_extra_test.go b/internal/graycodeerr/classify_extra_test.go index 44e90a0c..9225733c 100644 --- a/internal/graycodeerr/classify_extra_test.go +++ b/internal/graycodeerr/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("graycode-router: 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 graycode-router 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/multiagent/worker.go b/internal/multiagent/worker.go index a7319021..19dcad71 100644 --- a/internal/multiagent/worker.go +++ b/internal/multiagent/worker.go @@ -111,7 +111,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.GraycodeRouterMessage{Role: "user", Content: workerPrompt}) sess.AddUser(workerPrompt) events, err := sess.Stream(ctx) @@ -125,9 +125,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.GraycodeRouterMessage{Role: "assistant", Content: ev.Content}) case "tool_use": - _ = writer.Write(types.EyrieMessage{ + _ = writer.Write(types.GraycodeRouterMessage{ 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.GraycodeRouterMessage, 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 80b9e8e3..3889370a 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 GraycodeRouterMessages. 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.GraycodeRouterMessage, *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.GraycodeRouterMessage 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.GraycodeRouterMessage 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.GraycodeRouterMessage) 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.GraycodeRouterMessage) 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 cfab5eb7..3ef9c182 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.GraycodeRouterMessage{ {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.GraycodeRouterMessage{Role: "user", Content: "Start"}); err != nil { t.Fatalf("Write: %v", err) } if err := w.Close(); err != nil { diff --git a/internal/observability/conformance/schema.go b/internal/observability/conformance/schema.go index f071713b..035664bf 100644 --- a/internal/observability/conformance/schema.go +++ b/internal/observability/conformance/schema.go @@ -1,5 +1,5 @@ // Package conformance verifies that emitted telemetry spans always match the -// documented OpenTelemetry schema (docs/OTEL-CONVENTIONS.md and the eyrie +// documented OpenTelemetry schema (docs/OTEL-CONVENTIONS.md and the graycode-router // gen_ai.* semantic-convention constants), so the schema cannot silently drift // across Graycode and its independent ecosystem repositories. // @@ -17,7 +17,7 @@ import ( // Attribute vocabulary shared across the ecosystem. These mirror the keys // emitted by the span starters in internal/observability/oteltrace and the -// gen_ai.* constants in eyrie/internal/observability. +// gen_ai.* constants in graycode-router/internal/observability. const ( AttrGenAISystem = "gen_ai.system" AttrGenAIRequestModel = "gen_ai.request.model" diff --git a/internal/provider/gateway/engine_client.go b/internal/provider/gateway/engine_client.go index 6176f01a..2359a85b 100644 --- a/internal/provider/gateway/engine_client.go +++ b/internal/provider/gateway/engine_client.go @@ -1,9 +1,9 @@ -// Package gateway is Graycode's single boundary to Eyrie's provider runtime. It is -// the only package that imports Eyrie; everything else speaks the graycode-owned +// Package gateway is Graycode's single boundary to GraycodeRouter's provider runtime. It is +// the only package that imports GraycodeRouter; everything else speaks the graycode-owned // Provider interface and the internal/types DTOs. // -// graycode = product face (UX/agent/sessions) · eyrie = provider engine -// One-way dependency only: eyrie never imports graycode. See README ecosystems. +// graycode = product face (UX/agent/sessions) · graycode-router = provider engine +// One-way dependency only: graycode-router never imports graycode. 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" "github.com/GrayCodeAI/graycode-cli/internal/types" + graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" + "github.com/GrayCodeAI/graycode-router/llm" ) -// Provider is Graycode's graycode-owned view of the Eyrie engine: a composition of the -// role interfaces below. It wraps the concrete *eyrieengine.Engine (whose fields +// Provider is Graycode's graycode-owned view of the GraycodeRouter engine: a composition of the +// role interfaces below. It wraps the concrete *graycoderouterengine.Engine (whose fields // are unexported and therefore not directly mockable) so Graycode 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 graycoderouterengine.GenerateRequest) (*graycoderouterengine.GenerateResponse, error) + Stream(ctx context.Context, req graycoderouterengine.GenerateRequest) (graycoderouterengine.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 graycoderouterengine.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) ([]graycoderouterengine.Model, error) + ListLiveModels(ctx context.Context, providerID string) ([]graycoderouterengine.Model, error) + ListPublicModels(ctx context.Context, providerID string) ([]graycoderouterengine.Model, error) + ModelInfo(ctx context.Context, modelID string) (graycoderouterengine.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 graycoderouterengine.ModelClass, fallback string) string + PreferredModels(ctx context.Context, primaryProvider string, class graycoderouterengine.ModelClass, limit int) []string + ModelClassOf(ctx context.Context, modelID string) graycoderouterengine.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) (graycoderouterengine.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) (graycoderouterengine.CredentialStatus, error) RemoveCredential(ctx context.Context, providerID string) error - CredentialStatus(ctx context.Context, providerID string) (eyrieengine.CredentialStatus, error) + CredentialStatus(ctx context.Context, providerID string) (graycoderouterengine.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) graycoderouterengine.CredentialResolution + CredentialProviders(context.Context) []graycoderouterengine.CredentialProvider + ApplyCredentials(ctx context.Context, providerID string) (graycoderouterengine.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) graycoderouterengine.Route + EffectiveSelection(ctx context.Context, opts graycoderouterengine.SelectionOptions) graycoderouterengine.Selection SetActiveProvider(ctx context.Context, provider string) error SetActiveModel(ctx context.Context, modelID string) error SetSelection(ctx context.Context, provider, modelID string) error @@ -86,60 +86,60 @@ 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() []graycoderouterengine.Gateway + Gateways(ctx context.Context) []graycoderouterengine.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) (graycoderouterengine.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) (graycoderouterengine.CatalogSnapshot, error) + CatalogHealth(ctx context.Context) graycoderouterengine.CatalogHealth + StatePaths() graycoderouterengine.StatePaths DefaultProviderFilter(ctx context.Context) string - PreflightWithOptions(ctx context.Context, opts eyrieengine.PreflightOptions) eyrieengine.PreflightReport - ProviderStateSecurityStatus() eyrieengine.ProviderStateSecurity + PreflightWithOptions(ctx context.Context, opts graycoderouterengine.PreflightOptions) graycoderouterengine.PreflightReport + ProviderStateSecurityStatus() graycoderouterengine.ProviderStateSecurity MigrateProviderSecrets() error } -// engineProvider is the production Provider: a thin wrapper over Eyrie's +// engineProvider is the production Provider: a thin wrapper over GraycodeRouter's // concrete engine facade. type engineProvider struct { - eng *eyrieengine.Engine + eng *graycoderouterengine.Engine } -func newEngineProvider(eng *eyrieengine.Engine) *engineProvider { +func newEngineProvider(eng *graycoderouterengine.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 graycoderouterengine.GenerateRequest) (*graycoderouterengine.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 graycoderouterengine.GenerateRequest) (graycoderouterengine.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) ([]graycoderouterengine.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) ([]graycoderouterengine.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) ([]graycoderouterengine.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) (graycoderouterengine.Model, bool, error) { return p.eng.ModelInfo(ctx, modelID) } @@ -151,15 +151,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 graycoderouterengine.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 graycoderouterengine.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) graycoderouterengine.ModelClass { return p.eng.ModelClassOf(ctx, modelID) } @@ -175,7 +175,7 @@ func (p *engineProvider) ModelNames(ctx context.Context) []string { return p.eng.ModelNames(ctx) } -func (p *engineProvider) StatePaths() eyrieengine.StatePaths { +func (p *engineProvider) StatePaths() graycoderouterengine.StatePaths { return p.eng.StatePaths() } @@ -183,19 +183,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) (graycoderouterengine.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) (graycoderouterengine.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) (graycoderouterengine.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) (graycoderouterengine.CredentialStatus, error) { return p.eng.SaveCredential(ctx, providerID, secret) } @@ -203,7 +203,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) (graycoderouterengine.CredentialStatus, error) { return p.eng.CredentialStatus(ctx, providerID) } @@ -219,19 +219,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) graycoderouterengine.CredentialResolution { return p.eng.ResolveCredential(ctx, secret) } -func (p *engineProvider) CredentialProviders(ctx context.Context) []eyrieengine.CredentialProvider { +func (p *engineProvider) CredentialProviders(ctx context.Context) []graycoderouterengine.CredentialProvider { return p.eng.CredentialProviders(ctx) } -func (p *engineProvider) GatewayDefinitions() []eyrieengine.Gateway { +func (p *engineProvider) GatewayDefinitions() []graycoderouterengine.Gateway { return p.eng.GatewayDefinitions() } -func (p *engineProvider) Gateways(ctx context.Context) []eyrieengine.Gateway { +func (p *engineProvider) Gateways(ctx context.Context) []graycoderouterengine.Gateway { return p.eng.Gateways(ctx) } @@ -259,7 +259,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) (graycoderouterengine.DeploymentSummary, error) { return p.eng.DeploymentSummary(ctx, activeModel) } @@ -267,19 +267,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) graycoderouterengine.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 graycoderouterengine.PreflightOptions) graycoderouterengine.PreflightReport { return p.eng.PreflightWithOptions(ctx, opts) } -func (p *engineProvider) ActiveSelection(ctx context.Context) eyrieengine.Route { +func (p *engineProvider) ActiveSelection(ctx context.Context) graycoderouterengine.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 graycoderouterengine.SelectionOptions) graycoderouterengine.Selection { return p.eng.EffectiveSelection(ctx, opts) } @@ -299,7 +299,7 @@ func (p *engineProvider) ClearSelection(ctx context.Context) error { return p.eng.ClearSelection(ctx) } -func (p *engineProvider) ProviderStateSecurityStatus() eyrieengine.ProviderStateSecurity { +func (p *engineProvider) ProviderStateSecurityStatus() graycoderouterengine.ProviderStateSecurity { return p.eng.ProviderStateSecurityStatus() } @@ -311,13 +311,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 graycoderouterengine.NativeCompactionRequest) (string, error) { return p.eng.CompactNative(ctx, req) } // translateProvider bridges the graycode-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 <-> graycoderouterengine.*) are the single, centralized // translation point — Graycode's conversation DTOs never leak past it. type translateProvider struct { generator Generator @@ -328,7 +328,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.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { response, err := c.generator.Generate(ctx, toEngineRequest(messages, opts, types.ContinuationConfig{})) if err != nil { return nil, err @@ -336,14 +336,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.GraycodeRouterMessage, 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.GraycodeRouterStreamEvent, 64) streamCtx, cancel := context.WithCancel(ctx) go func() { defer close(events) @@ -361,7 +361,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.GraycodeRouterStreamEvent{Type: "error", Error: err.Error()}: case <-streamCtx.Done(): } } @@ -374,7 +374,7 @@ func (c *translateProvider) StreamChatContinue(ctx context.Context, messages []t } // ManagesResilience tells Graycode not to add provider retry, continuation, or -// protocol-recovery layers around Eyrie's routed transport. +// protocol-recovery layers around GraycodeRouter's routed transport. func (c *translateProvider) ManagesResilience() bool { return true } // NativeCompaction reports whether the bound compactor supports provider-native @@ -387,43 +387,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 graycoderouterengine.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.GraycodeRouterMessage, opts types.ChatOptions, continuation types.ContinuationConfig) graycoderouterengine.GenerateRequest { thinkingEnabled := opts.ThinkingEnabled if thinkingEnabled == nil { thinkingEnabled = opts.GLMThinkingEnabled } reasoningEnabled := thinkingEnabled != nil && *thinkingEnabled - request := eyrieengine.GenerateRequest{ + request := graycoderouterengine.GenerateRequest{ Messages: toEngineMessages(messages), SystemPrompt: opts.System, Tools: toEngineTools(opts.Tools), - Requirements: eyrieengine.Requirements{ + Requirements: graycoderouterengine.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: graycoderouterengine.Preference{ PreferredProvider: opts.Provider, PreferredModelID: opts.Model, }, - Limits: eyrieengine.Limits{ + Limits: graycoderouterengine.Limits{ MaxOutputTokens: opts.MaxTokens, MaxContinuations: continuation.MaxContinuations, MaxTotalOutputTokens: continuation.MaxTotalTokens, }, - Metadata: eyrieengine.Metadata{UserID: opts.MetadataUserID}, + Metadata: graycoderouterengine.Metadata{UserID: opts.MetadataUserID}, Temperature: opts.Temperature, OutputSchema: firstNonEmpty(opts.OutputSchema, responseSchema(opts.ResponseFormat)), - Options: eyrieengine.GenerationOptions{ + Options: graycoderouterengine.GenerationOptions{ EnableCaching: opts.EnableCaching, ReasoningEffort: opts.ReasoningEffort, ThinkingBudgetTokens: opts.ThinkingBudgetTokens, ThinkingMode: opts.ThinkingMode, ThinkingDisplay: opts.ThinkingDisplay, ThinkingEnabled: thinkingEnabled, GLMThinkingEnabled: thinkingEnabled, @@ -444,57 +444,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.GraycodeRouterMessage) []graycoderouterengine.Message { return messages } -func toEngineMessages(messages []types.EyrieMessage) []eyrieengine.Message { +func toEngineMessages(messages []types.GraycodeRouterMessage) []graycoderouterengine.Message { return messages } -func toEngineTools(tools []types.EyrieTool) []eyrieengine.Tool { +func toEngineTools(tools []types.GraycodeRouterTool) []graycoderouterengine.Tool { return tools } -func toEngineToolChoice(choice *types.ToolChoiceOption) *eyrieengine.ToolChoice { +func toEngineToolChoice(choice *types.ToolChoiceOption) *graycoderouterengine.ToolChoice { return choice } -func fromEngineResponse(response *eyrieengine.GenerateResponse) *types.EyrieResponse { +func fromEngineResponse(response *graycoderouterengine.GenerateResponse) *types.GraycodeRouterResponse { return response } -func fromEngineEvent(event eyrieengine.Event) (types.EyrieStreamEvent, bool) { - out := types.EyrieStreamEvent{ +func fromEngineEvent(event graycoderouterengine.Event) (types.GraycodeRouterStreamEvent, bool) { + out := types.GraycodeRouterStreamEvent{ 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 graycoderouterengine.EventRouteSelected: out.Type = "route_selected" - case eyrieengine.EventRouteChanged: + case graycoderouterengine.EventRouteChanged: out.Type = "route_changed" - case eyrieengine.EventContentDelta: + case graycoderouterengine.EventContentDelta: out.Type = "content" - case eyrieengine.EventThinkingDelta: + case graycoderouterengine.EventThinkingDelta: out.Type = "thinking" - case eyrieengine.EventToolCallStart, eyrieengine.EventToolCallDone: + case graycoderouterengine.EventToolCallStart, graycoderouterengine.EventToolCallDone: out.Type = "tool_call" - case eyrieengine.EventToolCallDelta: + case graycoderouterengine.EventToolCallDelta: out.Type = "tool_input_delta" - case eyrieengine.EventUsage: + case graycoderouterengine.EventUsage: out.Type = "usage" - case eyrieengine.EventTTFT: + case graycoderouterengine.EventTTFT: out.Type = "ttft" out.TTFT = event.TTFTms - case eyrieengine.EventDone: + case graycoderouterengine.EventDone: out.Type = "done" - case eyrieengine.EventContinuation: + case graycoderouterengine.EventContinuation: out.Type = "continuation" - case eyrieengine.EventWarning: + case graycoderouterengine.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 graycode-router 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) @@ -536,7 +536,7 @@ func cloneMetadata(in map[string]string) map[string]string { return out } -func messagesContainVision(messages []types.EyrieMessage) bool { +func messagesContainVision(messages []types.GraycodeRouterMessage) 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 5b1a70eb..adcc674e 100644 --- a/internal/provider/gateway/engine_client_test.go +++ b/internal/provider/gateway/engine_client_test.go @@ -3,22 +3,22 @@ package gateway import ( "testing" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" "github.com/GrayCodeAI/graycode-cli/internal/types" + graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" ) func TestEngineAdapterPreservesGraycodeRequestOptions(t *testing.T) { topP := 0.75 thinking := true request := toEngineRequest( - []types.EyrieMessage{{ + []types.GraycodeRouterMessage{{ 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.GraycodeRouterTool{{Name: "read_file", Description: "read", Parameters: map[string]interface{}{"type": "object"}}}, System: "system", EnableCaching: true, ReasoningEffort: "high", GLMThinkingEnabled: &thinking, TopP: &topP, ServiceTier: "priority", MetadataUserID: "graycode-user-1", @@ -63,29 +63,29 @@ func TestEngineAdapterOnlyRequiresGLMReasoningWhenEnabled(t *testing.T) { func TestEngineAdapterNormalizesEventsForGraycodeLoop(t *testing.T) { tests := []struct { - in eyrieengine.Event + in graycoderouterengine.Event wantType string emit bool provider string model string }{ { - in: eyrieengine.Event{Type: eyrieengine.EventRouteSelected, Route: &eyrieengine.Route{ + in: graycoderouterengine.Event{Type: graycoderouterengine.EventRouteSelected, Route: &graycoderouterengine.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: graycoderouterengine.Event{Type: graycoderouterengine.EventRouteChanged, Route: &graycoderouterengine.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: graycoderouterengine.Event{Type: graycoderouterengine.EventContentDelta, Content: "x"}, wantType: "content", emit: true}, + {in: graycoderouterengine.Event{Type: graycoderouterengine.EventThinkingDelta}, wantType: "thinking", emit: true}, + {in: graycoderouterengine.Event{Type: graycoderouterengine.EventToolCallDone, ToolCall: &graycoderouterengine.ToolCall{Name: "read"}}, wantType: "tool_call", emit: true}, + {in: graycoderouterengine.Event{Type: graycoderouterengine.EventUsage, Usage: &graycoderouterengine.Usage{TotalTokens: 8}}, wantType: "usage", emit: true}, + {in: graycoderouterengine.Event{Type: graycoderouterengine.EventDone, StopReason: "end_turn", Usage: &graycoderouterengine.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 TestEngineAdapterNormalizesEventsForGraycodeLoop(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 == graycoderouterengine.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(&graycoderouterengine.GenerateResponse{ Content: "ok", - Route: &eyrieengine.Route{ + Route: &graycoderouterengine.Route{ Provider: "openai", Model: "openai/gpt-5", DeploymentRouting: true, }, }) diff --git a/internal/provider/gateway/gateway.go b/internal/provider/gateway/gateway.go index cddafb8e..aad53c21 100644 --- a/internal/provider/gateway/gateway.go +++ b/internal/provider/gateway/gateway.go @@ -1,9 +1,9 @@ -// Package gateway is Graycode's single boundary to Eyrie's provider runtime. It is -// the only package that imports Eyrie; everything else speaks the graycode-owned +// Package gateway is Graycode's single boundary to GraycodeRouter's provider runtime. It is +// the only package that imports GraycodeRouter; everything else speaks the graycode-owned // Provider interface and the internal/types DTOs. // -// graycode = product face (UX/agent/sessions) · eyrie = provider engine -// One-way dependency only: eyrie never imports graycode. See README ecosystems. +// graycode = product face (UX/agent/sessions) · graycode-router = provider engine +// One-way dependency only: graycode-router never imports graycode. 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" + graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" + "github.com/GrayCodeAI/graycode-router/llm" ) -// Gateway is Graycode's single boundary to the Eyrie provider runtime. It embeds +// Gateway is Graycode's single boundary to the GraycodeRouter provider runtime. It embeds // Provider so every engine method is forwarded, and it is the only type that // constructs one (via New). All other Graycode packages hold a *Gateway or speak -// the Provider interface — never an *eyrieengine.Engine. +// the Provider interface — never an *graycoderouterengine.Engine. // -// Construction is centralized here: New is the only call to eyrieengine.New and +// Construction is centralized here: New is the only call to graycoderouterengine.New and // the place Graycode declares its identity to the credential store. -// Gateway is Graycode's single boundary to the Eyrie provider runtime. It embeds the +// Gateway is Graycode's single boundary to the GraycodeRouter 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 Graycode packages hold a *Gateway or speak -// the Provider interface — never an *eyrieengine.Engine. *Gateway satisfies the +// the Provider interface — never an *graycoderouterengine.Engine. *Gateway satisfies the // composite Provider interface. type Gateway struct { Generator @@ -37,27 +37,27 @@ type Gateway struct { CatalogMaintenance } -// declareGraycodeIdentity sets Eyrie's OS keychain service name to "graycode" so existing -// credentials (filed under "graycode") stay readable under Eyrie's now host-neutral +// declareGraycodeIdentity sets GraycodeRouter's OS keychain service name to "graycode" so existing +// credentials (filed under "graycode") stay readable under GraycodeRouter'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 declareGraycodeIdentity = sync.OnceFunc(func() { - eyrieengine.SetSecretStoreServiceName("graycode") + graycoderouterengine.SetSecretStoreServiceName("graycode") }) -// 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 GraycodeRouter engine for one effective settings snapshot and wraps it +// as a Provider. It is the single composition root — every graycoderouterengine.New call // in Graycode flows through here. func New(ctx context.Context, providers []CustomProviderConfig) (*Gateway, error) { // Declare graycode'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 + // GraycodeRouter's host-neutral default service name. The OnceFunc makes this // safe to call from every construction path. declareGraycodeIdentity() gateways := customGatewaysFromSettings(providers) - eng, err := eyrieengine.New(eyrieengine.Options{CustomGateways: gateways}) + eng, err := graycoderouterengine.New(graycoderouterengine.Options{CustomGateways: gateways}) if err != nil { return nil, err } @@ -74,16 +74,16 @@ func New(ctx context.Context, providers []CustomProviderConfig) (*Gateway, error } // BuildCustomGateways maps Graycode's OpenAI-compatible provider config onto -// Eyrie's CustomGateway spec. Shared by gateway.New, config.eyrie_engine, and +// GraycodeRouter's CustomGateway spec. Shared by gateway.New, config.graycode_router_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) []graycoderouterengine.CustomGateway { + gateways := make([]graycoderouterengine.CustomGateway, 0, len(providers)) for _, provider := range providers { if provider.Name == "" && provider.BaseURL == "" { continue } - gateways = append(gateways, eyrieengine.CustomGateway{ + gateways = append(gateways, graycoderouterengine.CustomGateway{ ID: provider.Name, BaseURL: provider.BaseURL, CredentialEnv: provider.APIKeyEnv, DefaultModel: provider.Model, }) @@ -92,7 +92,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) []graycoderouterengine.CustomGateway { return BuildCustomGateways(providers) } @@ -106,7 +106,7 @@ type CustomProviderConfig struct { Model string } -// ModelInfo is Graycode's product-facing view of Eyrie model metadata. +// ModelInfo is Graycode's product-facing view of GraycodeRouter model metadata. type ModelInfo struct { Name string `json:"name"` Provider string `json:"provider"` @@ -117,7 +117,7 @@ type ModelInfo struct { Recommended bool `json:"recommended,omitempty"` } -func fromEngineModel(model eyrieengine.Model) ModelInfo { +func fromEngineModel(model graycoderouterengine.Model) ModelInfo { return ModelInfo{ Name: model.ID, Provider: model.ProviderID, ContextSize: model.ContextWindow, @@ -139,10 +139,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 *graycoderouterengine.Engine as a Gateway. Tests that +// inject an GraycodeRouter SecretStore (e.g. compaction-support detection) use it so the // rest of Graycode still speaks the Gateway boundary. -func NewFromEngine(eng *eyrieengine.Engine) *Gateway { +func NewFromEngine(eng *graycoderouterengine.Engine) *Gateway { if eng == nil { return nil } @@ -159,8 +159,8 @@ func NewFromEngine(eng *eyrieengine.Engine) *Gateway { } // --- Stateless package-level lookups ------------------------------------- -// These delegate Eyrie reads to one shared default gateway so graycode-owned -// policy packages (routing, config) never import Eyrie themselves. eyrie's +// These delegate GraycodeRouter reads to one shared default gateway so graycode-owned +// policy packages (routing, config) never import GraycodeRouter themselves. graycode-router'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 @@ -274,7 +274,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 GraycodeRouter'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 { @@ -319,46 +319,46 @@ func ModelNames(ctx context.Context) []string { return g.ModelNames(ctx) } -// --- graycode-owned mirror of Eyrie's ModelClass tier enum ------------------- +// --- graycode-owned mirror of GraycodeRouter'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 graycoderouterengine.ModelClass. +type ModelClass = graycoderouterengine.ModelClass const ( ModelClassEconomical = llm.ModelClassEconomical ModelClassBalanced = llm.ModelClassBalanced ModelClassPremium = llm.ModelClassPremium - CheckFail = eyrieengine.CheckFail + CheckFail = graycoderouterengine.CheckFail ) // NormalizeProviderID canonicalizes a host-facing provider/gateway id. func NormalizeProviderID(id string) string { - return eyrieengine.NormalizeProviderID(id) + return graycoderouterengine.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. +// --- GraycodeRouter report/type re-exports config internals consume ---------------- +// These alias GraycodeRouter types that a few config-only report paths return. They +// live in gateway (the single GraycodeRouter 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 = graycoderouterengine.PreflightReport + PreflightOptions = graycoderouterengine.PreflightOptions + ProviderStateSecurity = graycoderouterengine.ProviderStateSecurity + DeploymentSummary = graycoderouterengine.DeploymentSummary + CredentialStorageReport = graycoderouterengine.CredentialStorageReport + CredentialStatus = graycoderouterengine.CredentialStatus + CredentialResolution = graycoderouterengine.CredentialResolution + CredentialProvider = graycoderouterengine.CredentialProvider + GatewayDefs = graycoderouterengine.Gateway + CatalogSnapshot = graycoderouterengine.CatalogSnapshot + Model = graycoderouterengine.Model + StatePaths = graycoderouterengine.StatePaths + SelectionOptions = graycoderouterengine.SelectionOptions + Selection = graycoderouterengine.Selection + NativeCompactionRequest = graycoderouterengine.NativeCompactionRequest ) -// Package-level Eyrie helpers that config delegates to (gateway stays the only importer). +// Package-level GraycodeRouter helpers that config delegates to (gateway stays the only importer). func PreflightReportWithOptions(ctx context.Context, opts PreflightOptions) PreflightReport { return PreflightWithProviders(ctx, nil, opts) @@ -376,57 +376,57 @@ func PreflightWithProviders(ctx context.Context, providers []CustomProviderConfi } func FormatPreflight(report PreflightReport) string { - return eyrieengine.FormatPreflight(report) + return graycoderouterengine.FormatPreflight(report) } func IsCatalogCacheRequired(err error) bool { - return eyrieengine.IsCatalogCacheRequired(err) + return graycoderouterengine.IsCatalogCacheRequired(err) } -// RegisteredProviderCount exposes Eyrie's first-class provider count through -// Graycode's single provider-runtime boundary. The count derives from Eyrie's -// provider registry, so adding a provider in Eyrie never requires a Graycode edit. +// RegisteredProviderCount exposes GraycodeRouter's first-class provider count through +// Graycode's single provider-runtime boundary. The count derives from GraycodeRouter's +// provider registry, so adding a provider in GraycodeRouter never requires a Graycode edit. func RegisteredProviderCount() int { - return eyrieengine.RegisteredGatewayCount() + return graycoderouterengine.RegisteredGatewayCount() } -func SecretStoreName() string { return eyrieengine.SecretStoreName() } +func SecretStoreName() string { return graycoderouterengine.SecretStoreName() } func CredentialStorage(ctx context.Context) CredentialStorageReport { - return eyrieengine.CredentialStorage(ctx) + return graycoderouterengine.CredentialStorage(ctx) } func MigrateEnvFileCredentials(ctx context.Context) (int, error) { - return eyrieengine.MigrateEnvFileCredentials(ctx) + return graycoderouterengine.MigrateEnvFileCredentials(ctx) } func CredentialGuidance(providerID, secret string) string { - return eyrieengine.CredentialGuidance(providerID, secret) + return graycoderouterengine.CredentialGuidance(providerID, secret) } func FormatSetupError(providerID string, err error) string { - return eyrieengine.FormatSetupError(providerID, err) + return graycoderouterengine.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, []graycoderouterengine.ToolCall) { + return graycoderouterengine.ParseInlineToolCalls(content) } func DefaultThinkingDisabled(providerID string) bool { - return eyrieengine.DefaultThinkingDisabled(providerID) + return graycoderouterengine.DefaultThinkingDisabled(providerID) } func ThinkingToggleSupported(providerID string) bool { - return eyrieengine.ThinkingToggleSupported(providerID) + return graycoderouterengine.ThinkingToggleSupported(providerID) } func (g *Gateway) DefaultThinkingDisabled(providerID string) bool { - return eyrieengine.DefaultThinkingDisabled(providerID) + return graycoderouterengine.DefaultThinkingDisabled(providerID) } func (g *Gateway) ThinkingToggleSupported(providerID string) bool { - return eyrieengine.ThinkingToggleSupported(providerID) + return graycoderouterengine.ThinkingToggleSupported(providerID) } // --- Test fixtures ----------------------------------------------------- @@ -434,16 +434,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 = graycoderouterengine.SetDefaultStore // DefaultStore returns the process-wide credential store (for tests). -var DefaultStore = eyrieengine.DefaultStore +var DefaultStore = graycoderouterengine.DefaultStore // MapStore is the in-memory credential store for tests (alias). -type MapStore = eyrieengine.MapStore +type MapStore = graycoderouterengine.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 graycoderouterengine.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) } +func HasSecret(ctx context.Context, envKey string) bool { + return graycoderouterengine.HasSecret(ctx, envKey) +} diff --git a/internal/provider/gateway/provider_stub_test.go b/internal/provider/gateway/provider_stub_test.go index 154a5a70..a5cb33a6 100644 --- a/internal/provider/gateway/provider_stub_test.go +++ b/internal/provider/gateway/provider_stub_test.go @@ -4,146 +4,152 @@ import ( "context" "testing" - eyrieengine "github.com/GrayCodeAI/eyrie/engine" "github.com/GrayCodeAI/graycode-cli/internal/types" + graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" ) // 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. +// GraycodeRouter engine. This is the swappable-Engine guarantee Phase 3 delivers. type stubProvider struct { - resp *eyrieengine.GenerateResponse + resp *graycoderouterengine.GenerateResponse err error } -func (s *stubProvider) Resolve(context.Context, eyrieengine.SelectionRequest) (eyrieengine.Route, error) { - return eyrieengine.Route{}, nil +func (s *stubProvider) Resolve(context.Context, graycoderouterengine.SelectionRequest) (graycoderouterengine.Route, error) { + return graycoderouterengine.Route{}, nil } -func (s *stubProvider) Generate(context.Context, eyrieengine.GenerateRequest) (*eyrieengine.GenerateResponse, error) { +func (s *stubProvider) Generate(context.Context, graycoderouterengine.GenerateRequest) (*graycoderouterengine.GenerateResponse, error) { return s.resp, s.err } -func (s *stubProvider) Stream(context.Context, eyrieengine.GenerateRequest) (eyrieengine.EventStreamer, error) { +func (s *stubProvider) Stream(context.Context, graycoderouterengine.GenerateRequest) (graycoderouterengine.EventStreamer, error) { return nil, nil } -func (s *stubProvider) ListModels(context.Context, string, bool) ([]eyrieengine.Model, error) { +func (s *stubProvider) ListModels(context.Context, string, bool) ([]graycoderouterengine.Model, error) { return nil, nil } -func (s *stubProvider) ListLiveModels(context.Context, string) ([]eyrieengine.Model, error) { +func (s *stubProvider) ListLiveModels(context.Context, string) ([]graycoderouterengine.Model, error) { return nil, nil } -func (s *stubProvider) ListPublicModels(context.Context, string) ([]eyrieengine.Model, error) { +func (s *stubProvider) ListPublicModels(context.Context, string) ([]graycoderouterengine.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) (graycoderouterengine.Model, bool, error) { + return graycoderouterengine.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, graycoderouterengine.ModelClass, string) string { return "" } -func (s *stubProvider) PreferredModels(context.Context, string, eyrieengine.ModelClass, int) []string { +func (s *stubProvider) PreferredModels(context.Context, string, graycoderouterengine.ModelClass, int) []string { return nil } -func (s *stubProvider) ModelClassOf(context.Context, string) eyrieengine.ModelClass { +func (s *stubProvider) ModelClassOf(context.Context, string) graycoderouterengine.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) DefaultProviderFilter(context.Context) string { return "" } -func (s *stubProvider) Catalog(context.Context) (eyrieengine.CatalogSnapshot, error) { - return eyrieengine.CatalogSnapshot{}, nil +func (s *stubProvider) StatePaths() graycoderouterengine.StatePaths { + return graycoderouterengine.StatePaths{} +} +func (s *stubProvider) DefaultProviderFilter(context.Context) string { return "" } +func (s *stubProvider) Catalog(context.Context) (graycoderouterengine.CatalogSnapshot, error) { + return graycoderouterengine.CatalogSnapshot{}, nil } -func (s *stubProvider) RefreshCatalog(context.Context, string) (eyrieengine.CatalogSnapshot, error) { - return eyrieengine.CatalogSnapshot{}, nil +func (s *stubProvider) RefreshCatalog(context.Context, string) (graycoderouterengine.CatalogSnapshot, error) { + return graycoderouterengine.CatalogSnapshot{}, nil } -func (s *stubProvider) ApplyCredentials(context.Context, string) (eyrieengine.CatalogSnapshot, error) { - return eyrieengine.CatalogSnapshot{}, nil +func (s *stubProvider) ApplyCredentials(context.Context, string) (graycoderouterengine.CatalogSnapshot, error) { + return graycoderouterengine.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) (graycoderouterengine.CredentialStatus, error) { + return graycoderouterengine.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) (graycoderouterengine.CredentialStatus, error) { + return graycoderouterengine.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) graycoderouterengine.CredentialResolution { + return graycoderouterengine.CredentialResolution{} } -func (s *stubProvider) CredentialProviders(context.Context) []eyrieengine.CredentialProvider { +func (s *stubProvider) CredentialProviders(context.Context) []graycoderouterengine.CredentialProvider { return nil } -func (s *stubProvider) GatewayDefinitions() []eyrieengine.Gateway { return nil } -func (s *stubProvider) Gateways(context.Context) []eyrieengine.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 "" } -func (s *stubProvider) CanonicalModel(context.Context, string) string { return "" } -func (s *stubProvider) ApplyGatewayEnvironment(context.Context, string) {} -func (s *stubProvider) DeploymentRoutingEnabled(*bool) bool { return false } + +func (s *stubProvider) GatewayDefinitions() []graycoderouterengine.Gateway { return nil } + +func (s *stubProvider) Gateways(context.Context) []graycoderouterengine.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 "" } +func (s *stubProvider) CanonicalModel(context.Context, string) string { return "" } +func (s *stubProvider) ApplyGatewayEnvironment(context.Context, string) {} +func (s *stubProvider) DeploymentRoutingEnabled(*bool) bool { return false } 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) (graycoderouterengine.DeploymentSummary, error) { + return graycoderouterengine.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) graycoderouterengine.CatalogHealth { + return graycoderouterengine.CatalogHealth{} } -func (s *stubProvider) Preflight(context.Context) eyrieengine.PreflightReport { - return eyrieengine.PreflightReport{} +func (s *stubProvider) Preflight(context.Context) graycoderouterengine.PreflightReport { + return graycoderouterengine.PreflightReport{} } -func (s *stubProvider) PreflightWithOptions(context.Context, eyrieengine.PreflightOptions) eyrieengine.PreflightReport { - return eyrieengine.PreflightReport{} +func (s *stubProvider) PreflightWithOptions(context.Context, graycoderouterengine.PreflightOptions) graycoderouterengine.PreflightReport { + return graycoderouterengine.PreflightReport{} } -func (s *stubProvider) ActiveSelection(context.Context) eyrieengine.Route { return eyrieengine.Route{} } +func (s *stubProvider) ActiveSelection(context.Context) graycoderouterengine.Route { + return graycoderouterengine.Route{} +} -func (s *stubProvider) EffectiveSelection(context.Context, eyrieengine.SelectionOptions) eyrieengine.Selection { - return eyrieengine.Selection{} +func (s *stubProvider) EffectiveSelection(context.Context, graycoderouterengine.SelectionOptions) graycoderouterengine.Selection { + return graycoderouterengine.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() graycoderouterengine.ProviderStateSecurity { + return graycoderouterengine.ProviderStateSecurity{} } func (s *stubProvider) MigrateProviderSecrets() error { return nil } func (s *stubProvider) MigrateProviderSecretsContext(context.Context) error { return nil } 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, graycoderouterengine.NativeCompactionRequest) (string, error) { return "", nil } func TestStubProviderDrivesChatClient(t *testing.T) { - stub := &stubProvider{resp: &eyrieengine.GenerateResponse{Content: "from stub", FinishReason: "end_turn"}} + stub := &stubProvider{resp: &graycoderouterengine.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.GraycodeRouterMessage{{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 01490973..cc6e7a4c 100644 --- a/internal/provider/routing/catalog.go +++ b/internal/provider/routing/catalog.go @@ -1,6 +1,6 @@ // Package routing provides Graycode-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). +// GraycodeRouter's engine facade through the gateway package (the single GraycodeRouter boundary). package routing import ( @@ -10,11 +10,11 @@ import ( "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) -// ModelInfo is Graycode's product-facing view of Eyrie model metadata (the gateway -// layer owns the Eyrie conversation that produces it). +// ModelInfo is Graycode's product-facing view of GraycodeRouter model metadata (the gateway +// layer owns the GraycodeRouter 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 GraycodeRouter. 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..4faab060 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 graycode-router 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 bc7397eb..d525a934 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 Graycode's workflow policy while asking Eyrie for the +// DefaultRoles keeps Graycode's workflow policy while asking GraycodeRouter 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 a193209d..9884b0a8 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 graycode-router. package recipe import ( diff --git a/internal/replaycache/replaycache.go b/internal/replaycache/replaycache.go index 9a653fde..b165e4ea 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.GraycodeRouterMessage, 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.GraycodeRouterMessage) 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.GraycodeRouterResponse, bool) { data, err := os.ReadFile(c.path("resp", key)) if err != nil { return nil, false } - var resp types.EyrieResponse + var resp types.GraycodeRouterResponse 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.GraycodeRouterResponse) 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.GraycodeRouterStreamEvent, bool) { data, err := os.ReadFile(c.path("stream", key)) if err != nil { return nil, false } - var events []types.EyrieStreamEvent + var events []types.GraycodeRouterStreamEvent 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.GraycodeRouterStreamEvent) 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 821d9012..38df8ab2 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.GraycodeRouterMessage{{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.GraycodeRouterMessage{{Role: "user", Content: "hi"}}, 10) + want := &types.GraycodeRouterResponse{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.GraycodeRouterMessage{{Role: "user", Content: "s"}}, 10) + events := []types.GraycodeRouterStreamEvent{ {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.GraycodeRouterResponse{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.GraycodeRouterMessage{{Role: "user", Content: "e"}}, 5) + if err := c.Put(k, &types.GraycodeRouterResponse{Content: "r"}); err != nil { t.Fatal(err) } - if err := c.PutStream(k, []types.EyrieStreamEvent{{Type: "done"}}); err != nil { + if err := c.PutStream(k, []types.GraycodeRouterStreamEvent{{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.GraycodeRouterMessage{ {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/session/conversion.go b/internal/session/conversion.go index 650d48c9..84ba3b68 100644 --- a/internal/session/conversion.go +++ b/internal/session/conversion.go @@ -3,7 +3,7 @@ package session import "github.com/GrayCodeAI/graycode-cli/internal/types" // FromRuntimeMessages converts Graycode runtime messages into persisted session messages. -func FromRuntimeMessages(in []types.EyrieMessage) []Message { +func FromRuntimeMessages(in []types.GraycodeRouterMessage) []Message { if len(in) == 0 { return nil } @@ -34,13 +34,13 @@ func FromRuntimeToolResults(in []types.ToolResult) []ToolResult { } // ToRuntimeMessages converts persisted session messages back into Graycode runtime messages. -func ToRuntimeMessages(in []Message) []types.EyrieMessage { +func ToRuntimeMessages(in []Message) []types.GraycodeRouterMessage { if len(in) == 0 { return nil } - out := make([]types.EyrieMessage, len(in)) + out := make([]types.GraycodeRouterMessage, len(in)) for i, msg := range in { - out[i] = types.EyrieMessage{ + out[i] = types.GraycodeRouterMessage{ Role: msg.Role, Content: msg.Content, Thinking: msg.Thinking, diff --git a/internal/session/conversion_test.go b/internal/session/conversion_test.go index 554b91db..0d318fd3 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.GraycodeRouterMessage{ { Role: "assistant", Content: "working", diff --git a/internal/session/session.go b/internal/session/session.go index c4d45f66..3ca9c5cc 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -16,11 +16,11 @@ import ( "sync" "time" - contracts "github.com/GrayCodeAI/eyrie/tools" "github.com/GrayCodeAI/graycode-cli/internal/eventlog" "github.com/GrayCodeAI/graycode-cli/internal/eventlog/zstdz" "github.com/GrayCodeAI/graycode-cli/internal/storage" "github.com/GrayCodeAI/graycode-cli/internal/types" + contracts "github.com/GrayCodeAI/graycode-router/tools" ) // Message is a persisted conversation message. diff --git a/internal/storage/paths.go b/internal/storage/paths.go index 24c35f58..c7c2a8ce 100644 --- a/internal/storage/paths.go +++ b/internal/storage/paths.go @@ -9,12 +9,12 @@ import ( ) const ( - appName = "graycode" - envConfigDir = "GRAYCODE_CONFIG_DIR" - envEyrieConfigDir = "EYRIE_CONFIG_DIR" - envStateDir = "GRAYCODE_STATE_DIR" - envCacheDir = "GRAYCODE_CACHE_DIR" - projectIDHashLen = 12 + appName = "graycode" + envConfigDir = "GRAYCODE_CONFIG_DIR" + envGraycodeRouterConfigDir = "GRAYCODE_ROUTER_CONFIG_DIR" + envStateDir = "GRAYCODE_STATE_DIR" + envCacheDir = "GRAYCODE_CACHE_DIR" + projectIDHashLen = 12 ) // ConfigDir returns the per-user configuration directory for Graycode. @@ -49,10 +49,10 @@ func SettingsPath() string { } func ProviderConfigPath() string { - // Eyrie owns provider routing state. Keep GRAYCODE_CONFIG_DIR as the - // compatibility fallback, but let Eyrie's host-neutral override win when + // GraycodeRouter owns provider routing state. Keep GRAYCODE_CONFIG_DIR as the + // compatibility fallback, but let GraycodeRouter's host-neutral override win when // both variables are configured. - if dir := cleanEnvDir(envEyrieConfigDir); dir != "" { + if dir := cleanEnvDir(envGraycodeRouterConfigDir); dir != "" { return filepath.Join(dir, "provider.json") } return filepath.Join(ConfigDir(), "provider.json") diff --git a/internal/storage/paths_extra_test.go b/internal/storage/paths_extra_test.go index 8f0cd1b2..718b86b3 100644 --- a/internal/storage/paths_extra_test.go +++ b/internal/storage/paths_extra_test.go @@ -228,7 +228,7 @@ func TestRepoMapCacheDir(t *testing.T) { func TestConfigDir_Default(t *testing.T) { // Clear env var to test default behavior t.Setenv(envConfigDir, "") - t.Setenv(envEyrieConfigDir, "") + t.Setenv(envGraycodeRouterConfigDir, "") got := ConfigDir() if got == "" { @@ -273,12 +273,12 @@ 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_GraycodeRouterOverride(t *testing.T) { + graycodeRouterDir := filepath.Join(t.TempDir(), "graycode-router") + t.Setenv(envGraycodeRouterConfigDir, graycodeRouterDir) got := ProviderConfigPath() - want := filepath.Join(eyrieDir, "provider.json") + want := filepath.Join(graycodeRouterDir, "provider.json") if got != want { t.Errorf("ProviderConfigPath() = %q, want %q", got, want) } @@ -287,7 +287,7 @@ func TestProviderConfigPath_EyrieOverride(t *testing.T) { func TestProviderConfigPath_GraycodeFallback(t *testing.T) { graycodeDir := filepath.Join(t.TempDir(), "graycode") t.Setenv(envConfigDir, graycodeDir) - t.Setenv(envEyrieConfigDir, "") + t.Setenv(envGraycodeRouterConfigDir, "") got := ProviderConfigPath() want := filepath.Join(graycodeDir, "provider.json") @@ -296,10 +296,10 @@ func TestProviderConfigPath_GraycodeFallback(t *testing.T) { } } -func TestProviderConfigPath_EyrieWhitespaceIgnored(t *testing.T) { +func TestProviderConfigPath_GraycodeRouterWhitespaceIgnored(t *testing.T) { graycodeDir := filepath.Join(t.TempDir(), "graycode") t.Setenv(envConfigDir, graycodeDir) - t.Setenv(envEyrieConfigDir, " ") // whitespace should be treated as empty + t.Setenv(envGraycodeRouterConfigDir, " ") // whitespace should be treated as empty got := ProviderConfigPath() want := filepath.Join(graycodeDir, "provider.json") diff --git a/internal/storage/paths_test.go b/internal/storage/paths_test.go index 094bd6c4..3fd51978 100644 --- a/internal/storage/paths_test.go +++ b/internal/storage/paths_test.go @@ -25,14 +25,14 @@ func TestStorageDirsRespectOverrides(t *testing.T) { } } -func TestProviderConfigPathUsesEyrieOverrideWithoutMovingGraycodeSettings(t *testing.T) { +func TestProviderConfigPathUsesGraycodeRouterOverrideWithoutMovingGraycodeSettings(t *testing.T) { graycodeDir := filepath.Join(t.TempDir(), "graycode") - eyrieDir := filepath.Join(t.TempDir(), "eyrie") + graycodeRouterDir := filepath.Join(t.TempDir(), "graycode-router") t.Setenv(envConfigDir, graycodeDir) - t.Setenv(envEyrieConfigDir, eyrieDir) + t.Setenv(envGraycodeRouterConfigDir, graycodeRouterDir) - 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(graycodeRouterDir, "provider.json"); got != want { + t.Fatalf("ProviderConfigPath() = %q, want GRAYCODE_ROUTER_CONFIG_DIR path %q", got, want) } if got, want := SettingsPath(), filepath.Join(graycodeDir, "settings.json"); got != want { t.Fatalf("SettingsPath() = %q, want GRAYCODE_CONFIG_DIR path %q", got, want) @@ -42,7 +42,7 @@ func TestProviderConfigPathUsesEyrieOverrideWithoutMovingGraycodeSettings(t *tes func TestProviderConfigPathFallsBackToGraycodeOverride(t *testing.T) { graycodeDir := filepath.Join(t.TempDir(), "graycode") t.Setenv(envConfigDir, graycodeDir) - t.Setenv(envEyrieConfigDir, " ") + t.Setenv(envGraycodeRouterConfigDir, " ") if got, want := ProviderConfigPath(), filepath.Join(graycodeDir, "provider.json"); got != want { t.Fatalf("ProviderConfigPath() = %q, want GRAYCODE_CONFIG_DIR fallback %q", got, want) diff --git a/internal/storage/testdirs.go b/internal/storage/testdirs.go index bface990..ab02daa9 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(envGraycodeRouterConfigDir, filepath.Join(root, "graycode-router")) 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 15738711..09e8f4b3 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 Graycode code uses only -// Eyrie's stable engine facade. Tests may import lower packages for fixtures. -func TestNoDirectLowerEyrieImports(t *testing.T) { +// TestNoDirectLowerGraycodeRouterImports verifies production Graycode code uses only +// GraycodeRouter's stable engine facade. Tests may import lower packages for fixtures. +func TestNoDirectLowerGraycodeRouterImports(t *testing.T) { root := repoRoot(t) paths := []string{ filepath.Join(root, "internal"), @@ -202,37 +202,37 @@ 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/graycode-router/") || + path == "github.com/GrayCodeAI/graycode-router/engine" || + strings.HasPrefix(path, "github.com/GrayCodeAI/graycode-router/engine/") { continue } - // Graycode uses the full vendored Eyrie API surface for provider, + // Graycode uses the full vendored GraycodeRouter 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/graycode-router/llm", + "github.com/GrayCodeAI/graycode-router/graph", + "github.com/GrayCodeAI/graycode-router/tools": continue } - // The gateway package is Graycode's single Eyrie boundary; it may - // import eyrie/credentials to declare Graycode's OS keychain service + // The gateway package is Graycode's single GraycodeRouter boundary; it may + // import graycode-router/credentials to declare Graycode's OS keychain service // name (the host-neutral default would otherwise orphan existing - // secrets). All other production code must use eyrie/engine only. + // secrets). All other production code must use graycode-router/engine only. if strings.HasPrefix(rel, "internal/provider/gateway/") && - path == "github.com/GrayCodeAI/eyrie/credentials" { + path == "github.com/GrayCodeAI/graycode-router/credentials" { 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 GraycodeRouter import %q at %s:%d; use github.com/GrayCodeAI/graycode-router/engine", path, rel, pos.Line) } } } } // TestNoLazyProviderConstructionInGraycode verifies Graycode does not construct lazy -// provider transports directly. Provider/model transport resolution belongs in Eyrie. +// provider transports directly. Provider/model transport resolution belongs in GraycodeRouter. func TestNoLazyProviderConstructionInGraycode(t *testing.T) { root := repoRoot(t) paths := []string{ @@ -257,7 +257,7 @@ func TestNoLazyProviderConstructionInGraycode(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 graycode-router lazy provider construction at %s:%d; use the graycode-router/engine facade", rel, pos.Line) return true }) } diff --git a/internal/testaudit/docs_audit_test.go b/internal/testaudit/docs_audit_test.go index 41025592..e715269b 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 `graycode-router/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 94448a6e..6ff57391 100644 --- a/internal/testaudit/package_boundaries_test.go +++ b/internal/testaudit/package_boundaries_test.go @@ -13,11 +13,11 @@ import ( ) const ( - graycodeModule = "github.com/GrayCodeAI/graycode-cli" - eyrieModule = "github.com/GrayCodeAI/eyrie" + graycodeModule = "github.com/GrayCodeAI/graycode-cli" + graycodeRouterModule = "github.com/GrayCodeAI/graycode-router" ) -var supportEngines = []string{"eyrie", "harrier", "shrike", "swift", "kestrel", "merlin"} +var supportEngines = []string{"graycode-router", "harrier", "shrike", "swift", "kestrel", "merlin"} type packageImport struct { file string @@ -32,27 +32,27 @@ type packageImport struct { func TestPackageDependencyGraph(t *testing.T) { root := repoRoot(t) - checkGraycodeEyrieFacade(t, root) + checkGraycodeGraycodeRouterFacade(t, root) checkGraycodeInternalLayers(t, root) checkSupportRepositoryBoundaries(t, root) } -func checkGraycodeEyrieFacade(t *testing.T, root string) { +func checkGraycodeGraycodeRouterFacade(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, graycodeRouterModule+"/") { continue } - if imp.path == eyrieModule+"/engine" || strings.HasPrefix(imp.path, eyrieModule+"/engine/") { + if imp.path == graycodeRouterModule+"/engine" || strings.HasPrefix(imp.path, graycodeRouterModule+"/engine/") { continue } - // Graycode uses the full vendored Eyrie API surface for provider, graph, + // Graycode uses the full vendored GraycodeRouter 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 graycodeRouterModule + "/llm", graycodeRouterModule + "/graph", graycodeRouterModule + "/tools": continue } // Graycode's gateway declares the credential service name so existing @@ -60,14 +60,14 @@ func checkGraycodeEyrieFacade(t *testing.T, root string) { // production exception. relFile, relErr := filepath.Rel(root, imp.file) if relErr == nil && filepath.ToSlash(filepath.Dir(relFile)) == "internal/provider/gateway" && - imp.path == eyrieModule+"/credentials" { + imp.path == graycodeRouterModule+"/credentials" { continue } - violations = append(violations, formatImportViolation(root, imp, "use the eyrie/engine facade")) + violations = append(violations, formatImportViolation(root, imp, "use the graycode-router/engine facade")) } } - assertNoPackageViolations(t, "Graycode Eyrie facade", violations) + assertNoPackageViolations(t, "Graycode GraycodeRouter facade", violations) } func checkGraycodeInternalLayers(t *testing.T, root string) { diff --git a/internal/testutil/storage.go b/internal/testutil/storage.go index 3216da59..b71d3321 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", "GRAYCODE_CONFIG_DIR", "GRAYCODE_STATE_DIR", "GRAYCODE_CACHE_DIR", "EYRIE_CONFIG_DIR"} + keys := []string{"HOME", "GRAYCODE_CONFIG_DIR", "GRAYCODE_STATE_DIR", "GRAYCODE_CACHE_DIR", "GRAYCODE_ROUTER_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{ - "GRAYCODE_CONFIG_DIR": "config", - "GRAYCODE_STATE_DIR": "state", - "GRAYCODE_CACHE_DIR": "cache", - "EYRIE_CONFIG_DIR": "eyrie-config", + "GRAYCODE_CONFIG_DIR": "config", + "GRAYCODE_STATE_DIR": "state", + "GRAYCODE_CACHE_DIR": "cache", + "GRAYCODE_ROUTER_CONFIG_DIR": "graycode-router-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..a6453ffd 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 graycode-router/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 graycode-router's WaitUntilDone but inlined to +// stay boundary-compliant (no graycode-router/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 ba2908c0..3660a559 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. graycode does not bundle a media provider; a host (or a provider-backed -// eyrie integration) wires one in via SetMediaEngine. The tool and the +// graycode-router 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..e15983ea 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 GraycodeRouterTools 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. +// GraycodeRouterTools 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 GraycodeRouterTools. 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 2ef92c58..f02cd523 100644 --- a/internal/tool/safety.go +++ b/internal/tool/safety.go @@ -357,7 +357,7 @@ func expandCommandPathVariables(command string) string { }{ {name: "HOME", value: home.MustDir()}, {name: "GRAYCODE_CONFIG_DIR", value: strings.TrimSpace(env.Getenv("GRAYCODE_CONFIG_DIR"))}, - {name: "EYRIE_CONFIG_DIR", value: strings.TrimSpace(env.Getenv("EYRIE_CONFIG_DIR"))}, + {name: "GRAYCODE_ROUTER_CONFIG_DIR", value: strings.TrimSpace(env.Getenv("GRAYCODE_ROUTER_CONFIG_DIR"))}, } { if item.value == "" { continue @@ -377,11 +377,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 + // GRAYCODE_ROUTER_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 ${GRAYCODE_ROUTER_CONFIG_DIR%/}/provider.json. + if strings.Contains(command, "GRAYCODE_ROUTER_CONFIG_DIR") { + return "command references GRAYCODE_ROUTER_CONFIG_DIR, blocked for security" } if !strings.ContainsAny(command, "/.") { return "" @@ -412,7 +412,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 GRAYCODE_ROUTER_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 d8e0975c..746d0f53 100644 --- a/internal/tool/safety_test.go +++ b/internal/tool/safety_test.go @@ -269,24 +269,24 @@ func TestIsSensitivePath_GraycodeConfigDir(t *testing.T) { } } -func TestIsSensitivePath_EyrieConfigDirTakesPrecedence(t *testing.T) { +func TestIsSensitivePath_GraycodeRouterConfigDirTakesPrecedence(t *testing.T) { graycodeDir := filepath.Join(t.TempDir(), "graycode") - eyrieDir := filepath.Join(t.TempDir(), "eyrie") + graycodeRouterDir := filepath.Join(t.TempDir(), "graycode-router") t.Setenv("GRAYCODE_CONFIG_DIR", graycodeDir) - t.Setenv("EYRIE_CONFIG_DIR", eyrieDir) + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", graycodeRouterDir) - 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(graycodeRouterDir, "provider.json")); reason == "" { + t.Fatal("expected GRAYCODE_ROUTER_CONFIG_DIR/provider.json to be blocked") } if reason := IsSensitivePath(filepath.Join(graycodeDir, "settings.json")); reason != "" { t.Fatalf("expected Graycode 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 TestFileToolsBlockGraycodeRouterProviderConfig(t *testing.T) { + graycodeRouterDir := filepath.Join(t.TempDir(), "graycode-router") + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", graycodeRouterDir) + providerPath := filepath.Join(graycodeRouterDir, "provider.json") readInput, _ := json.Marshal(map[string]string{"path": providerPath}) editInput, _ := json.Marshal(map[string]string{ @@ -342,12 +342,12 @@ func TestIsSensitivePath_GraycodeConfigDirEnv(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 { + graycodeRouterDir := filepath.Join(t.TempDir(), "graycode-router") + if err := os.MkdirAll(graycodeRouterDir, 0o755); err != nil { t.Fatal(err) } - t.Setenv("EYRIE_CONFIG_DIR", eyrieDir) - providerPath := filepath.Join(eyrieDir, "provider.json") + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", graycodeRouterDir) + providerPath := filepath.Join(graycodeRouterDir, "provider.json") if err := os.WriteFile(providerPath, []byte(`{"key":"x"}`), 0o600); err != nil { t.Fatal(err) } @@ -862,17 +862,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_GraycodeRouterConfigDir(t *testing.T) { + graycodeRouterDir := filepath.Join(t.TempDir(), "graycode-router config with spaces") + t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", graycodeRouterDir) 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(graycodeRouterDir, "provider.json") + `"`, + `cat "$GRAYCODE_ROUTER_CONFIG_DIR/provider.json"`, + `cat "${GRAYCODE_ROUTER_CONFIG_DIR}/provider.json"`, + `cat "${GRAYCODE_ROUTER_CONFIG_DIR%/}/provider.json"`, + `printf '%s\n' "$GRAYCODE_ROUTER_CONFIG_DIR"`, + "cat " + strings.ReplaceAll(filepath.Join(graycodeRouterDir, "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..dbe7becb 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 graycode-router/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 bb98782d..7a175aae 100644 --- a/internal/tool/tool.go +++ b/internal/tool/tool.go @@ -38,7 +38,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. +// GraycodeRouterTool.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. @@ -72,7 +72,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 GraycodeRouterTool.Parameters. func (s ToolSchema) ToJSONSchema() map[string]interface{} { props := make(map[string]interface{}, len(s.Properties)) for name, p := range s.Properties { @@ -289,7 +289,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 GraycodeRouterTools to the listed primary // names (lazy model surface). Get/Execute still reach every registered tool. modelVisible map[string]bool } @@ -348,17 +348,17 @@ func (r *Registry) Filter(allow []string) *Registry { return NewRegistry(filtered...) } -// EyrieTools converts model-visible tools to Graycode runtime tool definitions. +// GraycodeRouterTools converts model-visible tools to Graycode runtime tool definitions. // When lazy model surface is enabled, only promoted/essential tools are listed. -func (r *Registry) EyrieTools() []types.EyrieTool { +func (r *Registry) GraycodeRouterTools() []types.GraycodeRouterTool { r.mu.RLock() defer r.mu.RUnlock() - out := make([]types.EyrieTool, 0, len(r.primary)) + out := make([]types.GraycodeRouterTool, 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.GraycodeRouterTool{ 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..9ffc4694 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_GraycodeRouterTools_WithTools(t *testing.T) { t.Parallel() registry := NewRegistry(&BashTool{}, &FileReadTool{}) - eyrieTools := registry.EyrieTools() + graycodeRouterTools := registry.GraycodeRouterTools() - if len(eyrieTools) != 2 { - t.Errorf("EyrieTools() returned %d, want 2", len(eyrieTools)) + if len(graycodeRouterTools) != 2 { + t.Errorf("GraycodeRouterTools() returned %d, want 2", len(graycodeRouterTools)) } - for _, et := range eyrieTools { + for _, et := range graycodeRouterTools { if et.Name == "" { - t.Error("EyrieTool.Name should not be empty") + t.Error("GraycodeRouterTool.Name should not be empty") } } } diff --git a/internal/tool/tool_test.go b/internal/tool/tool_test.go index 2ad33f8f..bd81ef15 100644 --- a/internal/tool/tool_test.go +++ b/internal/tool/tool_test.go @@ -435,7 +435,7 @@ func TestRegistryExposesArchiveNamesAndAcceptsAliases(t *testing.T) { } var exposed []string - for _, t := range r.EyrieTools() { + for _, t := range r.GraycodeRouterTools() { 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 e5b05197..5c94c922 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/graycode-router/llm" ) // ContentPart is a provider-neutral multimodal message part. Graycode owns this -// conversation shape; the Eyrie engine adapter translates it at the transport +// conversation shape; the GraycodeRouter 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 Graycode'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 []GraycodeRouterMessage, opts ChatOptions) (*GraycodeRouterResponse, error) + StreamChat(ctx context.Context, messages []GraycodeRouterMessage, 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 []GraycodeRouterMessage, opts ChatOptions) (*GraycodeRouterResponse, error) + StreamChatContinue(ctx context.Context, messages []GraycodeRouterMessage, opts ChatOptions, cfg ContinuationConfig) (*StreamResult, error) } // ResponseFormat specifies the desired output format for a Graycode runtime request. @@ -37,8 +37,8 @@ type ResponseFormat = llm.ResponseFormat // ToolChoiceOption controls how the model uses tools. type ToolChoiceOption = llm.ToolChoiceOption -// EyrieTool is Graycode's runtime tool definition DTO. -type EyrieTool = llm.EyrieTool +// GraycodeRouterTool is Graycode's runtime tool definition DTO. +type GraycodeRouterTool = llm.GraycodeRouterTool // ChatOptions holds Graycode-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 Graycode runtime calls. type ContinuationConfig = llm.ContinuationConfig -// DefaultContinuationConfig is Graycode's agent-loop continuation policy. Eyrie +// DefaultContinuationConfig is Graycode's agent-loop continuation policy. GraycodeRouter // 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 Graycode's runtime tool result DTO. type ToolResult = llm.ToolResult -// EyrieUsage tracks token usage for Graycode runtime responses and streams. -type EyrieUsage = llm.EyrieUsage +// GraycodeRouterUsage tracks token usage for Graycode runtime responses and streams. +type GraycodeRouterUsage = llm.GraycodeRouterUsage // ResolvedRoute is Graycode's view of the concrete provider/model route selected -// by the provider engine. Keeping this projection Graycode-owned prevents Eyrie's +// by the provider engine. Keeping this projection Graycode-owned prevents GraycodeRouter's // transport DTOs from leaking into product state and observability payloads. type ResolvedRoute = llm.ResolvedRoute -// EyrieResponse is Graycode's runtime chat response DTO. -type EyrieResponse = llm.EyrieResponse +// GraycodeRouterResponse is Graycode's runtime chat response DTO. +type GraycodeRouterResponse = llm.GraycodeRouterResponse -// EyrieStreamEvent is Graycode's runtime stream event DTO. -type EyrieStreamEvent = llm.EyrieStreamEvent +// GraycodeRouterStreamEvent is Graycode's runtime stream event DTO. +type GraycodeRouterStreamEvent = llm.GraycodeRouterStreamEvent // StreamResult wraps a Graycode-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/graycode-router/llm. type StreamResult = llm.StreamResult -// EyrieMessage is Graycode's runtime conversation DTO. +// GraycodeRouterMessage is Graycode's runtime conversation DTO. // It intentionally mirrors the engine boundary shape while remaining Graycode-owned. -type EyrieMessage = llm.EyrieMessage +type GraycodeRouterMessage = llm.GraycodeRouterMessage diff --git a/internal/types/client_test.go b/internal/types/client_test.go index ab5e3d11..281ec788 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/graycode-router/llm" ) func TestContentPartJSONContract(t *testing.T) { - in := EyrieMessage{ + in := GraycodeRouterMessage{ 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 GraycodeRouterMessage if err := json.Unmarshal(raw, &got); err != nil { t.Fatalf("Unmarshal() error = %v", err) } diff --git a/lefthook.yml b/lefthook.yml index 5dabc351..e95fd7fc 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -115,8 +115,8 @@ pre-push: boundary-shared-types: run: bash scripts/check-shared-types-imports.sh - boundary-eyrie-client: - run: bash scripts/check-eyrie-client-imports.sh + boundary-graycode-router-client: + run: bash scripts/check-graycode-router-client-imports.sh boundary-support-repo: run: bash scripts/check-support-repo-coupling.sh diff --git a/scripts/check-eyrie-client-imports.sh b/scripts/check-graycode-router-client-imports.sh similarity index 56% rename from scripts/check-eyrie-client-imports.sh rename to scripts/check-graycode-router-client-imports.sh index 21ab6147..605fd85a 100755 --- a/scripts/check-eyrie-client-imports.sh +++ b/scripts/check-graycode-router-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/graycode-router/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/graycode-router/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/graycode-router/client found:" echo "${violations}" echo - echo "Graycode production code must go through github.com/GrayCodeAI/eyrie/engine" + echo "Graycode production code must go through github.com/GrayCodeAI/graycode-router/engine" exit 1 fi -echo "eyrie/client boundary guard passed (zero production imports)" +echo "graycode-router/client boundary guard passed (zero production imports)" diff --git a/scripts/check-eyrie-engine-boundary.sh b/scripts/check-graycode-router-engine-boundary.sh similarity index 61% rename from scripts/check-eyrie-engine-boundary.sh rename to scripts/check-graycode-router-engine-boundary.sh index 28e5e765..9ff97843 100755 --- a/scripts/check-eyrie-engine-boundary.sh +++ b/scripts/check-graycode-router-engine-boundary.sh @@ -5,25 +5,25 @@ 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/[^\"]+"' \ + graycoderouter_imports="$( + rg -n '"github\.com/GrayCodeAI/graycode-router/[^\"]+"' \ --glob '*.go' --glob '!*_test.go' . || true )" else - eyrie_imports="$( + graycoderouter_imports="$( grep -RInE --include='*.go' --exclude='*_test.go' \ - '"github\.com/GrayCodeAI/eyrie/[^\"]+"' . || true + '"github\.com/GrayCodeAI/graycode-router/[^\"]+"' . || true )" fi -# Graycode uses the full vendored Eyrie API surface for provider, graph, and +# Graycode uses the full vendored GraycodeRouter API surface for provider, graph, and # tooling contracts that the engine facade does not re-export. -violations="$(printf '%s\n' "$eyrie_imports" | grep -vE '"github\.com/GrayCodeAI/eyrie/(engine|llm|graph|tools)(/|\")' || true)" +violations="$(printf '%s\n' "$graycoderouter_imports" | grep -vE '"github\.com/GrayCodeAI/graycode-router/(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 graycode-router/engine facade found:" echo "$violations" echo - echo "route every Graycode production integration through github.com/GrayCodeAI/eyrie/engine" + echo "route every Graycode production integration through github.com/GrayCodeAI/graycode-router/engine" exit 1 fi @@ -39,4 +39,4 @@ if [[ -n "$credential_symbols" ]]; then exit 1 fi -echo "eyrie engine boundary passed (zero lower-level production imports)" +echo "graycode-router 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 e24f22fe..b3f46736 100755 --- a/scripts/check-module-release-parity.sh +++ b/scripts/check-module-release-parity.sh @@ -15,7 +15,7 @@ done < <("${ROOT_DIR}/scripts/ecosystem-manifest.sh" list workspace) failed=0 # Directory and module path differ when a repo is renamed (e.g. graycode-router -# hosts module github.com/GrayCodeAI/eyrie), so resolve via the manifest. +# hosts module github.com/GrayCodeAI/graycode-router), 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 8826c719..b25e7e42 100644 --- a/testdata/compatibility-matrix.json +++ b/testdata/compatibility-matrix.json @@ -7,7 +7,7 @@ "graycode", "sparrow", "robin", - "eyrie", + "graycode-router", "kestrel", "merlin", "shrike", @@ -15,15 +15,15 @@ "swift" ], "dependencies": { - "graycode": ["eyrie", "kestrel", "merlin", "shrike"], + "graycode": ["graycode-router", "kestrel", "merlin", "shrike"], "sparrow": ["graycode"], "robin": ["graycode"], - "kestrel": ["graycode", "eyrie"], + "kestrel": ["graycode", "graycode-router"], "merlin": ["graycode"], "swift": [], "shrike": ["graycode"], "harrier": [], - "eyrie": [] + "graycode-router": [] }, "matrices": [ { @@ -33,7 +33,7 @@ "graycode": "0.1.0", "sparrow": "0.1.0", "robin": "0.1.0", - "eyrie": "0.1.0", + "graycode-router": "0.1.0", "kestrel": "0.1.0", "merlin": "0.1.0", "shrike": "0.1.0", @@ -48,7 +48,7 @@ "graycode": "main", "sparrow": "main", "robin": "main", - "eyrie": "main", + "graycode-router": "main", "kestrel": "main", "merlin": "main", "shrike": "main", diff --git a/testdata/golden/help_root.txt b/testdata/golden/help_root.txt index 3ea7ebfc..47e72d48 100644 --- a/testdata/golden/help_root.txt +++ b/testdata/golden/help_root.txt @@ -1,6 +1,6 @@ graycode 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 graycode-router, 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. @@ -42,7 +42,7 @@ Available Commands: credentials Manage secure API key storage (macOS Keychain / Linux secret service) daemon Manage the graycode background server doctor Run local diagnostics - ecosystem Show eyrie, harrier, and shrike integration status + ecosystem Show graycode-router, harrier, and shrike integration status eval Evaluate model performance on coding benchmarks exec Execute a single command non-interactively features List and manage feature flags @@ -59,7 +59,7 @@ Available Commands: manpage Generate man page in roff format mcp Show MCP configuration; run or register graycode as an MCP server mission Run a multi-agent mission (parallel feature execution) - models Deployment-aware model catalog (via eyrie) + models Deployment-aware model catalog (via graycode-router) path Developer path readiness (setup, security, sandbox, ecosystem) permissions List and manage exact permission rules plan Create and manage structured development plans @@ -113,7 +113,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 graycode-router 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") @@ -125,7 +125,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 graycode-router 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