From 429dbccf9b4c5c33d35a98356d6b6bd6eea76496 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:04:21 +0100 Subject: [PATCH 01/16] feat(mcp): add BROski Economy MCP server scaffold - Add agents/broski-economy-mcp/README.md - Define tools: award_tokens, spend_tokens, get_balance - Define resources: broski://balance/{id}, broski://transactions/{id} - Prepare for docker-compose service on agent-net Part of Track 1: Deep MCP Integration (Agent-Ready Architecture). --- agents/broski-economy-mcp/README.md | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 agents/broski-economy-mcp/README.md diff --git a/agents/broski-economy-mcp/README.md b/agents/broski-economy-mcp/README.md new file mode 100644 index 00000000..b012c8d1 --- /dev/null +++ b/agents/broski-economy-mcp/README.md @@ -0,0 +1,35 @@ +# BROski Economy MCP Server + +MCP server exposing the BROski$ token economy as tools and resources for AI agents. + +## Capabilities + +### Tools + +- `award_tokens(discord_id: str, amount: int, reason: str)` + Award BROski$ tokens to a user. Wraps the existing `award_tokens()` SQL function. + +- `spend_tokens(discord_id: str, amount: int, item_slug: str)` + Spend BROski$ tokens on a shop item or action. Wraps `spend_tokens()`. + +- `get_balance(discord_id: str)` + Return the current `broski_tokens` balance for a user. + +### Resources + +- `broski://balance/{discord_id}` + Read-only resource exposing a user's balance. + +- `broski://transactions/{discord_id}?limit=N` + Read-only resource exposing recent token transactions. + +## Architecture + +- Runs on `agent-net` alongside other agents. +- Connects to the shared PostgreSQL database (async engine). +- Uses Docker secrets for DB credentials. +- Exposes an MCP server over stdio / HTTP (depending on deployment). + +## Deployment + +See `docker-compose.broski-economy-mcp.yml` in the repo root for the service definition. From a426797be0f15505ab5aaa1c3adfc43bb5109bd3 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:05:19 +0100 Subject: [PATCH 02/16] feat(mcp): add BROski Economy MCP server implementation - Add agents/broski-economy-mcp/server.py - Implement tools: award_tokens, spend_tokens, get_balance - Implement resources: broski://balance/{id}, broski://transactions/{id} - FastAPI-based MCP-over-HTTP server on port 8099 - Uses asyncpg pool, DATABASE_URL env var Part of Track 1: Deep MCP Integration (Agent-Ready Architecture). --- agents/broski-economy-mcp/server.py | 264 ++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 agents/broski-economy-mcp/server.py diff --git a/agents/broski-economy-mcp/server.py b/agents/broski-economy-mcp/server.py new file mode 100644 index 00000000..b582ab77 --- /dev/null +++ b/agents/broski-economy-mcp/server.py @@ -0,0 +1,264 @@ +""" +BROski Economy MCP Server + +Exposes BROski$ token economy as MCP tools and resources. + +Tools: + - award_tokens(discord_id, amount, reason) + - spend_tokens(discord_id, amount, item_slug) + - get_balance(discord_id) + +Resources: + - broski://balance/{discord_id} + - broski://transactions/{discord_id}?limit=N +""" + +import os +import json +from typing import Optional +from contextlib import asynccontextmanager + +import asyncio +from asyncpg import create_pool, Pool + +# MCP-style primitives (simplified; can be replaced with official MCP SDK later) +# Tools: callables with JSON Schema descriptions +# Resources: URI templates + handlers + +DATABASE_URL = os.environ.get("DATABASE_URL") +if not DATABASE_URL: + raise RuntimeError("DATABASE_URL env var must be set") + + +@asynccontextmanager +async def get_db_pool(): + pool: Pool = await create_pool( + DATABASE_URL, + min_size=2, + max_size=10, + ) + try: + yield pool + finally: + await pool.close() + + +# ---------- Tool Implementations ---------- + + +async def award_tokens(discord_id: str, amount: int, reason: str) -> dict: + """ + Award BROski$ tokens to a user. + Wraps the existing award_tokens() SQL function (SECURITY DEFINER). + """ + async with get_db_pool() as pool: + async with pool.acquire() as conn: + row = await conn.fetchrow( + """ + SELECT award_tokens($1, $2, $3) AS success; + """, + discord_id, + amount, + reason, + ) + success = bool(row["success"]) + return {"success": success, "action": "award_tokens", "discord_id": discord_id, "amount": amount, "reason": reason} + + +async def spend_tokens(discord_id: str, amount: int, item_slug: str) -> dict: + """ + Spend BROski$ tokens on a shop item or action. + Wraps the existing spend_tokens() SQL function (SECURITY DEFINER). + """ + async with get_db_pool() as pool: + async with pool.acquire() as conn: + row = await conn.fetchrow( + """ + SELECT spend_tokens($1, $2, $3) AS success; + """, + discord_id, + amount, + item_slug, + ) + success = bool(row["success"]) + return {"success": success, "action": "spend_tokens", "discord_id": discord_id, "amount": amount, "item_slug": item_slug} + + +async def get_balance(discord_id: str) -> dict: + """ + Return the current broski_tokens balance for a user. + """ + async with get_db_pool() as pool: + async with pool.acquire() as conn: + row = await conn.fetchrow( + """ + SELECT broski_tokens AS balance + FROM public.users + WHERE discord_id = $1; + """, + discord_id, + ) + balance = row["balance"] if row else 0 + return {"discord_id": discord_id, "balance": balance} + + +# ---------- Resource Implementations ---------- + + +async def get_balance_resource(discord_id: str) -> dict: + """ + Resource handler for broski://balance/{discord_id} + """ + return await get_balance(discord_id) + + +async def get_transactions_resource(discord_id: str, limit: int = 10) -> list: + """ + Resource handler for broski://transactions/{discord_id}?limit=N + Returns recent token transactions for the user. + """ + async with get_db_pool() as pool: + async with pool.acquire() as conn: + rows = await conn.fetch( + """ + SELECT id, discord_id, amount, balance_after, transaction_type, + item_slug, reason, created_at + FROM public.token_transactions + WHERE discord_id = $1 + ORDER BY created_at DESC + LIMIT $2; + """, + discord_id, + limit, + ) + return [dict(r) for r in rows] + + +# ---------- MCP Server Skeleton ---------- +# This is a simplified MCP-over-HTTP style server. +# In a later iteration, this can be replaced/wrapped by the official MCP SDK. + +from fastapi import FastAPI, Request, Response +import uvicorn + +app = FastAPI(title="BROski Economy MCP Server") + +TOOLS = { + "award_tokens": { + "name": "award_tokens", + "description": "Award BROski$ tokens to a user.", + "inputSchema": { + "type": "object", + "properties": { + "discord_id": {"type": "string"}, + "amount": {"type": "integer"}, + "reason": {"type": "string"}, + }, + "required": ["discord_id", "amount", "reason"], + }, + }, + "spend_tokens": { + "name": "spend_tokens", + "description": "Spend BROski$ tokens on a shop item or action.", + "inputSchema": { + "type": "object", + "properties": { + "discord_id": {"type": "string"}, + "amount": {"type": "integer"}, + "item_slug": {"type": "string"}, + }, + "required": ["discord_id", "amount", "item_slug"], + }, + }, + "get_balance": { + "name": "get_balance", + "description": "Return the current broski_tokens balance for a user.", + "inputSchema": { + "type": "object", + "properties": { + "discord_id": {"type": "string"}, + }, + "required": ["discord_id"], + }, + }, +} + +RESOURCES = { + "broski_balance": { + "uriTemplate": "broski://balance/{discord_id}", + "name": "broski_balance", + "description": "Read-only resource exposing a user's BROski$ balance.", + }, + "broski_transactions": { + "uriTemplate": "broski://transactions/{discord_id}?limit=N", + "name": "broski_transactions", + "description": "Read-only resource exposing recent token transactions for a user.", + }, +} + + +@app.get("/health") +async def health(): + return {"status": "ok"} + + +@app.get("/.well-known/mcp") +async def mcp_discovery(): + """ + MCP discovery endpoint (simplified). + Returns tools and resources available on this server. + """ + return { + "tools": TOOLS, + "resources": RESOURCES, + } + + +@app.post("/mcp/tools/{tool_name}") +async def call_tool(tool_name: str, request: Request): + """ + MCP tool invocation endpoint. + Expects JSON body matching the tool's inputSchema. + """ + body = await request.json() + + if tool_name == "award_tokens": + result = await award_tokens( + discord_id=body["discord_id"], + amount=body["amount"], + reason=body.get("reason", ""), + ) + elif tool_name == "spend_tokens": + result = await spend_tokens( + discord_id=body["discord_id"], + amount=body["amount"], + item_slug=body["item_slug"], + ) + elif tool_name == "get_balance": + result = await get_balance(discord_id=body["discord_id"]) + else: + return {"error": f"Unknown tool: {tool_name}"}, 404 + + return {"result": result} + + +@app.get("/mcp/resources/broski://balance/{discord_id}") +async def resource_balance(discord_id: str): + """ + MCP resource: broski://balance/{discord_id} + """ + result = await get_balance_resource(discord_id) + return result + + +@app.get("/mcp/resources/broski://transactions/{discord_id}") +async def resource_transactions(discord_id: str, limit: int = 10): + """ + MCP resource: broski://transactions/{discord_id}?limit=N + """ + result = await get_transactions_resource(discord_id, limit=limit) + return result + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8099) From bb7f6c6adf0c405974edca487bb4829adcec2651 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:05:39 +0100 Subject: [PATCH 03/16] feat(mcp): add requirements for BROski Economy MCP server - FastAPI + uvicorn for HTTP server - asyncpg for async PostgreSQL access Part of Track 1: Deep MCP Integration. --- agents/broski-economy-mcp/requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 agents/broski-economy-mcp/requirements.txt diff --git a/agents/broski-economy-mcp/requirements.txt b/agents/broski-economy-mcp/requirements.txt new file mode 100644 index 00000000..4e87d802 --- /dev/null +++ b/agents/broski-economy-mcp/requirements.txt @@ -0,0 +1,3 @@ +fastapi==0.117.0 +uvicorn[standard]==0.34.0 +asyncpg==0.30.0 From 2c76575a1f84df49f87523fc5b0c82c9eaebe1de Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:06:05 +0100 Subject: [PATCH 04/16] feat(mcp): add Dockerfile for BROski Economy MCP server - Follows Phase 9 security patterns (Part A + Part B) - python:3.11-slim base, non-root user, minimal runtime - Exposes port 8099 for MCP-over-HTTP Part of Track 1: Deep MCP Integration. --- agents/broski-economy-mcp/Dockerfile | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 agents/broski-economy-mcp/Dockerfile diff --git a/agents/broski-economy-mcp/Dockerfile b/agents/broski-economy-mcp/Dockerfile new file mode 100644 index 00000000..7a25840d --- /dev/null +++ b/agents/broski-economy-mcp/Dockerfile @@ -0,0 +1,38 @@ +# BROski Economy MCP Server Dockerfile +# Follows Phase 9 security patterns from HyperCode. + +FROM python:3.11-slim AS base + +# ---------- Part A: OS hardening ---------- +RUN apt-get update --allow-releaseinfo-change && \ + apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + ca-certificates curl libexpat1 openssl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# ---------- Part B: pip pinning ---------- +RUN pip install --upgrade --no-cache-dir \ + "pip==26.0.1" "setuptools>=80.0.0" "wheel==0.46.2" \ + "jaraco.context>=6.0.0" "jaraco.functools>=4.1.0" "jaraco.text>=4.0.0" + +# ---------- Runtime stage ---------- +FROM base AS runtime + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY server.py . + +# Non-root user (Phase 9 pattern) +RUN groupadd -o -g 999 docker && \ + useradd --create-home --shell /bin/bash --gid 999 --uid 1000 appuser || true +USER appuser + +EXPOSE 8099 + +CMD ["python", "server.py"] From 51bb2134436a12f59860b51591672b632d85d393 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:09:09 +0100 Subject: [PATCH 05/16] feat(mcp): add docker-compose override for BROski Economy MCP server - New service: broski-economy-mcp on agent-net + data-net - Healthcheck on /health (port 8099) - Memory limit 512M, uses DATABASE_URL secret - Designed to be included alongside main compose files Part of Track 1: Deep MCP Integration. --- docker-compose.broski-economy-mcp.yml | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docker-compose.broski-economy-mcp.yml diff --git a/docker-compose.broski-economy-mcp.yml b/docker-compose.broski-economy-mcp.yml new file mode 100644 index 00000000..88c00e33 --- /dev/null +++ b/docker-compose.broski-economy-mcp.yml @@ -0,0 +1,37 @@ +# BROski Economy MCP Server +# Part of Track 1: Deep MCP Integration (Agent-Ready Architecture). + +services: + broski-economy-mcp: + build: + context: ./agents/broski-economy-mcp + dockerfile: Dockerfile + container_name: broski-economy-mcp + restart: unless-stopped + networks: + - agent-net + - data-net + environment: + - DATABASE_URL=${DATABASE_URL} + secrets: + - database_url + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8099/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + deploy: + resources: + limits: + memory: 512M + +secrets: + database_url: + file: ./secrets/database_url.txt + +networks: + agent-net: + external: true + data-net: + external: true From dddde55157777355e53c47bc2143e5873f492577 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:09:57 +0100 Subject: [PATCH 06/16] docs: add BROski Economy MCP integration guide - Explain tools and resources exposed by the MCP server - Show how to run and health-check the service - Provide curl examples for tools and resources - Outline agent integration and future enhancements Part of Track 1: Deep MCP Integration (Agent-Ready Architecture). --- docs/mcp/BROSKI_ECONOMY_MCP.md | 134 +++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 docs/mcp/BROSKI_ECONOMY_MCP.md diff --git a/docs/mcp/BROSKI_ECONOMY_MCP.md b/docs/mcp/BROSKI_ECONOMY_MCP.md new file mode 100644 index 00000000..10b6af31 --- /dev/null +++ b/docs/mcp/BROSKI_ECONOMY_MCP.md @@ -0,0 +1,134 @@ +# BROski Economy MCP Server + +This doc covers how to run and test the BROski Economy MCP server, which exposes the BROski$ token economy as MCP tools and resources. + +## Overview + +The server provides: + +### Tools + +- `award_tokens(discord_id, amount, reason)` — Award BROski$ to a user. +- `spend_tokens(discord_id, amount, item_slug)` — Spend BROski$ on an item. +- `get_balance(discord_id)` — Get a user's token balance. + +### Resources + +- `broski://balance/{discord_id}` — Read-only balance resource. +- `broski://transactions/{discord_id}?limit=N` — Recent transactions. + +This follows the Model Context Protocol (MCP) pattern: tools, resources, and prompts exposed over a standard interface.[web:16][web:19][web:25] + +## Running the Server + +### 1. Ensure secrets exist + +You need a `secrets/database_url.txt` file with your Postgres URL (same pattern as other services): + +```bash +# From HyperCode-V2.4 root +echo "postgresql://user:pass@postgres:5432/hypercode" > secrets/database_url.txt +``` + +Adjust host/user/pass/db to match your environment. + +### 2. Start the service + +Include the new compose file when starting the stack: + +```powershell +docker compose ` + -f docker-compose.yml ` + -f docker-compose.secrets.yml ` + -f docker-compose.broski-economy-mcp.yml ` + up -d broski-economy-mcp +``` + +Or add `broski-economy-mcp` to your existing `docker compose up -d` command. + +### 3. Verify health + +```powershell +curl http://localhost:8099/health +``` + +Expected: `{"status":"ok"}` + +### 4. Check MCP discovery + +```powershell +curl http://localhost:8099/.well-known/mcp +``` + +Expected: JSON with `tools` and `resources` matching the definitions in `server.py`. + +## Testing Tools + +### Get balance + +```powershell +curl -X POST http://localhost:8099/mcp/tools/get_balance ` + -H "Content-Type: application/json" ` + -d '{"discord_id": "123456789012345678"}' +``` + +### Award tokens + +```powershell +curl -X POST http://localhost:8099/mcp/tools/award_tokens ` + -H "Content-Type: application/json" ` + -d '{"discord_id": "123456789012345678", "amount": 50, "reason": "Completed lesson 1"}' +``` + +### Spend tokens + +```powershell +curl -X POST http://localhost:8099/mcp/tools/spend_tokens ` + -H "Content-Type: application/json" ` + -d '{"discord_id": "123456789012345678", "amount": 100, "item_slug": "agent-sandbox-access"}' +``` + +## Testing Resources + +### Balance resource + +```powershell +curl "http://localhost:8099/mcp/resources/broski://balance/123456789012345678" +``` + +### Transactions resource + +```powershell +curl "http://localhost:8099/mcp/resources/broski://transactions/123456789012345678?limit=5" +``` + +## Integrating with Agents + +To use this from your existing agents: + +1. Have the agent call `GET /.well-known/mcp` to discover available tools. +2. When the agent needs to award/spend/check tokens, it POSTs to `/mcp/tools/{tool_name}` with the appropriate JSON body. +3. For read-only views, agents can GET the `broski://...` resource URLs. + +Later, you can wire this into MCP clients (Claude Desktop, VS Code, Cursor) by configuring them to point at this server's endpoint. + +## Observability + +- Logs: `docker logs broski-economy-mcp` +- Metrics: integrate with Prometheus via `/metrics` in a future iteration. +- Traces: add OpenTelemetry OTLP export to align with HyperCode's existing tracing. + +## Security Notes + +- The server runs as non-root (`appuser`) following Phase 9 patterns. +- It uses the shared `DATABASE_URL` secret; ensure DB user has appropriate permissions for `award_tokens` / `spend_tokens` functions. +- Rate limiting and auth can be added at the API gateway / ingress layer if exposed externally. + +## Next Steps + +Future iterations can: + +- Add OpenTelemetry tracing (OTLP) to match HyperCode core. +- Add a `/metrics` endpoint for Prometheus. +- Wrap this server with an official MCP SDK implementation for broader client compatibility. +- Extend with more resources (e.g., shop items, referral stats). From 71da8effea0fcd4157dbed74b6c62770ba65fac3 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:05:34 +0100 Subject: [PATCH 07/16] =?UTF-8?q?docs:=20add=20ARCHITECTURE=5FOS.md=20?= =?UTF-8?q?=E2=80=94=20HyperCode=20as=20an=20Agentic=20AI=20OS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Define HyperCode explicitly as an Agentic AI Operating System. - Map components to OS concepts: kernel, process manager, device drivers (MCP), security module, observability, user space. - Introduce Islands (Local, Edge, Cloud) and Island routing (Track 3). - Outline Temporal-style workflows (Track 4) and neurodivergent-first design principles. - Link to existing ARCHITECTURE.md, brain-architecture.md, and MCP docs. Part of the Track 1 deep-dive and mental model lock-in. --- docs/ARCHITECTURE_OS.md | 307 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 docs/ARCHITECTURE_OS.md diff --git a/docs/ARCHITECTURE_OS.md b/docs/ARCHITECTURE_OS.md new file mode 100644 index 00000000..9653b00e --- /dev/null +++ b/docs/ARCHITECTURE_OS.md @@ -0,0 +1,307 @@ +# HyperCode as an Agentic AI Operating System + +> **Status:** Living document — updated as the system evolves. +> **Owner:** @welshDog (BROski♾️) +> **Related:** [ARCHITECTURE.md](ARCHITECTURE.md), [ai/brain-architecture.md](ai/brain-architecture.md), [mcp/BROSKI_ECONOMY_MCP.md](mcp/BROSKI_ECONOMY_MCP.md) + +--- + +## Why This Doc Exists + +HyperCode has grown from "cool agent stack" into something bigger: a **neurodivergent‑first Agentic AI Operating System** (Agentic AI OS). + +This doc: + +- Names the OS abstraction explicitly. +- Maps HyperCode components to OS concepts (kernel, processes, drivers, security, observability). +- Anchors the evolution tracks (MCP integration, policy‑aware orchestration, island routing, temporal workflows). +- Gives new humans (and agents) a stable mental model to reason about the system. + +--- + +## What Is an Agentic AI OS? + +An **Agentic AI Operating System** is a software infrastructure layer that manages the full lifecycle of autonomous AI agents, including: + +- Scheduling and resource management. +- Memory and state management. +- Tool orchestration and capability discovery. +- Governance policy enforcement (security, privacy, compliance). +- Observability (metrics, logs, traces). + +Unlike traditional automation (rigid, pre‑scripted rules) or basic LLM APIs (single‑prompt responses), an Agentic AI OS provides what a conventional OS provides to applications: **resource management, process isolation, and security controls — but at the cognitive layer**.[web:15][web:18][web:24] + +HyperCode already implements most of these capabilities. This doc makes that explicit. + +--- + +## HyperCode OS: Component Map + +This section maps HyperCode components to OS concepts. + +### Kernel: `hypercode-core` + Data Layer + +**Components:** + +- `backend/app/main.py` — FastAPI core. +- PostgreSQL database (`postgres` service). +- Redis caches (`redis` service, DB 1 = cache, DB 2 = rate limits). +- Celery task queue + workers. + +**Responsibilities:** + +- HTTP + WebSocket API for agents and clients. +- Persistent storage for: + - Users, subscriptions, BROski$ balances. + - Token transactions (append‑only ledger). + - Agent state, workflow state, audit logs (future). +- Caching and rate limiting. +- Background task execution (Celery). +- DB migrations (Alembic). + +**OS analogy:** + +- **Kernel** — core system services, resource arbitration, system calls (APIs). +- **Memory manager** — Postgres + Redis manage persistent and short‑term state. +- **Scheduler** — Celery + queues schedule background work. + +--- + +### Process Manager: Crew Orchestrator + Celery + +**Components:** + +- Crew Orchestrator agent (in `agents/`). +- Celery workers (`celery-worker` services). +- Priority queues: `hypercode-{high,normal,low}` + DLQ (`hypercode-dlq`). + +**Responsibilities:** + +- Agent lifecycle management (spawn, monitor, recover). +- Task dispatch to specialized agents. +- Queue management with priority and dead‑letter handling. +- Metrics on queue depth, task duration, failure rates (exposed to Prometheus). + +**OS analogy:** + +- **Process manager** — creates, schedules, and monitors processes (agents). +- **Init system** — ensures critical services are restarted on failure. +- **Job scheduler** — Celery queues + priority semantics. + +--- + +### Device Drivers: MCP Servers + +**Components:** + +- `agents/broski-economy-mcp` — BROski$ token economy MCP server. +- Future MCP servers: + - Stripe MCP (checkout, subscriptions, webhooks). + - Course stats MCP (enrollment, completion, token sync events). + - Agent orchestrator MCP (task dispatch, registry access). + +**Responsibilities:** + +- Wrap domain services behind a standard protocol (MCP). +- Expose **tools** (actions) and **resources** (data views) to agents and external MCP clients. +- Provide discovery via `/.well-known/mcp`. + +**OS analogy:** + +- **Device drivers** — abstract hardware/devices; here, they abstract domain capabilities. +- **System buses** — MCP is the "USB‑C for AI", a standard connector for capabilities.[web:28] + +This is where **Track 1: Deep MCP Integration** lives. Each MCP server is a driver for a subsystem (economy, payments, courses, orchestration). + +--- + +### Security Module: Auth, Rate Limits, Circuit Breakers, Policy Engine (future) + +**Current components:** + +- Auth middleware on core API routes. +- Rate limiting (Redis‑backed, Stripe webhook exempt). +- Circuit breakers on critical paths (`llm-router`, `crew-orchestrator`, `stripe-api`). +- Docker socket proxy split (read‑only for most agents, restricted POST for healer/throttle). +- Non‑root containers, Phase 9 Dockerfile hardening. + +**Future components (Track 2):** + +- **Agent Registry** — table of agents with roles, locations, trust scores, allowed data domains. +- **Policy Engine** — rules like: + - "No PII leaves Local Island." + - "Pet chat can only read X tables." + - "Focus mode = no non‑critical notifications." +- **Audit Log** — append‑only log of task routing decisions and policy evaluations. + +**OS analogy:** + +- **Security module** — access control, capability enforcement, auditing. +- **Mandatory access control** — policy engine + registry. +- **System call filtering** — circuit breakers + rate limits. + +--- + +### Observability: Prometheus, Grafana, Loki, Tempo + +**Components:** + +- Prometheus (`prometheus` service) — metrics collection. +- Grafana (`grafana` service) — dashboards (Mission Control, Tier 3 pools/queues). +- Loki (`loki`) + Promtail — log aggregation. +- Tempo (`tempo`) — distributed tracing (OTLP). + +**Responsibilities:** + +- Collect metrics from: + - HyperCode core (`/metrics`). + - Agents (health, task counts, durations). + - DB pools, Celery queues, DLQ depth. +- Visualize system health, agent uptime, request rates, error rates. +- Correlate logs, metrics, and traces for incident investigation. + +**OS analogy:** + +- **System monitor** — performance counters, resource usage. +- **Debugger / profiler** — traces + logs for root cause analysis. + +--- + +### User Space: Agents, Workflows, Applications + +**Components:** + +- Specialist agents (frontend, backend, database, QA, etc.). +- Healer agent (self‑healing, MAPE‑K loop). +- DevOps engineer agent (CI/CD, autonomous evolution). +- BROskiPets agents (pet chat, XP, dNFT logic). +- Hyper‑Vibe course frontend + Supabase backend. +- External MCP clients (Claude Desktop, VS Code, Cursor — future). + +**Responsibilities:** + +- Implement domain logic (code generation, testing, healing, pet interactions). +- Execute workflows (multi‑step pipelines, e.g., code → test → deploy). +- Interact with users (Discord, web UI, future MCP clients). + +**OS analogy:** + +- **User applications** — run on top of the OS, using its services. +- **Daemons / services** — long‑running background agents (healer, devops). + +--- + +## Islands: Deployment Topology + +HyperCode is designed to run across multiple "islands" with different trust, cost, and performance profiles. + +### Island Types + +- **Local Island** + - Your main HyperCode box (Windows/WSL, Docker Desktop). + - Runs `hypercode-core`, Postgres, Redis, most agents. + - Highest trust, lowest latency for local data. + +- **Edge Island** + - Raspberry Pi or similar edge device. + - Can run lightweight agents, local Ollama models, pet chat. + - Medium trust, constrained resources. + +- **Cloud Island** + - Vercel (Hyper‑Vibe frontend), Supabase (course DB), Railway, managed services. + - Public‑facing services, managed databases, serverless functions. + - Lower trust for sensitive data, higher scalability. + +### Island Routing (Track 3) + +Future work will introduce **island‑aware routing**: + +- Classify tasks by: + - Sensitivity (PII, financial, token ops). + - Compute intensity (LLM inference, video processing). + - Latency requirements. +- Route tasks to islands based on: + - Policy rules (e.g., "token ops must stay on Local Island"). + - Resource availability (GPU, CPU, memory). + - Cost constraints. + +This is inspired by the **IslandRun** model of privacy‑aware, multi‑objective orchestration across heterogeneous personal computing ecosystems.[web:11] + +--- + +## Workflows: Temporal‑Style Pipelines (Track 4) + +HyperCode already has: + +- Celery queues with priority and DLQ. +- DB pool + queue depth metrics. +- Idempotency guards (e.g., `CourseSyncEvent` for token sync). + +Next evolution: + +- **Workflow definitions** (YAML/JSON) for multi‑step agent pipelines: + - Example: `code_review.yml` → [agent-x: write code] → [qa-agent: test] → [healer: validate] → [deploy]. +- **Checkpointing** — persist workflow state in Postgres at each step. +- **Replay API** — re‑run a workflow from a given step on demand. +- **Deadline‑aware scheduling** — prioritize tasks based on urgency, dependencies, and user impact. + +This moves HyperCode toward **Temporal‑style** exactly‑once, deterministic workflows while keeping the existing Celery + Redis infrastructure.[web:12] + +--- + +## Evolution Tracks Recap + +These tracks are the roadmap for evolving HyperCode OS: + +1. **Track 1: Deep MCP Integration** + - Wrap domain services as MCP servers (BROski economy ✅, Stripe, courses, orchestrator). + - Standardize tool + resource discovery for agents and external clients. + +2. **Track 2: Policy‑Aware Crew Orchestrator** + - Add Agent Registry + Policy Engine. + - Enforce data‑flow policies and trust boundaries. + - Implement tamper‑evident audit logging. + +3. **Track 3: Island‑Style Routing** + - Define Local, Edge, and Cloud islands. + - Implement routing policies based on sensitivity, cost, and performance. + +4. **Track 4: Temporal‑Style Workflows** + - Add workflow definitions, checkpointing, and replay. + - Introduce deadline‑aware, priority scheduling on top of Celery. + +--- + +## Neurodivergent‑First Design + +HyperCode OS is explicitly designed for neurodivergent creators (ADHD, dyslexia, autism). + +Design principles: + +- **Chunked capabilities** — MCP servers expose small, focused tools/resources. +- **Observable state** — dashboards and logs make system behavior visible and predictable. +- **Forgiving failure** — healer agent + circuit breakers + DLQ prevent cascading crashes. +- **User‑controlled policies** — future policy engine lets users encode their comfort zones (e.g., focus mode, panic mode). +- **Gamified progress** — BROski$ tokens, pet XP, and achievements turn dev work into a game. + +This is not just an OS for agents — it's a **cognitive architecture** that honors how neurodivergent brains actually work. + +--- + +## Glossary + +- **Agentic AI OS** — Operating system for autonomous AI agents. +- **MCP** — Model Context Protocol, an open standard for connecting AI models to tools and data.[web:16][web:19] +- **Island** — A deployment environment (Local, Edge, Cloud) with distinct trust/cost/performance characteristics. +- **Workflow** — A multi‑step, possibly long‑running pipeline of agent tasks with checkpointing and replay. +- **Policy Engine** — Rules that govern agent behavior, data flow, and resource access. + +--- + +## References + +- Model Context Protocol (MCP) docs and guides.[web:16][web:19][web:25][web:28] +- Agentic AI OS buyer guides and comparisons.[web:15][web:18][web:24] +- Multi‑Agent Orchestration Protocol research.[web:7] +- IslandRun privacy‑aware orchestration.[web:11] +- Temporal‑style workflow architectures.[web:12] From e29ef9970d0e2f4d268f206963c3e6b766238108 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:46:00 +0100 Subject: [PATCH 08/16] feat(mcp): add Stripe MCP server README - Define tools: create_checkout, handle_webhook_event, get_subscription - Define resources: stripe://subscription/{user_id}, stripe://plans - Prepare for docker-compose service on agent-net Part of Track 1: Deep MCP Integration (Agent-Ready Architecture). --- agents/stripe-mcp/README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 agents/stripe-mcp/README.md diff --git a/agents/stripe-mcp/README.md b/agents/stripe-mcp/README.md new file mode 100644 index 00000000..937c9b19 --- /dev/null +++ b/agents/stripe-mcp/README.md @@ -0,0 +1,35 @@ +# Stripe MCP Server + +MCP server exposing Stripe checkout, webhooks, and subscriptions as tools and resources for AI agents. + +## Capabilities + +### Tools + +- `create_checkout(price_id: str, user_id: str)` + Create a Stripe Checkout Session for a given price and user. Wraps the existing `create_checkout_session()` logic. + +- `handle_webhook_event(payload: str, sig_header: str)` + Verify and handle a Stripe webhook event. Wraps the webhook handling logic from `stripe_service.py`. + +- `get_subscription(user_id: str)` + Return the current subscription status for a user (from local DB or Stripe). + +### Resources + +- `stripe://subscription/{user_id}` + Read-only resource exposing a user's subscription status. + +- `stripe://plans` + Read-only resource listing available Stripe plans (starter, builder, hyper, pro, elite). + +## Architecture + +- Runs on `agent-net` alongside other agents. +- Connects to the shared PostgreSQL database (async engine) and Stripe API. +- Uses Docker secrets for Stripe keys and DB credentials. +- Exposes an MCP server over HTTP on port 8100. + +## Deployment + +See `docker-compose.stripe-mcp.yml` in the repo root for the service definition. From 3624104a622680a3082994c803720824f1033c38 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:47:05 +0100 Subject: [PATCH 09/16] feat(mcp): add Stripe MCP server implementation - Tools: create_checkout, handle_webhook_event, get_subscription - Resources: stripe://subscription/{user_id}, stripe://plans - FastAPI-based MCP-over-HTTP server on port 8100 - Uses asyncpg + stripe SDK, env vars for keys/prices Part of Track 1: Deep MCP Integration. --- agents/stripe-mcp/server.py | 291 ++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 agents/stripe-mcp/server.py diff --git a/agents/stripe-mcp/server.py b/agents/stripe-mcp/server.py new file mode 100644 index 00000000..afcbb0c5 --- /dev/null +++ b/agents/stripe-mcp/server.py @@ -0,0 +1,291 @@ +""" +Stripe MCP Server + +Exposes Stripe checkout, webhooks, and subscriptions as MCP tools and resources. + +Tools: + - create_checkout(price_id, user_id) + - handle_webhook_event(payload, sig_header) + - get_subscription(user_id) + +Resources: + - stripe://subscription/{user_id} + - stripe://plans +""" + +import os +import json +import stripe +from typing import Optional +from contextlib import asynccontextmanager + +import asyncio +from asyncpg import create_pool, Pool +from fastapi import FastAPI, Request, HTTPException +import uvicorn + +# ---------- Config ---------- + +STRIPE_SECRET_KEY = os.environ.get("STRIPE_SECRET_KEY") +DATABASE_URL = os.environ.get("DATABASE_URL") + +if not STRIPE_SECRET_KEY: + raise RuntimeError("STRIPE_SECRET_KEY env var must be set") +if not DATABASE_URL: + raise RuntimeError("DATABASE_URL env var must be set") + +stripe.api_key = STRIPE_SECRET_KEY + +# Price map (mirrors stripe_service.py) +PRICE_MAP = { + "starter": "STRIPE_PRICE_STARTER", + "builder": "STRIPE_PRICE_BUILDER", + "hyper": "STRIPE_PRICE_HYPER", + "pro_monthly": "STRIPE_PRICE_PRO_MONTHLY", + "pro_yearly": "STRIPE_PRICE_PRO_YEARLY", + "hyper_monthly": "STRIPE_PRICE_HYPER_MONTHLY", + "hyper_yearly": "STRIPE_PRICE_HYPER_YEARLY", +} + +# ---------- DB Helpers ---------- + + +@asynccontextmanager +async def get_db_pool(): + pool: Pool = await create_pool( + DATABASE_URL, + min_size=2, + max_size=10, + ) + try: + yield pool + finally: + await pool.close() + + +async def get_user_stripe_customer_id(user_id: str) -> Optional[str]: + """ + Lookup Stripe customer_id for a user from the DB. + Adjust table/column names to match your schema. + """ + async with get_db_pool() as pool: + async with pool.acquire() as conn: + row = await conn.fetchrow( + """ + SELECT stripe_customer_id + FROM public.users + WHERE discord_id = $1; + """, + user_id, + ) + return row["stripe_customer_id"] if row else None + + +async def get_user_subscription(user_id: str) -> dict: + """ + Return subscription status for a user. + First tries local DB, then Stripe if needed. + """ + async with get_db_pool() as pool: + async with pool.acquire() as conn: + row = await conn.fetchrow( + """ + SELECT subscription_status, stripe_subscription_id, stripe_plan_id + FROM public.users + WHERE discord_id = $1; + """, + user_id, + ) + if row and row["subscription_status"]: + return { + "user_id": user_id, + "status": row["subscription_status"], + "stripe_subscription_id": row["stripe_subscription_id"], + "stripe_plan_id": row["stripe_plan_id"], + } + + # Fallback: no local record; could query Stripe by customer_id here if desired + return {"user_id": user_id, "status": "none"} + + +# ---------- Tool Implementations ---------- + + +async def create_checkout(price_id: str, user_id: str) -> dict: + """ + Create a Stripe Checkout Session for a given price and user. + Wraps logic similar to stripe_service.create_checkout_session. + """ + if price_id not in PRICE_MAP: + raise HTTPException(status_code=400, detail=f"Unknown price_id: {price_id}") + + price_env_name = PRICE_MAP[price_id] + price = os.environ.get(price_env_name) + if not price: + raise HTTPException(status_code=500, detail=f"Price not configured: {price_env_name}") + + # Optionally lookup/create customer here; for now, we pass client_reference_id + session = stripe.checkout.Session.create( + payment_method_types=["card"], + line_items=[{"price": price, "quantity": 1}], + mode="subscription", + success_url=os.environ.get("STRIPE_SUCCESS_URL", "http://localhost:3000/payment-success"), + cancel_url=os.environ.get("STRIPE_CANCEL_URL", "http://localhost:3000/pricing"), + client_reference_id=user_id, + ) + + return { + "success": True, + "checkout_url": session.url, + "session_id": session.id, + "price_id": price_id, + "user_id": user_id, + } + + +async def handle_webhook_event(payload: str, sig_header: str) -> dict: + """ + Verify and handle a Stripe webhook event. + Wraps logic similar to stripe_service.handle_webhook. + """ + webhook_secret = os.environ.get("STRIPE_WEBHOOK_SECRET") + try: + event = stripe.Webhook.construct_event(payload, sig_header, webhook_secret) + except (ValueError, stripe.error.SignatureVerificationError) as e: + return {"success": False, "error": str(e)} + + # Here we just return the event type + id; in a fuller version, you'd + # replicate the event handling logic from stripe_service.py (award tokens, etc.) + return { + "success": True, + "event_type": event["type"], + "event_id": event["id"], + "note": "Event verified; full handling logic can be wired here.", + } + + +async def get_subscription(user_id: str) -> dict: + """ + Return the current subscription status for a user. + """ + return await get_user_subscription(user_id) + + +# ---------- Resource Implementations ---------- + + +async def get_subscription_resource(user_id: str) -> dict: + """ + Resource handler for stripe://subscription/{user_id} + """ + return await get_user_subscription(user_id) + + +async def get_plans_resource() -> list: + """ + Resource handler for stripe://plans + Returns list of available plan IDs. + """ + return list(PRICE_MAP.keys()) + + +# ---------- MCP Server ---------- + +app = FastAPI(title="Stripe MCP Server") + +TOOLS = { + "create_checkout": { + "name": "create_checkout", + "description": "Create a Stripe Checkout Session for a given price and user.", + "inputSchema": { + "type": "object", + "properties": { + "price_id": {"type": "string"}, + "user_id": {"type": "string"}, + }, + "required": ["price_id", "user_id"], + }, + }, + "handle_webhook_event": { + "name": "handle_webhook_event", + "description": "Verify and handle a Stripe webhook event.", + "inputSchema": { + "type": "object", + "properties": { + "payload": {"type": "string"}, + "sig_header": {"type": "string"}, + }, + "required": ["payload", "sig_header"], + }, + }, + "get_subscription": { + "name": "get_subscription", + "description": "Return the current subscription status for a user.", + "inputSchema": { + "type": "object", + "properties": { + "user_id": {"type": "string"}, + }, + "required": ["user_id"], + }, + }, +} + +RESOURCES = { + "stripe_subscription": { + "uriTemplate": "stripe://subscription/{user_id}", + "name": "stripe_subscription", + "description": "Read-only resource exposing a user's subscription status.", + }, + "stripe_plans": { + "uriTemplate": "stripe://plans", + "name": "stripe_plans", + "description": "Read-only resource listing available Stripe plans.", + }, +} + + +@app.get("/health") +async def health(): + return {"status": "ok"} + + +@app.get("/.well-known/mcp") +async def mcp_discovery(): + return {"tools": TOOLS, "resources": RESOURCES} + + +@app.post("/mcp/tools/{tool_name}") +async def call_tool(tool_name: str, request: Request): + body = await request.json() + + if tool_name == "create_checkout": + result = await create_checkout( + price_id=body["price_id"], + user_id=body["user_id"], + ) + elif tool_name == "handle_webhook_event": + result = await handle_webhook_event( + payload=body["payload"], + sig_header=body["sig_header"], + ) + elif tool_name == "get_subscription": + result = await get_subscription(user_id=body["user_id"]) + else: + raise HTTPException(status_code=404, detail=f"Unknown tool: {tool_name}") + + return {"result": result} + + +@app.get("/mcp/resources/stripe://subscription/{user_id}") +async def resource_subscription(user_id: str): + return await get_subscription_resource(user_id) + + +@app.get("/mcp/resources/stripe://plans") +async def resource_plans(): + return await get_plans_resource() + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8100) From 22b1014a950afbd156747fb6bec0b7f7052c61ed Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:47:58 +0100 Subject: [PATCH 10/16] feat(mcp): add requirements for Stripe MCP server - FastAPI + uvicorn for HTTP server - asyncpg for async PostgreSQL access - stripe SDK for Stripe API calls Part of Track 1: Deep MCP Integration. --- agents/stripe-mcp/requirements.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 agents/stripe-mcp/requirements.txt diff --git a/agents/stripe-mcp/requirements.txt b/agents/stripe-mcp/requirements.txt new file mode 100644 index 00000000..fcb733fb --- /dev/null +++ b/agents/stripe-mcp/requirements.txt @@ -0,0 +1,4 @@ +fastapi==0.117.0 +uvicorn[standard]==0.34.0 +asyncpg==0.30.0 +stripe==11.3.0 From f725bd525165f5d286b8bec8923775ad00c55d24 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:48:30 +0100 Subject: [PATCH 11/16] feat(mcp): add Dockerfile for Stripe MCP server - Follows Phase 9 security patterns (Part A + Part B) - python:3.11-slim base, non-root user, minimal runtime - Exposes port 8100 for MCP-over-HTTP Part of Track 1: Deep MCP Integration. --- agents/stripe-mcp/Dockerfile | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 agents/stripe-mcp/Dockerfile diff --git a/agents/stripe-mcp/Dockerfile b/agents/stripe-mcp/Dockerfile new file mode 100644 index 00000000..560f2922 --- /dev/null +++ b/agents/stripe-mcp/Dockerfile @@ -0,0 +1,38 @@ +# Stripe MCP Server Dockerfile +# Follows Phase 9 security patterns from HyperCode. + +FROM python:3.11-slim AS base + +# ---------- Part A: OS hardening ---------- +RUN apt-get update --allow-releaseinfo-change && \ + apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + ca-certificates curl libexpat1 openssl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# ---------- Part B: pip pinning ---------- +RUN pip install --upgrade --no-cache-dir \ + "pip==26.0.1" "setuptools>=80.0.0" "wheel==0.46.2" \ + "jaraco.context>=6.0.0" "jaraco.functools>=4.1.0" "jaraco.text>=4.0.0" + +# ---------- Runtime stage ---------- +FROM base AS runtime + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY server.py . + +# Non-root user (Phase 9 pattern) +RUN groupadd -o -g 999 docker && \ + useradd --create-home --shell /bin/bash --gid 999 --uid 1000 appuser || true +USER appuser + +EXPOSE 8100 + +CMD ["python", "server.py"] From 43910e0a505013570326997956e54feb6b227af0 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:49:27 +0100 Subject: [PATCH 12/16] feat(mcp): add docker-compose override for Stripe MCP server - New service: stripe-mcp on agent-net + data-net - Healthcheck on /health (port 8100) - Memory limit 512M, uses DATABASE_URL + Stripe secrets - Exposes all price env vars + success/cancel URLs Part of Track 1: Deep MCP Integration. --- docker-compose.stripe-mcp.yml | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docker-compose.stripe-mcp.yml diff --git a/docker-compose.stripe-mcp.yml b/docker-compose.stripe-mcp.yml new file mode 100644 index 00000000..a7bfc7be --- /dev/null +++ b/docker-compose.stripe-mcp.yml @@ -0,0 +1,54 @@ +# Stripe MCP Server +# Part of Track 1: Deep MCP Integration (Agent-Ready Architecture). + +services: + stripe-mcp: + build: + context: ./agents/stripe-mcp + dockerfile: Dockerfile + container_name: stripe-mcp + restart: unless-stopped + networks: + - agent-net + - data-net + environment: + - DATABASE_URL=${DATABASE_URL} + - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - STRIPE_PRICE_STARTER=${STRIPE_PRICE_STARTER} + - STRIPE_PRICE_BUILDER=${STRIPE_PRICE_BUILDER} + - STRIPE_PRICE_HYPER=${STRIPE_PRICE_HYPER} + - STRIPE_PRICE_PRO_MONTHLY=${STRIPE_PRICE_PRO_MONTHLY} + - STRIPE_PRICE_PRO_YEARLY=${STRIPE_PRICE_PRO_YEARLY} + - STRIPE_PRICE_HYPER_MONTHLY=${STRIPE_PRICE_HYPER_MONTHLY} + - STRIPE_PRICE_HYPER_YEARLY=${STRIPE_PRICE_HYPER_YEARLY} + - STRIPE_SUCCESS_URL=${STRIPE_SUCCESS_URL} + - STRIPE_CANCEL_URL=${STRIPE_CANCEL_URL} + secrets: + - database_url + - stripe_secret_key + - stripe_webhook_secret + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8100/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + deploy: + resources: + limits: + memory: 512M + +secrets: + database_url: + file: ./secrets/database_url.txt + stripe_secret_key: + file: ./secrets/stripe_secret_key.txt + stripe_webhook_secret: + file: ./secrets/stripe_webhook_secret.txt + +networks: + agent-net: + external: true + data-net: + external: true From 1af87d43f496a59dc172418c6049639497e05777 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:19:13 +0100 Subject: [PATCH 13/16] docs: add Stripe MCP integration guide - Explain tools and resources exposed by the MCP server - Show how to run and health-check the service - Provide curl examples for tools and resources - Outline agent integration and future enhancements Part of Track 1: Deep MCP Integration. --- docs/mcp/STRIPE_MCP.md | 162 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 docs/mcp/STRIPE_MCP.md diff --git a/docs/mcp/STRIPE_MCP.md b/docs/mcp/STRIPE_MCP.md new file mode 100644 index 00000000..9e48a9df --- /dev/null +++ b/docs/mcp/STRIPE_MCP.md @@ -0,0 +1,162 @@ +# Stripe MCP Server + +This doc covers how to run and test the Stripe MCP server, which exposes Stripe checkout, webhooks, and subscriptions as MCP tools and resources. + +## Overview + +The server provides: + +### Tools + +- `create_checkout(price_id, user_id)` — Create a Stripe Checkout Session for a given price and user. +- `handle_webhook_event(payload, sig_header)` — Verify and handle a Stripe webhook event. +- `get_subscription(user_id)` — Get a user's subscription status. + +### Resources + +- `stripe://subscription/{user_id}` — Read-only subscription status resource. +- `stripe://plans` — List of available Stripe plans (starter, builder, hyper, pro, elite). + +This follows the Model Context Protocol (MCP) pattern: tools, resources, and prompts exposed over a standard interface.[web:16][web:19][web:25] + +## Running the Server + +### 1. Ensure secrets exist + +You need the following secrets in `secrets/`: + +```bash +# From HyperCode-V2.4 root +echo "postgresql://user:pass@postgres:5432/hypercode" > secrets/database_url.txt +echo "sk_live_xxx" > secrets/stripe_secret_key.txt +echo "whsec_xxx" > secrets/stripe_webhook_secret.txt +``` + +And the following env vars in your `.env` / compose environment: + +```env +STRIPE_SECRET_KEY=sk_live_xxx +STRIPE_WEBHOOK_SECRET=whsec_xxx +STRIPE_PRICE_STARTER=price_xxx +STRIPE_PRICE_BUILDER=price_xxx +STRIPE_PRICE_HYPER=price_xxx +STRIPE_PRICE_PRO_MONTHLY=price_xxx +STRIPE_PRICE_PRO_YEARLY=price_xxx +STRIPE_PRICE_HYPER_MONTHLY=price_xxx +STRIPE_PRICE_HYPER_YEARLY=price_xxx +STRIPE_SUCCESS_URL=http://localhost:3000/payment-success +STRIPE_CANCEL_URL=http://localhost:3000/pricing +``` + +Adjust URLs and price IDs to match your Stripe dashboard. + +### 2. Start the service + +Include the new compose file when starting the stack: + +```powershell +docker compose ` + -f docker-compose.yml ` + -f docker-compose.secrets.yml ` + -f docker-compose.stripe-mcp.yml ` + up -d stripe-mcp +``` + +Or add `stripe-mcp` to your existing `docker compose up -d` command. + +### 3. Verify health + +```powershell +curl http://localhost:8100/health +``` + +Expected: `{"status":"ok"}` + +### 4. Check MCP discovery + +```powershell +curl http://localhost:8100/.well-known/mcp +``` + +Expected: JSON with `tools` and `resources` matching the definitions in `server.py`. + +## Testing Tools + +### Create checkout + +```powershell +curl -X POST http://localhost:8100/mcp/tools/create_checkout ` + -H "Content-Type: application/json" ` + -d '{"price_id": "starter", "user_id": "123456789012345678"}' +``` + +Expected: `{"success": true, "checkout_url": "https://checkout.stripe.com/...", ...}` + +### Handle webhook event + +Use Stripe CLI to forward a test event, or manually POST a signed payload: + +```powershell +curl -X POST http://localhost:8100/mcp/tools/handle_webhook_event ` + -H "Content-Type: application/json" ` + -d '{"payload": "{\"id\":\"evt_xxx\",\"type\":\"checkout.session.completed\"}", "sig_header": "t=xxx,v1=xxx"}' +``` + +Expected: `{"success": true, "event_type": "checkout.session.completed", ...}` + +### Get subscription + +```powershell +curl -X POST http://localhost:8100/mcp/tools/get_subscription ` + -H "Content-Type: application/json" ` + -d '{"user_id": "123456789012345678"}' +``` + +Expected: `{"user_id": "123456789012345678", "status": "active" | "none"}` + +## Testing Resources + +### Subscription resource + +```powershell +curl "http://localhost:8100/mcp/resources/stripe://subscription/123456789012345678" +``` + +### Plans resource + +```powershell +curl "http://localhost:8100/mcp/resources/stripe://plans" +``` + +Expected: `["starter", "builder", "hyper", "pro_monthly", "pro_yearly", ...]` + +## Integrating with Agents + +To use this from your existing agents: + +1. Have the agent call `GET /.well-known/mcp` to discover available tools. +2. When the agent needs to create a checkout or check a subscription, it POSTs to `/mcp/tools/{tool_name}` with the appropriate JSON body. +3. For read-only views, agents can GET the `stripe://...` resource URLs. + +Later, you can wire this into MCP clients (Claude Desktop, VS Code, Cursor) by configuring them to point at this server's endpoint. + +## Observability + +- Logs: `docker logs stripe-mcp` +- Metrics: integrate with Prometheus via `/metrics` in a future iteration. +- Traces: add OpenTelemetry OTLP export to align with HyperCode's existing tracing. + +## Security Notes + +- The server runs as non-root (`appuser`) following Phase 9 patterns. +- It uses shared secrets for DB and Stripe; ensure these are properly secured and rotated. +- Rate limiting and auth can be added at the API gateway / ingress layer if exposed externally. + +## Next Steps + +Future iterations can: + +- Add OpenTelemetry tracing (OTLP) to match HyperCode core. +- Add a `/metrics` endpoint for Prometheus. +- Wrap this server with an official MCP SDK implementation for broader client compatibility. +- Extend with more resources (e.g., `stripe://customer/{user_id}`, `stripe://payments/{user_id}`). From 4e5d66718c88c4ec800c032aa91961c05e9897c5 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:22:19 +0100 Subject: [PATCH 14/16] alembic: add migration 010 for agent policy schema - Create agent_registry, policy_rules, audit_log tables. - Use UUID + gen_random_uuid(), JSONB for conditions/capabilities. - Indexes on name, priority, timestamp, agent_id. Part of Track 2: Policy-Aware Crew Orchestrator. --- .../versions/010_agent_policy_schema.py | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 backend/alembic/versions/010_agent_policy_schema.py diff --git a/backend/alembic/versions/010_agent_policy_schema.py b/backend/alembic/versions/010_agent_policy_schema.py new file mode 100644 index 00000000..2f4adb9d --- /dev/null +++ b/backend/alembic/versions/010_agent_policy_schema.py @@ -0,0 +1,128 @@ +"""agent_policy_schema + +Revision ID: 010 +Revises: 009 +Create Date: 2026-08-14 + +Part of Track 2: Policy-Aware Crew Orchestrator. +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '010' +down_revision = '009' +branch_labels = None +depends_on = None + + +def upgrade(): + # --- Agent Registry --- + op.create_table( + 'agent_registry', + sa.Column( + 'id', + postgresql.UUID(as_uuid=True), + primary_key=True, + server_default=sa.text('gen_random_uuid()'), + ), + sa.Column('name', sa.Text(), nullable=False), + sa.Column('role', sa.Text(), nullable=False), + sa.Column('location', sa.Text(), nullable=False), + sa.Column('trust_score', sa.Integer(), nullable=False, server_default='50'), + sa.Column( + 'allowed_data_domains', + postgresql.JSONB(astext_type=sa.Text()), + nullable=False, + server_default='[]', + ), + sa.Column( + 'capabilities', + postgresql.JSONB(astext_type=sa.Text()), + nullable=False, + server_default='[]', + ), + sa.Column( + 'created_at', + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + 'updated_at', + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + ) + op.create_index('ix_agent_registry_name', 'agent_registry', ['name'], unique=True) + + # --- Policy Rules --- + op.create_table( + 'policy_rules', + sa.Column( + 'id', + postgresql.UUID(as_uuid=True), + primary_key=True, + server_default=sa.text('gen_random_uuid()'), + ), + sa.Column('name', sa.Text(), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('condition', postgresql.JSONB(astext_type=sa.Text()), nullable=False), + sa.Column('action', sa.Text(), nullable=False), + sa.Column('priority', sa.Integer(), nullable=False, server_default='0'), + sa.Column('enabled', sa.Boolean(), nullable=False, server_default='true'), + sa.Column( + 'created_at', + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + 'updated_at', + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + ) + op.create_index('ix_policy_rules_name', 'policy_rules', ['name'], unique=True) + op.create_index('ix_policy_rules_priority', 'policy_rules', ['priority']) + + # --- Audit Log (tamper-evident) --- + op.create_table( + 'audit_log', + sa.Column( + 'id', + postgresql.UUID(as_uuid=True), + primary_key=True, + server_default=sa.text('gen_random_uuid()'), + ), + sa.Column( + 'timestamp', + sa.TIMESTAMP(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column('agent_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('task_id', sa.Text(), nullable=True), + sa.Column('action', sa.Text(), nullable=False), + sa.Column('data_domain', sa.Text(), nullable=True), + sa.Column('policy_result', sa.Text(), nullable=False), + sa.Column('details', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('hash_prev', sa.Text(), nullable=True), + sa.Column('hash_self', sa.Text(), nullable=False), + sa.ForeignKeyConstraint(['agent_id'], ['agent_registry.id'], ondelete='SET NULL'), + ) + op.create_index('ix_audit_log_timestamp', 'audit_log', ['timestamp']) + op.create_index('ix_audit_log_agent_id', 'audit_log', ['agent_id']) + + +def downgrade(): + op.drop_index('ix_audit_log_agent_id', table_name='audit_log') + op.drop_index('ix_audit_log_timestamp', table_name='audit_log') + op.drop_index('ix_policy_rules_priority', table_name='policy_rules') + op.drop_index('ix_policy_rules_name', table_name='policy_rules') + op.drop_table('audit_log') + op.drop_table('policy_rules') + op.drop_table('agent_registry') From 2df8a1b703247132194e8f6f8d97aedc23469fe1 Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:23:54 +0100 Subject: [PATCH 15/16] feat(policy): add PolicyEngine service for Track 2 - Evaluate agent actions against policy_rules (priority-ordered). - Simple condition language: eq, ne, in, not_in, exists. - Tamper-evident audit_log with chained hashes. - Default allow if no rule matches. Part of Track 2: Policy-Aware Crew Orchestrator. --- backend/app/services/policy_engine.py | 193 ++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 backend/app/services/policy_engine.py diff --git a/backend/app/services/policy_engine.py b/backend/app/services/policy_engine.py new file mode 100644 index 00000000..97e7e70e --- /dev/null +++ b/backend/app/services/policy_engine.py @@ -0,0 +1,193 @@ +""" +Policy Engine for Track 2: Policy-Aware Crew Orchestrator. + +Evaluates agent actions against policy rules and writes tamper-evident audit logs. +""" + +import hashlib +import json +from dataclasses import dataclass +from typing import Optional, List, Dict, Any +from enum import Enum + +from asyncpg import Pool + + +class PolicyAction(str, Enum): + ALLOW = "allow" + DENY = "deny" + REQUIRE_APPROVAL = "require_approval" + + +class PolicyResult(str, Enum): + ALLOWED = "allowed" + DENIED = "denied" + PENDING_APPROVAL = "pending_approval" + + +@dataclass +class PolicyCheck: + agent_id: Optional[str] + task_id: Optional[str] + action: str + data_domain: Optional[str] + details: Optional[Dict[str, Any]] = None + + +@dataclass +class PolicyDecision: + result: PolicyResult + matched_rule_name: Optional[str] + reason: str + + +class PolicyEngine: + """ + Simple policy engine: + - Loads enabled rules from policy_rules, ordered by priority DESC. + - Evaluates conditions against the incoming check. + - Returns the first matching rule's action as the decision. + - Writes an audit log entry with chained hash. + """ + + def __init__(self, db_pool: Pool): + self.db_pool = db_pool + + async def evaluate(self, check: PolicyCheck) -> PolicyDecision: + """ + Evaluate a policy check against all enabled rules. + Returns the decision from the highest-priority matching rule. + """ + async with self.db_pool.acquire() as conn: + # Load rules + rows = await conn.fetch( + """ + SELECT id, name, condition, action, priority + FROM policy_rules + WHERE enabled = true + ORDER BY priority DESC; + """ + ) + + # Evaluate conditions + for row in rows: + condition: dict = row["condition"] + if self._matches(condition, check): + action = PolicyAction(row["action"]) + result = self._action_to_result(action) + return PolicyDecision( + result=result, + matched_rule_name=row["name"], + reason=f"Matched rule {row['name']} (priority {row['priority']})", + ) + + # Default: allow if no rule matches + return PolicyDecision( + result=PolicyResult.ALLOWED, + matched_rule_name=None, + reason="No matching policy rule; default allow", + ) + + def _matches(self, condition: dict, check: PolicyCheck) -> bool: + """ + Simple condition evaluator. + Supported ops: eq, ne, in, not_in, exists. + Condition shape examples: + {"field": "data_domain", "op": "in", "value": ["users", "token_transactions"]} + {"field": "action", "op": "eq", "value": "award_tokens"} + {"field": "agent_id", "op": "exists"} + """ + field = condition.get("field") + op = condition.get("op") + value = condition.get("value") + + # Extract field value from check + if field == "data_domain": + field_value = check.data_domain + elif field == "action": + field_value = check.action + elif field == "agent_id": + field_value = check.agent_id + elif field == "task_id": + field_value = check.task_id + else: + field_value = None + + if op == "eq": + return field_value == value + elif op == "ne": + return field_value != value + elif op == "in": + return field_value in value + elif op == "not_in": + return field_value not in value + elif op == "exists": + return field_value is not None + else: + # Unknown op = no match + return False + + def _action_to_result(self, action: PolicyAction) -> PolicyResult: + if action == PolicyAction.ALLOW: + return PolicyResult.ALLOWED + elif action == PolicyAction.DENY: + return PolicyResult.DENIED + elif action == PolicyAction.REQUIRE_APPROVAL: + return PolicyResult.PENDING_APPROVAL + else: + return PolicyResult.ALLOWED + + async def log_decision( + self, + check: PolicyCheck, + decision: PolicyDecision, + ) -> None: + """ + Write a tamper-evident audit log entry. + hash_prev = hash of the most recent row. + hash_self = sha256 of (timestamp, agent_id, task_id, action, data_domain, policy_result, hash_prev). + """ + async with self.db_pool.acquire() as conn: + # Get last hash + last_row = await conn.fetchrow( + """ + SELECT hash_self + FROM audit_log + ORDER BY timestamp DESC + LIMIT 1; + """ + ) + hash_prev = last_row["hash_self"] if last_row else None + + # Build hash_self + payload = { + "timestamp": None, # will be set by DB + "agent_id": check.agent_id, + "task_id": check.task_id, + "action": check.action, + "data_domain": check.data_domain, + "policy_result": decision.result.value, + "hash_prev": hash_prev, + } + payload_json = json.dumps(payload, sort_keys=True) + hash_self = hashlib.sha256(payload_json.encode("utf-8")).hexdigest() + + await conn.execute( + """ + INSERT INTO audit_log ( + agent_id, task_id, action, data_domain, policy_result, details, hash_prev, hash_self + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8); + """, + check.agent_id, + check.task_id, + check.action, + check.data_domain, + decision.result.value, + json.dumps(check.details) if check.details else None, + hash_prev, + hash_self, + ) + + +async def create_policy_engine(db_pool: Pool) -> PolicyEngine: + return PolicyEngine(db_pool) From 21c2062a6b867577ee9e1cd5477080acbde32f9e Mon Sep 17 00:00:00 2001 From: Mr Lyndon Williams <68136524+welshDog@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:25:05 +0100 Subject: [PATCH 16/16] docs: add TRACK2_POLICY_ENGINE.md - Explain agent_registry, policy_rules, audit_log schema. - Show PolicyEngine API and usage pattern. - Provide example policy rules and wiring into orchestrator. - Link to ARCHITECTURE_OS.md and multi-agent orchestration research. Part of Track 2: Policy-Aware Crew Orchestrator. --- docs/TRACK2_POLICY_ENGINE.md | 211 +++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 docs/TRACK2_POLICY_ENGINE.md diff --git a/docs/TRACK2_POLICY_ENGINE.md b/docs/TRACK2_POLICY_ENGINE.md new file mode 100644 index 00000000..d0d9f3eb --- /dev/null +++ b/docs/TRACK2_POLICY_ENGINE.md @@ -0,0 +1,211 @@ +# Track 2: Policy-Aware Crew Orchestrator + +This doc describes the **Policy Engine** and **Agent Registry** that turn HyperCode's crew orchestrator into a policy‑aware, governance‑capable system. + +## Overview + +Track 2 adds: + +- **Agent Registry** — a table of known agents with roles, locations, trust scores, and allowed data domains. +- **Policy Rules** — a priority‑ordered list of conditions → actions (allow/deny/require_approval). +- **Audit Log** — a tamper‑evident log of every policy decision, with chained hashes. +- **Policy Engine** — a Python service that evaluates agent actions against rules and writes audit entries. + +This aligns with the "Security Module" in [ARCHITECTURE_OS.md](ARCHITECTURE_OS.md) and the Multi‑Agent Orchestration Protocol pattern of a central orchestrator + policy engine + audit log.[web:7] + +## Database Schema + +Three new tables (migration `010_agent_policy_schema.py`): + +### `agent_registry` + +| Column | Type | Description | +|--------|------|-------------| +| `id` | uuid | Primary key. | +| `name` | text | Unique agent name (e.g. `healer`, `coder-agent`, `pet-chat`). | +| `role` | text | Role (e.g. `healer`, `orchestrator`, `specialist`). | +| `location` | text | Deployment island: `local`, `edge`, `cloud`. | +| `trust_score` | int | 0–100 trust score. | +| `allowed_data_domains` | jsonb | List of allowed domains, e.g. `["users", "token_transactions"]`. | +| `capabilities` | jsonb | List of capabilities, e.g. `["read_users", "write_tokens"]`. | +| `created_at` | timestamptz | Creation timestamp. | +| `updated_at` | timestamptz | Last update timestamp. | + +### `policy_rules` + +| Column | Type | Description | +|--------|------|-------------| +| `id` | uuid | Primary key. | +| `name` | text | Unique rule name. | +| `description` | text | Human-readable description. | +| `condition` | jsonb | Condition object (see below). | +| `action` | text | `allow`, `deny`, or `require_approval`. | +| `priority` | int | Higher = evaluated first. | +| `enabled` | bool | Whether the rule is active. | +| `created_at` | timestamptz | Creation timestamp. | +| `updated_at` | timestamptz | Last update timestamp. | + +**Condition shape:** + +```json +{ + "field": "data_domain", + "op": "in", + "value": ["users", "token_transactions"] +} +``` + +Supported ops: `eq`, `ne`, `in`, `not_in`, `exists`. + +Fields: `data_domain`, `action`, `agent_id`, `task_id`. + +### `audit_log` + +| Column | Type | Description | +|--------|------|-------------| +| `id` | uuid | Primary key. | +| `timestamp` | timestamptz | Event timestamp. | +| `agent_id` | uuid | Reference to `agent_registry.id`. | +| `task_id` | text | Optional task identifier. | +| `action` | text | Action attempted (e.g. `award_tokens`). | +| `data_domain` | text | Data domain accessed (e.g. `token_transactions`). | +| `policy_result` | text | `allowed`, `denied`, `pending_approval`. | +| `details` | jsonb | Optional structured details. | +| `hash_prev` | text | Hash of previous audit row (for tamper evidence). | +| `hash_self` | text | SHA256 of this row's key fields + `hash_prev`. | + +The `hash_prev` / `hash_self` chain makes it computationally expensive to alter history without detection. + +## Policy Engine API + +Located at `backend/app/services/policy_engine.py`. + +### Core types + +```python +@dataclass +class PolicyCheck: + agent_id: Optional[str] + task_id: Optional[str] + action: str + data_domain: Optional[str] + details: Optional[Dict[str, Any]] = None + +@dataclass +class PolicyDecision: + result: PolicyResult # ALLOWED, DENIED, PENDING_APPROVAL + matched_rule_name: Optional[str] + reason: str +``` + +### Usage pattern + +```python +from app.services.policy_engine import PolicyEngine, PolicyCheck, create_policy_engine +from asyncpg import create_pool + +async def main(): + db_pool = await create_pool(DATABASE_URL) + engine = await create_policy_engine(db_pool) + + check = PolicyCheck( + agent_id="some-uuid", + task_id="task-123", + action="award_tokens", + data_domain="token_transactions", + details={"amount": 50, "reason": "Completed lesson 1"}, + ) + + decision = await engine.evaluate(check) + await engine.log_decision(check, decision) + + if decision.result == "allowed": + # proceed with action + ... + else: + # deny or require approval + ... +``` + +## Example Policy Rules + +Here are example rules you might insert: + +```sql +-- 1. Only trusted local agents can touch token transactions +INSERT INTO policy_rules (name, description, condition, action, priority, enabled) +VALUES ( + 'local_trusted_tokens', + 'Allow only local agents with trust >= 70 to access token_transactions', + '{"field": "data_domain", "op": "eq", "value": "token_transactions"}', + 'allow', + 100, + true +); + +-- 2. Deny cloud agents from accessing users table +INSERT INTO policy_rules (name, description, condition, action, priority, enabled) +VALUES ( + 'no_cloud_users', + 'Deny any agent with location=cloud from accessing users', + '{"field": "data_domain", "op": "eq", "value": "users"}', + 'deny', + 90, + true +); + +-- 3. Require approval for high-value token awards +INSERT INTO policy_rules (name, description, condition, action, priority, enabled) +VALUES ( + 'approve_large_awards', + 'Require approval for award_tokens with amount > 1000', + '{"field": "action", "op": "eq", "value": "award_tokens"}', + 'require_approval', + 80, + true +); +``` + +In a fuller implementation, you'd add more granular conditions (e.g. on `details.amount`) and tie `location` to the `agent_registry`. + +## Wiring into the Crew Orchestrator + +To integrate this with the existing crew orchestrator: + +1. **Initialize the policy engine** at app startup alongside the DB pool. +2. **Before dispatching a task** to an agent, construct a `PolicyCheck` with: + - `agent_id` (from `agent_registry`) + - `task_id` + - `action` (e.g. `award_tokens`, `create_checkout`) + - `data_domain` (e.g. `token_transactions`, `stripe_payments`) +3. **Call `engine.evaluate(check)`** and inspect `decision.result`: + - `ALLOWED` → proceed with dispatch. + - `DENIED` → reject task, log, and optionally notify. + - `PENDING_APPROVAL` → queue for human/lead approval before dispatch. +4. **Always call `engine.log_decision(check, decision)`** to record the decision in `audit_log`. + +This turns every agent action into a policy‑checked, auditable event. + +## Neurodivergent‑First Policy Design + +The policy engine can encode neurodivergent‑friendly constraints: + +- **Focus mode policies** — e.g. "no non‑critical notifications during focus sessions". +- **Privacy boundaries** — e.g. "pet chat can only read `users.discord_id` and `broski_tokens`". +- **Island rules** — e.g. "token ops must stay on Local Island". + +These become explicit, testable rules instead of ad‑hoc assumptions. + +## Next Steps + +Future enhancements: + +- Add a small admin UI or CLI to manage `policy_rules` and view `audit_log`. +- Extend condition language (e.g. numeric comparisons on `details.amount`). +- Integrate with the orchestrator's existing task dispatch logic. +- Add Prometheus metrics for policy decisions (allow/deny rates, pending approvals). + +## References + +- Multi‑Agent Orchestration Protocol research (policy engine + audit log pattern).[web:7] +- HyperCode OS architecture: [ARCHITECTURE_OS.md](ARCHITECTURE_OS.md).