Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sphinx · Agent HITL Control Plane

Framework-agnostic, self-hosted, open-source human-in-the-loop approval control plane for agent workflows.

Sphinx is the missing middle layer between agent frameworks and the humans who must supervise them. It sits between LangGraph / OpenAI SDK / CrewAI / any MCP-capable agent and the reviewer, and gives you the things every framework leaves out:

  • Unified approval entry point — one web console + one approval queue for every framework, instead of one bespoke UI per framework.
  • Approval SLA with auto-degradation — timeouts that escalate, auto-approve or auto-reject per policy, so "human-in-the-loop" does not decay into "blind clicking OK".
  • Decision log & feedback loop — records the delta between what the agent proposed and what a human decided, and feeds governance metrics: escalation rate, error escape rate, reviewer agreement, correction rate, SLA compliance, decision latency.
  • Full decision capture — intercepts every tool call, LLM inference and state change into a tamper-evident trail (SHA3-256 hash chain + Ed25519 signatures), verifiable anytime via GET /api/capture/verify.
  • MCP-native — every capability is exposed as MCP tools, aligned with the 2026 protocol convergence direction. Any MCP-capable agent can request approval without new SDK code.
  • Single self-contained binary — the REST API, WebSocket live-events, MCP server and web console run in one Go process (CGO-free, SQLite embedded).

Repository layout

.
├── cmd/sphinx/            entrypoint — REST :8001, MCP :8100, web console, policy engine
├── internal/
│   ├── api/               REST + WebSocket handlers, CORS / logging middleware
│   ├── core/              policy engine, metrics, event bus, delta diffing, capture chain, store
│   ├── db/                SQLite bootstrap (modernc.org/sqlite, CGO-free)
│   ├── mcp/               MCP streamable-HTTP server exposing sphinx_* tools
│   ├── models/            domain types + DTOs (wire-compatible with the REST contract)
│   ├── seed/              default policies + realistic demo data + a capture chain
│   ├── config/            SPHINX_* environment configuration
│   └── web/               embedded web console (React build compiled into the binary)
├── web/                   React + Vite + TS console: Queue / Decisions / Metrics / Policies
├── docker/                multi-stage Dockerfile (web → go → minimal runtime)
├── docker-compose.yml     one-command stack: REST+MCP+console on :8001 / :8100
└── docs/                  full documentation set (see below)

Documentation

doc contents
docs/API.md full REST + WebSocket reference with JSON examples
docs/MCP.md MCP tools, config snippets, transport options
docs/SDK.md integrate your agents (Go) — REST/MCP lifecycle + capture trail
docs/USER_GUIDE.md install, run, configure, deploy — the operating manual
docs/ARCHITECTURE.md component breakdown, key flows, capture chain internals

Quick start

Option A — Docker Compose

docker compose up --build
Service URL
Web console http://localhost:8001
REST API http://localhost:8001/api
MCP endpoint http://localhost:8100/mcp
Live events (WS) ws://localhost:8001/api/ws

The stack boots with default policies and 28 rows of realistic demo data, so every page is populated immediately.

Option B — local development

go run ./cmd/sphinx                # or: make run
# with demo data and a fresh database
SPHINX_SEED_DEMO_DATA=1 SPHINX_DATABASE_URL=sqlite:////tmp/sphinx.db go run ./cmd/sphinx

The web console is embedded in the binary, so no separate frontend server is needed. During frontend development use Vite's dev server instead:

cd web && npm install && npm run dev

Vite runs on :5173 and proxies /api to :8001 and /mcp to :8100.


Core concepts

Approval requests

An agent submits an action_payload for approval with a risk_level (low/medium/high/critical). Sphinx picks a matching policy, stamps an SLA deadline, and the request lands in the queue.

  • low risk under a low-risk-auto policy → instant auto-approve (humans only see things that matter).
  • otherwise it stays pending until a human approves/rejects, the agent cancels, or the SLA fires.

Policies & SLA auto-degradation

A background engine scans pending requests every second. When a request exceeds its timeout_seconds, the policy's on_timeout action runs:

on_timeout effect
escalate marks the request escalated (stays pending, shows in console)
auto_approve auto-approves with the agent's payload
auto_reject auto-rejects

This is the anti-fatigue layer: if nobody looks at a ticket in time, the policy decides deterministically instead of leaving the agent stuck forever.

Decision log & governance metrics

