154 lines
9.1 KiB
Markdown
154 lines
9.1 KiB
Markdown
# 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.
|