Skip to main content

ffwf-tau-jmfts

A JMFTS-backed session store for Tau, a programmable coding agent harness, plus the agent tools that search it.

Tau stores a conversation as a tree of entries. JMFTS — a retrieval appliance combining matryoshka embeddings, ColBERT-style late interaction, and BM25 hybrid search over PostgreSQL with pgvector — stores a corpus as a tree of documents. This package is the observation that these are the same shape: point tau --store jmfts at a JMFTS server and every entry in the session is a document, with the same id, the same parent links, and the same search machinery as anything else in the corpus. Memory is not a feature bolted onto the agent; the agent's history is simply kept somewhere that can already be searched.

Why it is a separate package

Because it is optional, and staying optional is the point. ffwf-tau-coding-agent does not depend on it, and tau_coding_agent never imports tau_jmfts at module scope — the store is resolved lazily, only when --store jmfts (or the equivalent config key) selects it. A default Tau install has no JMFTS in it at all. The dependency arrow points tau-jmfts → tau-agent-core, never tau-coding-agent → tau-jmfts.

Install

pip install ffwf-tau-jmfts

Python 3.11 or newer. Pulls in ffwf-tau-agent-core. If you are installing it for use with the tau command, pip install 'ffwf-tau-coding-agent[jmfts]' is the same thing spelled as an extra.

You also need a running JMFTS server — see https://github.com/jmccardle/jmfts.

Use it as a session store

// ~/.tau/config.json
{
  "session_store": {
    "backend": "jmfts",
    "url": "http://localhost:8100",   // or $JMFTS_API_URL
    "token": null,                    // or $JMFTS_API_TOKEN
    "parent_id": null,      // optional: host document for new conversation roots
    "index": "tau"          // optional: BM25 index to register roots into
  }
}
tau --store jmfts                          # per-run override
tau --export-session REF out.jsonl         # JMFTS subtree → JSONL, then exit
tau --import-session out.jsonl             # JSONL → JMFTS subtree, then exit

Each session becomes one tau:conversation root document, and each entry — user message, assistant message, tool call, compaction summary, branch marker — becomes one child with a tau: usetype. The entry payload lives in structured_content; the document's content is a plain-text projection so search has something to match. Writes pass auto_embed=False, so a conversational turn never waits on a GPU forward pass.

Three things fall out of the shared shape rather than being implemented:

  • Fork is subtree copy. Branching a conversation is the same operation as copying any document subtree, with cross-referencing entry fields remapped onto the new ids.
  • Scope is parent_id. JMFTS's subtree filter is, unchanged, "only hits in this conversation".
  • A session survives its file. The file store and the JMFTS store are interchangeable views of the same tree, and --import-session / --export-session move between them losslessly.

Give the agent recall

Two extensions ship in tau_jmfts.ext. τ loads extensions by file path — pass one to -e, or drop a copy into the ~/.tau/extensions discovery directory:

tau -e "$(python -c 'import tau_jmfts.ext.tools as m; print(m.__file__)')"
  • tools.py registers jmfts_search, jmfts_read, and jmfts_ingest. Recall is a tool call, not an injection. There is no hook that quietly prepends "relevant memories" to the prompt: when the agent wants to remember, it calls jmfts_search, and that call and its results become real toolCall and toolResult entries on the session path — persisted, visible in the transcript, forkable, and subject to compaction like everything else. You can read a session later and see exactly what the agent recalled and when.
  • enrich.py runs on session_shutdown: it embeds every substantive entry (chunking long ones first) and indexes the conversation root into a BM25 index. τ's write path never embeds, so this is the deferred half of that bargain. Both steps are idempotent and resumable from server state, so a pass that crashes halfway can simply run again.

The tools work regardless of which session store is active — a file-backed session can still search JMFTS. Only the scope="conversation" shorthand needs a JMFTS-backed session, and it says so rather than quietly searching everything.

Use the client directly

from tau_jmfts import JmftsClient, JmftsSessionLog

with JmftsClient("http://localhost:8100", token="...") as client:
    log = JmftsSessionLog.create(client, cwd=".", model="gpt-4o", backend="openai")
    for entry in log.entries():
        print(entry["type"])

JmftsClient is a thin synchronous httpx wrapper that raises JmftsError on any non-2xx response. JmftsSessionCatalog is the discovery side — list, resolve a ref, create, load, fork, delete. import_session and export_session are the JSONL round-trip.

What the seam refuses to do

Failures at this boundary are handled by refusal, not repair. A memory system that silently degrades is worse than one that stops.

  • A --store jmfts run with a missing URL, a bad token, or an unreachable server exits with an error at startup. There is no fall-back to file storage.
  • --session-dir combined with --store jmfts is a hard error, not a guess about which one you meant.
  • load() rejects a root that is not a well-formed tau:conversation, and raises if the entry sequence shows a second writer touched the tree.
  • Foreign documents filed under a conversation are tolerated and surfaced, but can never move the session's cursor — an out-of-band write cannot redirect where the next turn lands.
  • Forking raises on an unresolvable cross-reference rather than copying a dangling anchor.
  • jmfts_ingest refuses any usetype beginning with tau:. That namespace belongs to the store, and an agent must not be able to forge conversation entries into its own history.
  • --no-session uses an ephemeral in-memory log that never touches the server.

Docs

  • docs/JMFTS-INTEGRATION-PLAN.md — the design, and its delivery status.
  • docs/SESSION-TREE-IMPLEMENTATION.md — the entry algebra this store backs.

Repository: https://github.com/jmccardle/tau

The rest of Tau

Distribution Imports as What it is
ffwf-tau-llm tau_llm the provider and streaming layer
ffwf-tau-agent-core tau_agent_core the agent loop, tools, sessions, extensions
ffwf-tau-coding-agent tau_coding_agent the tau command and the Textual TUI

MIT © Fight Fire with Fire Robotics, LLC

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ffwf_tau_jmfts-0.9.2.tar.gz (71.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ffwf_tau_jmfts-0.9.2-py3-none-any.whl (51.3 kB view details)

Uploaded Python 3

File details

Details for the file ffwf_tau_jmfts-0.9.2.tar.gz.

File metadata

  • Download URL: ffwf_tau_jmfts-0.9.2.tar.gz
  • Upload date:
  • Size: 71.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.2

File hashes

Hashes for ffwf_tau_jmfts-0.9.2.tar.gz
Algorithm Hash digest
SHA256 13aa3a467629e367fc537fcb67d07b5ed84ba8fdacf5d118841b5173fd834795
MD5 548c13183e27b7cef7057a257f6f8ff4
BLAKE2b-256 f70c3fa4ff49bdc7d693688810bb784907b63a5889c7c19135926a372dbb3c88

See more details on using hashes here.

File details

Details for the file ffwf_tau_jmfts-0.9.2-py3-none-any.whl.

File metadata

  • Download URL: ffwf_tau_jmfts-0.9.2-py3-none-any.whl
  • Upload date:
  • Size: 51.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.2

File hashes

Hashes for ffwf_tau_jmfts-0.9.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b707e6da1c8ec57dc849c27405b32c0811d0bce9ff7de097f8710b00a084a65e
MD5 a417f97f1483f8b0ce5ce38134416025
BLAKE2b-256 bfe24314d78248ddf4958ee22f5b75eae624ea311e62d65bde32fe148b75bc73

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page