Metadata-Version: 2.5
Name: thalamus-memory
Version: 0.1.1
Summary: Federated graph memory for coding agents — with a trust model and a measured utility loop
Project-URL: Homepage, https://github.com/Ybx-jp/thalamus
Project-URL: Repository, https://github.com/Ybx-jp/thalamus
Project-URL: Issues, https://github.com/Ybx-jp/thalamus/issues
Author: Jackson Porter
License-Expression: MIT
License-File: LICENSE
Keywords: agents,evaluation,gremlin,knowledge-graph,llm,memory
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: fastmcp>=3.4.0
Requires-Dist: gremlinpython<3.8,>=3.7
Requires-Dist: pluggy>=1.5
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: claims-ledger>=0.0.4; extra == 'dev'
Requires-Dist: httpx>=0.28.1; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-xdist>=3.6; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: ty==0.0.74; extra == 'dev'
Provides-Extra: frames
Requires-Dist: numpy>=2.1; extra == 'frames'
Requires-Dist: pillow>=10.0; extra == 'frames'
Requires-Dist: scipy>=1.14; extra == 'frames'
Provides-Extra: nli
Requires-Dist: sentence-transformers>=3.3; extra == 'nli'
Requires-Dist: torch>=2.5; extra == 'nli'
Requires-Dist: transformers>=4.46; extra == 'nli'
Description-Content-Type: text/markdown

# Thalamus

Memory for coding agents that can show where each thing it remembers came from.

When a Claude Code, Cursor or codex session ends, Thalamus reads the transcript and
writes what happened into a graph database: the decisions and the reasons for them, the
problems and how they were fixed, and the work left unfinished. The next session starts
with the unfinished work in front of it and can search the rest.

Every stored item points back to the session or document it came from, and that
transcript or document is kept unedited outside the graph. Each item also records whose
word it rests on: yours, the agent's own reasoning, or a document the agent read. That
record is what lets you ask the agent why it believes something and get an answer you
can check.

