diff --git a/AGENTS.md b/AGENTS.md index 52a5fcb6a5..37ce9f9573 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,161 +4,150 @@ Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks, Cohere, etc.) -with **MCP Server** (16 tools for agent control) and **A2A v0.3 Protocol** (Agent-to-Agent orchestration). +with **MCP Server** (16 tools) and **A2A v0.3 Protocol**. ## Stack -- **Runtime**: Next.js 16 (App Router), Node.js, ES Modules +- **Runtime**: Next.js 16 (App Router), Node.js, ES Modules (`"type": "module"`) - **Language**: TypeScript 5.9 (`src/`) + JavaScript (`open-sse/`) - **Database**: better-sqlite3 (SQLite) — `DATA_DIR` configurable, default `~/.omniroute/` - **Streaming**: SSE via `open-sse` internal package - **Styling**: Tailwind CSS v4 -- **Docker**: Multi-stage Dockerfile, 3 profiles (base / cli / host) -- **i18n**: next-intl with 30 languages (`src/i18n/messages/`) +- **i18n**: next-intl with 30 languages + +--- + +## Build, Lint, and Test Commands + +| Command | Description | +| ----------------------------------- | --------------------------------- | +| `npm run dev` | Start Next.js dev server | +| `npm run build` | Production build (isolated) | +| `npm run start` | Run production build | +| `npm run build:cli` | Build CLI package | +| `npm run lint` | ESLint on all source files | +| `npm run typecheck:core` | TypeScript core type checking | +| `npm run typecheck:noimplicit:core` | Strict checking (no implicit any) | +| `npm run check` | Run lint + test | +| `npm run check:cycles` | Check for circular dependencies | + +### Running Tests + +```bash +# All tests +npm run test:all + +# Single test file (Node.js native test runner — most tests use this) +node --import tsx/esm --test tests/unit/your-file.test.mjs +node --import tsx/esm --test tests/unit/plan3-p0.test.mjs +node --import tsx/esm --test tests/unit/fixes-p1.test.mjs +node --import tsx/esm --test tests/unit/security-fase01.test.mjs + +# Integration tests +node --import tsx/esm --test tests/integration/*.test.mjs + +# Vitest (MCP server, autoCombo) +npm run test:vitest + +# E2E with Playwright +npm run test:e2e + +# Coverage (55% min thresholds) +npm run test:coverage +``` + +--- + +## Code Style Guidelines + +### Formatting (Prettier — enforced via lint-staged) + +2 spaces · semicolons required · double quotes (`"`) · 100 char width · es5 trailing commas. +Always run `prettier --write` on changed files. + +### TypeScript + +- **Target**: ES2022 · **Module**: `esnext` · **Resolution**: `bundler` +- `strict: false` — prefer explicit types, don't rely on inference +- Path aliases: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/` + +### ESLint Rules + +- **Security (error, everywhere)**: `no-eval`, `no-implied-eval`, `no-new-func` +- **Relaxed in `open-sse/` and `tests/`**: `@typescript-eslint/no-explicit-any` = warn +- React hooks rules disabled in `open-sse/` + +### Naming + +| Element | Convention | Example | +| ------------------- | -------------------------------- | ------------------------------------ | +| Files | kebab-case | `chatCore.ts`, `tokenHealthCheck.ts` | +| React components | PascalCase | `Dashboard.tsx`, `ProviderCard.tsx` | +| Functions/variables | camelCase | `getHealth()`, `switchCombo()` | +| Constants | UPPER_SNAKE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` | +| Interfaces | PascalCase (`I` prefix optional) | `ProviderConfig` | +| Enums | PascalCase (members too) | `LogLevel.Error` | + +### Imports + +- **Order**: external → internal (`@/`, `@omniroute/open-sse`) → relative (`./`, `../`) +- **No barrel imports** from `localDb.ts` — import from the specific `db/` module instead + +### Error Handling + +- try/catch with specific error types; always log with context (pino logger) +- Never silently swallow errors in SSE streams — use abort signals for cleanup +- Return proper HTTP status codes (4xx client, 5xx server) + +### Security + +- **NEVER** commit API keys, secrets, or credentials +- Validate all user inputs with Zod schemas +- Auth middleware required on all API routes +- Never log SQLite encryption keys +- Sanitize user content (dompurify for HTML) + +--- ## Architecture ### Data Layer (`src/lib/db/`) -All persistence uses SQLite through domain-specific modules: - -| Module | Responsibility | -| -------------- | ------------------------------------------ | -| `core.ts` | SQLite engine, migrations, WAL, encryption | -| `providers.ts` | Provider connections & nodes | -| `models.ts` | Model aliases, MITM aliases, custom models | -| `combos.ts` | Combo configurations | -| `apiKeys.ts` | API key management & validation | -| `settings.ts` | Settings, pricing, proxy config | -| `backup.ts` | Backup / restore operations | - -`src/lib/localDb.ts` is a **re-export layer only** — all 27+ consumers import from it, -but the real logic lives in `src/lib/db/`. +All persistence uses SQLite through domain-specific modules (`core.ts`, `providers.ts`, +`models.ts`, `combos.ts`, `apiKeys.ts`, `settings.ts`, `backup.ts`). +`src/lib/localDb.ts` is a **re-export layer only** — never add logic there. ### Request Pipeline (`open-sse/`) -| Handler | Role | -| ----------------------- | ------------------------------------------- | -| `chatCore.js` | Main chat completions proxy (SSE / non-SSE) | -| `responsesHandler.js` | OpenAI Responses API compat | -| `responseTranslator.js` | Format translation for Responses API | -| `embeddings.js` | Embedding proxy | -| `imageGeneration.js` | Image generation proxy | -| `sseParser.js` | SSE stream parser | -| `usageExtractor.js` | Token usage extraction from responses | +`chatCore.ts` → executor → upstream provider. Translations in `open-sse/translator/`. -Translation between provider formats: `open-sse/translator/` - -**Upstream model extra headers** (`compatByProtocol` / custom models): merged in executors after default auth; **same header name replaces** the executor value (e.g. custom `Authorization` overrides Bearer). In `open-sse/handlers/chatCore.ts`, the primary request merges headers for **both** the client model id and `resolveModelAlias(clientModel)` (resolved id wins on key conflicts). **T5 intra-family fallback** recomputes headers using only the fallback model id and `resolveModelAlias(fallback)` so sibling models do not inherit another model’s headers. Forbidden header names live in `src/shared/constants/upstreamHeaders.ts` — keep sanitize (`models.ts`), Zod (`schemas.ts`), and unit tests aligned when editing that list. +**Upstream headers**: merged after default auth; same header name replaces executor value. +**T5 intra-family fallback** recomputes headers using only the fallback model id. +Forbidden header names: `src/shared/constants/upstreamHeaders.ts` — keep sanitize, +Zod schemas, and unit tests aligned when editing. ### MCP Server (`open-sse/mcp-server/`) -16 tools for AI agent control via **3 transport modes**: - -- **stdio** — Local IDE integration (Claude Desktop, Cursor, VS Code) -- **SSE** — Remote Server-Sent Events at `/api/mcp/sse` -- **Streamable HTTP** — Modern bidirectional HTTP at `/api/mcp/stream` - -HTTP transports run in-process via `httpTransport.ts` singleton using `WebStandardStreamableHTTPServerTransport`. - -| Category | Tools | -| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Essential | `get_health`, `list_combos`, `get_combo_metrics`, `switch_combo`, `check_quota`, `route_request`, `cost_report`, `list_models_catalog` | -| Advanced | `simulate_route`, `set_budget_guard`, `set_resilience_profile`, `test_combo`, `get_provider_metrics`, `best_combo_for_task`, `explain_route`, `get_session_snapshot` | - -- Scoped authorization (9 scopes), audit logging, Zod schemas -- IDE configs for Claude Desktop, Cursor, VS Code Copilot +16 tools, 3 transports (stdio / SSE / Streamable HTTP). Scoped auth (9 scopes), Zod schemas. ### A2A Server (`src/lib/a2a/`) -Agent-to-Agent v0.3 protocol: - -- JSON-RPC 2.0: `message/send`, `message/stream`, `tasks/get`, `tasks/cancel` -- Agent Card at `/.well-known/agent.json` -- Skills: `smart-routing`, `quota-management` -- SSE streaming with 15s heartbeat -- Task Manager with state machine and TTL-based cleanup - -### Auto-Combo Engine (`open-sse/services/autoCombo/`) - -Self-healing routing optimization: - -- 6-factor scoring, 4 mode packs, bandit exploration -- Progressive cooldown, probe-based re-admission - -### Dashboard (`src/app/(dashboard)/`) - -| Page | Description | -| ------------------------ | --------------------------------------------------------------- | -| `/dashboard` | Home with quick start, provider overview | -| `/dashboard/endpoint` | **Endpoints** (tabbed): Endpoint Proxy, MCP, A2A, API Endpoints | -| `/dashboard/providers` | Provider management and connections | -| `/dashboard/combos` | Combo configurations with routing strategies | -| `/dashboard/logs` | Request, Proxy, Audit, Console logs (tabbed) | -| `/dashboard/analytics` | Usage analytics and evaluations | -| `/dashboard/costs` | Cost tracking and breakdown | -| `/dashboard/health` | Uptime, circuit breakers, latency | -| `/dashboard/cli-tools` | CLI tool integrations (Claude, Codex, Antigravity, etc.) | -| `/dashboard/media` | Image, Video, Music generation playground | -| `/dashboard/settings` | System settings with multiple tabs | -| `/dashboard/api-manager` | API key management with model permissions | - -### OAuth & Tokens (`src/lib/oauth/`) - -18 modules handling OAuth flows, token refresh, and provider credentials. -Default credentials are hardcoded in `src/lib/oauth/constants/oauth.ts`, -overridable via env vars or `data/provider-credentials.json`. - -### Supporting Systems - -| System | Location | -| -------------------------- | ------------------------------------------------- | -| Usage tracking & analytics | `src/lib/usageDb.ts`, `src/lib/usageAnalytics.ts` | -| Token health checks | `src/lib/tokenHealthCheck.ts` | -| Cloud sync | `src/lib/cloudSync.ts` | -| Proxy logging | `src/lib/proxyLogger.ts` | -| Data paths resolution | `src/lib/dataPaths.ts` | +JSON-RPC 2.0, SSE streaming, Task Manager with TTL cleanup. Agent Card at `/.well-known/agent.json`. ### Adding a New Provider 1. Register in `src/shared/constants/providers.ts` 2. Add executor in `open-sse/executors/` -3. Add translator rules in `open-sse/translator/` (if non-OpenAI format) +3. Add translator in `open-sse/translator/` (if non-OpenAI format) 4. Add OAuth config in `src/lib/oauth/constants/oauth.ts` (if OAuth-based) +--- + ## Review Focus -### Security - -- No hardcoded API keys or secrets in commits -- Auth middleware on all API routes -- Input validation on user-facing endpoints (Zod schemas) -- SQLite encryption key must not be logged - -### Architecture - -- DB operations go through `src/lib/db/` modules, never raw SQL in routes -- Provider requests flow through `open-sse/handlers/` -- Translations use `open-sse/translator/` modules -- `localDb.ts` is re-exports only — add new functions to the proper `db/*.ts` module -- MCP and A2A pages are embedded as tabs inside `/dashboard/endpoint`, not standalone routes - -### Code Quality - -- Consistent error handling with try/catch -- Proper HTTP status codes -- No memory leaks in SSE streams (abort signals, cleanup) -- Rate limit headers must be parsed correctly -- All API inputs validated with Zod schemas - -### Docker - -- Dockerfile has two targets: `runner-base` and `runner-cli` -- `docker-compose.yml` — development (3 profiles) -- `docker-compose.prod.yml` — isolated production instance (port 20130) -- Data persists in named volumes (`omniroute-data` / `omniroute-prod-data`) - -### Review Mode - -- Provide analysis and suggestions only -- Focus on bugs, security, performance, and best practices +- **DB ops** go through `src/lib/db/` modules, never raw SQL in routes +- **Provider requests** flow through `open-sse/handlers/` +- **MCP/A2A pages** are tabs inside `/dashboard/endpoint`, not standalone routes +- **No memory leaks** in SSE streams (abort signals, cleanup) +- **Rate limit headers** must be parsed correctly +- All API inputs validated with **Zod schemas** diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d1a354407..90f6d6c7a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### 🛠️ Maintenance + +- **AGENTS.md rewrite:** Condensed from 297→153 lines. Added build/lint/test commands (including single-test execution), code style guidelines (Prettier, TypeScript, ESLint, naming, imports, error handling, security), and trimmed verbose architecture tables for AI agent consumption. + ## [3.4.1] - 2026-03-31 > [!WARNING]