Files
ai-agent/README.md
Gabriel Vidal 47e536cdd9 docs: standalone repo scaffolding — README + .gitignore
Extracted from the homelab monorepo (git subtree split of services/ai-agent,
full history preserved). Ticks the 'publish as a standalone repo' box in
GOAL.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 13:18:15 +02:00

121 lines
6.6 KiB
Markdown

# ai-agent
**A self-hosted agent layer for your computer** — spawn and watch coding-agent
sessions from an installable PWA, browse every conversation with real per-turn
token costs, and manage the whole Claude context (CLAUDE.md files, skills,
hooks, memories, projects) that shapes what the agent does.
Think *Lovable, but open source and running on your own hardware*: the agent
builds things, the app shows you everything it touches — conversations, files,
projects, costs, deploys — and nothing leaves your machine beyond the model API
calls you configure.
> Extracted from my [homelab monorepo](https://git.gabvdl.xyz/gabrielvidal/homelab)
> after ~280 commits; full history preserved. It runs my homelab's AI operations
> daily at `ai-agent.lab.gabvdl.xyz`.
## What it does
- **Spawn / resume / interrupt agent sessions** from the composer — pick a
model (fetched live from the Anthropic API), an effort level, attach files —
and watch the run stream into a live thread over SSE within seconds.
- **Conversation archive** — every Claude Code transcript parsed into a rich
thread view: per-turn token usage and dollar cost, tool cards with duration
and diff stats, thinking blocks, task panels — each element with its own
visibility switch.
- **Claude-context editor** — every `CLAUDE.md` and the `.claude/` tree
(skills, hooks, agents, settings) as an editable file list with *real* token
counts and per-model costs, recomputed only when content changes.
- **Catalogs** — projects gallery (git history, goals, costs per project),
services catalog (static compose + Traefik parsing), assistant memories,
scaffolding templates, plans with estimated-vs-actual metrics.
- **Cron agents** — scheduled sessions defined by a cron expression plus a
prompt file; harness/model/effort declared in the prompt's frontmatter.
- **Notification hub** — the source of truth for the agent's "done"/"ask"
push notifications (Home Assistant integration included), with tappable
answers POSTed back to the blocked session.
## Architecture
```
┌─────────────────────────── Docker container ───────────────────────────┐
│ FastAPI backend (:8080) ──serves── React PWA (Vite, installable) │
│ • transcript parser (resumable, O(new bytes) per live tick) │
│ • SQLite store: token counts, costs, skill analytics │
│ • SSE hub: one event bus invalidates the UI │
│ • cron scheduler, notification hub, trusted-caller gate │
└──────────────┬─────────────────────────────────────────────────────────┘
│ POST /spawn|/resume|/interrupt (Bearer token)
┌──────▼───────────────┐
│ sidecar (runner) │ launches `claude -p … --session-id …`
│ host process — or │ transcripts land in watched dirs and
│ inside the container │ stream back into the viewer
└──────────────────────┘
```
The same `sidecar/sidecar.py` runs in one of two places and the backend can't
tell the difference: **on the host** (a session then has the host user's own
auth, hooks and skills — exactly a terminal session) or **inside the container**
(`RUNNER_IN_CONTAINER=1`, the standalone default — the image bundles the Claude
Code CLI and spawns sessions with no host process at all).
Notable engineering, hard-won on a real archive (~900 transcripts / 458 MB):
- **Resumable transcript parsing** — a live session appends every couple of
seconds; a `ParserState` per growing file is fed only the new bytes instead
of re-parsing from byte 0 (O(n²) over a session, before).
- **One git walk, not one per item** — both catalogs share a HEAD-keyed
`git log --name-only` bucketed per directory. `/api/services` went from two
`git log` forks per service per request (11 s) to <1 s.
- **Metadata-only list endpoints** + per-file content loads + one-row SSE
patches: the conversation list went 1.3 MB → 89 KB, `/api/bundle`
12.5 s → 26 ms, idle CPU ~58% → ~7% while a session streams.
- **Blue-green deploys** — `deploy.sh` health-gates a standby container on
`/api/health`, cuts the reverse proxy over, recreates, restores: zero
downtime, and a bad build never touches the live container.
- **Trusted-caller security gate** — the API answers only the reverse proxy,
the host, or a shared token; read-only API keys allow safe methods only
(a desk phone reads notifications aloud but can never spawn a session);
uploaded bytes are served `nosniff` + `attachment` so user content never
executes on the app origin.
- **Schema-drift canary** — the Claude Code transcript format is internal and
undocumented; `scripts/schema-drift.py` diffs a structural footprint of
fresh transcripts against a committed baseline and cross-checks the real
parser against raw record counts.
- **In-browser mock backend** — `?mock=1` swaps `fetch` + `EventSource` for a
deterministic, combinatorial in-memory seed (every UI state reachable from a
cold load), so the PWA demos and tests with no backend at all.
## Stack
FastAPI · Python 3.12 · SQLite (WAL) · React 18 · TypeScript · Vite · Tailwind ·
TanStack Query (IndexedDB-persisted) · Zustand (server-persisted) · SSE ·
orval (API types generated from the OpenAPI spec) · Docker · Traefik.
## Run it
Standalone (no host mounts — catalogs degrade gracefully when a mount is
absent):
```bash
docker compose -f docker-compose.standalone.yml up -d
# → http://localhost:8080 (set ANTHROPIC_API_KEY, or mount a logged-in
# Claude home at the runner's $HOME, to spawn sessions)
```
`scripts/standalone-smoke.sh` boots the image against a throwaway workspace
and asserts every catalog endpoint, the PWA shell and the bundled runner
answer correctly.
In my homelab it runs behind Traefik + Authelia with the host-side sidecar;
that deployment config (compose mounts, Traefik router, blue-green `deploy.sh`)
lives in the [homelab repo](https://git.gabvdl.xyz/gabrielvidal/homelab)'s
`services/ai-agent/` shim. The copies of those files in this repo's history are
kept as reference.
## Where it's going
See [GOAL.md](GOAL.md) — the north star is the open-source, intuitive, private
and secure agent layer of any computer: one container, zipgo-backed hosting,
in-container self-update.