AGENTS.md

August 7, 2026 · View on GitHub

Guidance for AI coding agents (and humans) working in the Agent37 Starter Kit. CLAUDE.md imports this file via @AGENTS.md, so this is the single source of truth — edit here, not there.

First-time setup

Setting this up from a fresh clone? Follow SETUP.md — the complete runbook (it's what the README tells adopters to hand you). Two login-gated secrets are human-supplied: AGENT37_API_KEY (plus a funded Agent37 wallet) and SUPABASE_ACCESS_TOKEN; npm run setup does the rest. Never print or commit the sk_live_ key.

What this project is

A full-stack starter for building your own agent app, built entirely on top of the public Agent37 B2B Agents API: email + password auth (open signup, no verification), a multi-agent fleet, and — for each agent — native in-dashboard Chat, a Files browser, Integrations (Composio), and a Settings tab. Forkers rebrand it (src/config/branding.ts) and ship it; their end users sign up, get workspaces, invite teammates, and create / manage agents.

Everything this app can do is a subset of the Agent37 /v1 API — control plane and data plane. This repo is a client of that API — it does not implement agent infrastructure itself. So the API docs, not this code, are the authority on what an agent can and cannot do.

The API this is built on — read the docs first

This product is built on top of our public API. Before adding or changing any agent capability, consult the docs — they define the full surface and its limits. Two machine-readable entry points are designed for you (an AI agent) to fetch directly:

Documented capability map

Two planes, one sk_live_ key — and this template now drives both. The control plane manages instances (and the per-agent Composio integrations); the data plane powers the native Chat and Files tabs.

Control plane — https://api.agent37.com/v1/* (the sk_live_ key this app holds):

PageCoversUsed here
Core conceptsthe model, auth, the two planesread first
Instancescreate / list / get / start / stop / restart / update / resize / delete
Instance URLsshort-lived signed URLs to open an agent's ports
Templatesthe agent images you can provision
Managed services & budgetsper-agent managed-spend cap
Billingwallet, compute prepay, usage✅ (usage)
Run commandsexec a command inside an instanceavailable, not used
Errorsmachine-readable error codes✅ (mapped in Agent37Error)

The Integrations tab is also control plane: it manages a per-agent Composio entity through /instances/{id}/integrations/* (toolkits / connect / connections).

Data plane — https://{instanceId}.agent37.app/v1/* (talk to one agent's gateway). Data-plane requests authenticate with the X-Agent37-Key: sk_live_... header (raw key, no Bearer prefix; Authorization passes through to the app inside the instance), while the control plane stays Authorization: Bearer. The native Chat and Files tabs call these endpoints directly (through this app's BFF). The signed-URL "open in new tab" shortcuts still exist too — they just complement the in-dashboard UIs now rather than replace them:

PageCoversUsed here
Send a messagepost a message, get a response (/v1/responses)✅ (Chat)
Streamingstream responses (SSE)✅ (Chat)
Sessions & modelsconversation state, model selection✅ (Chat)
Fileslist / read / write / archive files✅ (Files)
Build a chat append-to-end guide for a chat UIreference

So: what's possible = the whole map above, and this template now exercises most of it: the control-plane rows marked ✅, the native data-plane Chat and Files tabs, the per-agent Integrations tab, and the signed-URL buttons that open each agent's own dashboard / terminal / files UI in a new tab.

How this app fits together

Browser ─▶ Next.js (this app) ─▶ control plane  https://api.agent37.com/v1   (instances, integrations)
   │            │              └▶ data plane     https://{instance}.agent37.app/v1   (chat, files)
   │            │                                 (one server-side sk_live_ key, both planes:
   │            │                                  Bearer on the control plane, X-Agent37-Key on the instance)
   │            │
   │            └─▶ Supabase: Auth (browser, anon key) + Postgres (server-only, service-role key):
   │                          users, workspaces, members, agent mirror

   └──────────────▶ https://{instance}.agent37.app  (agent's own UI, via short-lived signed URLs)
  • One key, many app workspaces. A single sk_live_ key, server-side only, is shared by the whole app. Every agent is created under your one Agent37 workspace and tagged metadata.app_workspace; a Supabase mirror table is the source of truth for which app-workspace owns which agent.
  • Isolation is enforced in the server (BFF), not in the browser. Clients have no direct table access — the schema migration (0001_init.sql) grants tables only to the service role, so the browser only uses Supabase for auth. Every read and write goes through src/app/api/** using the service-role client (src/lib/supabase/admin.ts, which bypasses RLS); the TypeScript checks in src/lib/auth.ts (requireUser / requireMember / requireAdmin / requireAgentAccess) are the authorization boundary. RLS policies stay enabled as a backstop but are dormant (clients can't reach the tables). Neither the sk_live_ key nor the service-role key ever reaches the browser.
  • src/lib/agent37.ts is the only thing that calls the Agent37 API (server-only) — both the control-plane base and each instance's data-plane host. Internal src/app/api/** routes are this app's BFF: the browser calls them, they authenticate + check workspace ownership in TS, then call agent37.ts and/or the DB via the service-role client. The browser never calls the upstream API or the DB directly.
  • The UI is a fleet + a per-agent workspace. The (fleet) route group is the multi-agent dashboard (agents, members, invitations, workspace settings). Clicking an agent opens /dashboard/agents/{agentId}/{tab} — a tabbed workspace (Chat / Files / Integrations / Settings) where the active agent is bound to the URL and switchable from a dropdown. Creating an agent is one screen: pick a type from the curated catalog (AGENT_TYPES) and an optional name; shape and budget are fixed server-side (DEFAULT_AGENT).
  • Naming: the upstream API calls these resources instances; this app brands them agents. Paths stay /instances; the client methods read agent….

Where things live

PathWhat
src/lib/agent37.tsThe Agent37 /v1 client — the single egress to both planes
src/app/api/**This app's own API routes (BFF); enforce auth + ownership
src/app/api/agents/[id]/{chat,files}/**Data-plane BFF: native Chat + Files proxied to the instance
src/app/api/agents/[id]/integrations/**Composio integrations BFF (control plane)
src/app/dashboard/agents/[agentId]/[[...tab]]/The per-agent tabbed workspace route (Chat / Files / Integrations / Settings)
src/config/agents.tsSHAPE_PRESETS, DEFAULT_AGENT, the AGENT_TYPES catalog, PORT_LABELS (labels only), and templateAppPorts — the per-template openable app ports (the API no longer reports per-instance ports)
src/config/branding.tsappName / logoUrl code constants (branding lives here, not in env)
src/lib/types.tsApp + upstream /v1 types
supabase/migrations/0001_init.sqlSchema, RLS policies (dormant backstop), SECURITY DEFINER RPCs; grants tables to the service role only (clients have no direct DB access)
src/lib/supabase/admin.tsService-role client (server-only, bypasses RLS) — the DB egress
scripts/setup.mjsOne-command Supabase setup (npm run setup)

Commands

npm install
npm run setup       # configure Supabase end-to-end (idempotent; needs SUPABASE_ACCESS_TOKEN)
npm run dev         # http://localhost:3000
npm run build
npm run typecheck   # tsc --noEmit

There is no test suite; the gate before shipping is a clean npm run typecheck and npm run build. Setup is "paste two keys + npm run setup" — no manual dashboard steps.

Custom agent image (out of scope here)

There is no Docker in this repo. The catalog ships Hermes and OpenClaw, which run on Agent37's stock images, and nothing in src/** or scripts/** builds, pushes, or references an image. Don't add a Dockerfile here — building a custom agent image is a separate concern with its own repo and its own docs page:

Once that template is registered in your workspace, wiring it into this app is one entry in AGENT_TYPES (src/config/agents.ts) whose template is the template name.

House rules

  • The API is the final authority. Shapes, disks, templates, budgets — the /v1 API can reject anything your account's tier disallows, regardless of what src/config lists. Check the docs before assuming a capability exists.
  • Never expose AGENT37_API_KEY to the browser. It stays server-side; all agent calls go through src/app/api/**src/lib/agent37.ts.
  • Payments are intentionally excluded. Add Stripe (or anything) yourself when you're ready to charge your own customers — the create route (src/app/api/agents/route.ts) has a commented canCreateAgent() seam marking where an entitlement gate would go.
  • Branding lives in src/config/branding.ts (appName / logoUrl constants), not in env. The old NEXT_PUBLIC_APP_NAME / NEXT_PUBLIC_LOGO_URL vars are gone; keep it code-side.
  • Keep changes small and focused; don't add unrequested features or touch unrelated code.