From 9e733096b2e5abf513b8e23bbc6c79318a9cd63b Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 27 Mar 2026 15:00:12 -0400 Subject: [PATCH] feat: add Codex CLI memory cookbook example --- applications/codex-memory/.gitignore | 3 + applications/codex-memory/README.md | 199 +++++++++++++++++++++ applications/codex-memory/requirements.txt | 1 + applications/codex-memory/seed_memory.py | 75 ++++++++ 4 files changed, 278 insertions(+) create mode 100644 applications/codex-memory/.gitignore create mode 100644 applications/codex-memory/README.md create mode 100644 applications/codex-memory/requirements.txt create mode 100644 applications/codex-memory/seed_memory.py diff --git a/applications/codex-memory/.gitignore b/applications/codex-memory/.gitignore new file mode 100644 index 0000000..b6cf5f0 --- /dev/null +++ b/applications/codex-memory/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +*.pyc +.env diff --git a/applications/codex-memory/README.md b/applications/codex-memory/README.md new file mode 100644 index 0000000..3ed0b51 --- /dev/null +++ b/applications/codex-memory/README.md @@ -0,0 +1,199 @@ +--- +description: "Give OpenAI Codex CLI persistent memory across sessions using Hindsight hooks" +tags: { sdk: "hindsight-codex", topic: "Agents" } +--- + +# Codex Memory + +Give [OpenAI Codex CLI](https://github.com/openai/codex) persistent memory across sessions using Hindsight. Three Python hook scripts automatically recall relevant context before each prompt and store conversations after each turn — no changes to your Codex workflow required. + +## What This Demonstrates + +- **Automatic recall** — Hindsight injects relevant memories before every Codex prompt via `UserPromptSubmit` hook +- **Session retention** — conversations are stored to Hindsight after each turn via `Stop` hook +- **Cross-session continuity** — Codex remembers project context, preferences, and decisions between separate sessions +- **Per-project isolation** — optional dynamic bank IDs keep memory scoped to each repository +- **Zero workflow changes** — install once; memory works silently in the background + +## Architecture + +``` +┌─────────────────────────────────────────────────────┐ +│ OpenAI Codex CLI │ +│ │ +│ SessionStart ──→ session_start.py ──→ warm up │ +│ │ +│ UserPromptSubmit │ +│ │ │ +│ ▼ │ +│ recall.py ──→ Hindsight API ──→ inject memories │ +│ │ │ │ +│ │ [relevant facts from past sessions] │ +│ ▼ │ +│ Codex sees prompt + memory context │ +│ │ │ +│ ▼ (after response) │ +│ Stop hook │ +│ │ │ +│ ▼ │ +│ retain.py ──→ Hindsight API ──→ store session │ +└─────────────────────────────────────────────────────┘ +``` + +## Prerequisites + +1. **OpenAI Codex CLI v0.116.0+** — hooks were added in this release + ```bash + npm install -g @openai/codex + codex --version + ``` + +2. **Python 3.9+** + ```bash + python3 --version + ``` + +3. **Hindsight** — cloud or local + - [Hindsight Cloud](https://hindsight.vectorize.io) (sign up for an API key) + - Or run locally: `docker run -p 9077:9077 ghcr.io/vectorize-io/hindsight:latest` + +## Quick Start + +### 1. Install the Hindsight Codex plugin + +```bash +git clone https://github.com/vectorize-io/hindsight.git +cd hindsight/hindsight-integrations/codex +./install.sh +``` + +This copies hook scripts to `~/.hindsight/codex/scripts/`, writes `~/.codex/hooks.json`, and enables `codex_hooks = true` in `~/.codex/config.toml`. + +### 2. Configure your connection + +Create `~/.hindsight/codex.json`: + +```json +{ + "hindsightApiUrl": "https://api.hindsight.vectorize.io", + "hindsightApiToken": "hsk_your_token_here", + "bankId": "codex" +} +``` + +For a local Hindsight instance, set `"hindsightApiUrl": "http://localhost:9077"` and omit the token. + +### 3. (Optional) Seed your memory bank + +To see recall working immediately without waiting for sessions to build up: + +```bash +pip install -r requirements.txt + +# Point to your Hindsight instance +export HINDSIGHT_URL=https://api.hindsight.vectorize.io +export HINDSIGHT_API_KEY=hsk_your_token_here + +python seed_memory.py +``` + +### 4. Start Codex + +```bash +codex +``` + +Ask something like `what testing framework do I prefer?` — Hindsight will inject the relevant context before your prompt reaches the model. + +## How It Works + +### SessionStart hook — `session_start.py` + +Runs when Codex starts. In daemon mode, this warms up the local Hindsight server so the first recall doesn't have a cold-start delay. In cloud mode, it verifies the connection is reachable. + +### UserPromptSubmit hook — `recall.py` + +Runs before every user message. Queries Hindsight with the current prompt (and optionally prior context turns), retrieves the most relevant memories, and injects them as `additionalContext`: + +``` + +Relevant memories from past conversations (prioritize recent when conflicting)... +Current time - 2026-03-27 18:00 + +- User prefers pytest for Python testing [experience] (2026-03-20) +- Current project uses FastAPI + PostgreSQL [world] (2026-03-25) + +``` + +Codex sees this context as part of the conversation before generating a response. + +### Stop hook — `retain.py` + +Runs after every Codex response. Reads the session transcript, strips previously injected memory tags (to prevent feedback loops), and POSTs the conversation to Hindsight. The session ID is used as the document ID, so re-running the same session upserts rather than duplicates. + +## Configuration + +Edit `~/.hindsight/codex.json` to override defaults: + +| Key | Default | Description | +|-----|---------|-------------| +| `hindsightApiUrl` | `""` | Hindsight server URL (required) | +| `hindsightApiToken` | `null` | API token for Hindsight Cloud | +| `bankId` | `"codex"` | Memory bank name | +| `retainEveryNTurns` | `10` | Store memory every N turns (use `1` while testing) | +| `autoRecall` | `true` | Inject memories before each prompt | +| `autoRetain` | `true` | Store conversations after each turn | +| `dynamicBankId` | `false` | Use separate bank per project/directory | +| `dynamicBankGranularity` | `["agent", "project"]` | How to partition dynamic banks | +| `recallBudget` | `"mid"` | Token budget for recall: `"low"`, `"mid"`, `"high"` | +| `debug` | `false` | Print debug output to stderr | + +Environment variable overrides are also supported: `HINDSIGHT_API_URL`, `HINDSIGHT_API_TOKEN`, `HINDSIGHT_BANK_ID`, `HINDSIGHT_DEBUG`. + +## Core Files + +| File | Description | +|------|-------------| +| `seed_memory.py` | Seeds sample developer facts for demo purposes | +| `requirements.txt` | Python dependencies for seed script | +| `~/.hindsight/codex/scripts/recall.py` | UserPromptSubmit hook — retrieves and injects memories | +| `~/.hindsight/codex/scripts/retain.py` | Stop hook — stores conversation to Hindsight | +| `~/.hindsight/codex/scripts/session_start.py` | SessionStart hook — warms up server | +| `~/.hindsight/codex.json` | Your personal config overrides | + +## Common Issues + +**Hooks not firing** + +Check that `~/.codex/config.toml` contains: +```toml +[features] +codex_hooks = true +``` + +Re-run `install.sh` to write this automatically. + +**No memories recalled on the first session** + +Recall only returns results after something has been retained. Either: +- Run `seed_memory.py` to pre-populate facts, or +- Complete one session (Codex responds to at least one message) and then start a new session + +**Memory not being stored** + +`retainEveryNTurns` defaults to 10 — retain only fires every 10 turns. While testing, add `"retainEveryNTurns": 1` to `~/.hindsight/codex.json`. + +**Debug mode** + +Add `"debug": true` to `~/.hindsight/codex.json` to see what Hindsight is doing on each turn: +``` +[Hindsight] Recalling from bank 'codex', query length: 42 +[Hindsight] Injecting 3 memories +[Hindsight] Retaining to bank 'codex', doc 'sess-abc123', 2 messages, 847 chars +``` + +## Built With + +- [OpenAI Codex CLI](https://github.com/openai/codex) — AI coding agent +- [Hindsight](https://hindsight.vectorize.io) — long-term memory for AI agents +- [Hindsight Codex plugin](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/codex) — hook scripts and installer diff --git a/applications/codex-memory/requirements.txt b/applications/codex-memory/requirements.txt new file mode 100644 index 0000000..5a55b53 --- /dev/null +++ b/applications/codex-memory/requirements.txt @@ -0,0 +1 @@ +hindsight-client>=0.4.19 diff --git a/applications/codex-memory/seed_memory.py b/applications/codex-memory/seed_memory.py new file mode 100644 index 0000000..e74ff1b --- /dev/null +++ b/applications/codex-memory/seed_memory.py @@ -0,0 +1,75 @@ +"""Seed sample developer facts into Hindsight for the Codex memory demo. + +Run this once before starting Codex to pre-populate your memory bank. +Then ask Codex questions like "what testing framework do I prefer?" to +see Hindsight recall the right context automatically. + +Usage: + python seed_memory.py # seed default facts + python seed_memory.py --reset # clear the bank first, then seed + +Prerequisites: + - pip install -r requirements.txt + - Hindsight running locally, or set HINDSIGHT_URL to your cloud endpoint + - Set HINDSIGHT_API_KEY if using Hindsight Cloud +""" + +import asyncio +import os +import sys + +from hindsight_client import Hindsight + +BANK_ID = os.environ.get("BANK_ID", "codex") +HINDSIGHT_URL = os.environ.get("HINDSIGHT_URL", "http://localhost:9077") +HINDSIGHT_API_KEY = os.environ.get("HINDSIGHT_API_KEY") + +SAMPLE_FACTS = [ + "User prefers Python for scripting and TypeScript for web applications", + "User is currently migrating a monolith to microservices using Docker and Kubernetes", + "User's primary editor is VS Code with Vim keybindings enabled", + "User always writes tests before submitting PRs (test-driven development)", + "Preferred testing frameworks: pytest for Python, Jest for TypeScript/JavaScript", + "User's team uses GitHub Actions for CI/CD pipelines", + "User prefers functional programming patterns over object-oriented where possible", + "Current project stack: FastAPI backend, React frontend, PostgreSQL database", +] + + +async def reset_bank(client: Hindsight) -> None: + try: + await client.adelete_bank(bank_id=BANK_ID) + print(f"Cleared memory bank '{BANK_ID}'") + except Exception: + pass # Bank may not exist yet + + +async def main() -> None: + client_kwargs = {"base_url": HINDSIGHT_URL, "timeout": 30.0} + if HINDSIGHT_API_KEY: + client_kwargs["api_key"] = HINDSIGHT_API_KEY + + client = Hindsight(**client_kwargs) + + if "--reset" in sys.argv: + await reset_bank(client) + + content = "\n".join(f"- {fact}" for fact in SAMPLE_FACTS) + await client.aretain( + bank_id=BANK_ID, + content=content, + document_id="seed-demo", + ) + + print(f"Seeded {len(SAMPLE_FACTS)} developer facts into bank '{BANK_ID}'") + print() + print("Now start Codex and try asking:") + print(" - 'what testing framework do I prefer?'") + print(" - 'what's my current project stack?'") + print(" - 'how does my team handle CI/CD?'") + print() + print("Hindsight will inject the relevant context before each prompt.") + + +if __name__ == "__main__": + asyncio.run(main())