Every decision is written to the decision log with:

  • agent_decision — what the model proposed
  • human_decision — what actually happened (may be amended)
  • delta — a path-level diff of the two payloads (add / remove / replace)
  • agreement — did the human confirm the agent unchanged?
  • sourcehuman_review / policy_timeout / auto_policy / agent_feedback

The metrics endpoint turns that log into governance KPIs:

metric definition
escalation_rate escalated requests / total created
timeout_rate SLA auto-decided / decided
correction_rate human reviews that changed the payload / human reviews
reviewer_agreement human reviews confirming the agent unchanged
error_escape_rate approved actions reported with a negative outcome / approved with feedback
sla_compliance_rate decisions reached before the SLA deadline / decided
latency avg / p50 / p95 human review decision latency

Live events

Mutations publish to an in-process event bus; the WebSocket endpoint /api/ws fans them out (requests, decisions, policies, capture topics). The console uses this for instant badge updates (and falls back to polling).


Using Sphinx from your agents

Via MCP (any framework)

// mcp config
{
  "mcpServers": {
    "sphinx": { "url": "http://localhost:8100/mcp" }
  }
}

Tools: sphinx_request_approval, sphinx_get_status, sphinx_wait_for_decision, sphinx_get_decision, sphinx_submit_feedback, sphinx_list_policies.

Via plain REST

# agent proposes a risky action
curl -X POST http://localhost:8001/api/requests \
  -H 'Content-Type: application/json' \
  -d '{"agent_id":"refund-agent","title":"Approve refund of $980 for order ORD-77241",
       "action_payload":{"action":"refund","order_id":"ORD-77241","amount_usd":980},
       "risk_level":"medium"}'
# → {"id":"...","ref":"SPH-1A2B3C","status":"pending", ...}

# agent waits (polls status)
curl http://localhost:8001/api/requests/SPH-1A2B3C
# → {"status":"approved","decision_payload":{"action":"refund","order_id":"ORD-77241","amount_usd":980}, ...}

# agent executes with the (possibly human-amended) payload, then reports the outcome
curl -X POST http://localhost:8001/api/requests/<id>/feedback \
  -H 'Content-Type: application/json' \
  -d '{"outcome":"success","note":"refund completed"}'

Capturing every agent step

Stream every tool call, LLM inference and state change into a tamper-evident trail — each step is hashed (SHA3-256), chained via prev_hash and Ed25519-signed, and can be re-verified at any time:

curl -X POST http://localhost:8001/api/capture \
  -H 'Content-Type: application/json' \
  -d '{"agent_id":"refund-agent","session_id":"sess-1","events":[
        {"event_type":"tool_call","event_name":"lookup_order",
         "input_payload":{"order_id":"ORD-88231"},
         "output_payload":{"order":{"amount_usd":1240}},
         "metadata":{"tool":"order_db","duration_ms":41},"status":"ok"}]}'

# later: verify the trail is untampered
curl "http://localhost:8001/api/capture/verify?agent_id=refund-agent&session_id=sess-1"
# → {"valid":true,"checked":1,"chains":1,"errors":[]}

Capture is fail-open: if Sphinx is unreachable, events are dropped with a log line and the agent's main path never blocks. See docs/API.md for the full capture endpoints.

Testing

make test        # go vet + go test ./... + frontend vitest

The Go suite covers the service layer, delta diffing, metrics math, the SLA scheduler, the full REST surface, WebSocket live events, MCP tools over a real streamable-HTTP server, capture-chain integrity and seed data. The frontend suite covers the queue, decisions, metrics, policies pages and live-update hooks.

Configuration

All settings are read from SPHINX_* environment variables (see internal/config/config.go):

variable default purpose
SPHINX_DATABASE_URL sqlite:///./sphinx.db SQLite URL (WAL mode, CGO-free)
SPHINX_API_PORT 8001 REST API + WS + console port
SPHINX_MCP_PORT 8100 MCP streamable HTTP port
SPHINX_SCHEDULER_INTERVAL_SECONDS 1.0 SLA engine tick interval
SPHINX_DEFAULT_POLICY_SEED true seed the 4 default policies at startup
SPHINX_SEED_DEMO_DATA false seed 28 demo requests at startup
SPHINX_CORS_ORIGINS * CORS allow-list
SPHINX_LOG_LEVEL info log level

License

Open source. Built for the LANDSLIDE human-computer collaboration initiative.

About

Sphinx Go rewrite — Agent HITL Control Plane: framework-agnostic human-in-the-loop approval, decision capture (SHA3-256 chain + Ed25519), MCP-native, single self-contained binary (REST + WS + MCP + web console)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages