AgentGate

Human-in-the-loop approval system for AI agents.
Agents request. Policies decide. Humans approve.
Keep humans in control of what AI agents can do.

npm version MIT License CI TypeScript

---

AgentGate Demo

Your AI agent wants to send an email, delete a file, or deploy to production. **Should it?** AgentGate lets you define policies that auto-approve safe actions, auto-deny dangerous ones, and route everything else to a human β€” via dashboard, Slack, Discord, or email. ### ✨ Highlights - πŸ›‘οΈ **Policy engine** β€” auto-approve, auto-deny, or route to humans based on rules - πŸ‘₯ **Multi-channel approvals** β€” Slack, Discord, email, or web dashboard - πŸ”Œ **TypeScript SDK + MCP** β€” works with any agent framework or Claude Desktop - πŸͺ **Webhooks with retry** β€” real-time notifications with exponential backoff - πŸ“ **Full audit trail** β€” every request, decision, and action logged, each policy decision tagged with the **OWASP LLM Top-10** risk it mitigates (compliance evidence) - 🐳 **Docker-ready** β€” one `docker-compose up` for the full stack - πŸ” **Production-hardened** β€” SSRF protection, ReDoS defense, structured logging, graceful shutdown - πŸ”— **One-click decision links** β€” approve or deny directly from notification emails and webhooks - β™Ώ **Accessible UI** β€” keyboard-navigable approval modals, focus trapping, ARIA labels - πŸ’€ **Skeleton loading** β€” smooth loading states across every dashboard page - ⚑ **Fast & lightweight** β€” Hono server, SQLite or PostgreSQL ## Quickstart ```bash # Self-host the full stack (server + dashboard + Postgres): docker compose up # …or from source: pnpm install && pnpm --filter @agentkitai/agentgate-server db:migrate && pnpm --filter @agentkitai/agentgate-server bootstrap && pnpm dev ``` Drop AgentGate into an MCP client (Claude Desktop / Cursor / VS Code) β€” point it at the gateway: ```jsonc { "mcpServers": { "agentgate": { "command": "npx", "args": ["@agentkitai/agentgate-mcp"] } } } ``` #### Dashboard See all pending requests at a glance, color-coded by urgency so you know what needs attention first. ![Dashboard](docs/screenshots/dashboard.png) #### Approval Requests Review, approve, or deny requests β€” filter by status to focus on what matters. ![Requests](docs/screenshots/requests.png) #### Audit Log Search through every decision with filters for event type, action, actor, and date range. ![Audit Log](docs/screenshots/audit-log.png) #### Request Detail Drill into any request to see parameters, context, timeline, and audit trail β€” with one-click Approve/Deny buttons. ![Request Detail](docs/screenshots/request-detail.png) #### API Keys Manage API keys with fine-grained scopes, rate limits, and usage tracking. Create, edit, or revoke keys from the dashboard. ![API Keys](docs/screenshots/api-keys.png) #### Webhooks Configure webhook endpoints for real-time notifications. Add URLs, pick events, and let AgentGate handle retries automatically. ![Webhooks](docs/screenshots/webhooks.png) #### Login Sign in with your API key β€” create one via the CLI or ask your admin. ![Login](docs/screenshots/login.png) --- ## Table of Contents - [Quick Start](#quick-start) - [Architecture](#architecture) - [Packages](#packages) - [SDK Usage](#sdk-usage) - [CLI](#cli) - [MCP Integration](#mcp-integration) - [Authentication](#authentication) - [API Endpoints](#api-endpoints) - [Rate Limiting](#rate-limiting) - [Webhooks](#webhooks) - [Configuration](#configuration) - [Docker Deployment](#docker-deployment) - [Development](#development) - [Troubleshooting](docs/troubleshooting.md) - [Contributing](#contributing) - [License](#license) --- ## Quick Start ### 1. Install dependencies ```bash pnpm install ``` ### 2. Run database migrations ```bash pnpm --filter @agentkitai/agentgate-server db:migrate ``` ### 3. Bootstrap (create admin API key) ```bash pnpm --filter @agentkitai/agentgate-server bootstrap ``` **Save the API key** - it's shown once only! Set it in your environment: ```bash export AGENTGATE_API_KEY="agk_..." ``` ### 4. Start the development environment ```bash # Start server (port 3000) and dashboard (port 5173) pnpm dev ``` ### 5. Run the demo In a new terminal (with API key set): ```bash export AGENTGATE_API_KEY="agk_..." pnpm demo ``` ### 6. Open the dashboard Visit **http://localhost:5173** to view and manage approval requests. ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ AI Agents β”‚ β”‚ (use @agentkitai/agentgate-sdk or MCP to request approvals) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ HTTP API (authenticated) β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ AgentGate Server β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Policy Engineβ”‚ β”‚ Request Storeβ”‚ β”‚ Audit Logger β”‚ β”‚ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ API Keys β”‚ β”‚ Webhooks β”‚ β”‚ MCP Server β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–Ό β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Web Dashboard β”‚ β”‚ Slack Bot β”‚ β”‚ Discord Bot β”‚ β”‚(React+Tailwind)β”‚ β”‚(approve in DM) β”‚ β”‚(approve in ch) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Humans β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Packages | Package | Description | Docs | |---------|-------------|------| | [`@agentkitai/agentgate-core`](./packages/core) | Types, schemas, policy engine | - | | [`@agentkitai/agentgate-server`](./packages/server) | Hono API server | - | | [`@agentkitai/agentgate-sdk`](./packages/sdk) | TypeScript SDK for agents | [README](./packages/sdk/README.md) | | [`@agentkitai/agentgate-cli`](./packages/cli) | Command-line interface | - | | [`@agentkitai/agentgate-mcp`](./packages/mcp) | MCP server for Claude Desktop | - | | [`@agentkitai/agentgate-slack`](./packages/slack) | Slack bot integration | [README](./packages/slack/README.md) | | [`@agentkitai/agentgate-discord`](./packages/discord) | Discord bot integration | [README](./packages/discord/README.md) | | [`@agentkitai/agentgate-dashboard`](./packages/dashboard) | React web dashboard | - | ## SDK Usage ```typescript import { AgentGateClient } from '@agentkitai/agentgate-sdk'; // Create client with API key const client = new AgentGateClient({ baseUrl: 'http://localhost:3000', apiKey: process.env.AGENTGATE_API_KEY, }); // Request approval const request = await client.request({ action: 'send_email', params: { to: 'customer@example.com', subject: 'Order shipped!', }, urgency: 'normal', }); // Wait for human decision const decided = await client.waitForDecision(request.id, { timeout: 60000, // 1 minute }); if (decided.status === 'approved') { // Execute the action await sendEmail(decided.params); } else { console.log('Action denied:', decided.decisionReason); } ``` ## CLI AgentGate includes a command-line interface for managing approval requests. ### Installation ```bash # From the monorepo pnpm --filter @agentkitai/agentgate-cli build # Or install globally (when published) npm install -g @agentkitai/agentgate-cli ``` ### Configuration Configure the CLI with your server URL and API key: ```bash # Set server URL agentgate config set serverUrl http://localhost:3000 # Set API key agentgate config set apiKey agk_your_api_key # View current config agentgate config show ``` Configuration is stored in `~/.agentgate/config.json`. You can also use environment variables: ```bash export AGENTGATE_URL=http://localhost:3000 export AGENTGATE_API_KEY=agk_... ``` ### Commands | Command | Description | |---------|-------------| | `agentgate config show` | Show current configuration | | `agentgate config set ` | Set a configuration value | | `agentgate request ` | Create a new approval request | | `agentgate status ` | Get status of a request | | `agentgate list` | List approval requests | | `agentgate approve ` | Approve a pending request | | `agentgate deny ` | Deny a pending request | ### Examples ```bash # Create a request agentgate request send_email \ --params '{"to": "user@example.com", "subject": "Hello"}' \ --urgency high # List pending requests agentgate list --status pending # Approve a request agentgate approve req_abc123 --reason "Looks good" # Deny a request agentgate deny req_abc123 --reason "Not authorized" # Output as JSON agentgate list --json ``` ## MCP Integration AgentGate includes a Model Context Protocol (MCP) server for integration with Claude Desktop and other MCP-compatible clients. ### Claude Desktop Configuration Add to your `claude_desktop_config.json`: ```json { "mcpServers": { "agentgate": { "command": "npx", "args": ["@agentkitai/agentgate-mcp"], "env": { "AGENTGATE_URL": "http://localhost:3000", "AGENTGATE_API_KEY": "agk_..." } } } } ``` ### Available MCP Tools | Tool | Description | |------|-------------| | `agentgate_request` | Submit a new approval request | | `agentgate_get` | Get the status of an approval request by ID | | `agentgate_list` | List approval requests with optional filters | | `agentgate_decide` | Approve or deny a pending request | | `agentgate_list_policies` | List all policies ordered by priority | | `agentgate_create_policy` | Create a new policy with rules | | `agentgate_update_policy` | Replace an existing policy | | `agentgate_delete_policy` | Delete a policy by ID | | `agentgate_list_audit_logs` | List audit log entries with filters and pagination | | `agentgate_get_audit_actors` | Get unique actor values from audit logs | ## Authentication AgentGate uses API keys for authentication. All API requests (except `/health`) require a valid API key. ### API Key Scopes | Scope | Description | |-------|-------------| | `admin` | Full access to all operations | | `request:create` | Create new approval requests | | `request:read` | Read approval requests | | `request:decide` | Approve or deny requests | | `webhook:manage` | Create/update/delete webhooks | ### Using API Keys **HTTP Header:** ```bash curl -H "Authorization: Bearer agk_..." http://localhost:3000/api/requests ``` **SDK:** ```typescript const client = new AgentGateClient({ baseUrl: 'http://localhost:3000', apiKey: process.env.AGENTGATE_API_KEY, }); ``` ### Creating Additional API Keys ```typescript // Via API (requires admin scope) POST /api/api-keys { "name": "My Agent", "scopes": ["request:create", "request:read"] } ``` ## API Endpoints | Method | Endpoint | Description | Required Scope | |--------|----------|-------------|----------------| | `POST` | `/api/requests` | Create approval request | `request:create` | | `GET` | `/api/requests` | List requests (with filters) | `request:read` | | `GET` | `/api/requests/:id` | Get request by ID | `request:read` | | `POST` | `/api/requests/:id/decide` | Submit approval/denial | `request:decide` | | `GET` | `/api/requests/:id/audit` | Get audit trail | `request:read` | | `GET` | `/api/policies` | List policies | `admin` | | `POST` | `/api/policies` | Create policy | `admin` | | `PUT` | `/api/policies/:id` | Update policy | `admin` | | `DELETE` | `/api/policies/:id` | Delete policy | `admin` | | `POST` | `/api/api-keys` | Create API key | `admin` | | `GET` | `/api/api-keys` | List API keys | `admin` | | `PATCH` | `/api/api-keys/:id` | Update API key | `admin` | | `DELETE` | `/api/api-keys/:id` | Revoke API key | `admin` | | `GET` | `/api/webhooks` | List webhooks | `webhook:manage` | | `POST` | `/api/webhooks` | Create webhook | `webhook:manage` | | `DELETE` | `/api/webhooks/:id` | Delete webhook | `webhook:manage` | | `GET` | `/health` | Health check | (none) | ## Rate Limiting AgentGate supports per-API-key rate limiting to prevent abuse and ensure fair usage. ### How It Works - Rate limits use a **sliding window algorithm** (requests per minute) - Limits are configured per API key - When exceeded, requests return `429 Too Many Requests` - Rate limit headers are included in all authenticated responses ### Rate Limit Headers | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Maximum requests per minute | | `X-RateLimit-Remaining` | Remaining requests in current window | | `X-RateLimit-Reset` | Seconds until window resets | ### Configuring Rate Limits Set rate limits when creating or updating API keys: ```typescript // Via API (requires admin scope) POST /api/api-keys { "name": "My Agent", "scopes": ["request:create", "request:read"], "rateLimit": 60 // 60 requests per minute } // null = unlimited { "name": "Internal Service", "scopes": ["admin"], "rateLimit": null } ``` ### Dashboard Rate limits can also be managed from the web dashboard under **Settings β†’ API Keys**. ## Webhooks AgentGate can notify external systems when request events occur. ### Setting Up Webhooks ```typescript // Create a webhook via API POST /api/webhooks { "url": "https://your-server.com/webhook", "events": ["request.created", "request.decided"], "secret": "optional-signing-secret" } ``` ### Webhook Events | Event | Description | |-------|-------------| | `request.created` | A new approval request was created | | `request.decided` | A request was approved or denied | | `request.expired` | A request expired without decision | ### Webhook Payload ```json { "event": "request.decided", "timestamp": "2024-01-15T10:30:00Z", "data": { "id": "abc123", "action": "send_email", "status": "approved", "decidedBy": "admin@example.com" } } ``` ### Webhook Signatures If you provide a `secret`, requests are signed with HMAC-SHA256: ``` X-AgentGate-Signature: sha256=... ``` Verify by computing `HMAC-SHA256(secret, body)` and comparing. ### Webhook Retry Failed webhook deliveries are retried automatically with exponential backoff. The server scans for pending deliveries and retries them with increasing delays (`2^attempts * 1000ms`) until successful or the maximum retry count is reached. ## Configuration ### Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `PORT` | `3000` | Server port | | `DATABASE_URL` | `./data/agentgate.db` | SQLite database path | | `AGENTGATE_API_KEY` | - | API key for SDK/CLI | | `SLACK_BOT_TOKEN` | - | Slack bot token (for Slack integration) | | `SLACK_SIGNING_SECRET` | - | Slack signing secret | | `DISCORD_BOT_TOKEN` | - | Discord bot token (for Discord integration) | | `DISCORD_DEFAULT_CHANNEL` | - | Default Discord channel for notifications | ### File-Based Secrets (`_FILE` suffix) For Docker secrets or Kubernetes secret mounts, AgentGate supports a `_FILE` suffix convention. Instead of setting a secret directly in an environment variable, point to a file containing the value: | Variable | Reads secret from file | |----------|----------------------| | `ADMIN_API_KEY_FILE` | Sets `ADMIN_API_KEY` | | `JWT_SECRET_FILE` | Sets `JWT_SECRET` | | `DATABASE_URL_FILE` | Sets `DATABASE_URL` | | `REDIS_URL_FILE` | Sets `REDIS_URL` | | `SLACK_BOT_TOKEN_FILE` | Sets `SLACK_BOT_TOKEN` | | `SLACK_SIGNING_SECRET_FILE` | Sets `SLACK_SIGNING_SECRET` | | `DISCORD_BOT_TOKEN_FILE` | Sets `DISCORD_BOT_TOKEN` | | `SMTP_PASS_FILE` | Sets `SMTP_PASS` | **Behavior:** - File contents are trimmed of leading/trailing whitespace - If both the env var and the `_FILE` variant are set, the explicit env var takes precedence - Missing or unreadable files produce a warning but do not crash the server Example with Docker Compose: ```yaml services: agentgate: environment: ADMIN_API_KEY_FILE: /run/secrets/admin_api_key JWT_SECRET_FILE: /run/secrets/jwt_secret secrets: - admin_api_key - jwt_secret secrets: admin_api_key: file: ./secrets/admin_api_key.txt jwt_secret: file: ./secrets/jwt_secret.txt ``` ### Policy Configuration Policies are stored in the database and can be managed via API: ```typescript // Example: Auto-approve low-risk emails { name: "auto-approve-emails", priority: 10, enabled: true, rules: [ { match: { action: "send_email" }, decision: "auto_approve" } ] } ``` ## Docker Deployment AgentGate provides Docker images for easy self-hosted deployments. ### Quick Start 1. **Copy the example environment file:** ```bash cp .env.example .env ``` 2. **Generate secure credentials:** ```bash # Generate admin API key (required) echo "ADMIN_API_KEY=$(openssl rand -hex 32)" >> .env # Generate JWT secret (recommended for production) echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env ``` 3. **Start all services:** ```bash docker-compose up -d ``` 4. **Access the services:** - **Dashboard:** http://localhost:3003 - **API Server:** http://localhost:3002 - **Health Check:** http://localhost:3002/health > Host ports are configurable via `SERVER_PORT` (default `3002`) and `DASHBOARD_PORT` (default `3003`); both map to the container's internal port 3000/80. ### Services | Service | Description | Host Port | |---------|-------------|------| | `server` | AgentGate API server | 3002 | | `dashboard` | Web dashboard (nginx) | 3003 | | `postgres` | PostgreSQL database | internal only* | | `redis` | Redis (rate limiting, queues) | internal only* | > \* PostgreSQL and Redis are on an internal Docker network (`agentgate-internal`) and are **not exposed to the host** by default. During development, `docker-compose.override.yml` is auto-loaded and exposes them on ports 5432/6379. For production, run `docker-compose -f docker-compose.yml up -d` to skip the override. ### With Slack or Discord Bots To include the bot services, use the `bots` profile: ```bash # Set required bot credentials in .env first docker-compose --profile bots up -d ``` ### Configuration All configuration is done via environment variables. See `.env.example` for all options. **Required variables:** - `ADMIN_API_KEY` β€” Admin API key (min 16 characters) **Recommended for production:** - `JWT_SECRET` β€” JWT signing secret (min 32 characters) - `CORS_ALLOWED_ORIGINS` β€” Restrict to your domain(s) - `POSTGRES_PASSWORD` β€” Use a strong password ### Building Images Build all images locally: ```bash docker-compose build ``` Build a specific service: ```bash docker-compose build server docker-compose build dashboard ``` ### Database Migrations Migrations run automatically when the server starts. For manual control: ```bash # Run migrations inside the container docker-compose exec server node -e " import('./dist/db/migrate.js').then(m => m.runMigrations()) " ``` ### Viewing Logs ```bash # All services docker-compose logs -f # Specific service docker-compose logs -f server # Last 100 lines docker-compose logs --tail=100 server ``` ### Stopping Services ```bash # Stop all docker-compose down # Stop and remove volumes (WARNING: deletes data) docker-compose down -v ``` ### Production Considerations 1. **Use a reverse proxy** (nginx, Caddy, Traefik) for TLS termination 2. **Set strong passwords** for PostgreSQL 3. **Restrict CORS origins** to your domain 4. **Use Docker secrets** for sensitive values in production 5. **Set up backups** for PostgreSQL data volume 6. **Monitor health endpoints** for uptime checks ## Development ```bash # Install dependencies pnpm install # Run migrations pnpm --filter @agentkitai/agentgate-server db:migrate # Bootstrap (create admin key) pnpm --filter @agentkitai/agentgate-server bootstrap # Start development (server + dashboard) pnpm dev ``` ### Testing AgentGate uses [Vitest](https://vitest.dev/) for testing across all packages. ```bash # Run all tests pnpm test # Run tests with coverage report pnpm test:coverage # Run tests in watch mode (single package) pnpm --filter @agentkitai/agentgate-server test:watch # Run a specific test file pnpm --filter @agentkitai/agentgate-server test -- src/__tests__/integration.test.ts ``` Coverage reports are generated per-package and include line, branch, and function coverage. ### Code Quality ```bash # Build all packages pnpm build # Type checking pnpm typecheck # Lint (ESLint) pnpm lint # Fix lint issues pnpm lint:fix # Format code (Prettier) pnpm format # Check formatting pnpm format:check ``` ## Project Structure ``` agentgate/ β”œβ”€β”€ packages/ β”‚ β”œβ”€β”€ core/ # Shared types, schemas, policy engine β”‚ β”œβ”€β”€ server/ # Hono API server β”‚ β”œβ”€β”€ sdk/ # TypeScript SDK β”‚ β”œβ”€β”€ cli/ # Command-line interface β”‚ β”œβ”€β”€ mcp/ # MCP server for Claude Desktop β”‚ β”œβ”€β”€ slack/ # Slack bot β”‚ β”œβ”€β”€ discord/ # Discord bot β”‚ └── dashboard/ # React dashboard β”œβ”€β”€ apps/ β”‚ └── demo/ # Demo application β”œβ”€β”€ docker-compose.yml # Docker deployment └── package.json # Monorepo root ``` ## Contributing Contributions are welcome! To get started: 1. Fork the repository 2. Clone and install dependencies (`pnpm install`) 3. Follow the [Development](#development) section above to set up your local environment 4. Create a feature branch and make your changes 5. Run `pnpm build && pnpm test` to verify everything works 6. Open a pull request Please make sure all tests pass and code is formatted (`pnpm format:check && pnpm lint`) before submitting. ## 🧰 AgentKit Ecosystem | Project | Description | | |---------|-------------|-| | [AgentLens](https://github.com/agentkitai/agentlens) | Observability & audit trail for AI agents | | | [Lore](https://github.com/agentkitai/lore) | Cross-agent memory and lesson sharing | | | **AgentGate** | Human-in-the-loop approval gateway | ⬅️ you are here | | [FormBridge](https://github.com/agentkitai/formbridge) | Agent-human mixed-mode forms | | | [AgentEval](https://github.com/agentkitai/agenteval) | Testing & evaluation framework | | | [agentkit-cli](https://github.com/agentkitai/agentkit-cli) | Unified CLI orchestrator | | ## License MIT