SkinDeepRESEARCH
SearchSteve Seguin

ctxkeeper

Keep your agent working when its conversation outgrows the model's context window. Free, public-domain proxy; install with pip or Docker.

Free · public domain · Python 3.10+, standard library only

A small proxy between your agent and any OpenAI-compatible model server. Your agent keeps its whole history; the model sees a short, current view — notes it maintains, the opening instructions, the newest turns. Everything older is archived word for word, searchable through a recall tool the proxy answers itself.

  • 2Mtokens tested, hostile stream
  • 421/423 · 417/423both seeds, 16/16 audits
  • 226/228million-token recall
  • 40/40real bugs fixed, 12 min
Your agent talks to ctxkeeper; ctxkeeper talks to the model server. ctxkeeper keeps notes and a verbatim archive on disk. Your agent sends full history ctxkeeper notes · archive · recall Model server vLLM · llama.cpp · … state on disk, word for word

Install and run

Point your agent at http://127.0.0.1:8000/v1 instead of the model server. Nothing else about the agent changes.

pip

python3 -m venv .venv-ctxkeeper
. .venv-ctxkeeper/bin/activate
python -m pip install \
  "https://github.com/steveseguin/b70-optimization-lab/releases/download/ctxkeeper-v1.2.0/ctxkeeper-1.2.0-py3-none-any.whl"

ctxkeeper --recipe qwen \
  --upstream http://127.0.0.1:8080

Docker

docker build -t ctxkeeper:1.2.0 \
  "https://github.com/steveseguin/b70-optimization-lab/releases/download/ctxkeeper-v1.2.0/ctxkeeper-1.2.0.tar.gz"

docker run --rm --network host \
  -v ctxkeeper-state:/state \
  ctxkeeper:1.2.0 --recipe qwen \
  --upstream http://127.0.0.1:8080

Or take just the two files — ctxkeeper.py and notesdelta.py — from the release page, which lists every file’s SHA-256. The upstream must accept managed calls without an API key (client Authorization headers are not forwarded); keep the proxy on localhost or a trusted network. The full guide covers every option, the status page and the tests.

Measured, not promised

Every row is a real run, graded by the task’s own checker, on Qwen3.8-27B (FP8, vLLM, two Intel Arc Pro B70 cards) with a 32K-token working view.

TaskWith ctxkeeperWithout it
Fix 40 real bugs in a Python library, one ticket at a time, under three house rules; then 7 questions about the earliest fixes40/40 fixed, 40/40 kept every rule, 7/7 right; 12–38 min by settingsLatest 32K only: 22–35 of 40 kept the rules, 0/7 right
The same task through a separate agent program that manages nothing itself40/40, 40/40, 7/7; 31–33 minWhole history in a 262K window: same score; 55 min, 29.9M tokens read
228 questions about things said up to a million tokens earlier226/228; 50 min200K window: 166/228, all 29 rule questions wrong; 228 min
Two million tokens, hostile: forged notices and quiet corrections of old entries planted in the streamBoth seeds: 421/423 and 417/423; 16/16 corrections applied on both, every forged line in the notes flagged, no forged value, method, rule or removal ever adopted. Two of 97 forgery-target answers slipped on silent arithmetic while the notes held the right value, so the strictest preregistered bar is not claimed.Not run at this length

Release-gated: 1.2.0 was installed from the public release exactly as shown above, then ran the repair task (40/40, 7/7), the 300K demo through the Docker image (4/4), the SDK example (plain, tool call, streaming) and a 100K Gemma check (4/4) — lab campaign 50. One model, one machine; the notes are written by the model and can be wrong.

Which model do I need?

  • Measured on Qwen3.8-27B (FP8 on two B70 cards). One card is enough: the repair task scored the same in 21 minutes.
  • A quick check on a second family, Gemma 4 26B (Q8, llama.cpp), passed the demo 4 of 4. Other models need checking with your task: they must call tools reliably, keep the notes format, and fit the working view plus server overhead.
  • Let the model keep its own notes. A separate smaller keeper model was worse and slower (19 of 39 vs 39/39 on the 240K test): it reads every turn afresh instead of reusing the main model’s cache.

