Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3566bce
feat(fp-cli): open-source the Cloud CLI as `fp-cli`, command `fp`
NiveditJain Aug 17, 2026
e4f8890
docs(changelog): record the fp-cli open-sourcing (#702)
NiveditJain Aug 17, 2026
c4d9a71
feat(fp-cli): re-establish the agent-skill mirror, now as skills/fp-cli
NiveditJain Aug 17, 2026
ae14887
fix(fp-cli): remove a customer identifier, restore publish authorization
NiveditJain Aug 17, 2026
29d04e8
fix(fp-cli): stop a stdin read draining a query, and a tripwire namin…
NiveditJain Aug 17, 2026
7900b01
chore: re-trigger CI after GitHub 429s in Set up job
NiveditJain Aug 17, 2026
dc6364e
chore: re-trigger CI again — GitHub Actions still in a partial outage
NiveditJain Aug 17, 2026
80affea
chore: re-trigger CI (attempt 4) — one job per run still losing setup…
NiveditJain Aug 17, 2026
7670c90
feat(sdk): open-source the telemetry SDK as `failproofai-sdk` in sdk/…
NiveditJain Aug 17, 2026
cd52279
fix(sdk): make the zero-dependency check work on Python 3.10
NiveditJain Aug 17, 2026
7691199
test(sdk): stop the configure() thread-safety test depending on execu…
NiveditJain Aug 17, 2026
3b302ca
fix(sdk): address four CodeRabbit findings — all four were real
NiveditJain Aug 17, 2026
bc039ac
fix(fp-cli): two docs claims that contradict the shipped code
NiveditJain Aug 17, 2026
b30c492
fix(fp-cli): two --help examples that fail when you run them
SiddarthAA Aug 18, 2026
10364f3
fix(sdk): seven ways the SDK lost events without saying so
SiddarthAA Aug 18, 2026
18ef5c3
feat(fp-cli): move the session into ~/.failproofai/fpcli/cli-auth.json
SiddarthAA Aug 18, 2026
acfca52
fix(fp-cli): name the right stale session file for FP_HOME users
SiddarthAA Aug 18, 2026
e3c7e71
fix(fp-cli): three help strings still printed the pre-move config path
SiddarthAA Aug 18, 2026
ce2012d
feat(fp-cli): adopt a pre-move session instead of signing the machine…
SiddarthAA Aug 18, 2026
3b3d666
test(fp-cli): adoption must not resurrect a session after logout
SiddarthAA Aug 18, 2026
5bacab7
fix(sdk): stop the test suite writing into the developer's real spool
SiddarthAA Aug 18, 2026
33cc78a
feat(sdk)!: default the spool to ~/.failproofai/custom-agents
SiddarthAA Aug 18, 2026
d1b16b1
Merge origin/main into feat/fp-cli
SiddarthAA Aug 18, 2026
2fa3e57
test(sdk): pin what the SDK may create inside ~/.failproofai
SiddarthAA Aug 18, 2026
cc28451
fix(sdk): four review findings — durability, u32 range, NaN, cancella…
SiddarthAA Aug 18, 2026
c99dc2d
fix(sdk): five bugs found by adversarial testing, two of them crashes
SiddarthAA Aug 18, 2026
2fe57f4
test(sdk): stop the spool-contract test aborting the suite outside th…
SiddarthAA Aug 18, 2026
bb1cc99
test(fp-cli): guard the credential path against drifting from the reg…
SiddarthAA Aug 19, 2026
3c8bc31
[fp-cli] fp policies, fp fleet and fp guardrails — the dashboard's en…
SiddarthAA Aug 19, 2026
7215375
Merge origin/main into feat/fp-cli
SiddarthAA Aug 20, 2026
ee735f5
[sdk] Native support for LangChain/LangGraph, CrewAI, LlamaIndex and …
SiddarthAA Aug 20, 2026
84a7b3c
ci: ignore an unfixable chromadb advisory that cannot reach a user
SiddarthAA Aug 20, 2026
ae10911
docs(changelog): one Docs section per version, not two
SiddarthAA Aug 20, 2026
6d416b4
ci: ignore the advisory under its primary id, not only its alias
SiddarthAA Aug 20, 2026
3a0e57b
ci: make the root osv-scanner.toml govern every lockfile it scans
SiddarthAA Aug 20, 2026
0c0ff4c
fix(fp-cli): stop the customer tripwire from publishing the name it h…
SiddarthAA Aug 20, 2026
e3271fc
docs(sdk): correct the spool root the README tells readers to look in
SiddarthAA Aug 20, 2026
9bbccaa
fix(failproofaid): --help printed nothing and started the daemon
SiddarthAA Aug 20, 2026
320598a
ci(sdk): actually run the framework integration suites
SiddarthAA Aug 20, 2026
ad48c92
chore(fp-cli): stop fixtures and help text naming a real domain
SiddarthAA Aug 20, 2026
f1e68f7
fix(fp-cli): `fp issues show <malformed-id>` never reached its not-fo…
SiddarthAA Aug 20, 2026
de388b1
fix(collect): park a batch the server stored none of, instead of dele…
SiddarthAA Aug 20, 2026
6372ecf
fix(fp-cli): resolve a member by the email the caller actually typed
SiddarthAA Aug 20, 2026
b672543
fix(sdk): document model_response's real keyword, and guard the class
SiddarthAA Aug 20, 2026
bed3d39
docs(integrations): let the page shape match what is being explained
SiddarthAA Aug 20, 2026
ab4d76c
docs(skill): correct three stale claims, and guard the prose
SiddarthAA Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,35 @@ updates:
rust-dependencies:
patterns:
- "*"

# The `fp` CLI (PyPI: fp-cli) — the only Python in the repo. Its lockfile is
# `fp-cli/uv.lock`, so the ecosystem is `uv`, and the directory is the package
# root rather than `/`. Grouped for the same reason as the Rust tree: the whole
# dependency set is verified together by one pytest run, and separate PRs each
# rebuild the lockfile the others just changed.
- package-ecosystem: uv
directory: /fp-cli
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 5
groups:
python-dependencies:
patterns:
- "*"

# The telemetry SDK (PyPI: failproofai-sdk). Its own lockfile and its own uv
# ecosystem entry — dependabot resolves per directory, so fp-cli's entry above
# does not see this tree at all. The only things in the lockfile are the test
# runner and its transitive deps: the package itself declares NO runtime
# dependencies, and `tests/test_zero_dependencies.py` fails if that changes.
- package-ecosystem: uv
directory: /sdk/python
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 5
groups:
python-dependencies:
patterns:
- "*"
219 changes: 218 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ name: CI
on:
push:
branches: [main]
# `main` AND the long-lived branches other PRs stack onto. A pull request is
# tested against the branch it will actually merge into, and a stacked PR
# targeting anything but `main` matched nothing here — so #730, thirty commits
# of SDK work, ran no unit tests, no build and no lint at all. The only signal
# it produced was the daemon cross-compile, and only because it touched
# `crates/`. A PR that cannot go red is not a reviewed PR.
pull_request:
branches: [main]
branches: [main, feat/fp-cli]

concurrency:
group: ci-${{ github.ref }}
Expand Down Expand Up @@ -257,6 +263,217 @@ jobs:
if: steps.crates.outputs.present == 'true'
run: cargo test --workspace

# The `fp` CLI (PyPI: fp-cli). The only Python in this repo, and the only job that
# tests it — none of the bun/cargo jobs above look at fp-cli/ at all. It is matrixed
# across the Python versions pyproject.toml's requires-python advertises, because
# claiming >=3.10 and testing only one of them is how a 3.10 user finds the break.
fp-cli:
runs-on: ubuntu-latest
# a uv sync plus pytest across two interpreters; the bound exists so a stalled
# package mirror cannot hold a release for six hours (#726).
timeout-minutes: 10
defaults:
run:
working-directory: fp-cli
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13"]
steps:
- uses: actions/checkout@v7.0.1

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: fp-cli/uv.lock

- name: Install dependencies
run: uv sync --locked --extra dev --python ${{ matrix.python-version }}

# FP_CLI_REQUIRE_CONTRACT makes test_fp_home_contract.py FAIL rather than skip
# when it cannot read src/hooks/fp-home.ts. That file is the register for a
# home this CLI writes a credential into, and the assertions against it are
# the only thing keeping the two sides from drifting — before they existed, a
# rename on either side left both suites green. A guard that can quietly
# degrade to a skip is the same guard the SDK's spool contract used to be.
- name: Test
env:
FP_CLI_REQUIRE_CONTRACT: "1"
run: uv run pytest tests/ -q

- name: Build the distribution
run: uv build

# A stale `include = [...]` in [tool.setuptools.packages.find] builds a
# SUCCESSFUL but EMPTY wheel — pip installs it and the console script then
# ImportErrors. Nothing else in this job would notice, so assert the payload.
- name: Verify the wheel actually contains the package
run: |
python3 - <<'PY'
import glob, sys, zipfile
wheel = glob.glob("dist/*.whl")[0]
names = zipfile.ZipFile(wheel).namelist()
modules = [n for n in names if n.startswith("fp_cli/") and n.endswith(".py")]
print(f"{wheel}: {len(modules)} modules")
if len(modules) < 20:
sys.exit(f"wheel looks empty — only {len(modules)} modules under fp_cli/")
PY

# `fp` is the contract users type. Prove the entry point resolves from a clean
# install of the built artifact, not from the source tree.
- name: Smoke-test the installed console script
run: |
uv venv /tmp/fp-smoke
VIRTUAL_ENV=/tmp/fp-smoke uv pip install dist/*.whl
/tmp/fp-smoke/bin/fp --version
/tmp/fp-smoke/bin/fp help > /tmp/fp-help.txt
if grep -qi agenteye /tmp/fp-help.txt; then
echo '::error::retired product name present in the fp help output'
exit 1
fi

# The telemetry SDK (PyPI: failproofai-sdk, import failproofai_sdk). Second Python
# component, sibling of fp-cli above, and deliberately its own job: it shares no
# lockfile, no dependencies and no release cadence with the CLI.
#
# The matrix is wider than fp-cli's two versions on purpose. This package declares
# no dependencies at all, so it has no third-party floor quietly constraining which
# interpreters it is really exercised on — and it is installed into other people's
# agent processes, whose Python version we do not choose. Every version
# requires-python advertises is tested.
failproofai-sdk:
runs-on: ubuntu-latest
# the same, across five — every version requires-python advertises; the bound exists so a stalled
# package mirror cannot hold a release for six hours (#726).
timeout-minutes: 10
defaults:
run:
working-directory: sdk/python
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7.0.1

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock

- name: Install dependencies
run: uv sync --locked --extra dev --python ${{ matrix.python-version }}

# FAILPROOFAI_SDK_REQUIRE_CONTRACT makes test_spool_contract.py FAIL rather than
# skip when it cannot find crates/fpai-collect or src/hooks/fp-home.ts. Those
# assertions are the only check that this SDK writes where the daemon reads, and
# a guard that can silently degrade to a skip is not a guard — its predecessor in
# the agenteye repo skipped in every CI run for exactly this reason.
- name: Test
env:
FAILPROOFAI_SDK_REQUIRE_CONTRACT: "1"
run: uv run pytest tests/ -q

- name: Build the distribution
run: uv build

# A stale `include = [...]` in [tool.setuptools.packages.find] builds a
# SUCCESSFUL but EMPTY wheel — pip installs it and the import then fails.
# py.typed goes the same way if package-data stops naming it, and nothing
# else in this job would notice either. Assert the payload.
- name: Verify the wheel actually contains the package
run: |
python3 - <<'PY'
import glob, sys, zipfile
wheel = glob.glob("dist/*.whl")[0]
names = zipfile.ZipFile(wheel).namelist()
modules = [n for n in names if n.startswith("failproofai_sdk/") and n.endswith(".py")]
print(f"{wheel}: {len(modules)} modules")
if len(modules) < 7:
sys.exit(f"wheel looks empty — only {len(modules)} modules under failproofai_sdk/")
if not any(n.endswith("py.typed") for n in names):
sys.exit("py.typed is missing from the wheel — the type hints do not count without it")
if not any(n.endswith("LICENSE") for n in names):
sys.exit("LICENSE is missing from the wheel")
PY

# `--no-deps` is the assertion, not an optimisation. "Zero dependencies" is the
# reason this package is safe to drop into someone else's agent, so prove it
# against the built artifact rather than the source tree: install it with nothing
# else present, emit real events, and read them back off disk.
- name: Smoke-test the installed package with no dependencies
run: |
uv venv /tmp/sdk-smoke
VIRTUAL_ENV=/tmp/sdk-smoke uv pip install --no-deps dist/*.whl
AGENTEYE_HOME=/tmp/sdk-spool /tmp/sdk-smoke/bin/python -c "
import failproofai_sdk as s
print(s.__version__)
s.event.agent_start(session_id='smoke', agent_id='a', goal='ci')
s.event.tool_use(session_id='smoke', agent_id='a', tool_name='t', tool_call_id='c')
s.event.tool_result(session_id='smoke', agent_id='a', tool_name='t', tool_call_id='c', output='ok')
"
python3 - <<'PY'
import glob, json, sys
batches = glob.glob("/tmp/sdk-spool/events/*.jsonl")
if not batches:
sys.exit("the installed wheel wrote no event batch")
events = [json.loads(line) for p in batches for line in open(p) if line.strip()]
types = {e["type"] for e in events}
if types != {"agent_start", "tool_use", "tool_result"}:
sys.exit(f"unexpected events from the installed wheel: {sorted(types)}")
# Emitted by the artifact, so this also proves the wire format survived
# packaging rather than only surviving an in-tree import.
if not all("environment" in e for e in events):
sys.exit("an event reached disk without the `environment` field")
print(f"{len(events)} events written by the installed artifact")
PY

# The adapters' ONLY automated evidence. It is a separate job, not a leg of the
# matrix above, because installing five agent frameworks costs minutes and gains
# nothing from being repeated across five interpreters — the adapters bind to
# framework APIs, not to interpreter version.
#
# WHY THIS EXISTS: tests/integrations/* already honour
# AGENTEYE_TESTS_REQUIRE_FRAMEWORKS, and their comments say "CI leg sets
# AGENTEYE_TESTS_REQUIRE_FRAMEWORKS=1". No such leg was ever added, and the
# frameworks live in extras that `--extra dev` does not pull, so all four
# modules skipped at import in every run: 168 test functions across 6,071 lines,
# green, never executed. An adapter could break against a new framework release
# and nothing here would say so. That is the same "a guard that can silently
# degrade to a skip is not a guard" this file already hardened
# test_spool_contract.py against — the integration suites were simply missed.
failproofai-sdk-integrations:
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: sdk/python
steps:
- uses: actions/checkout@v7.0.1

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock

# Every framework extra. `--locked` keeps this honest: the lockfile already
# resolves all five, so a drift here fails rather than silently re-resolving.
- name: Install dependencies (all framework extras)
run: >-
uv sync --locked --extra dev
--extra langchain --extra langgraph --extra crewai
--extra llamaindex --extra pydantic-ai
--python 3.12

# AGENTEYE_TESTS_REQUIRE_FRAMEWORKS turns each module's import skip into a
# hard failure. Without it a botched install above would read as "4 skipped"
# and the job would pass having tested nothing — which is exactly the state
# this job was added to end.
- name: Test the framework adapters
env:
AGENTEYE_TESTS_REQUIRE_FRAMEWORKS: "1"
run: uv run pytest tests/integrations -q

test:
runs-on: ubuntu-latest
# The retry above nominally allows 3 attempts x 10 minutes. Capping the job
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/osv-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,19 @@ jobs:
id: scan
uses: google/osv-scanner-action/osv-scanner-action@f4cfcc01edc9c8b756a9b873b7a623ca674da51e # v2.3.8
with:
# `--config` is load-bearing, not tidiness. Without it the scanner
# resolves a config PER SCANNED FILE, relative to that file — so the
# root `osv-scanner.toml` governed `bun.lock` and `Cargo.lock` and
# reached neither `uv.lock`. An ignore written for a finding in
# `sdk/python/uv.lock` loaded, filtered nothing, and was reported as
# an "unused ignore" while the gate stayed red. Naming it once makes
# one allow-list authoritative for every lockfile below.
scan-args: |-
--config=osv-scanner.toml
--lockfile=bun.lock
--lockfile=Cargo.lock
--lockfile=fp-cli/uv.lock
--lockfile=sdk/python/uv.lock
# Only the schedule run notifies — nothing on main touched the lockfile,
# so nobody is watching it the way a PR author watches their own checks
# or a push failure shows up against the commit they just merged. Same
Expand Down
Loading