failproofai-sdk 0.0.1b1__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 (77) hide show
  1. failproofai_sdk-0.0.1b1/LICENSE +42 -0
  2. failproofai_sdk-0.0.1b1/MANIFEST.in +21 -0
  3. failproofai_sdk-0.0.1b1/PKG-INFO +548 -0
  4. failproofai_sdk-0.0.1b1/README.md +466 -0
  5. failproofai_sdk-0.0.1b1/failproofai_sdk/__init__.py +131 -0
  6. failproofai_sdk-0.0.1b1/failproofai_sdk/_context.py +218 -0
  7. failproofai_sdk-0.0.1b1/failproofai_sdk/_environment.py +78 -0
  8. failproofai_sdk-0.0.1b1/failproofai_sdk/_events.py +752 -0
  9. failproofai_sdk-0.0.1b1/failproofai_sdk/_resolver.py +118 -0
  10. failproofai_sdk-0.0.1b1/failproofai_sdk/_runtime.py +21 -0
  11. failproofai_sdk-0.0.1b1/failproofai_sdk/_schema.py +317 -0
  12. failproofai_sdk-0.0.1b1/failproofai_sdk/_scopes.py +422 -0
  13. failproofai_sdk-0.0.1b1/failproofai_sdk/_version.py +1 -0
  14. failproofai_sdk-0.0.1b1/failproofai_sdk/_writer.py +750 -0
  15. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/__init__.py +274 -0
  16. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/_compat.py +247 -0
  17. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/_core.py +1076 -0
  18. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/crewai.py +1669 -0
  19. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/langchain.py +2151 -0
  20. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/llama_index.py +1588 -0
  21. failproofai_sdk-0.0.1b1/failproofai_sdk/integrations/pydantic_ai.py +977 -0
  22. failproofai_sdk-0.0.1b1/failproofai_sdk/py.typed +0 -0
  23. failproofai_sdk-0.0.1b1/failproofai_sdk.egg-info/PKG-INFO +548 -0
  24. failproofai_sdk-0.0.1b1/failproofai_sdk.egg-info/SOURCES.txt +75 -0
  25. failproofai_sdk-0.0.1b1/failproofai_sdk.egg-info/dependency_links.txt +1 -0
  26. failproofai_sdk-0.0.1b1/failproofai_sdk.egg-info/requires.txt +25 -0
  27. failproofai_sdk-0.0.1b1/failproofai_sdk.egg-info/top_level.txt +1 -0
  28. failproofai_sdk-0.0.1b1/pyproject.toml +92 -0
  29. failproofai_sdk-0.0.1b1/setup.cfg +4 -0
  30. failproofai_sdk-0.0.1b1/tests/__init__.py +0 -0
  31. failproofai_sdk-0.0.1b1/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  32. failproofai_sdk-0.0.1b1/tests/__pycache__/conftest.cpython-312-pytest-9.1.1.pyc +0 -0
  33. failproofai_sdk-0.0.1b1/tests/__pycache__/test_context.cpython-312-pytest-9.1.1.pyc +0 -0
  34. failproofai_sdk-0.0.1b1/tests/__pycache__/test_docs.cpython-312-pytest-9.1.1.pyc +0 -0
  35. failproofai_sdk-0.0.1b1/tests/__pycache__/test_durability.cpython-312-pytest-9.1.1.pyc +0 -0
  36. failproofai_sdk-0.0.1b1/tests/__pycache__/test_encoding.cpython-312-pytest-9.1.1.pyc +0 -0
  37. failproofai_sdk-0.0.1b1/tests/__pycache__/test_integrations.cpython-312-pytest-9.1.1.pyc +0 -0
  38. failproofai_sdk-0.0.1b1/tests/__pycache__/test_no_customer_identifiers.cpython-312-pytest-9.1.1.pyc +0 -0
  39. failproofai_sdk-0.0.1b1/tests/__pycache__/test_packaging.cpython-312-pytest-9.1.1.pyc +0 -0
  40. failproofai_sdk-0.0.1b1/tests/__pycache__/test_resolver_umbrella.cpython-312-pytest-9.1.1.pyc +0 -0
  41. failproofai_sdk-0.0.1b1/tests/__pycache__/test_scopes.cpython-312-pytest-9.1.1.pyc +0 -0
  42. failproofai_sdk-0.0.1b1/tests/__pycache__/test_sdk.cpython-312-pytest-9.1.1.pyc +0 -0
  43. failproofai_sdk-0.0.1b1/tests/__pycache__/test_server_contract.cpython-312-pytest-9.1.1.pyc +0 -0
  44. failproofai_sdk-0.0.1b1/tests/__pycache__/test_site_docs.cpython-312-pytest-9.1.1.pyc +0 -0
  45. failproofai_sdk-0.0.1b1/tests/__pycache__/test_skill_snippets.cpython-312-pytest-9.1.1.pyc +0 -0
  46. failproofai_sdk-0.0.1b1/tests/__pycache__/test_spool_contract.cpython-312-pytest-9.1.1.pyc +0 -0
  47. failproofai_sdk-0.0.1b1/tests/__pycache__/test_spool_creation.cpython-312-pytest-9.1.1.pyc +0 -0
  48. failproofai_sdk-0.0.1b1/tests/__pycache__/test_wire_format.cpython-312-pytest-9.1.1.pyc +0 -0
  49. failproofai_sdk-0.0.1b1/tests/__pycache__/test_zero_dependencies.cpython-312-pytest-9.1.1.pyc +0 -0
  50. failproofai_sdk-0.0.1b1/tests/conftest.py +169 -0
  51. failproofai_sdk-0.0.1b1/tests/integrations/__init__.py +6 -0
  52. failproofai_sdk-0.0.1b1/tests/integrations/__pycache__/__init__.cpython-312.pyc +0 -0
  53. failproofai_sdk-0.0.1b1/tests/integrations/__pycache__/test_crewai.cpython-312-pytest-9.1.1.pyc +0 -0
  54. failproofai_sdk-0.0.1b1/tests/integrations/__pycache__/test_langchain.cpython-312-pytest-9.1.1.pyc +0 -0
  55. failproofai_sdk-0.0.1b1/tests/integrations/__pycache__/test_llama_index.cpython-312-pytest-9.1.1.pyc +0 -0
  56. failproofai_sdk-0.0.1b1/tests/integrations/__pycache__/test_pydantic_ai.cpython-312-pytest-9.1.1.pyc +0 -0
  57. failproofai_sdk-0.0.1b1/tests/integrations/test_crewai.py +1299 -0
  58. failproofai_sdk-0.0.1b1/tests/integrations/test_langchain.py +2338 -0
  59. failproofai_sdk-0.0.1b1/tests/integrations/test_llama_index.py +1576 -0
  60. failproofai_sdk-0.0.1b1/tests/integrations/test_pydantic_ai.py +1102 -0
  61. failproofai_sdk-0.0.1b1/tests/test_context.py +410 -0
  62. failproofai_sdk-0.0.1b1/tests/test_docs.py +411 -0
  63. failproofai_sdk-0.0.1b1/tests/test_durability.py +1352 -0
  64. failproofai_sdk-0.0.1b1/tests/test_encoding.py +571 -0
  65. failproofai_sdk-0.0.1b1/tests/test_integrations.py +1127 -0
  66. failproofai_sdk-0.0.1b1/tests/test_no_customer_identifiers.py +214 -0
  67. failproofai_sdk-0.0.1b1/tests/test_packaging.py +185 -0
  68. failproofai_sdk-0.0.1b1/tests/test_resolver_umbrella.py +178 -0
  69. failproofai_sdk-0.0.1b1/tests/test_scopes.py +487 -0
  70. failproofai_sdk-0.0.1b1/tests/test_sdk.py +923 -0
  71. failproofai_sdk-0.0.1b1/tests/test_server_contract.py +650 -0
  72. failproofai_sdk-0.0.1b1/tests/test_site_docs.py +371 -0
  73. failproofai_sdk-0.0.1b1/tests/test_skill_snippets.py +165 -0
  74. failproofai_sdk-0.0.1b1/tests/test_spool_contract.py +287 -0
  75. failproofai_sdk-0.0.1b1/tests/test_spool_creation.py +360 -0
  76. failproofai_sdk-0.0.1b1/tests/test_wire_format.py +279 -0
  77. failproofai_sdk-0.0.1b1/tests/test_zero_dependencies.py +378 -0