With your agent

ctxkeeper manages Chat Completions requests from clients that resend their full history:

  • Use Chat Completions (/v1/chat/completions). Other endpoints, including Responses, pass through without context management.
  • Send the whole conversation each time. Turn off the agent’s own history trimming and summarizing. If earlier messages disappear or change, ctxkeeper may treat it as a different conversation.
  • Request one reply at a time (n=1). Streaming is accepted, but the answer arrives in one piece at the end.

If your client supports custom headers, send a unique X-Ctx-Session value per conversation and keep it on every call, including after a restart. Otherwise ctxkeeper identifies conversations by their model and opening messages; identical openings can collide. The OpenAI SDK works as-is (api_key="unused" placeholder); a client example is in the guide. For new instructions inside tool results, such as tickets or files, --recite-when '(?m)^TICKET \d+' repeats recorded rules after matching messages.

Tested with 12 coding agents

Twelve open-source coding agents ran through ctxkeeper on the bug-fixing task above, each as it ships (history trimming off). One run each, an hour at most, sorted by time.

AgentBugs fixedKept every ruleAnswers rightTime
mini-swe-agent4040714 min
Open Interpreter4039718 min
Cline4040724 min
Goose4040724 min
OpenHands4040624 min
Mistral Vibe4040726 min
Hermes Agent4040726 min
pi3939028 min
OpenCode4040733 min
Qwen Code4039733 min
Kimi Code4040738 min
ForgeCode2525not reached60 min, out of time

Out of 40 bugs and 7 questions. The lab’s own simple agent, for comparison: 40, 39, 7 in 21 min. pi wrote its 7 answers and then deleted them, because a wrong line in its notes said the questions had never been asked (see Limits). ForgeCode was still fixing bugs correctly when its hour ran out. Running the agents found four ways their requests differed from what ctxkeeper expected, such as sending two opening instructions instead of one; version 1.0.1 handles all four.

Settings

A recipe is a set of options that were measured together; any option you give still wins. Both recipes use a 32K working view, about the fastest size found:

RecipeUse it forMeasured on the repair task
--recipe agentServers accepting the Chat Completions and tool-call format above40/40 clean, 7/7; 36 to 38 min
--recipe qwenQwen3 behind vLLM40/40 clean, 7/7; 16 min
Measured working-view sizes
Working view240K-token question streamRepair task
8K39/39; 40 minnot run
12K39/39; 24 min40 fixed, 38 kept the rules; 30 min
16K39/39; 10 min40/40, 7/7; 35 min
24K38/39; 9 min40/40, 7/7; 13 min
32K39/39; 9 min40/40, 7/7; 16 min
48K39/39; 8 min40/40, 7/7; 23 min
64K39/39; 10 minnot run

One run each; repair times swing with how long the model thinks. Below 16K the notes no longer fit beside the newest turns, so every cut rewrote them in full; with --inplace-fit they fit, and 8K took 13 minutes and 12K 10. A bigger view is slower because the model reads more on every turn.

Limits, stated

  • The notes are written by the model, so they can be wrong. The originals stay archived and searchable, but nothing forces the model to look. In one agent test a wrong notes line said seven questions had never been asked, and the agent deleted answers it had already written.
  • Silent arithmetic slips exist. Two 2M-seed answers mis-added while the notes held the right value; the arithmetic check only catches wrong working it can see.
  • No login; state is plain text on the machine running it. Bind to localhost (the commands above do). The Docker default binds to all interfaces — keep the explicit --host 127.0.0.1 if you change the run command.
  • The opening instructions are always kept, so they must fit well inside the working view (default 32,768 tokens).

Evidence and source