agentic-ledger 0.2.0__tar.gz → 0.3.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/.gitignore +4 -0
  2. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/PKG-INFO +116 -24
  3. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/README.md +111 -19
  4. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/__init__.py +1 -1
  5. agentic_ledger-0.3.0a2/agentledger/cli.py +177 -0
  6. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/__main__.py +6 -1
  7. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/alerts.py +1 -1
  8. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/app.py +340 -40
  9. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/dashboard.py +12 -5
  10. agentic_ledger-0.3.0a2/agentledger/proxy/detect.py +123 -0
  11. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/export.py +4 -4
  12. agentic_ledger-0.3.0a2/agentledger/proxy/loops.py +355 -0
  13. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/mcp.py +65 -1
  14. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/normalize.py +61 -7
  15. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/otel.py +2 -2
  16. agentic_ledger-0.3.0a2/agentledger/proxy/otlp_ingest.py +170 -0
  17. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/pricing.py +58 -7
  18. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/redact.py +4 -1
  19. agentic_ledger-0.3.0a2/agentledger/proxy/static/assets/index-D4Qv-Grl.js +40 -0
  20. agentic_ledger-0.3.0a2/agentledger/proxy/static/assets/index-zOG9lcmm.css +1 -0
  21. agentic_ledger-0.3.0a2/agentledger/proxy/static/index.html +13 -0
  22. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/store.py +332 -7
  23. agentic_ledger-0.3.0a2/agentledger/proxy/stream.py +254 -0
  24. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/pyproject.toml +21 -5
  25. agentic_ledger-0.2.0/agentledger/proxy/stream.py +0 -157
  26. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/LICENSE +0 -0
  27. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/__init__.py +0 -0
  28. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/auth.py +0 -0
  29. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/mascot.jpg +0 -0
  30. {agentic_ledger-0.2.0 → agentic_ledger-0.3.0a2}/agentledger/proxy/ratelimit.py +0 -0
@@ -41,3 +41,7 @@ coverage.xml
41
41
 
42
42
  # Jupyter
43
43
  .ipynb_checkpoints/
44
+
45
+ # Web app (dashboard-app builds into agentledger/proxy/static/)
46
+ dashboard-app/node_modules/
47
+ agentledger/proxy/static/
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-ledger
3
- Version: 0.2.0
3
+ Version: 0.3.0a2
4
4
  Summary: Runtime observability for AI agents — see exactly what your agent did, why it did it, and what it cost.
5
- Project-URL: Homepage, https://github.com/ShekharBhardwaj/AgentLedger
6
- Project-URL: Repository, https://github.com/ShekharBhardwaj/AgentLedger
7
- Project-URL: Issues, https://github.com/ShekharBhardwaj/AgentLedger/issues
5
+ Project-URL: Homepage, https://github.com/ShekharBhardwaj/AgenticLedger
6
+ Project-URL: Repository, https://github.com/ShekharBhardwaj/AgenticLedger
7
+ Project-URL: Issues, https://github.com/ShekharBhardwaj/AgenticLedger/issues
8
8
  Author: Shekhar Bhardwaj
9
9
  License-Expression: MIT
10
10
  License-File: LICENSE
@@ -21,7 +21,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Requires-Python: >=3.10
22
22
  Requires-Dist: aiosqlite>=0.19
23
23
  Requires-Dist: fastapi>=0.110
24
- Requires-Dist: httpx>=0.27
24
+ Requires-Dist: httpx<1.0.dev0,>=0.27
25
25
  Requires-Dist: uvicorn[standard]>=0.29
26
26
  Provides-Extra: dev
27
27
  Requires-Dist: aiosqlite>=0.19; extra == 'dev'
@@ -40,10 +40,10 @@ Provides-Extra: postgres
40
40
  Requires-Dist: asyncpg>=0.29; extra == 'postgres'
41
41
  Description-Content-Type: text/markdown
42
42
 
43
- # AgentLedger
43
+ # Agentic Ledger
44
44
 