What it does not show yet is that recalled memory makes an agent finish tasks better.
It shows that memory gets surfaced; the [status](https://github.com/Ybx-jp/thalamus#status) section says what would
settle the rest.

The name is from the brain: the thalamus relays nearly every signal bound for a
specialized region of the cortex, and gates what gets through.

```
session ends → transcript read → decisions, fixes and open work written to the graph
                                              ↓
                  next session starts with the open work and can search the rest
```

## How it works

The project has its own vocabulary, and every term below is defined in
[Concepts](https://github.com/Ybx-jp/thalamus/blob/master/docs/concepts.md). This section uses as little of it as it can.

### Memory writes itself when a session ends

You don't curate it. A hook reads the retained transcript and extracts claims (a
decision, a problem, a fix) and open threads (work the session did not finish). The
next session opens already knowing where you left off.

### Where an item came from decides how far it is trusted

Every item carries a trust tier: 0 for what you wrote down yourself, 1 for what an agent
observed or worked out in its own sessions, 2 for outside documents from sources you
approved, and 3 for outside content from anywhere else.

The data model defines an item's effective trust as the lowest tier anywhere in what it
was derived from, so an agent's summary of a paper is trusted like the
paper.<!-- (A0051, cites-as-live) -->
No code computes that rule across a whole chain of derivations yet.

What is enforced today is narrower. When a session fetched pages with the agent's web
tools, the claims that rest on those pages are stored at outside-document
trust.<!-- (A0052, cites-as-live) -->
The extractor marks them, and a mechanical check also catches a claim that repeats the
page's wording when the mark is missing. A page fetched through the shell, and a claim
that restates a page in different words, are not caught.

Recalled items come back quoted, with their source and tier attached: they inform the
agent and never instruct it.<!-- (A0054, cites-as-live) -->

### Each specialist keeps its own memory

Knowledge is split by domain. Each specialist, called an expert (a literature reviewer,
a quality engineer, an architect), has its own graph of curated documents plus its own
session history, and the pair is called a scope. A session runs as exactly one scope,
fixed when the session starts.<!-- (A0001, cites-as-live) -->
Asking another expert is a recorded consultation, never a search that happens to reach
across.<!-- (A0094, cites-as-live) -->

### A bad write is refused, not filtered later

One set of rules, the federation contract, is at once the data schema, the permission
system and the trust boundary. A write that breaks it is rejected when it is written,
rather than filtered out when it is read.<!-- (A0025, cites-as-live) -->

### You can see what memory cost

Every memory lookup is logged<!-- (A0097, cites-as-live) -->
and priced. `thalamus pulse`, from the `thalamus-measure` package, serves a live
dashboard of what retrieval actually cost.<!-- (A0085, cites-as-live) -->

## Packages

Thalamus is a core and five optional packages, each its own distribution with its own
version. A plugin adds its commands to `thalamus` when it is installed beside the core,
and a plugin that fails to load costs only its own
commands.<!-- (A0233, cites-as-live) -->

| Package | What it adds | Depends on |
|---|---|---|
| `thalamus-memory` | the core: distillation, recall, the MCP server, experts, pinning, consultations, and the trace tap that logs and prices every memory lookup. Imported as `thalamus`, run as `thalamus` | — |
| `thalamus-console` | rooms and the console: pinned sessions as tmux windows, messages between them, and a browser surface that works from a phone | the core, tmux |
| `thalamus-ingest` | curated ingestion: third-party documents into an expert's knowledge, gated by its manifest | the core |
| `thalamus-measure` | the eval loop's reports (`thalamus eval report`, `uses`, `pins`, …) and the `thalamus pulse` dashboard | the core |
| `thalamus-reflex` | the memory reflex: when a command in a Claude Code session fails, what the graph holds about its output is served into the session unasked | the core |
| `thalamus-arch` | a structural gate for any Python repository: its import graph against the layers it declares. `thalamus-arch`, or `thalamus arch` beside the core | nothing of thalamus |

A clone installs all of them. From PyPI, install the core and add the packages you want
to the same environment; a package that is not on PyPI yet installs from a clone with
`pip install ./packages/<package>`.
[CONTRIBUTING.md](https://github.com/Ybx-jp/thalamus/blob/master/CONTRIBUTING.md#releasing) says how each one is released.

## Quick start

Your graph starts empty and stays yours. Thalamus ships no seed graph, no export and
no fixture corpus: a graph is one operator's session history, so every install is
fresh, for everyone.

### Prerequisites

| | Why |
|---|---|
| **Docker** | runs the graph (Gremlin Server on TinkerGraph) |
| **Python ≥3.11** and [**uv**](https://docs.astral.sh/uv/) | the package and its CLI |
| **jq** | every hook parses its stdin with it; without it the hook layer dies silently |
| **A coding-agent CLI**: Claude Code (`claude`), Cursor (`agent`), codex (`codex`), or any mix | distillation shells out to it |

The console, in the `thalamus-console` plugin, additionally needs **tmux**: it runs
pinned sessions as tmux windows ([docs/console.md](https://github.com/Ybx-jp/thalamus/blob/master/docs/console.md)).

### Install

From a clone, with every package and the development tools:

```bash
git clone https://github.com/Ybx-jp/thalamus && cd thalamus

docker compose up -d           # the graph, on 127.0.0.1:8182; no licence, no account
uv sync --extra dev            # creates .venv with the core, every package and the CLI
uv run thalamus init           # wire your editor, then verify what it wired
```

The graph answers a few seconds after `up -d` returns, because Docker publishes the
port before the server finishes starting. `docker compose ps` says `healthy` when it is
actually serving.

Or from PyPI, the core and the packages you choose:

```bash
uv tool install thalamus-memory --with thalamus-arch   # or: pip install thalamus-memory
thalamus graph up              # the graph, from the compose file the core ships; waits until healthy
thalamus init
```

An installed core has no checkout. It finds its own hook scripts and skills, its hooks
run the CLI under the interpreter `thalamus init` records, and `init` copies two
example experts into `~/.thalamus/config/experts/`. The examples below write `uv run
thalamus`, which is the clone's spelling; with a PyPI install it is `thalamus`.

`thalamus init` installs at user scope, so the harness arms in every directory rather
than only inside this checkout. It wires every supported harness by default; use
`--harness claude`, `--harness cursor` or `--harness codex` for one. `--dry-run`
reports without writing, and `--check` re-verifies any time.

Because user scope means outside this checkout, it lists what it will write and asks
before writing: `~/.claude/settings.json`, `~/.cursor/hooks.json` and
`~/.codex/hooks.json`, `~/.claude.json` (the MCP server), plus skill symlinks and one
derived agent per expert. Pass `--yes` to skip the prompt in a script; a
non-interactive stdin declines rather than assumes. `thalamus init --uninstall` takes
all of it back out, removing only what it can prove it installed, and leaving your
graph, `~/.thalamus/` and the transcript archive alone.

### Then relaunch your editor

Hooks and the MCP server arm per process, so an already-running session picks up
nothing. Quit and reopen your editor; `/clear` is not enough.

A new session should greet you with a memory prompt and its pinned scope. From there,
memory builds itself. Distillation runs when a session ends, detached, and says nothing
in your terminal. So have a real session, quit the editor, and ask:

```bash
uv run thalamus status         # sessions in the graph, and the last distillation run
```

The count going from 0 to 1 is the confirmation that it works. `thalamus init --check`
answers the other half: whether the wiring that writes it is armed.

The full walkthrough, including what the first run looks like, is
[docs/getting-started.md](https://github.com/Ybx-jp/thalamus/blob/master/docs/getting-started.md).

## Upgrading

A clone upgrades with `git pull` then `uv sync --extra dev`; the sync is what installs
a package the pull added or moved. A PyPI install upgrades with `uv tool upgrade
thalamus-memory`, or `pip install -U` on each package. Run `thalamus init` again after
adding or removing a package that brings hooks, so they are wired or taken out.

One feature is experimental and off by default: frame themes, which render the
console's pane inside artwork, behind `thalamus console --frames PATH`. Without the
flag there are no controls and no key bindings, and no artwork ships.

## What's live

- **The graph.** A property graph (Apache TinkerPop / TinkerGraph) with ten node kinds:
  `Session`, `Claim`, `Thread`, `Source`, `Artifact`, `Chunk`, `Entity`, `Exchange`,
  `Trace` and `Agent`, every one carrying provenance and a
  scope.<!-- (A0086, cites-as-live) -->
  Orphans and contract violations are rejected at write time.
- **The evidence archive.** Memory is bootstrapped from retained session transcripts,
  held in an immutable content-addressed store outside the
  repo.<!-- (A0087, cites-as-live) -->
  The graph is a view over that log that can be rebuilt from it: re-extract, never
  migrate.
- **Curated ingestion**, in `thalamus-ingest`. An expert's document half is fed one
  document at a time.
  `thalamus ingest <url|path> --scope <expert>` refuses bytes whose serving origin the
  scope's manifest does not allowlist<!-- (A0088, cites-as-live) -->,
  retains what survives the gate in the archive, and on `--write` lands it as a `Source`
  with the text indexed as `Chunk` vertices beside the claims drawn from it.
  `--check` runs that same path and stops at the model call, reporting the host that
  actually served the bytes for no model spend.<!-- (A0089, cites-as-live) -->
  The bytes are retained either way; the graph is what `--write` gates.
- **Experts.** Each scope is declared by a manifest in `config/experts/` and
  nothing else.<!-- (A0004, cites-as-live) -->
  This repository's `config/` holds five, and an installed wheel ships two examples;
  write a YAML file and you have another.
- **Role boundaries.** Where a scope is defined by what it must not produce, its
  manifest declares a `write_boundary` and a PreToolUse hook enforces it against the
  file-editing tools.<!-- (A0091, cites-as-live) -->
  The shipped `qe` manifest is the worked example: it holds the adversarial suite and is
  denied `src/`, so the scope that asserts against an implementation cannot quietly
  repair it.<!-- (A0092, cites-as-live) -->
  The reverse denial is in the ownership table, so no other scope can soften what it
  asserts either.<!-- (A0093, cites-as-live) -->
- **Session pinning.** One OS process, one immutable pin. `thalamus pin <scope>`
  hands the terminal to a session pinned to that scope. The MCP server
  reads the scope from its environment at startup and no tool accepts a scope argument,
  so a model cannot widen its own view by asking. Nor by shelling out: the graph client
  reaches the whole graph<!-- (A0148, cites-as-live) -->,
  so a PreToolUse guard keeps a pinned session off it and on the tools that confine a
  read to the pin.
- **The console**, in `thalamus-console`, and the part that needs tmux: it runs pinned
  sessions as tmux windows so all of them are addressable from one place.
  `thalamus console`
  serves them to a browser: a tab per window, the live pane, a composer, and one tap
  to spawn an expert in a project. It installs as a PWA on a phone over a tailnet.
- **Consultations.** Cross-expert questions ride single-use tickets where minting the
  ticket is writing the exchange record, and answers must cite nodes inside the
  consulted scope.<!-- (A0094, cites-as-live) -->
  Beside it, `thalamus quick ask` forks an expert's live session rather than
  cold-starting one.<!-- (A0063, cites-as-live) -->
- **Rooms**, in `thalamus-console` with the console: a private set of live sessions
  whose members see and message each other and nobody else, enforced by a per-room
  config directory and an outbound guard.<!-- (A0096, cites-as-live) -->
- **The eval loop.** Every memory-tool call is logged as a `Trace` node, judged used or
  ignored against the retained transcript, and priced in injected
  tokens.<!-- (A0097, cites-as-live) -->
  The core writes those nodes; `thalamus-measure` reads them back as reports and the
  `pulse` dashboard. Above it, a counterfactual harness runs one task with memory on,
  off and degraded in a confined worktree, graded by an oracle whose rungs are
  validated against a mutant set before any run is scored. The harness is research
  instrumentation and lives in a private companion repository.
- **The memory reflex**, in `thalamus-reflex`. When a Bash call in a Claude Code session
  fails, a hook retrieves against the identifiers in its output and serves what the
  graph holds about them into the session, marked as unsolicited.
- **Trust enforcement, first pass.** The transcript-ingress floor down-tiers distilled
  claims that rest on pages fetched with the agent's web tools.

## Status

Built and running: the graph, the archive, curated ingestion, experts and pinning, the
console, consultations, rooms, the memory reflex, and the eval loop's trace,
attribution and cost layers.

In progress: counterfactual measurement at a scale that can settle whether recalled
memory changes task outcomes. What the instrument shows today is that memory gets
surfaced; that it improves results is not yet demonstrated, and saying so is part of
the design. Computing trust across whole derivation chains, closing the shell-fetch
and paraphrase gaps, and end-to-end audit chains come after.

Roadmap and open work live in
[GitHub issues and milestones](https://github.com/Ybx-jp/thalamus/issues).

## Documentation

| | |
|---|---|
| [Getting started](https://github.com/Ybx-jp/thalamus/blob/master/docs/getting-started.md) | Install, first run, and what each step should look like |
| [Concepts](https://github.com/Ybx-jp/thalamus/blob/master/docs/concepts.md) | Scopes, experts, the federation contract, trust tiers, distillation |
| [CLI reference](https://github.com/Ybx-jp/thalamus/blob/master/docs/cli.md) | Every command |
| [The console](https://github.com/Ybx-jp/thalamus/blob/master/docs/console.md) | Driving your pinned sessions from your phone: the PWA, reaching it off the box, keeping it up |
| [Contributing](https://github.com/Ybx-jp/thalamus/blob/master/CONTRIBUTING.md) | Tests, conventions, how the pieces fit |

## Layout

```
src/thalamus/
  substrate/   storage kernel: schema, Gremlin writer, Gremlin reader, query span tap
  contract/    the federation boundary: ontology, expert manifests, conformance
  archive/     immutable content-addressed store for retained evidence
  harness/     where it meets the agent: MCP server, hooks, skills, bootstrap
  plugins.py   the extension points a plugin package registers through
  eval/        trace tap, attribution, sync, cost: the live-serving half of the eval loop.
               The counterfactual harness (task battery, arms, oracle) is research
               instrumentation; it lives in the private thalamus-eval companion repo
  growth.py    what the memory store accumulates, and what nothing refers to: `thalamus growth`
packages/      the optional packages, each its own distribution: five plugins that depend
               on the core alone, and thalamus-arch, which depends on nothing of thalamus
  thalamus-arch/     the structural gate: a declared-policy import extractor, its metrics and the three arch gates. Depends on nothing of thalamus; `thalamus-arch`, or `thalamus arch` beside the core
  thalamus-console/  rooms and the console: tmux windows, `spawn`, `room`, `dispatch`, `ceremony`, `console`
  thalamus-ingest/   curated ingestion: `thalamus ingest`, `thalamus backfill-chunks`
  thalamus-measure/  the eval loop's reports and the dashboard: `eval report|uses|pins|withholding|gremlin|recipes|profile`, `thalamus pulse`
  thalamus-reflex/   the memory reflex: its two hooks, `thalamus reflex`, `eval reflex|vocabulary`
arch/          model.yaml, the committed architecture model the gates check against
config/        expert manifests
ledger/        the claims ledger: one entry per documented commitment, pinned by
               digest to the section of source that keeps it true
docs/          user documentation
docker/        the confinement image a counterfactual arm runs inside
tools/         frame-theme authoring scripts (`--extra frames`), the install-alone check
tests/         the suite, including `tests/qe/`, the adversarial suite owned by `qe`
```

## License

MIT. See [LICENSE](https://github.com/Ybx-jp/thalamus/blob/master/LICENSE).
