The agent integrations Hugging Face maintains for funes —
claude, codex, hermes and pi — and the catalog funes add <id> installs them from.
Integrations maintained by their authors live wherever their authors put them;
COMMUNITY.md is where they are listed.
One directory per integration, each a bundle as funes's
integration contract
describes it: a manifest.json, a setup executable, whatever the integration needs beside them,
and its own tests under test/. What each one installs into its agent, which of its events it
hooks, and what it needs on the box is in its README; what they share is below.
hf-traces/ is the one directory that is not an integration. It converts Hugging Face agent-traces
datasets, and installs nothing.
Each bundle gives its agent the funes read tools — funes mcp registered as an MCP server, or
fronted by the extension where the agent has no MCP client — and drives one script of its own,
funes-index.sh, in two modes. Per turn, it converts the session that just changed into funes's
spool and runs funes index --harness <id>, then converts any other session changed since it last
ran, so a session whose own hook never fired — untrusted, timed out, a host that died mid-turn — is
caught at the next turn; the run is time-boxed, so a backlog fills in a bounded step per turn. At
the session boundaries, as funes-index.sh --publish and only with a memory bound, it converts,
indexes — waiting out a per-turn run's lock — and pushes; it runs at the session's end and again at
the next start, catching up whatever a missed end left behind. The conversion is the bundle's own
converter: a script for Claude Code and Codex, the extension or plugin itself for pi and Hermes. The
work runs detached, so a hook returns in well under a second and never blocks the turn or trips a
timeout. The memory rides in a file beside the scripts, so every hooks file is static.
| Bundle | Per turn | Publish, with a memory bound |
|---|---|---|
claude |
Stop, SubagentStop |
SessionEnd, SessionStart |
codex |
Stop |
SessionEnd, SessionStart |
hermes |
post_llm_call |
on_session_finalize, on_session_start |
pi |
turn_end |
session_shutdown, and session_start when the process is fresh |
At install, setup add converts the sessions the agent has already written. The claude and
codex setups share a mark with their hooks' sweeps, so a re-add reads only the sessions written
since the last of either, and leaves out each one whose last turn the local memory holds, as its
funes sessions row says (funes 1.9.0 and newer). Both keep those rows beside their scripts: a
session listed there and gone from the memory since has the whole history read again, and for
claude, a session the memory holds without the messages typed while Claude was working keeps
that numbering, and never gets them.
What funes guarantees around these — local-first indexing, one writer at a time, the secrets gate, the wrong-memory guard — is in funes's automation docs.
Indexing an agent's history on a machine that never ran the automation — an archive, a colleague's
export — is the bundle's converter into a directory of your own, then funes index on that
directory, as funes's add docs
describe. Each converter is its bundle's own, and emits the session and turn ids the hooks do.
<bundle> is ~/.funes/agents/<id> once funes add <id> has run on any machine, or <id>/ in
this checkout:
| Bundle | Converter |
|---|---|
claude |
<bundle>/claude-plugin/funes/convert <transcript.jsonl> [out.funes.jsonl] — needs jq; one argument prints to stdout |
codex |
<bundle>/codex-plugin/plugins/funes/convert <rollout.jsonl> [out.funes.jsonl] — needs jq; one argument prints to stdout |
pi |
node <bundle>/convert.mjs <session.jsonl | sessions-root> <out-dir> — a root converts every session under it |
hermes |
python3 <bundle>/plugin/convert.py <state.db> <out-dir> [session-id] — every session in the store unless one is named |
for t in /archive/.claude/projects/*/*.jsonl; do
~/.funes/agents/claude/claude-plugin/funes/convert "$t" ~/imports/"$(basename "$t" .jsonl)".funes.jsonl
done
funes index ~/importsThe Hub keeps a parquet copy of an agent-traces dataset on its refs/convert/parquet branch.
hf-traces/convert.py turns it into one turns file per session. It needs pyarrow, which uv run
installs from the script's own header:
hf download <org>/<repo> --repo-type dataset --revision refs/convert/parquet --local-dir traces
uv run hf-traces/convert.py traces ~/imports
funes index ~/importsIt maps a session as funes's own reader did when funes index <org>/<repo> read these datasets, so
a dataset a memory already holds keeps its turn ids and a re-import adds nothing. A session id that
is not a plain file name is reported and skipped.
A working copy installs into funes straight from the checkout:
funes add pi --from ./pifunes confirms at the terminal before running files it did not publish — once, until they change. Each bundle's tests run without a funes binary, against fakes:
sh pi/test/run.shThe hf-traces test needs pyarrow. PYTHON names the interpreter that has it:
PYTHON="uv run --with pyarrow python" sh hf-traces/test/run.shA release is an integration's directory packed as <id>.tar.gz, published under <id>/<version>/
in the huggingface/funes-integrations bucket with a SHA256SUMS beside it, and listed in the
bucket's catalog.json. Versions are the integration's own — version in its manifest,
MAJOR.MINOR.PATCH — independent of funes's, and a published version is never replaced.
- Bump
versionin<id>/manifest.jsonand merge. - Tag
<id>-v<version>—pi-v1.0.1— and push the tag, or run the Release workflow by hand with the id and version.
The workflow checks the manifest against the tag, runs the bundle's tests, packs and publishes the
archive, and adds the release to the catalog. It needs HF_FUNES_INTEGRATIONS_RELEASE_TOKEN: a token
that can write that bucket, and nothing else. Releases run one at a time, since the catalog is
read, amended and written back.
catalog.json, catalog_version 1:
{
"catalog_version": 1,
"integrations": {
"pi": {
"repo": "huggingface/funes-integrations",
"releases": [
{
"version": "1.0.0",
"contract_version": 1,
"url": "hf://buckets/huggingface/funes-integrations/pi/1.0.0/pi.tar.gz",
"sha256": "…"
}
]
}
}
}For the id it was asked for, funes takes the newest release whose contract_version is the one it
speaks, fetches the archive at url with the SHA256SUMS beside it, and refuses it unless its
digest is sha256 and its manifest declares version. Fields funes does not know are ignored.