45
- [![CI](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/ci.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/ci.yml)
46
- [![CodeQL](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/codeql.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/codeql.yml)
45
+ [![CI](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/ci.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/ci.yml)
46
+ [![CodeQL](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/codeql.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/codeql.yml)
47
47
  [![PyPI](https://img.shields.io/pypi/v/agentic-ledger)](https://pypi.org/project/agentic-ledger/)
48
48
  [![Python versions](https://img.shields.io/pypi/pyversions/agentic-ledger)](https://pypi.org/project/agentic-ledger/)
49
49
  [![Docker](https://img.shields.io/badge/docker-ghcr.io-blue)](https://ghcr.io/shekharbhardwaj/agentledger)
@@ -51,16 +51,18 @@ Description-Content-Type: text/markdown
51
51
 
52
52
  Runtime observability for AI agents — see exactly what your agent did, why it did it, and what it cost.
53
53
 
54
+ > **Alpha** — `v0.3.0-alpha.1` is an early release of the loop engine, Loop Lens web app, and framework integrations. Interfaces and schema may still change between alpha releases. Found something broken or missing? [Open an issue](https://github.com/ShekharBhardwaj/AgenticLedger/issues/new/choose) — alpha feedback directly shapes what ships next.
55
+
54
56
  Works with **any agent framework**, **any LLM provider**, **any model gateway**. Zero code changes required. Point your agent at the proxy and everything is captured automatically.
55
57
 
56
58
  ---
57
59
 
58
60
  ## How it works
59
61
 
60
- AgentLedger runs as a transparent proxy between your agent and the LLM provider. It intercepts every request and response, assigns it an `action_id`, stores it, and returns the upstream response unmodified. Your agent never knows the proxy is there.
62
+ Agentic Ledger runs as a transparent proxy between your agent and the LLM provider. It intercepts every request and response, assigns it an `action_id`, stores it, and returns the upstream response unmodified. Your agent never knows the proxy is there.
61
63
 
62
64
  ```
63
- Your Agent → AgentLedger Proxy → OpenAI / Anthropic / LiteLLM / any LLM
65
+ Your Agent → Agentic Ledger Proxy → OpenAI / Anthropic / LiteLLM / any LLM
64
66
 
65
67
  SQLite or Postgres
66
68
 
@@ -149,10 +151,10 @@ client = anthropic.Anthropic(
149
151
 
150
152
  **LiteLLM / OpenRouter / any gateway:**
151
153
  ```bash
152
- # Point AgentLedger at your gateway
154
+ # Point Agentic Ledger at your gateway
153
155
  AGENTLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agentledger.proxy
154
156
 
155
- # Then point your agent at AgentLedger
157
+ # Then point your agent at Agentic Ledger
156
158
  client = OpenAI(base_url="http://localhost:8000/v1", ...)
157
159
  ```
158
160
 
@@ -161,10 +163,15 @@ client = OpenAI(base_url="http://localhost:8000/v1", ...)
161
163
  **Step 3 — Open the dashboard**
162
164
 
163
165
  ```
164
- http://localhost:8000
166
+ http://localhost:8000/app
165
167
  ```
166
168
 
167
- The dashboard updates live via WebSocket as calls come in. No refresh needed.
169
+ The web app updates live via WebSocket as calls come in. No refresh needed.
170
+
171
+ - **Loop Lens** — every loop run with status (`running` / `flagged` / `complete`), a cost-per-iteration chart, and a per-iteration table (calls, tokens, cache reads, flags, errors)
172
+ - **Sessions** — every session with expandable call cards: response, thinking, tool calls, tool results, cache tokens, cost, loop flags
173
+
174
+ The classic single-file dashboard still lives at `http://localhost:8000`:
168
175
 
169
176
  - **Calls tab** — every LLM call with full prompt, system prompt, tool calls, tool results, output, tokens, cost, latency, and errors
170
177
  - **Flow tab** — visual DAG of your multi-agent system. Each agent is a node with aggregated cost, latency, and call count. Edges represent handoffs. Click a node to highlight its calls.
@@ -173,6 +180,69 @@ The dashboard updates live via WebSocket as calls come in. No refresh needed.
173
180
 
174
181
  ---
175
182
 
183
+ ## Coding agents — Claude Code, Ralph loops & friends
184
+
185
+ Claude Code (and most coding agents) can be pointed at the proxy with a single
186
+ environment variable — no headers, no code changes:
187
+
188
+ ```bash
189
+ AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com uv run python -m agentledger.proxy
190
+ ```
191
+
192
+ ```bash
193
+ export ANTHROPIC_BASE_URL=http://localhost:8000
194
+ claude
195
+ ```
196
+
197
+ Agentic Ledger fingerprints Claude Code traffic automatically: every call is
198
+ tagged `framework=claude-code`, and instead of one undifferentiated bucket,
199
+ each Claude Code session appears under its **real session UUID** (the same id
200
+ `claude --resume` shows), with prompt-cache reads/writes captured and priced
201
+ correctly — cache traffic is where most of a coding agent's real spend lives.
202
+
203
+ Running an overnight loop (Ralph-style `while :; do cat PROMPT.md | claude -p; done`)?
204
+ Use the built-in loop runner — it re-executes your command each iteration,
205
+ attributes every call to the run (via the base URL, no headers needed), and
206
+ stops on a completion promise, a budget ceiling, or the iteration cap:
207
+
208
+ ```bash
209
+ AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com \
210
+ AGENTLEDGER_COMPLETION_PROMISE="ALL TASKS COMPLETE" \
211
+ uv run python -m agentledger.proxy
212
+ ```
213
+
214
+ ```bash
215
+ agentledger run --max-iterations 50 --budget 25 -- \
216
+ claude -p "$(cat PROMPT.md)" --dangerously-skip-permissions
217
+ ```
218
+
219
+ Each iteration shows up as *iteration N of the run* in `/api/runs`; when the
220
+ agent prints the completion promise in a response, run status flips to
221
+ `complete` and the loop exits with a cost/token summary. Any existing loop
222
+ script works too — poll `GET /api/runs/{run_id}` yourself, or let the proxy's
223
+ budgets (`AGENTLEDGER_BUDGET_DAILY=25.00`) hard-stop a runaway loop.
224
+
225
+ The same recipe works for any client with a base-URL override (Codex CLI,
226
+ opencode, OpenClaw, LiteLLM-based stacks) — set the OpenAI/Anthropic base URL
227
+ to the proxy and traffic is captured; add `x-agentledger-*` headers when you
228
+ want explicit attribution.
229
+
230
+ **OTel-native tools** (Gemini CLI, Codex `[otel]`, AutoGen/AG2, Pydantic AI,
231
+ Vercel AI SDK) don't need the proxy at all — point their OTLP exporter at the
232
+ ledger and GenAI spans are ingested directly:
233
+
234
+ ```bash
235
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8000
236
+ export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
237
+ ```
238
+
239
+ **Framework guides:** [BMAD-METHOD](docs/integrations/bmad.md) — persona
240
+ auto-detection (`bmad:sm`, `bmad:dev`, `bmad:qa`, …) and per-story budgets ·
241
+ [OpenClaw](docs/integrations/openclaw.md) — the hard spend cap OpenClaw
242
+ doesn't have, plus per-agent attribution.
243
+
244
+ ---
245
+
176
246
  ## What gets captured
177
247
 
178
248
  Every LLM call is stored with:
@@ -192,11 +262,14 @@ Every LLM call is stored with:
192
262
  | `content` | Model's text output |
193
263
  | `stop_reason` | Why the model stopped |
194
264
  | `tokens_in` / `tokens_out` | Token usage |
265
+ | `cache_read_tokens` / `cache_write_tokens` | Prompt-cache usage — reads and writes are priced correctly per provider |
266
+ | `thinking` | Extended-thinking output (Anthropic), captured separately from `content` |
195
267
  | `cost_usd` | Estimated cost based on model pricing |
196
268
  | `latency_ms` | End-to-end response time |
197
269
  | `status_code` | HTTP status from upstream — errors are captured too |
198
270
  | `error_detail` | Upstream error message for non-200 responses |
199
- | `agent_name` | From `x-agentledger-agent-name` header |
271
+ | `agent_name` | From `x-agentledger-agent-name` header, or auto-detected (e.g. `claude-code`) |
272
+ | `framework` | From `x-agentledger-framework` header, or fingerprint-detected (e.g. `claude-code`, `litellm`) |
200
273
  | `user_id` | From `x-agentledger-user-id` header |
201
274
  | `app_id` | From `x-agentledger-app-id` header |
202
275
  | `environment` | From `x-agentledger-environment` header |
@@ -217,13 +290,17 @@ Every LLM call is stored with:
217
290
  | `GET` | `/` | Live dashboard |
218
291
  | `WS` | `/ws` | WebSocket stream — powers live dashboard updates |
219
292
  | `GET` | `/api/sessions` | List recent sessions with aggregated stats |
293
+ | `GET` | `/api/runs` | List loop runs (explicit or auto-inferred) with iterations, cost, status, and flagged-call counts |
294
+ | `GET` | `/api/runs/{run_id}` | One run's status (`running` / `flagged` / `complete`) — poll this from loop scripts |
295
+ | `GET` | `/api/sessions/{session_id}/tools` | Derived tool executions — each tool call paired with its result, latency, and error status |
220
296
  | `DELETE` | `/api/sessions/{session_id}` | Delete a session and all its calls |
221
297
  | `GET` | `/api/search?q=...` | Full-text search across all captured calls |
222
298
  | `GET` | `/session/{session_id}` | All calls in a session, ordered by time |
223
299
  | `GET` | `/explain/{action_id}` | Single call by action ID |
224
300
  | `GET` | `/export/{session_id}` | JSON compliance export with SHA-256 integrity hash |
225
301
  | `GET` | `/export/{session_id}/report` | Printable HTML audit report |
226
- | `POST` | `/mcp` | MCP tool server — `list_sessions`, `explain`, `get_session`, `search` |
302
+ | `POST` | `/mcp` | MCP tool server — `list_sessions`, `explain`, `get_session`, `search`, `list_runs`, `get_run_status` |
303
+ | `POST` | `/v1/traces` | OTLP/HTTP JSON ingest — GenAI spans from OTel-native tools become ledger calls (`/v1/logs`, `/v1/metrics` acked) |
227
304
 
228
305
  **Examples:**
229
306
  ```bash
@@ -244,7 +321,7 @@ open http://localhost:8000/export/run-1/report
244
321
 
245
322
  ## MCP server
246
323
 
247
- AgentLedger exposes its captured data as an MCP (Model Context Protocol) tool server at `POST /mcp`. Point Claude Desktop, Cursor, or any MCP-compatible client at it to query traces directly from your AI assistant.
324
+ Agentic Ledger exposes its captured data as an MCP (Model Context Protocol) tool server at `POST /mcp`. Point Claude Desktop, Cursor, or any MCP-compatible client at it to query traces directly from your AI assistant.
248
325
 
249
326
  **Tools available:**
250
327
 
@@ -254,6 +331,8 @@ AgentLedger exposes its captured data as an MCP (Model Context Protocol) tool se
254
331
  | `explain(action_id)` | Full trace for a single LLM call — prompt, tool calls, output, tokens, cost |
255
332
  | `get_session(session_id)` | All calls in a session in chronological order |
256
333
  | `search(query)` | Full-text search across all captured calls |
334
+ | `list_runs` | Loop runs with iterations, cost, and status |
335
+ | `get_run_status(run_id)` | One run's status — lets an agent inspect its own loop and decide whether to continue |
257
336
 
258
337
  **Configure in `claude_desktop_config.json`:**
259
338
  ```json
@@ -300,7 +379,7 @@ Once connected, you can ask your assistant things like:
300
379
  | `AGENTLEDGER_API_KEY` | No | _(none)_ | Master admin key. When set, the dashboard, read, and management endpoints require authentication; the key grants the `admin` role and bootstraps API tokens (below). Skip for local dev; set when the proxy is on a server — you choose the value. |
301
380
  | `AGENTLEDGER_INGEST_KEY` | No | _(none)_ | When set, the proxy forwards a request only if it carries a matching `x-agentledger-ingest-key` header — closing the open relay. Off by default; a loud startup warning fires when unset. |
302
381
  | `AGENTLEDGER_EXPORT_HMAC_KEY` | No | _(none)_ | When set, compliance exports carry a tamper-evident keyed `hmac-sha256` integrity tag instead of a plain `sha256` checksum. |
303
- | `AGENTLEDGER_EXTRA_PATHS` | No | _(none)_ | Comma-separated additional request paths to capture, e.g. `v1/responses,v1/custom`. Built-in paths (`v1/chat/completions`, `v1/messages`, `v1/responses`) are always captured. |
382
+ | `AGENTLEDGER_EXTRA_PATHS` | No | _(none)_ | Comma-separated additional request paths to capture, e.g. `v1/responses,v1/custom`. Built-in paths (`v1/chat/completions`, `v1/messages`, `v1/responses`, plus `v1/messages/count_tokens` recorded as a free call) are always captured. |
304
383
  | `AGENTLEDGER_ASYNC_CAPTURE` | No | `off` | Persist captures on a background worker so storage never adds latency to the agent's call. Trade-off: reads become **eventually consistent** (a just-captured call may not be queryable for a brief moment). Recommended for high throughput. |
305
384
  | `AGENTLEDGER_CAPTURE_QUEUE_MAX` | No | `10000` | Max captures buffered in async mode before load is shed (drops are counted in `/metrics`). |
306
385
  | `AGENTLEDGER_CAPTURE_LEVEL` | No | `full` | `full` stores everything; `metadata` stores only metrics/metadata (model, tokens, cost, latency, agent, status) and drops prompts, responses, and tools. |
@@ -327,6 +406,16 @@ Once connected, you can ask your assistant things like:
327
406
  | `AGENTLEDGER_RATE_LIMIT_AGENT_RPM` | _(none)_ | Max requests per minute per `agent_name`. |
328
407
  | `AGENTLEDGER_RATE_LIMIT_USER_RPM` | _(none)_ | Max requests per minute per `user_id`. |
329
408
 
409
+ **Loop engine** — every call is stitched into ReAct threads (`thread_id`, `step_index`, `prev_action_id`) and fresh-context loop iterations are grouped into runs, with stuck-loop detection:
410
+
411
+ | Variable | Default | Description |
412
+ |---|---|---|
413
+ | `AGENTLEDGER_LOOP_ACTION` | `warn` | `warn` records `loop_flags` and fires a `loop_flag` webhook alert; `block` additionally returns HTTP 429 (`loop_detected`) for a session that tripped a guard; `off` disables inference. |
414
+ | `AGENTLEDGER_LOOP_REPEAT_THRESHOLD` | `3` | Consecutive identical tool calls (same tool, same arguments) before a thread is flagged stuck. |
415
+ | `AGENTLEDGER_LOOP_MAX_STEPS` | _(none)_ | Flag (and in block mode, stop) threads that exceed this many ReAct steps. |
416
+ | `AGENTLEDGER_LOOP_RUN_GAP_SECONDS` | `900` | Max gap between fresh-context spawns (same system prompt) that still count as iterations of one run. |
417
+ | `AGENTLEDGER_COMPLETION_PROMISE` | _(none)_ | Regex matched against response text. On match the call is flagged `completion_promise` and the run's status becomes `complete` — loop runners poll `GET /api/runs/{run_id}` and stop. |
418
+
330
419
  **Alerts** — POST to your webhook when a threshold is breached (does not block calls — see [Alerts](#alerts)):
331
420
 
332
421
  | Variable | Default | Description |
@@ -417,7 +506,7 @@ curl -H "x-agentledger-api-key: my-secret" http://localhost:8000/api/tokens
417
506
  curl -X DELETE -H "x-agentledger-api-key: my-secret" http://localhost:8000/api/tokens/<token_id>
418
507
  ```
419
508
 
420
- > Auth is enforced only when `AGENTLEDGER_API_KEY` is set; the master key is the admin bootstrap for minting tokens. (Dashboard credential propagation over the live `/ws` feed is tracked as follow-upsee `ROADMAP.md`.)
509
+ > Auth is enforced only when `AGENTLEDGER_API_KEY` is set; the master key is the admin bootstrap for minting tokens. The live `/ws` feed accepts the same credentials (`?api_key=`, `?token=`, `Authorization: Bearer`, or `x-agentledger-token`) and rejects unauthenticated connects with close code 1008 the dashboard forwards its page credential to the socket automatically.
421
510
 
422
511
  ---
423
512
 
@@ -435,6 +524,9 @@ Pass these from your agent on each LLM call. All optional. They enrich captured
435
524
  | `x-agentledger-environment` | `development` | `production`, `staging`, or `development`. Shown in the dashboard. |
436
525
  | `x-agentledger-handoff-from` | _(none)_ | Agent handing off control (e.g. `"orchestrator"`). Renders as a directed edge in the Flow DAG. |
437
526
  | `x-agentledger-handoff-to` | _(none)_ | Agent receiving control (e.g. `"researcher"`). Renders as a directed edge in the Flow DAG. |
527
+ | `x-agentledger-framework` | _(auto-detected)_ | Framework/tool making the call (e.g. `"langgraph"`, `"bmad"`). When absent, well-known clients are fingerprinted automatically (Claude Code, LiteLLM). |
528
+ | `x-agentledger-run-id` | _(auto-inferred)_ | Groups sessions into a loop run (e.g. a Ralph overnight run). When absent, fresh-context sessions sharing a system prompt within `AGENTLEDGER_LOOP_RUN_GAP_SECONDS` are grouped automatically. |
529
+ | `x-agentledger-iteration` | _(auto-inferred)_ | Iteration number within the run. |
438
530
 
439
531
  **Single agent — fully annotated:**
440
532
  ```python
@@ -484,7 +576,7 @@ The Flow tab renders `orchestrator → researcher` as a DAG with cost and latenc
484
576
 
485
577
  **OpenAI Agents SDK (`openai-agents`) — per-agent clients:**
486
578
 
487
- The `openai-agents` SDK uses its own internal OpenAI client. To pass AgentLedger headers you need to create a client per agent using `OpenAIResponsesModel` and set it as the agent's `model`.
579
+ The `openai-agents` SDK uses its own internal OpenAI client. To pass Agentic Ledger headers you need to create a client per agent using `OpenAIResponsesModel` and set it as the agent's `model`.
488
580
 
489
581
  ```python
490
582
  import uuid
@@ -498,7 +590,7 @@ BASE_URL = os.getenv("OPENAI_BASE_URL") # e.g. http://localhost:8000/v1
498
590
 
499
591
  def al_model(agent_name: str, model: str = "gpt-4o-mini",
500
592
  handoff_from: str | None = None, handoff_to: str | None = None):
501
- """Create a model instance that sends AgentLedger metadata headers."""
593
+ """Create a model instance that sends Agentic Ledger metadata headers."""
502
594
  if not BASE_URL:
503
595
  return model # proxy not configured — use default client
504
596
  headers = {
@@ -527,7 +619,7 @@ Each agent's calls are tagged with its name and pipeline position. The Flow tab
527
619
 
528
620
  ## Alerts
529
621
 
530
- AgentLedger fires a `POST` to your webhook URL when a threshold is breached. You connect it to whatever you already use — Slack, PagerDuty, Discord, email, or a custom endpoint. AgentLedger sends the payload; the integration is on your side.
622
+ Agentic Ledger fires a `POST` to your webhook URL when a threshold is breached. You connect it to whatever you already use — Slack, PagerDuty, Discord, email, or a custom endpoint. Agentic Ledger sends the payload; the integration is on your side.
531
623
 
532
624
  **Payload format:**
533
625
  ```json
@@ -569,7 +661,7 @@ AgentLedger fires a `POST` to your webhook URL when a threshold is breached. You
569
661
 
570
662
  ## OpenTelemetry export
571
663
 
572
- AgentLedger can emit every intercepted LLM call as an OTel span to any OTLP-compatible collector: Grafana Tempo, Jaeger, Honeycomb, Datadog, Dynatrace, or any vendor that supports OTLP/HTTP.
664
+ Agentic Ledger can emit every intercepted LLM call as an OTel span to any OTLP-compatible collector: Grafana Tempo, Jaeger, Honeycomb, Datadog, Dynatrace, or any vendor that supports OTLP/HTTP.
573
665
 
574
666
  **Install the extra** (Docker image includes OTel — no extra step needed when using Docker):
575
667
  ```bash
@@ -663,7 +755,7 @@ This runs three jobs:
663
755
  ```
664
756
  PyPI project name: agentic-ledger
665
757
  Owner: ShekharBhardwaj
666
- Repository: AgentLedger
758
+ Repository: AgenticLedger
667
759
  Workflow name: release.yml
668
760
  Environment name: pypi
669
761
  ```
@@ -1,7 +1,7 @@
1
- # AgentLedger
1
+ # Agentic Ledger
2
2
 
3
- [![CI](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/ci.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/ci.yml)
4
- [![CodeQL](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/codeql.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgentLedger/actions/workflows/codeql.yml)
3
+ [![CI](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/ci.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/ci.yml)
4
+ [![CodeQL](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/codeql.yml/badge.svg)](https://github.com/ShekharBhardwaj/AgenticLedger/actions/workflows/codeql.yml)
5
5
  [![PyPI](https://img.shields.io/pypi/v/agentic-ledger)](https://pypi.org/project/agentic-ledger/)
6
6
  [![Python versions](https://img.shields.io/pypi/pyversions/agentic-ledger)](https://pypi.org/project/agentic-ledger/)
7
7
  [![Docker](https://img.shields.io/badge/docker-ghcr.io-blue)](https://ghcr.io/shekharbhardwaj/agentledger)
@@ -9,16 +9,18 @@
9
9
 
10
10
  Runtime observability for AI agents — see exactly what your agent did, why it did it, and what it cost.
11
11
 
12
+ > **Alpha** — `v0.3.0-alpha.1` is an early release of the loop engine, Loop Lens web app, and framework integrations. Interfaces and schema may still change between alpha releases. Found something broken or missing? [Open an issue](https://github.com/ShekharBhardwaj/AgenticLedger/issues/new/choose) — alpha feedback directly shapes what ships next.
13
+
12
14
  Works with **any agent framework**, **any LLM provider**, **any model gateway**. Zero code changes required. Point your agent at the proxy and everything is captured automatically.
13
15
 
14
16
  ---
15
17
 
16
18
  ## How it works
17
19
 
18
- AgentLedger runs as a transparent proxy between your agent and the LLM provider. It intercepts every request and response, assigns it an `action_id`, stores it, and returns the upstream response unmodified. Your agent never knows the proxy is there.
20
+ Agentic Ledger runs as a transparent proxy between your agent and the LLM provider. It intercepts every request and response, assigns it an `action_id`, stores it, and returns the upstream response unmodified. Your agent never knows the proxy is there.
19
21
 
20
22
  ```
21
- Your Agent → AgentLedger Proxy → OpenAI / Anthropic / LiteLLM / any LLM
23
+ Your Agent → Agentic Ledger Proxy → OpenAI / Anthropic / LiteLLM / any LLM
22
24
 
23
25
  SQLite or Postgres
24
26
 
@@ -107,10 +109,10 @@ client = anthropic.Anthropic(
107
109
 
108
110
  **LiteLLM / OpenRouter / any gateway:**
109
111
  ```bash
110
- # Point AgentLedger at your gateway
112
+ # Point Agentic Ledger at your gateway
111
113
  AGENTLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agentledger.proxy
112
114
 
113
- # Then point your agent at AgentLedger
115
+ # Then point your agent at Agentic Ledger
114
116
  client = OpenAI(base_url="http://localhost:8000/v1", ...)
115
117
  ```
116
118
 
@@ -119,10 +121,15 @@ client = OpenAI(base_url="http://localhost:8000/v1", ...)
119
121
  **Step 3 — Open the dashboard**
120
122
 
121
123
  ```
122
- http://localhost:8000
124
+ http://localhost:8000/app
123
125
  ```
124
126
 
125
- The dashboard updates live via WebSocket as calls come in. No refresh needed.
127
+ The web app updates live via WebSocket as calls come in. No refresh needed.
128
+
129
+ - **Loop Lens** — every loop run with status (`running` / `flagged` / `complete`), a cost-per-iteration chart, and a per-iteration table (calls, tokens, cache reads, flags, errors)
130
+ - **Sessions** — every session with expandable call cards: response, thinking, tool calls, tool results, cache tokens, cost, loop flags
131
+
132
+ The classic single-file dashboard still lives at `http://localhost:8000`:
126
133
 
127
134
  - **Calls tab** — every LLM call with full prompt, system prompt, tool calls, tool results, output, tokens, cost, latency, and errors
128
135
  - **Flow tab** — visual DAG of your multi-agent system. Each agent is a node with aggregated cost, latency, and call count. Edges represent handoffs. Click a node to highlight its calls.
@@ -131,6 +138,69 @@ The dashboard updates live via WebSocket as calls come in. No refresh needed.
131
138
 
132
139
  ---
133
140
 
141
+ ## Coding agents — Claude Code, Ralph loops & friends
142
+
143
+ Claude Code (and most coding agents) can be pointed at the proxy with a single
144
+ environment variable — no headers, no code changes:
145
+
146
+ ```bash
147
+ AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com uv run python -m agentledger.proxy
148
+ ```
149
+
150
+ ```bash
151
+ export ANTHROPIC_BASE_URL=http://localhost:8000
152
+ claude
153
+ ```
154
+
155
+ Agentic Ledger fingerprints Claude Code traffic automatically: every call is
156
+ tagged `framework=claude-code`, and instead of one undifferentiated bucket,
157
+ each Claude Code session appears under its **real session UUID** (the same id
158
+ `claude --resume` shows), with prompt-cache reads/writes captured and priced
159
+ correctly — cache traffic is where most of a coding agent's real spend lives.
160
+
161
+ Running an overnight loop (Ralph-style `while :; do cat PROMPT.md | claude -p; done`)?
162
+ Use the built-in loop runner — it re-executes your command each iteration,
163
+ attributes every call to the run (via the base URL, no headers needed), and
164
+ stops on a completion promise, a budget ceiling, or the iteration cap:
165
+
166
+ ```bash
167
+ AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com \
168
+ AGENTLEDGER_COMPLETION_PROMISE="ALL TASKS COMPLETE" \
169
+ uv run python -m agentledger.proxy
170
+ ```
171
+
172
+ ```bash
173
+ agentledger run --max-iterations 50 --budget 25 -- \
174
+ claude -p "$(cat PROMPT.md)" --dangerously-skip-permissions
175
+ ```
176
+
177
+ Each iteration shows up as *iteration N of the run* in `/api/runs`; when the
178
+ agent prints the completion promise in a response, run status flips to
179
+ `complete` and the loop exits with a cost/token summary. Any existing loop
180
+ script works too — poll `GET /api/runs/{run_id}` yourself, or let the proxy's
181
+ budgets (`AGENTLEDGER_BUDGET_DAILY=25.00`) hard-stop a runaway loop.
182
+
183
+ The same recipe works for any client with a base-URL override (Codex CLI,
184
+ opencode, OpenClaw, LiteLLM-based stacks) — set the OpenAI/Anthropic base URL
185
+ to the proxy and traffic is captured; add `x-agentledger-*` headers when you
186
+ want explicit attribution.
187
+
188
+ **OTel-native tools** (Gemini CLI, Codex `[otel]`, AutoGen/AG2, Pydantic AI,
189
+ Vercel AI SDK) don't need the proxy at all — point their OTLP exporter at the
190
+ ledger and GenAI spans are ingested directly:
191
+
192
+ ```bash
193
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8000
194
+ export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
195
+ ```
196
+
197
+ **Framework guides:** [BMAD-METHOD](docs/integrations/bmad.md) — persona
198
+ auto-detection (`bmad:sm`, `bmad:dev`, `bmad:qa`, …) and per-story budgets ·
199
+ [OpenClaw](docs/integrations/openclaw.md) — the hard spend cap OpenClaw
200
+ doesn't have, plus per-agent attribution.
201
+
202
+ ---
203
+
134
204
  ## What gets captured
135
205
 
136
206
  Every LLM call is stored with:
@@ -150,11 +220,14 @@ Every LLM call is stored with:
150
220
  | `content` | Model's text output |
151
221
  | `stop_reason` | Why the model stopped |
152
222
  | `tokens_in` / `tokens_out` | Token usage |
223
+ | `cache_read_tokens` / `cache_write_tokens` | Prompt-cache usage — reads and writes are priced correctly per provider |
224
+ | `thinking` | Extended-thinking output (Anthropic), captured separately from `content` |
153
225
  | `cost_usd` | Estimated cost based on model pricing |
154
226
  | `latency_ms` | End-to-end response time |
155
227
  | `status_code` | HTTP status from upstream — errors are captured too |
156
228
  | `error_detail` | Upstream error message for non-200 responses |
157
- | `agent_name` | From `x-agentledger-agent-name` header |
229
+ | `agent_name` | From `x-agentledger-agent-name` header, or auto-detected (e.g. `claude-code`) |
230
+ | `framework` | From `x-agentledger-framework` header, or fingerprint-detected (e.g. `claude-code`, `litellm`) |
158
231
  | `user_id` | From `x-agentledger-user-id` header |
159
232
  | `app_id` | From `x-agentledger-app-id` header |
160
233
  | `environment` | From `x-agentledger-environment` header |
@@ -175,13 +248,17 @@ Every LLM call is stored with:
175
248
  | `GET` | `/` | Live dashboard |
176
249
  | `WS` | `/ws` | WebSocket stream — powers live dashboard updates |
177
250
  | `GET` | `/api/sessions` | List recent sessions with aggregated stats |
251
+ | `GET` | `/api/runs` | List loop runs (explicit or auto-inferred) with iterations, cost, status, and flagged-call counts |
252
+ | `GET` | `/api/runs/{run_id}` | One run's status (`running` / `flagged` / `complete`) — poll this from loop scripts |
253
+ | `GET` | `/api/sessions/{session_id}/tools` | Derived tool executions — each tool call paired with its result, latency, and error status |
178
254
  | `DELETE` | `/api/sessions/{session_id}` | Delete a session and all its calls |
179
255
  | `GET` | `/api/search?q=...` | Full-text search across all captured calls |
180
256
  | `GET` | `/session/{session_id}` | All calls in a session, ordered by time |
181
257
  | `GET` | `/explain/{action_id}` | Single call by action ID |
182
258
  | `GET` | `/export/{session_id}` | JSON compliance export with SHA-256 integrity hash |
183
259
  | `GET` | `/export/{session_id}/report` | Printable HTML audit report |
184
- | `POST` | `/mcp` | MCP tool server — `list_sessions`, `explain`, `get_session`, `search` |
260
+ | `POST` | `/mcp` | MCP tool server — `list_sessions`, `explain`, `get_session`, `search`, `list_runs`, `get_run_status` |
261
+ | `POST` | `/v1/traces` | OTLP/HTTP JSON ingest — GenAI spans from OTel-native tools become ledger calls (`/v1/logs`, `/v1/metrics` acked) |
185
262
 
186
263
  **Examples:**
187
264
  ```bash
@@ -202,7 +279,7 @@ open http://localhost:8000/export/run-1/report
202
279
 
203
280
  ## MCP server
204
281
 
205
- AgentLedger exposes its captured data as an MCP (Model Context Protocol) tool server at `POST /mcp`. Point Claude Desktop, Cursor, or any MCP-compatible client at it to query traces directly from your AI assistant.
282
+ Agentic Ledger exposes its captured data as an MCP (Model Context Protocol) tool server at `POST /mcp`. Point Claude Desktop, Cursor, or any MCP-compatible client at it to query traces directly from your AI assistant.
206
283
 
207
284
  **Tools available:**
208
285
 
@@ -212,6 +289,8 @@ AgentLedger exposes its captured data as an MCP (Model Context Protocol) tool se
212
289
  | `explain(action_id)` | Full trace for a single LLM call — prompt, tool calls, output, tokens, cost |
213
290
  | `get_session(session_id)` | All calls in a session in chronological order |
214
291
  | `search(query)` | Full-text search across all captured calls |
292
+ | `list_runs` | Loop runs with iterations, cost, and status |
293
+ | `get_run_status(run_id)` | One run's status — lets an agent inspect its own loop and decide whether to continue |
215
294
 
216
295
  **Configure in `claude_desktop_config.json`:**
217
296
  ```json
@@ -258,7 +337,7 @@ Once connected, you can ask your assistant things like:
258
337
  | `AGENTLEDGER_API_KEY` | No | _(none)_ | Master admin key. When set, the dashboard, read, and management endpoints require authentication; the key grants the `admin` role and bootstraps API tokens (below). Skip for local dev; set when the proxy is on a server — you choose the value. |
259
338
  | `AGENTLEDGER_INGEST_KEY` | No | _(none)_ | When set, the proxy forwards a request only if it carries a matching `x-agentledger-ingest-key` header — closing the open relay. Off by default; a loud startup warning fires when unset. |
260
339
  | `AGENTLEDGER_EXPORT_HMAC_KEY` | No | _(none)_ | When set, compliance exports carry a tamper-evident keyed `hmac-sha256` integrity tag instead of a plain `sha256` checksum. |
261
- | `AGENTLEDGER_EXTRA_PATHS` | No | _(none)_ | Comma-separated additional request paths to capture, e.g. `v1/responses,v1/custom`. Built-in paths (`v1/chat/completions`, `v1/messages`, `v1/responses`) are always captured. |
340
+ | `AGENTLEDGER_EXTRA_PATHS` | No | _(none)_ | Comma-separated additional request paths to capture, e.g. `v1/responses,v1/custom`. Built-in paths (`v1/chat/completions`, `v1/messages`, `v1/responses`, plus `v1/messages/count_tokens` recorded as a free call) are always captured. |
262
341
  | `AGENTLEDGER_ASYNC_CAPTURE` | No | `off` | Persist captures on a background worker so storage never adds latency to the agent's call. Trade-off: reads become **eventually consistent** (a just-captured call may not be queryable for a brief moment). Recommended for high throughput. |
263
342
  | `AGENTLEDGER_CAPTURE_QUEUE_MAX` | No | `10000` | Max captures buffered in async mode before load is shed (drops are counted in `/metrics`). |
264
343
  | `AGENTLEDGER_CAPTURE_LEVEL` | No | `full` | `full` stores everything; `metadata` stores only metrics/metadata (model, tokens, cost, latency, agent, status) and drops prompts, responses, and tools. |
@@ -285,6 +364,16 @@ Once connected, you can ask your assistant things like:
285
364
  | `AGENTLEDGER_RATE_LIMIT_AGENT_RPM` | _(none)_ | Max requests per minute per `agent_name`. |
286
365
  | `AGENTLEDGER_RATE_LIMIT_USER_RPM` | _(none)_ | Max requests per minute per `user_id`. |
287
366
 
367
+ **Loop engine** — every call is stitched into ReAct threads (`thread_id`, `step_index`, `prev_action_id`) and fresh-context loop iterations are grouped into runs, with stuck-loop detection:
368
+
369
+ | Variable | Default | Description |
370
+ |---|---|---|
371
+ | `AGENTLEDGER_LOOP_ACTION` | `warn` | `warn` records `loop_flags` and fires a `loop_flag` webhook alert; `block` additionally returns HTTP 429 (`loop_detected`) for a session that tripped a guard; `off` disables inference. |
372
+ | `AGENTLEDGER_LOOP_REPEAT_THRESHOLD` | `3` | Consecutive identical tool calls (same tool, same arguments) before a thread is flagged stuck. |
373
+ | `AGENTLEDGER_LOOP_MAX_STEPS` | _(none)_ | Flag (and in block mode, stop) threads that exceed this many ReAct steps. |
374
+ | `AGENTLEDGER_LOOP_RUN_GAP_SECONDS` | `900` | Max gap between fresh-context spawns (same system prompt) that still count as iterations of one run. |
375
+ | `AGENTLEDGER_COMPLETION_PROMISE` | _(none)_ | Regex matched against response text. On match the call is flagged `completion_promise` and the run's status becomes `complete` — loop runners poll `GET /api/runs/{run_id}` and stop. |
376
+
288
377
  **Alerts** — POST to your webhook when a threshold is breached (does not block calls — see [Alerts](#alerts)):
289
378
 
290
379
  | Variable | Default | Description |
@@ -375,7 +464,7 @@ curl -H "x-agentledger-api-key: my-secret" http://localhost:8000/api/tokens
375
464
  curl -X DELETE -H "x-agentledger-api-key: my-secret" http://localhost:8000/api/tokens/<token_id>
376
465
  ```
377
466
 
378
- > Auth is enforced only when `AGENTLEDGER_API_KEY` is set; the master key is the admin bootstrap for minting tokens. (Dashboard credential propagation over the live `/ws` feed is tracked as follow-upsee `ROADMAP.md`.)
467
+ > Auth is enforced only when `AGENTLEDGER_API_KEY` is set; the master key is the admin bootstrap for minting tokens. The live `/ws` feed accepts the same credentials (`?api_key=`, `?token=`, `Authorization: Bearer`, or `x-agentledger-token`) and rejects unauthenticated connects with close code 1008 the dashboard forwards its page credential to the socket automatically.
379
468
 
380
469
  ---
381
470
 
@@ -393,6 +482,9 @@ Pass these from your agent on each LLM call. All optional. They enrich captured
393
482
  | `x-agentledger-environment` | `development` | `production`, `staging`, or `development`. Shown in the dashboard. |
394
483
  | `x-agentledger-handoff-from` | _(none)_ | Agent handing off control (e.g. `"orchestrator"`). Renders as a directed edge in the Flow DAG. |
395
484
  | `x-agentledger-handoff-to` | _(none)_ | Agent receiving control (e.g. `"researcher"`). Renders as a directed edge in the Flow DAG. |
485
+ | `x-agentledger-framework` | _(auto-detected)_ | Framework/tool making the call (e.g. `"langgraph"`, `"bmad"`). When absent, well-known clients are fingerprinted automatically (Claude Code, LiteLLM). |
486
+ | `x-agentledger-run-id` | _(auto-inferred)_ | Groups sessions into a loop run (e.g. a Ralph overnight run). When absent, fresh-context sessions sharing a system prompt within `AGENTLEDGER_LOOP_RUN_GAP_SECONDS` are grouped automatically. |
487
+ | `x-agentledger-iteration` | _(auto-inferred)_ | Iteration number within the run. |
396
488
 
397
489
  **Single agent — fully annotated:**
398
490
  ```python
@@ -442,7 +534,7 @@ The Flow tab renders `orchestrator → researcher` as a DAG with cost and latenc
442
534
 
443
535
  **OpenAI Agents SDK (`openai-agents`) — per-agent clients:**
444
536
 
445
- The `openai-agents` SDK uses its own internal OpenAI client. To pass AgentLedger headers you need to create a client per agent using `OpenAIResponsesModel` and set it as the agent's `model`.
537
+ The `openai-agents` SDK uses its own internal OpenAI client. To pass Agentic Ledger headers you need to create a client per agent using `OpenAIResponsesModel` and set it as the agent's `model`.
446
538
 
447
539
  ```python
448
540
  import uuid
@@ -456,7 +548,7 @@ BASE_URL = os.getenv("OPENAI_BASE_URL") # e.g. http://localhost:8000/v1
456
548
 
457
549
  def al_model(agent_name: str, model: str = "gpt-4o-mini",
458
550
  handoff_from: str | None = None, handoff_to: str | None = None):
459
- """Create a model instance that sends AgentLedger metadata headers."""
551
+ """Create a model instance that sends Agentic Ledger metadata headers."""
460
552
  if not BASE_URL:
461
553
  return model # proxy not configured — use default client
462
554
  headers = {
@@ -485,7 +577,7 @@ Each agent's calls are tagged with its name and pipeline position. The Flow tab
485
577
 
486
578
  ## Alerts
487
579
 
488
- AgentLedger fires a `POST` to your webhook URL when a threshold is breached. You connect it to whatever you already use — Slack, PagerDuty, Discord, email, or a custom endpoint. AgentLedger sends the payload; the integration is on your side.
580
+ Agentic Ledger fires a `POST` to your webhook URL when a threshold is breached. You connect it to whatever you already use — Slack, PagerDuty, Discord, email, or a custom endpoint. Agentic Ledger sends the payload; the integration is on your side.
489
581
 
490
582
  **Payload format:**
491
583
  ```json
@@ -527,7 +619,7 @@ AgentLedger fires a `POST` to your webhook URL when a threshold is breached. You
527
619
 
528
620
  ## OpenTelemetry export
529
621
 
530
- AgentLedger can emit every intercepted LLM call as an OTel span to any OTLP-compatible collector: Grafana Tempo, Jaeger, Honeycomb, Datadog, Dynatrace, or any vendor that supports OTLP/HTTP.
622
+ Agentic Ledger can emit every intercepted LLM call as an OTel span to any OTLP-compatible collector: Grafana Tempo, Jaeger, Honeycomb, Datadog, Dynatrace, or any vendor that supports OTLP/HTTP.
531
623
 
532
624
  **Install the extra** (Docker image includes OTel — no extra step needed when using Docker):
533
625
  ```bash
@@ -621,7 +713,7 @@ This runs three jobs:
621
713
  ```
622
714
  PyPI project name: agentic-ledger
623
715
  Owner: ShekharBhardwaj
624
- Repository: AgentLedger
716
+ Repository: AgenticLedger
625
717
  Workflow name: release.yml
626
718
  Environment name: pypi
627
719
  ```
@@ -1,5 +1,5 @@
1
1
  """
2
- AgentLedger — runtime observability for AI agents.
2
+ Agentic Ledger — runtime observability for AI agents.
3
3
 
4
4
  Run as a proxy:
5
5
  python -m agentledger.proxy