diff --git a/README.md b/README.md index 54cd9a2..77b0426 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,39 @@ RiskKernel is a **self-hosted agent reliability runtime** — the deterministic, It is **not** another gateway (LiteLLM/Portkey own routing), **not** another observability dashboard (Langfuse/Phoenix own traces), and **not** a content-guardrails engine (Guardrails AI/NeMo own PII/jailbreak). It interoperates with all of those and competes on the one thing nobody ships in a single self-hosted binary: **deterministic run controls** — the agent SRE layer. +## See it work first + +If you only try one thing, run the no-key Docker demo. It starts a mock LLM, sends +a deliberately runaway "agent" through RiskKernel, and stops at the hard loop +budget with HTTP 402. No provider key, no local Go/Python setup: + +```bash +cd examples/quickstart-compose +docker compose up +``` + +Expected shape: + +```text +call 1 -> 200 OK +call 2 -> 200 OK +call 3 -> 200 OK +call 4 -> 200 OK +call 5 -> 200 OK +call 6 -> HTTP 402 RiskKernel HALTED the run: + {"code":"loop_budget_exceeded", ...} +``` + +The second proof is crash recovery: kill the daemon halfway through a run, restart +it, and the agent resumes at step 6 with the governor still counting the 5 steps +already spent. + +
+ RiskKernel crash-resume demo: a run does 5 steps, the daemon is killed, the run resumes at step 6, and the final loop counter is 10 instead of 15 +
+ A crashed run resumes without re-spending. The daemon is killed after step 5; restart reloads the run, resumes at step 6, and the final loop counter is 10, not 15. (runnable example) +
+ ## What it does | Capability | What it means | @@ -49,6 +82,18 @@ It is **not** another gateway (LiteLLM/Portkey own routing), **not** another obs 2. **SDK (deep control).** `pip install riskkernel` (Python) or `npm install @riskkernel/sdk` (TypeScript), then governed runs, per-step loop/time budgets, checkpoints, and approval gates. Framework adapters for the Claude Agent SDK, OpenAI Agents SDK, LangChain, LlamaIndex, CrewAI, AutoGen, and PydanticAI (Python), and the Vercel AI SDK (TypeScript). 3. **OpenTelemetry (universal).** RiskKernel is an OTLP endpoint *and* emitter — ingest GenAI spans (`POST /v1/traces`) to meter apps already instrumented with OpenLLMetry / the OpenAI Agents SDK / the Vercel AI SDK, and export cost/halt/tool spans to the backend you already run. +## Pick a demo + +| You want to see | Run this | What it proves | +|---|---|---| +| **A runaway agent stopped** | [`examples/quickstart-compose`](examples/quickstart-compose) | A hard loop budget halts a runaway app with no API key. | +| **Crash recovery** | [`examples/kill-9-resume`](examples/kill-9-resume) | `kill -9` cannot reset the meter or make completed work run twice. | +| **A real agent loop halted** | [`examples/codebase-qa`](examples/codebase-qa) | A codebase QA agent loops until RiskKernel stops it on budget. | +| **Wrap an existing Python loop** | [`examples/wrap-your-agent`](examples/wrap-your-agent) | The SDK caps plain Python control flow in two minutes. | +| **LangChain integration** | [`examples/langchain`](examples/langchain) | A LangChain callback binds steps to a governed run. | +| **MCP tool governance** | [`examples/mcp`](examples/mcp) | Tool allowlists, approval gates, and audit trails work before the tool executes. | +| **Observability dashboards** | [`examples/otel`](examples/otel) | Cost, budget, halt, and tool spans land in your OTel backend. | + ## Quickstart (60 seconds) > **No key, one command?** [`examples/quickstart-compose`](examples/quickstart-compose) @@ -128,25 +173,6 @@ npm install @riskkernel/sdk # TypeScript → sdks/typescript See [`sdks/python`](sdks/python) and [`sdks/typescript`](sdks/typescript). Trace every run in your own backend: [`examples/otel`](examples/otel). -Want to *see* the headline feature? [`examples/codebase-qa`](examples/codebase-qa) -is a runnable agent that loops over a codebase until the governor kills it on its -loop/dollar budget — the deterministic kill, end to end, with a real model. - -And the moat: [`examples/kill-9-resume`](examples/kill-9-resume) `kill -9`s the -daemon mid-run and resumes without re-spending — `./demo.sh` scripts the whole -crash-and-recover and proves the counter doesn't double, key-free. - -Brand new to the SDK? [`examples/wrap-your-agent`](examples/wrap-your-agent) is the -no-key, two-minute version — a generic Python loop the governor caps at a loop -budget, the deterministic kill with nothing running but the daemon. - -On LangChain? [`examples/langchain`](examples/langchain) wraps a LangChain loop -with the callback handler and caps it at a loop budget — also key-free. - -Governing tools over MCP? [`examples/mcp`](examples/mcp) puts the MCP gateway in -front of a stub server and shows a tool blocked by the allowlist, a side-effecting -tool held for approval, and the audit trail — key-free. - Hit a snag? `riskkernel doctor` diagnoses most setups, and the [troubleshooting guide](docs/TROUBLESHOOTING.md) maps the common errors — missing key, port in use, the expected 402 budget halt — to fixes. diff --git a/examples/kill-9-resume/resume.cast b/examples/kill-9-resume/resume.cast new file mode 100644 index 0000000..0228211 --- /dev/null +++ b/examples/kill-9-resume/resume.cast @@ -0,0 +1,29 @@ +{"version":3,"term":{"cols":80,"rows":24,"type":"dumb"},"timestamp":1781988638,"command":"env PYTHONPATH=../../sdks/python RK_WORK_SECONDS=0.05 RISKKERNEL_BIN=../../riskkernel ./demo.sh","env":{"SHELL":"/bin/zsh"}} +[0.024, "o", "── 1. start the daemon ────────────────────────────────────────────────\r\n"] +[0.289, "o", " up (pid 93623)\r\n\r\n── 2. agent does 5 of 10 steps, checkpointing each ────────────────────\r\n"] +[0.152, "o", "▶ FRESH run 087cd4c2-3a96-4496-9184-4c2547c4b7d0 (budget: loops=50)\r\n\r\n"] +[0.057, "o", " step 1/10 done (checkpointed cursor=1)\r\n"] +[0.054, "o", " step 2/10 done (checkpointed cursor=2)\r\n"] +[0.057, "o", " step 3/10 done (checkpointed cursor=3)\r\n"] +[0.054, "o", " step 4/10 done (checkpointed cursor=4)\r\n"] +[0.058, "o", " step 5/10 done (checkpointed cursor=5)\r\n\r\n⏸ did 5 steps, then stopping (the demo crashes the daemon here).\r\n"] +[0.009, "o", "\r\n"] +[0.000, "o", "── 3. kill -9 the daemon (a hard crash — no graceful shutdown) ───────\r\n"] +[0.507, "o", "./demo.sh: line 44: 93623 Killed: 9 RISKKERNEL_DATA_DIR=\"${DATA}\" RISKKERNEL_PORT=\"${PORT}\" \"${BIN}\" serve >> \"${DATA}/serve.log\" 2>&1\r\n"] +[0.000, "o", " daemon killed. restarting it…\r\n"] +[0.290, "o", " ✓ time=2026-06-21T02:20:40.200+05:30 level=INFO msg=\"resumed runs from store\" count=1\r\n"] +[0.000, "o", "\r\n"] +[0.000, "o", "── 4. re-run the agent: it RESUMES and finishes ───────────────────────\r\n"] +[0.064, "o", "↻ RESUMING run 087cd4c2-3a96-4496-9184-4c2547c4b7d0\r\n the governor already counts 5 spent steps — resuming at cursor 5, not redoing them.\r\n\r\n"] +[0.055, "o", " step 6/10 done (checkpointed cursor=6)\r\n"] +[0.055, "o", " step 7/10 done (checkpointed cursor=7)\r\n"] +[0.054, "o", " step 8/10 done (checkpointed cursor=8)\r\n"] +[0.057, "o", " step 9/10 done (checkpointed cursor=9)\r\n"] +[0.057, "o", " step 10/10 done (checkpointed cursor=10)\r\n"] +[0.001, "o", "\r\n✅ completed all 10 steps. governor loop counter = 10 — exactly one per step of work. The steps finished before the crash were neither redone nor re-paid.\r\n"] +[0.009, "o", "\r\n"] +[0.000, "o", "── 5. proof — the run did 10 steps total across the crash, not 15 ─────\r\n"] +[0.012, "o", "ID NAME STATUS TOKENS DOLLARS LOOPS HALT CREATED\r\n087cd4c2-3a96-4496-9184-4c2547c4b7d0 kill-9-resume running 0 0.0000 10 - 2026-06-20 20:50:39\r\n"] +[0.001, "o", "\r\n"] +[0.000, "o", " loops = 10 (one per step of work). Re-running from zero would read 15.\r\n"] +[0.005, "x", "0"] diff --git a/examples/kill-9-resume/resume.gif b/examples/kill-9-resume/resume.gif new file mode 100644 index 0000000..0b44e4a Binary files /dev/null and b/examples/kill-9-resume/resume.gif differ