Skip to main content
ClaudeWave
Skill1.7k repo starsupdated 3d ago

failproofai-sdk

|-

Install in Claude Code
Copy
git clone --depth 1 https://github.com/FailproofAI/failproofai /tmp/failproofai-sdk && cp -r /tmp/failproofai-sdk/sdk/python/skill ~/.claude/skills/failproofai-sdk
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Failproof AI Python SDK

The SDK records what your agent did, from inside your agent. You call it at points
you choose; it appends structured events to local `.jsonl` files. A separate
collector ships those files to the platform.

```
your agent calls failproofai_sdk.event.*
  → SDK queues it in memory
  → flush thread writes <base_dir>/events/event-<timestamp>.jsonl
  → collector picks the file up and ships it
  → visible as sessions / events / errors / evals
```

**The SDK's job ends at the file.** That boundary is the most useful thing to know
about it: everything up to the `.jsonl` is yours to get right and yours to verify,
and it is verifiable on a laptop with no server, no API key, and no network.

The API is small — 15 event methods, all keyword-only. The hard parts are
**deciding where to call them** and **knowing which silences are bugs**, because
this SDK does not raise when you get it wrong. Sections 1-3 are the plan, 4 is the
code, 5-6 are the proof.

## 1. Install it

```bash
pip install failproofai-sdk        # or: uv add failproofai-sdk
```

The distribution is `failproofai-sdk` and the import is `failproofai_sdk`. Public
PyPI, no token, no dependencies.

**One command to never run: `pip install agenteye`.** That name belongs to a
stranded release of an old CLI — a different product that shipped under it before
moving to `fp-cloud-cli`. PyPI versions cannot be withdrawn, so the name still resolves
to that build forever. You get the CLI, `import failproofai_sdk` raises
`ModuleNotFoundError`, and on a codebase still using the pre-rename SDK (which
published under `agenteye` too) pip treats it as an upgrade and **removes the SDK**.

> **Tell:** if a coding agent proposes `pip install agenteye` to install the SDK,
> this skill never loaded. Stop and re-read it.

The CLI is a fine thing to want — it is what reads the telemetry back. Install it
separately, never with `pip` into your agent's environment:

```bash
pipx install fp-cloud-cli        # the command is `fp`
```

Confirm what you actually have before writing a line of instrumentation:

```bash
python -c "import failproofai_sdk; print(failproofai_sdk.__version__)"
```

A version like `0.0.1b1` is the SDK. `ModuleNotFoundError` means it is not
installed — check `pip show agenteye`, which returning anything means the wrong
name was installed. `references/install.md` covers migrating an existing
`import agenteye` integration.

## 2. Plan before you instrument

Instrumentation lands in code that already exists and already works. Read it
first, then decide. Two questions settle most of the design, and only the user can
answer the first:

> **What is one run of this agent?** That is your `session_id` — one value for the
> whole run, generated by you at the point the run starts. A chat turn, a job, a
> request, a workflow execution. If the agent handles concurrent runs, this must
> be per-run, not per-process.
>
> **What are the distinguishable actors in a run?** That is your `agent_id` — a
> stable *label*, not a unique id. `"planner"`, `"researcher"`, `"main"`. It is how
> the platform tells sub-agents apart, so reuse the same string across runs.

Get these two named and agreed before writing code. They are the axes every
surface groups by, and changing them later splits the history: old runs keep the
old labels and the trends break.

### The two events everything else hangs off

Most of the catalog is optional and incremental. These two are not:

| Event | Without it |
|---|---|
| `agent_start` | **The session does not exist.** No row on Sessions, no timeline, no evaluation — while every other event you emit still lands fine and shows up in the event stream. |
| `agent_end` | The run never closes, and it is not handed to the evaluator at the normal time. |

That first row is the single most common integration failure, and it is
completely silent: a run emitting 500 tool calls and no `agent_start` produces a
busy event stream and **zero sessions**. Sessions are *defined* as "something that
emitted `agent_start`". So:

**Emit `agent_start` at the top of the run and `agent_end` at every exit, and get
those two working end-to-end before you instrument anything else.** One event at
each end proves the whole path — install, identity, base dir, collector — with
almost no code to be wrong. Add tools, models, and hooks after that path is green.

### Then map the rest onto the agent's shape

Walk the agent loop and pick the points that exist in *this* codebase. Skip what
doesn't apply; there is no requirement to emit every type.

| In the code | Emit | Buys you |
|---|---|---|
| every exit path of a run — success, exception, early return | `agent_start` / `agent_end` | the session itself |
| the tool dispatcher, both sides of the call | `tool_use` / `tool_result` | what ran, in what order, how long |
| the LLM client wrapper, both sides | `model_request` / `model_response` | model mix, token spend, stop reasons |
| your `except` blocks | `error` | the Errors surface |
| a policy/guard/middleware layer | `hook_triggered` / `hook_completed` | hook behaviour |
| an approval gate or human handoff | `human_wait` / `human_input`, `human_pause`, `human_interrupt` | where runs sit waiting on people |
| a run that suspends and resumes — waiting for a human, throttled, user-paused | `agent_pause` / `agent_resume` | a real "paused" state: the agent isn't ended, the resume isn't a new agent, and wait time is excluded from active work |

If the codebase has one tool dispatcher and one LLM wrapper, you have two edit
sites for the bulk of the value. If tool calls are scattered inline across the
codebase, say so — a wrapper (§4) is worth more than 40 call sites.

Full field-by-field catalog: `references/events.md`.

## 3. The contract

Work with these; none of them raise, so none of them show up in testing.

- **There IS an ambient session, and it is the ergonomic path.** `session()`,
  `agent()` and `tool_call()` bind identity on contextvars, so `