# AGENTS.md ## Overview This is **Antigravity Agent** — an autonomous AI agent platform built with a Rust backend and a vanilla JS frontend. Users create "tasks" (goals), and the system dispatches an LLM-powered agent to accomplish them. Tasks can be run on-demand or on a cron schedule. Each execution produces logs and a final answer, all persisted to PostgreSQL. ## Architecture ``` ┌─────────────────────────────────────────────────────────┐ │ Frontend (Vite + Vanilla JS) │ │ - OAuth login via Authentik │ │ - Dashboard: recent runs, task list, logs, answers │ │ - Polls /api/tasks every 3s for live updates │ └──────────────────────┬──────────────────────────────────┘ │ /api/* ┌──────────────────────▼──────────────────────────────────┐ │ Axum HTTP Server (src/server/) │ │ - CORS, CSP, rate limiting (1 MB body) │ │ - Cookie-based + Bearer token auth │ │ - Routes: tasks CRUD, runs, auth (callback/refresh) │ ├─────────────────────────────────────────────────────────┤ │ Domain Layer (src/domain/) │ │ ├─ agent/ Agent loop, LLM API client, tool defs │ │ ├─ auth JwksVerifier, Authenticator (OIDC) │ │ └─ tasks Task execution, run management │ ├─────────────────────────────────────────────────────────┤ │ Scheduler (src/scheduler.rs) │ │ - tokio-cron-scheduler for recurring task execution │ ├─────────────────────────────────────────────────────────┤ │ Entities (src/entities/) │ │ - task, task_run (SeaORM models) │ ├─────────────────────────────────────────────────────────┤ │ PostgreSQL │ │ - Migrations managed via sea-orm-migration │ └─────────────────────────────────────────────────────────┘ ``` ## Project Structure ``` bot/ ├── Cargo.toml # Workspace root (members: ".", "migration") ├── src/ │ ├── main.rs # Entrypoint: tracing init → server::start() │ ├── config.rs # Config struct loaded from env vars │ ├── error.rs # AppError enum (thiserror) → Axum responses │ ├── scheduler.rs # Cron job scheduler (wraps tokio-cron-scheduler) │ ├── tests.rs # Unit tests for error handling & config │ ├── domain/ │ │ ├── agent/ │ │ │ ├── mod.rs # Agent struct: agentic loop with turn/time limits │ │ │ ├── api.rs # LLM request/response types, Tavily search client │ │ │ └── tools.rs # Tool definitions (google_search, finish) & dispatch │ │ ├── auth.rs # JwksVerifier (RSA/JWKS), Authenticator (code exchange, refresh) │ │ └── tasks.rs # Task execution logic, response DTOs │ ├── entities/ │ │ ├── task.rs # SeaORM entity: tasks table │ │ └── task_run.rs # SeaORM entity: task_runs table (belongs_to task) │ └── server/ │ ├── mod.rs # App bootstrap: DB, scheduler, auth, router, CORS │ ├── auth.rs # Auth routes & AuthenticatedUser extractor │ └── tasks.rs # Task CRUD & run endpoints ├── migration/ │ └── src/ # SeaORM migrations (tasks, answer col, runs table, cron col) ├── frontend/ │ ├── index.html # SPA shell with glassmorphism dark theme │ ├── src/ │ │ ├── main.js # All app logic: auth flow, task/run rendering, polling │ │ └── style.css # Styles │ ├── vite.config.js # Dev proxy: /api → localhost:3000 │ └── package.json # Deps: vite, marked, dompurify └── .forgejo/workflows/ └── pipeline.yaml # CI: build frontend + cargo build → deploy via systemd ``` ## Agent System The agent (`src/domain/agent/`) is a turn-based autonomous loop: 1. A system prompt is injected with the current date and instructions not to ask the user for clarification. 2. The user's goal is sent as the initial message. 3. Each turn calls the **Kimi K2.5** model via the Zen API (`https://opencode.ai/zen/v1/chat/completions`). 4. The model can invoke tools: - **`google_search`** — web search via the Tavily API. - **`finish`** — signals completion and provides the final answer. 5. Tool results are appended to the conversation and the loop continues. 6. The loop terminates when `finish` is called, the turn limit is hit (`AGENT_MAX_TURNS`, default 20), or the time limit expires (`AGENT_MAX_DURATION_SECS`, default 120s). All turns and tool calls are logged. The final answer (if any) and the full log are persisted to the `task_runs` table. ## Environment Variables | Variable | Required | Default | Description | |---------------------------|----------|---------|----------------------------------------------| | `DATABASE_URL` | ✅ | — | PostgreSQL connection string | | `PORT` | | `3000` | HTTP server port | | `ZEN_API_KEY` | | — | API key for Zen/Kimi LLM | | `TAVILY_API_KEY` | | — | API key for Tavily web search | | `AUTHENTIK_ISSUER` | ✅ | — | OIDC issuer URL (Authentik) | | `AUTHENTIK_CLIENT_ID` | ✅ | — | OAuth client ID | | `AUTHENTIK_CLIENT_SECRET` | ✅ | — | OAuth client secret | | `CORS_ALLOWED_ORIGINS` | | — | Comma-separated allowed origins (or mirror) | | `COOKIE_SECURE` | | `false` | Set `true` for HTTPS-only cookies | | `AGENT_MAX_TURNS` | | `20` | Max LLM turns per agent run | | `AGENT_MAX_DURATION_SECS` | | `120` | Max wall-clock seconds per agent run | ## API Routes All task/run routes require authentication (cookie or Bearer token). | Method | Path | Description | |--------|------------------------|-----------------------------------| | GET | `/api/tasks` | List all tasks with their runs | | POST | `/api/tasks` | Create a new task | | GET | `/api/tasks/:id` | Get a single task with runs | | PUT | `/api/tasks/:id` | Update task goal/cron schedule | | POST | `/api/tasks/:id/runs` | Trigger a manual re-run | | GET | `/api/runs/recent` | Latest 50 runs across all tasks | | GET | `/api/auth/session` | Check current session | | GET | `/api/auth/callback` | OAuth code → token exchange | | POST | `/api/auth/refresh` | Refresh access token | | POST | `/api/auth/logout` | Clear auth cookies | ## Database Schema **`tasks`** — `id` (UUID PK), `goal` (text), `cron` (text, nullable), `created_at` (timestamptz) **`task_runs`** — `id` (UUID PK), `task_id` (FK → tasks, cascade delete), `status` (text), `logs` (text), `answer` (text, nullable), `created_at` (timestamptz) Migrations are in `migration/src/` and run automatically on startup via `Migrator::up()`. ## Development ```bash # Backend (from repo root) cargo run # requires DATABASE_URL + Authentik vars # Frontend (from frontend/) npm install npm run dev # Vite dev server on :5173, proxies /api to :3000 # Tests cargo test ``` ## CI / Deployment The Forgejo Actions pipeline (`.forgejo/workflows/pipeline.yaml`) triggers on release publish: 1. Builds the frontend (`npm install && npm run build`). 2. Builds the Rust binary (`cargo build -r`). 3. Uploads the binary as a release asset. 4. Deploys to the host: copies binary + frontend dist, restarts the `bot` systemd user service.