@@ -0,0 +1,42 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ExosphereHost Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ Commons Clause License Condition v1.0
24
+
25
+ The Software is provided to you by the Licensor under the License, as defined
26
+ below, subject to the following condition.
27
+
28
+ Without limiting other conditions in the License, the grant of rights under
29
+ the License will not include, and the License does not grant to you, the right
30
+ to Sell the Software.
31
+
32
+ For purposes of the foregoing, "Sell" means practicing any or all of the
33
+ rights granted to you under the License to provide to third parties, for a
34
+ fee or other consideration (including without limitation fees for hosting or
35
+ consulting/support services related to the Software), a product or service
36
+ whose value derives, entirely or substantially, from the functionality of the
37
+ Software. Any license notice or attribution required by the License must also
38
+ include this Commons Clause License Condition notice.
39
+
40
+ Software: failproofai
41
+ License: MIT
42
+ Licensor: ExosphereHost Inc
@@ -0,0 +1,21 @@
1
+ # The sdist ships `tests/`, and setuptools' default heuristic picks up only
2
+ # top-level `test*.py` — so the suite went out WITHOUT `tests/conftest.py`,
3
+ # `tests/__init__.py` or `tests/integrations/`.
4
+ #
5
+ # That is not a cosmetic omission. `conftest.py` IS the isolation layer: it
6
+ # redirects the spool at IMPORT, straight into `os.environ`, because the
7
+ # `atexit` flush fires long after the last fixture is torn down. Without it,
8
+ # running the shipped suite (a distro packager, a conda-forge recipe, anyone
9
+ # doing `pip download --no-binary :all:` then `pytest tests/`) writes tens of
10
+ # thousands of synthetic events into the user's REAL
11
+ # `~/.failproofai/custom-agents/events` — measured at 20,001 in one run of
12
+ # `test_durability.py` alone — where a running daemon uploads them to a real
13
+ # dashboard as genuine agent activity.
14
+ #
15
+ # So the suite ships whole or not at all. `graft` takes the whole tree,
16
+ # including the subpackage and the conftest.
17
+ graft tests
18
+ include LICENSE
19
+ include README.md
20
+ include py.typed
21
+ recursive-include failproofai_sdk py.typed
@@ -0,0 +1,548 @@
1
+ Metadata-Version: 2.4
2
+ Name: failproofai-sdk
3
+ Version: 0.0.1b1
4
+ Summary: Emit agent telemetry to FailproofAI Cloud — zero-dependency, stdlib only
5
+ Author-email: Failproof AI <failproofai@exosphere.host>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 ExosphereHost Inc
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Commons Clause License Condition v1.0
29
+
30
+ The Software is provided to you by the Licensor under the License, as defined
31
+ below, subject to the following condition.
32
+
33
+ Without limiting other conditions in the License, the grant of rights under
34
+ the License will not include, and the License does not grant to you, the right
35
+ to Sell the Software.
36
+
37
+ For purposes of the foregoing, "Sell" means practicing any or all of the
38
+ rights granted to you under the License to provide to third parties, for a
39
+ fee or other consideration (including without limitation fees for hosting or
40
+ consulting/support services related to the Software), a product or service
41
+ whose value derives, entirely or substantially, from the functionality of the
42
+ Software. Any license notice or attribution required by the License must also
43
+ include this Commons Clause License Condition notice.
44
+
45
+ Software: failproofai
46
+ License: MIT
47
+ Licensor: ExosphereHost Inc
48
+
49
+ Project-URL: Homepage, https://befailproof.ai
50
+ Project-URL: Documentation, https://docs.befailproof.ai/reference/custom-agents
51
+ Project-URL: Source, https://github.com/FailproofAI/failproofai
52
+ Project-URL: Changelog, https://github.com/FailproofAI/failproofai/blob/main/sdk/python/CHANGELOG.md
53
+ Keywords: agents,observability,ai,monitoring,failproofai,telemetry,sdk
54
+ Classifier: Development Status :: 4 - Beta
55
+ Classifier: Intended Audience :: Developers
56
+ Classifier: Programming Language :: Python :: 3
57
+ Classifier: Programming Language :: Python :: 3.10
58
+ Classifier: Programming Language :: Python :: 3.11
59
+ Classifier: Programming Language :: Python :: 3.12
60
+ Classifier: Programming Language :: Python :: 3.13
61
+ Classifier: Programming Language :: Python :: 3.14
62
+ Classifier: Typing :: Typed
63
+ Requires-Python: >=3.10
64
+ Description-Content-Type: text/markdown
65
+ License-File: LICENSE
66
+ Provides-Extra: dev
67
+ Requires-Dist: pytest>=7; extra == "dev"
68
+ Requires-Dist: tomli>=2; python_version < "3.11" and extra == "dev"
69
+ Requires-Dist: pytest-asyncio<2,>=1.3; extra == "dev"
70
+ Provides-Extra: langchain
71
+ Requires-Dist: langchain-core<2,>=1.4.7; extra == "langchain"
72
+ Provides-Extra: langgraph
73
+ Requires-Dist: langgraph<2,>=1.2; extra == "langgraph"
74
+ Provides-Extra: crewai
75
+ Requires-Dist: crewai<2,>=1.13; extra == "crewai"
76
+ Requires-Dist: onnxruntime<1.24,>=1.14; python_version < "3.11" and extra == "crewai"
77
+ Provides-Extra: llamaindex
78
+ Requires-Dist: llama-index-core<0.15,>=0.14.23; extra == "llamaindex"
79
+ Provides-Extra: pydantic-ai
80
+ Requires-Dist: pydantic-ai-slim<3,>=2.0; extra == "pydantic-ai"
81
+ Dynamic: license-file
82
+
83
+ # failproofai-sdk
84
+
85
+ The Python SDK for [Failproof AI](https://befailproof.ai) agent observability. It
86
+ records what your agent did — tool calls, model requests, hooks, errors, waits for
87
+ a human — as structured events, and hands them to the daemon that ships them to
88
+ the platform.
89
+
90
+ - **PyPI distribution:** `failproofai-sdk`
91
+ - **Import name:** `failproofai_sdk`
92
+ - **Dependencies:** none. Standard library only, so installing it constrains
93
+ nothing else in your environment.
94
+
95
+ ## Installation
96
+
97
+ ```bash
98
+ pip install failproofai-sdk
99
+ # or
100
+ uv add failproofai-sdk
101
+ ```
102
+
103
+ > **Do not `pip install agenteye`.** That distribution name is occupied on PyPI by
104
+ > a stranded build of an old CLI, which ships a module called `agenteye_cli` and is
105
+ > not this SDK. Installing it gives you `ModuleNotFoundError` at best, and — if
106
+ > this SDK is already present — pip treats it as an upgrade and **removes the SDK**
107
+ > to install the CLI. The import that worked five minutes ago then stops working.
108
+
109
+ ## How events reach the platform
110
+
111
+ The SDK never opens a network connection. It appends events to an in-memory queue,
112
+ and a background thread writes them to local JSONL batches:
113
+
114
+ ```
115
+ your agent → failproofai_sdk → ~/.failproofai/custom-agents/events/*.jsonl → daemon → platform
116
+ ```
117
+
118
+ A daemon on the same host watches that directory and uploads each batch.
119
+ **`failproofaid` reads this root; the older `agenteye-collector` does not** — it
120
+ resolves `$AGENTEYE_HOME` or `~/.agenteye` and nothing else, so a host running
121
+ only that collector needs one of the three bridges in the upgrade note below, or
122
+ its batches pile up unread. If no daemon is running, batches simply accumulate on
123
+ disk — the SDK does not fail, and your agent does not block.
124
+
125
+ ## Agent frameworks
126
+
127
+ If your agent runs on LangChain/LangGraph, CrewAI, LlamaIndex or Pydantic AI,
128
+ one line captures it — runs, sub-agents, tools, model calls and their token
129
+ counts — without threading an id through anything:
130
+
131
+ ```python
132
+ import failproofai_sdk
133
+
134
+ failproofai_sdk.configure(environment="production")
135
+ failproofai_sdk.instrument() # auto-detects what is already imported
136
+
137
+ graph.invoke({"messages": [...]}) # unchanged
138
+ ```
139
+
140
+ ```bash
141
+ pip install 'failproofai-sdk[langgraph]' # or [langchain] [crewai] [llamaindex] [pydantic-ai]
142
+ ```
143
+
144
+ The adapter code ships in the base wheel and imports its framework lazily, so
145
+ the extras are a convenience — `pip install failproofai-sdk` still declares no
146
+ dependencies at all, and `import failproofai_sdk` loads nothing outside the
147
+ standard library. See `skill/references/frameworks.md` for the per-framework
148
+ mapping, and `docs/` for a per-framework integration guide with runnable
149
+ examples beside it.
150
+
151
+ ## Scopes
152
+
153
+ The same identity layer, for code the adapters do not cover. `session_id` and
154
+ `agent_id` are optional on every event method — omitted, they resolve from the
155
+ enclosing scope:
156
+
157
+ ```python
158
+ with failproofai_sdk.session() as sid:
159
+ with failproofai_sdk.agent("planner", goal=question):
160
+
161
+ with failproofai_sdk.tool_call("search", input={"q": q}) as t:
162
+ t.output = search(q) # tool_use / tool_result, timed
163
+
164
+ with failproofai_sdk.agent("writer"): # a sub-agent; parent inferred
165
+ failproofai_sdk.event.model_request(model="...")
166
+ ```
167
+
168
+ `agent()` brackets a run with `agent_start`/`agent_end` and records an `error`
169
+ before the end event when the block raises — a cancellation closes it as
170
+ `cancelled` rather than failed. Every scope works under `async with` too.
171
+
172
+ Contextvars do **not** cross into a new thread, so hand work over with
173
+ `propagate`:
174
+
175
+ ```python
176
+ pool.submit(failproofai_sdk.propagate(work), item)
177
+ ```
178
+
179
+ Nothing bound and nothing passed raises `TypeError` naming the fix. It is never
180
+ a silent emit: ingest skips an event with no session and answers `200`.
181
+
182
+ ## Quick start
183
+
184
+ ```python
185
+ import failproofai_sdk
186
+
187
+ # Call once at startup. Omit to use defaults (~/.failproofai/custom-agents;
188
+ # 500ms flush interval).
189
+ failproofai_sdk.configure(base_dir=None, flush_interval=0.5)
190
+
191
+ # Emit events via failproofai_sdk.event.<method>(...)
192
+ failproofai_sdk.event.agent_start(session_id="run-001", agent_id="planner", goal="answer user query")
193
+
194
+ failproofai_sdk.event.tool_use(
195
+ session_id="run-001",
196
+ agent_id="planner",
197
+ tool_name="web_search",
198
+ tool_call_id="toolu_01",
199
+ input={"query": "latest AI research"},
200
+ )
201
+
202
+ failproofai_sdk.event.tool_result(
203
+ session_id="run-001",
204
+ agent_id="planner",
205
+ tool_name="web_search",
206
+ tool_call_id="toolu_01", # matches tool_use — SDK auto-computes duration_ms
207
+ output={"results": ["..."]},
208
+ )
209
+
210
+ failproofai_sdk.event.agent_end(session_id="run-001", agent_id="planner", outcome="success")
211
+ ```
212
+
213
+ ## configure()
214
+
215
+ ```python
216
+ failproofai_sdk.configure(
217
+ base_dir=None, # Path | str | None. Default:
218
+ # ~/.failproofai/custom-agents (honours $FAILPROOFAI_HOME)
219
+ flush_interval=0.5, # float, seconds between flush cycles
220
+ environment=None, # str | None. Else $AGENTEYE_ENVIRONMENT, else "dev"
221
+ )
222
+ ```
223
+
224
+ Call once before any `event.*` call. Safe to omit — defaults work out of the box.
225
+ When `base_dir` is `None`, the SDK spools to `~/.failproofai/custom-agents`
226
+ (honouring `$FAILPROOFAI_HOME`, which moves the umbrella; the `custom-agents`
227
+ segment is always appended, so the spool is always inside it).
228
+
229
+ **`base_dir` is the only way to spool anywhere else.** No environment variable
230
+ redirects it.
231
+
232
+ **The default spool root moved.** It was `~/.agenteye`. The daemon this SDK ships
233
+ beside, `failproofaid`, watches **both** roots and always has, so on a host
234
+ running it this changes which directory the files land in and nothing else.
235
+ Batches already sitting in `~/.agenteye/events` are not orphaned — they stay put
236
+ and are still collected; that directory simply stops growing.
237
+
238
+ > [!IMPORTANT]
239
+ > **If you run the older `agenteye-collector`, point IT at this SDK — not the
240
+ > other way round.** That collector resolves `$AGENTEYE_HOME` or `~/.agenteye`
241
+ > and nothing else, so it does not watch where this SDK writes: no upload, no
242
+ > error, and an unread spool looks exactly like an idle one.
243
+ >
244
+ > `AGENTEYE_HOME` used to be the way back, and is not any more — this SDK no
245
+ > longer reads it, so exporting it moves the collector and leaves the SDK where
246
+ > it was. Pick one of:
247
+ >
248
+ > * run `failproofaid` instead — it watches **both** roots, so nothing needs
249
+ > configuring; or
250
+ > * set **the collector's** `AGENTEYE_HOME=~/.failproofai/custom-agents`, so it
251
+ > watches `~/.failproofai/custom-agents/events` — where this SDK writes; or
252
+ > * `configure(base_dir="~/.agenteye")` in the application, which is explicit
253
+ > and visible at the call site.
254
+
255
+ `AGENTEYE_SPOOL_TO_FAILPROOFAI` is **retired**. It selected this root, but also
256
+ required the directory to already exist — and nothing ever created it, so the
257
+ opt-in never fired. Anyone who set it already wanted this and now gets it.
258
+
259
+ ## Event reference
260
+
261
+ All event methods share two required fields:
262
+
263
+ | Field | Type | Description |
264
+ |-------|------|-------------|
265
+ | `session_id` | `str` | Identifies the top-level agent run |
266
+ | `agent_id` | `str` | Identifies which agent within the session emitted the event |
267
+
268
+ Every method also accepts arbitrary `**fields` for custom metadata (see [Custom fields](#custom-fields)).
269
+
270
+ ---
271
+
272
+ ### `event.tool_use()`
273
+
274
+ Emitted when an agent invokes a tool. Pair with `tool_result` — the SDK auto-computes `duration_ms`.
275
+
276
+ ```python
277
+ failproofai_sdk.event.tool_use(
278
+ session_id="run-001",
279
+ agent_id="planner",
280
+ tool_name="web_search", # str, required
281
+ tool_call_id="toolu_01", # str, required — correlation key for the matching tool_result
282
+ input={"query": "..."}, # dict | None
283
+ )
284
+ ```
285
+
286
+ ---
287
+
288
+ ### `event.tool_result()`
289
+
290
+ Emitted when a tool returns. Correlates with `tool_use` via `tool_call_id`.
291
+
292
+ ```python
293
+ failproofai_sdk.event.tool_result(
294
+ session_id="run-001",
295
+ agent_id="planner",
296
+ tool_name="web_search",
297
+ tool_call_id="toolu_01", # must match the prior tool_use
298
+ output={"results": ["..."]}, # Any | None
299
+ error=None, # str | None — set if the tool raised
300
+ # duration_ms is computed automatically — do not pass it
301
+ )
302
+ ```
303
+
304
+ ---
305
+
306
+ ### `event.model_request()`
307
+
308
+ Emitted just before sending a prompt to an LLM.
309
+
310
+ ```python
311
+ failproofai_sdk.event.model_request(
312
+ session_id="run-001",
313
+ agent_id="planner",
314
+ model="claude-opus-4-6", # str | None
315
+ messages=[ # list[dict] | None — conversation turns
316
+ {"role": "user", "content": "..."},
317
+ ],
318
+ system="You are helpful.", # Any | None — str or list of content blocks
319
+ tools=[ # list[dict] | None — tool schemas offered to the model
320
+ {"name": "search", "input_schema": {"type": "object"}},
321
+ ],
322
+ )
323
+ ```
324
+
325
+ `messages` entries accept either a plain string `content` or Anthropic-style list-of-blocks `content`. Sampling params (`temperature`, `max_tokens`, etc.) can be passed as extra kwargs.
326
+
327
+ ---
328
+
329
+ ### `event.model_response()`
330
+
331
+ Emitted when the LLM returns a response.
332
+
333
+ ```python
334
+ failproofai_sdk.event.model_response(
335
+ session_id="run-001",
336
+ agent_id="planner",
337
+ model="claude-opus-4-6", # str | None
338
+ stop_reason="end_turn", # str | None
339
+ input_tokens=1024, # int | None
340
+ output_tokens=256, # int | None
341
+ content=[ # Any | None — str, or list of content blocks
342
+ {"type": "text", "text": "..."},
343
+ ],
344
+ role="assistant", # str | None
345
+ )
346
+ ```
347
+
348
+ `content` accepts either a plain string (generic providers) or a list of Anthropic-style content blocks. Tool calls live inside `content` as `{"type": "tool_use", ...}` blocks — no separate `tool_calls` field.
349
+
350
+ ---
351
+
352
+ ### `event.agent_start()`
353
+
354
+ Emitted when an agent begins work.
355
+
356
+ ```python
357
+ failproofai_sdk.event.agent_start(
358
+ session_id="run-001",
359
+ agent_id="planner",
360
+ goal="answer user query", # str | None
361
+ parent_id=None, # str | None — parent agent_id for nested agents
362
+ )
363
+ ```
364
+
365
+ ---
366
+
367
+ ### `event.agent_end()`
368
+
369
+ Emitted when an agent finishes work.
370
+
371
+ ```python
372
+ failproofai_sdk.event.agent_end(
373
+ session_id="run-001",
374
+ agent_id="planner",
375
+ outcome="success", # str | None
376
+ summary="Answered query", # str | None
377
+ )
378
+ ```
379
+
380
+ ---
381
+
382
+ ### `event.agent_pause()`
383
+
384
+ Emitted when an agent is suspended (e.g. waiting for human input, user-requested
385
+ pause, throttling). Does **not** end the agent — pair it with `agent_resume`, and
386
+ the SDK auto-computes the paused `duration_ms`. Emit `agent_resume` instead of a
387
+ second `agent_start` when the agent continues.
388
+
389
+ ```python
390
+ failproofai_sdk.event.agent_pause(
391
+ session_id="run-001",
392
+ agent_id="planner",
393
+ pause_id="pause-abc", # str, required — correlation key (reuse it on agent_resume)
394
+ reason="waiting_for_user", # str | None
395
+ user_id="usr_42", # str | None — who paused, if user-initiated
396
+ )
397
+ ```
398
+
399
+ `pause_id` is emitted on both events, so a pause always pairs to its resume — even
400
+ when they happen in different processes (in that case `duration_ms` is omitted and
401
+ the interval is derived downstream from the two timestamps).
402
+
403
+ ---
404
+
405
+ ### `event.agent_resume()`
406
+
407
+ Emitted when a paused agent continues. Correlates with `agent_pause` via `pause_id`;
408
+ the SDK auto-computes `duration_ms` (how long the agent was paused).
409
+
410
+ ```python
411
+ failproofai_sdk.event.agent_resume(
412
+ session_id="run-001",
413
+ agent_id="planner",
414
+ pause_id="pause-abc", # str, required — must match the prior agent_pause
415
+ reason="user_resumed", # str | None
416
+ user_id="usr_42", # str | None
417
+ # duration_ms is computed automatically — do not pass it
418
+ )
419
+ ```
420
+
421
+ ---
422
+
423
+ ### `event.hook_triggered()`
424
+
425
+ Emitted when a hook fires. Pair with `hook_completed` — the SDK auto-computes `duration_ms`.
426
+
427
+ ```python
428
+ failproofai_sdk.event.hook_triggered(
429
+ session_id="run-001",
430
+ agent_id="planner",
431
+ hook_name="pre_tool_use", # str, required
432
+ hook_id="hook-abc", # str, required — correlation key
433
+ trigger_event="tool_use", # str | None
434
+ input={"tool": "search"}, # Any | None
435
+ )
436
+ ```
437
+
438
+ ---
439
+
440
+ ### `event.hook_completed()`
441
+
442
+ Emitted when a hook finishes. Correlates with `hook_triggered` via `hook_id`.
443
+
444
+ ```python
445
+ failproofai_sdk.event.hook_completed(
446
+ session_id="run-001",
447
+ agent_id="planner",
448
+ hook_name="pre_tool_use",
449
+ hook_id="hook-abc", # must match the prior hook_triggered
450
+ outcome="allow", # str | None
451
+ output=None, # Any | None
452
+ error=None, # str | None
453
+ # duration_ms is computed automatically — do not pass it
454
+ )
455
+ ```
456
+
457
+ ---
458
+
459
+ ### `event.error()`
460
+
461
+ Emitted when an unhandled error occurs.
462
+
463
+ ```python
464
+ failproofai_sdk.event.error(
465
+ session_id="run-001",
466
+ agent_id="planner",
467
+ error_type="TimeoutError", # str, required
468
+ message="timed out", # str, required
469
+ traceback="Traceback...", # str | None
470
+ )
471
+ ```
472
+
473
+ ---
474
+
475
+ ## Custom fields
476
+
477
+ Any extra keyword arguments are appended to the event after the standard fields:
478
+
479
+ ```python
480
+ failproofai_sdk.event.tool_use(
481
+ session_id="run-001",
482
+ agent_id="planner",
483
+ tool_name="db_query",
484
+ tool_call_id="toolu_02",
485
+ request_id="req-123", # custom field
486
+ tenant_id="acme", # custom field
487
+ )
488
+ ```
489
+
490
+ The field names `timestamp`, `type`, and `environment` are reserved and raise
491
+ `ValueError` if passed as custom fields. `session_id` and `agent_id` are required
492
+ parameters and cannot be supplied a second time. Set the environment with
493
+ `configure(environment=...)` or `AGENTEYE_ENVIRONMENT`.
494
+
495
+ Keep payloads as structured JSON when downstream queries need their fields. Values
496
+ JSON does not natively support—such as datetimes, UUIDs, decimals, sets, bytes, or
497
+ model objects—are converted to strings so the writer can continue flushing the batch.
498
+
499
+ ## JSONL output
500
+
501
+ Events are buffered in-process and flushed to disk every `flush_interval` seconds (default 500ms).
502
+ Each flush writes one JSONL file:
503
+
504
+ ```
505
+ ~/.failproofai/custom-agents/events/event-2026-04-01T12-00-00-000Z-48213-7.jsonl
506
+ ```
507
+
508
+ Each line is one JSON object. Example:
509
+
510
+ ```json
511
+ {"timestamp": "2026-04-01T12:00:00.000000Z", "session_id": "run-001", "agent_id": "planner", "type": "agent_start", "goal": "answer user query"}
512
+ {"timestamp": "2026-04-01T12:00:00.123456Z", "session_id": "run-001", "agent_id": "planner", "type": "tool_use", "tool_name": "web_search", "tool_call_id": "toolu_01"}
513
+ ```
514
+
515
+ The batch is published by writing a `.tmp` file and atomically renaming it to
516
+ `.jsonl`, so a daemon polling the directory never reads a half-written file. The
517
+ trailing `<pid>-<seq>` keeps two batches written in the same millisecond — by two
518
+ threads, or by two agent processes sharing the spool — from overwriting each
519
+ other. You do not need to manage these files directly.
520
+
521
+ ## Development
522
+
523
+ ```bash
524
+ # Install dev dependencies
525
+ uv sync --locked --extra dev
526
+
527
+ # Run the test suite
528
+ uv run pytest tests/ -v
529
+
530
+ # Run a single test
531
+ uv run pytest tests/test_sdk.py -k duration -v
532
+ ```
533
+
534
+ | Suite | What it holds |
535
+ |---|---|
536
+ | `test_sdk.py` | The public API — every event method, unit and on-disk |
537
+ | `test_wire_format.py` | Golden bytes for all 15 event types, frozen |
538
+ | `test_server_contract.py` | The keys ingest promotes to indexed columns |
539
+ | `test_spool_contract.py` | Agreement with the daemons that read the spool |
540
+ | `test_durability.py` | Concurrency, crash and retry paths — nothing silently lost |
541
+ | `test_zero_dependencies.py` | The stdlib-only guarantee |
542
+ | `test_no_customer_identifiers.py` | Nothing private ships in a public wheel |
543
+
544
+ Two suites reach for sources outside this package. `test_spool_contract.py` reads
545
+ the Rust and TypeScript in this repo and never skips; set
546
+ `FAILPROOFAI_SDK_REQUIRE_CONTRACT=1` (CI does) so a moved file fails instead of
547
+ skipping. Set `FP_AGENTEYE_ROOT` to an AgentEye checkout to additionally verify
548
+ against the older collector and the live ingest handler.