Repository navigation
Releases: lastreload/mnemonic-proxy
Release list
v0.3.19
- Context fitting with fine-tunes that ship their own chat template: when the tokenizer directory contains
chat_template.jinja, the proxy now counts the prompt with that template (sandboxed Jinja,[exact]extra) instead
of the built-in Qwen rendering. A Tiel/Sharp setup was under-counting by a few hundred tokens and sent requests the
engine rejected (#28, #43, #44). Startup reportstemplate=packorbuiltin-qwen; on the stock Qwen3.8 pack the two
counts are identical. Prompts that leave no room to answer are refused locally; a recognised engine context
rejection is retried once with smaller output limits. Short stderr summaries of key events (window guard, clamps,
masking, segment switches) with request phase labels; client disconnects no longer print a BrokenPipe traceback.
Pack-template mode is text-only: requests with images are refused explicitly. - A single fresh tool result that leaves no room for an answer is now archived behind the usual
recallplaceholder
(only whenrecallis exposed and only if this restores a useful answer budget), instead of the request being
forwarded withmax_tokensclamped to 1. If nothing fits, the proxy returns an explicit context error (#40, #42). - Tests: zstd fixtures no longer depend on the interpreter's
sitecustomize(fixes 7 failures on Ubuntu with
Python 3.14) (#39, #41).
v0.3.18
A recall call written as text is no longer accepted as a final answer (#36, #38), and Hermes Agent is now tested (#37).
- When the search is closed the proxy removes its own tools; Strata parses only declared tools, so a model that still calls
recallreturned<tool_call><function=recall>…as plain text withfinish_reason: stop, and the proxy passed it on as a successful answer. Found during a 64K test on Strata 0.2.7. Now it goes through the same final-answer retry and explicit notice as an empty reply, in streaming and non-streaming. Prose, quoted examples and client tool calls are unchanged. Verified on CPU with the real response as a fixture; not yet re-measured on the real model. - New guide
docs/hermes-agent.md: Hermes Agent with a custom provider andcompression.enabled: false. Real session on Qwen3.8 Flash Next through a 32K proxy window: 18 turns, 24 tool calls, history up to 51K tokens, two masking rounds and one recall, 0 errors, 99.4% prefix reuse over the first 10 turns.
v0.3.17
An explicit --tokenizer that cannot be loaded now stops the proxy at startup (#32, #33).
Before, if --tokenizer was given but the optional tokenizers package was missing from the proxy's environment (or the file was invalid), the proxy silently fell back to the chars/3.5 estimate. The window guard then worked on an estimate while you believed it was exact; in one benchmark run the estimate was 2,484 tokens (9%) short and the request asked for 897 tokens more than the window allowed (the engine trimmed it with fit_max_tokens).
- Now startup fails with the original cause and the fix:
pip install "mnemonic-proxy[exact]"in the same interpreter, or drop--tokenizerto run on the estimate deliberately. - Running without
--tokenizeris unchanged (estimated, as before).
If you install with plain pip install mnemonic-proxy and pass --tokenizer, install the [exact] extra before upgrading.
v0.3.16
No more silent empty answers after a closed search (#30, #31).
When the recall search is closed (round limit or no room) and the model keeps emitting recall calls instead of answering, the proxy retried twice and then dropped the call, returning an empty completion with finish_reason: stop. Seen in 2 of 46 runs of the 0.3.13 A/B and 3 of 100 proxy runs of the AgentLongBench confirmation run, always with the evidence already delivered.
- The last retry now sets
tool_choice: "none", so the model has to answer in text. Tool schemas and earlier attempts are unchanged, to keep the prompt prefix stable. - If there is still no public text, the client gets an explicit notice (
[context manager: search closed; the model did not produce a final text answer…], also in streaming), plusstrata_context.final_answer_missing=trueand afinal_answer_missingjournal event. The proxy never turns private reasoning into an answer.
Verified with the fake engine (model that obeys, model that returns only whitespace, model that ignores tool_choice), streaming and non-streaming; not yet re-measured on a real model.
v0.3.15
Window guard fix for Strata / PolyStrata on small windows (#28, reported by @akohlsmith).
- Recent Strata/PolyStrata encode special-token spellings quoted inside message text (for example a literal
</think>in a file or log) as ordinary text, while the proxy counted each as one special token, even with the right tokenizer. The prompt was undercounted and a request could exceed the engine window (HTTP 400). The guard now adds a conservative reserve for these literals, in the single path shared by every upstream call (client requests, notes, seal, anchor, recall rounds, retries; streaming and not). A prompt that cannot be counted with a loaded tokenizer is refused before sending. - Oversized budgets on small windows are adapted at startup with a warning (
response_floorandnotes_max_tokensto at most window/4;anchor_min_tokensscaled below 128K), andcheckreports the same. A Strata window override larger than the engine's context is no longer honoured. - README: recommended 32K settings and the limits of local token-count guarantees.
Reproduced on CPU with a synthetic prompt (23,225 tokens + 9,765 output → 400 before; 9,435 output, within the window, after). The reporter's own prompt was not available, so this is the verified cause of that failure pattern, not proof that it explains every failure in that session.
v0.3.14
Typed receipts now work with Claude Code and Codex (#21, #22).
Until 0.3.11 the detailed receipts of hidden outputs (read range, line count and sha256; write/edit outcome; shell exit status and last lines; test summary) only recognised pi's tool names. With Claude Code (Read, Write, Edit, MultiEdit, Bash) and Codex (shell, exec_command, apply_patch) hidden outputs fell back to the older "Start: «…»" receipt. Now they get the typed one, with each client's argument names (file_path, old_string/new_string, command as a string or argv list, patch headers). Unknown tools keep the fallback; pi receipts are byte-identical to 0.3.11.
Measured on the Claude Code session from the README (CPU only, frozen archive): 81 of its 135 hidden outputs change from the fallback to the typed receipt (79 Read, 2 Edit), on average +9 tokens per receipt. This changes receipt text, not what is hidden; we have not measured its effect on answer quality with Claude Code.
Upgrade note: receipts are rebuilt on every request, so an ongoing Claude Code or Codex conversation re-reads its prompt once after upgrading (the engine cache no longer matches from the first changed receipt).
Tests: 572 OK (Python 3.14), 539 OK (Python 3.10); CI green on Linux and Windows.
v0.3.13
Typed receipts now work with Claude Code and Codex (#21, #22).
Until 0.3.11 the detailed receipts of hidden outputs (read range, line count and sha256; write/edit outcome; shell exit status and last lines; test summary) only recognised pi's tool names. With Claude Code (Read, Write, Edit, MultiEdit, Bash) and Codex (shell, exec_command, apply_patch) hidden outputs fell back to the older "Start: «…»" receipt. Now they get the typed one, with each client's argument names (file_path, old_string/new_string, command as a string or argv list, patch headers). Unknown tools keep the fallback; pi receipts are byte-identical to 0.3.11.
Measured on the Claude Code session from the README (CPU only, frozen archive): 81 of its 135 hidden outputs change from the fallback to the typed receipt (79 Read, 2 Edit), on average +9 tokens per receipt. This changes receipt text, not what is hidden; we have not measured its effect on answer quality with Claude Code.
Upgrade note: receipts are rebuilt on every request, so an ongoing Claude Code or Codex conversation re-reads its prompt once after upgrading (the engine cache no longer matches from the first changed receipt).
Tests: 572 OK (Python 3.14), 539 OK (Python 3.10); CI green on Linux and Windows.
v0.3.12
Typed receipts now work with Claude Code and Codex (#21, #22).
Until 0.3.11 the detailed receipts of hidden outputs (read range, line count and sha256; write/edit outcome; shell exit status and last lines; test summary) only recognised pi's tool names. With Claude Code (Read, Write, Edit, MultiEdit, Bash) and Codex (shell, exec_command, apply_patch) hidden outputs fell back to the older "Start: «…»" receipt. Now they get the typed one, with each client's argument names (file_path, old_string/new_string, command as a string or argv list, patch headers). Unknown tools keep the fallback; pi receipts are byte-identical to 0.3.11.
Measured on the Claude Code session from the README (CPU only, frozen archive): 81 of its 135 hidden outputs change from the fallback to the typed receipt (79 Read, 2 Edit), on average +9 tokens per receipt. This changes receipt text, not what is hidden; we have not measured its effect on answer quality with Claude Code.
Upgrade note: receipts are rebuilt on every request, so an ongoing Claude Code or Codex conversation re-reads its prompt once after upgrading (the engine cache no longer matches from the first changed receipt).
Tests: 572 OK (Python 3.14), 539 OK (Python 3.10); CI green on Linux and Windows.
v0.3.11
New: mnemonic-proxy forget, delete one conversation in one step (#19, #20). Suggested by an independent review of the project on Radyz.cz.
mnemonic-proxy forget --data ./data --list
mnemonic-proxy forget --data ./data --config cfg.json --conv <id> --dry-run
mnemonic-proxy forget --data ./data --config cfg.json --conv <id>
- Removes the conversation's rows from every table with a
convcolumn (read from the schema), itskventries and the structured-recall index (rebuilt on demand); rebuilds FTS and vacuums. - Deletes its saved-state files and prompt sidecars, its
kv-archivemanifests and unreferenced blocks, its lines injournal.jsonl(atomic rewrite) and matching live/router metadata. --dry-runreports rows, files and bytes and changes nothing; it refuses when saved-state records exist but the state folder is unknown; non-zero exit if anything could not be removed.- The proxy now holds a lock on its data folder while it runs, so
forget(or a second proxy) on the same folder is refused. Proxies older than 0.3.11 do not take the lock: stop them first. - Not a forensic wipe: SSD remanence, snapshots, backups and the engine's memory are out of scope.
Checked on a copy of a real archive (9 conversations): the dry run left every file byte-identical; after forget, no row of the deleted id in any table, no FTS hit for words unique to it, no journal line; the other conversation's archive rows identical and recallable by id. Test suite: 564 tests OK.
v0.3.10
check --live now works on a freshly started Strata 0.2.x engine (#17, #18).
- Before: on a fresh Strata 0.2.x engine the check stopped with
could not save the current slot first: HTTP 400 no complete session to save(NOT READY), although saved state worked. - Now: when the engine refuses the backup because the slot is empty, the check skips the backup and the final restore, says so, and still verifies save/restore after generation. Other backup errors still stop the check before it touches the engine.
- The slot's
n_prompt_tokensis deliberately not used to detect an empty slot: Strata 0.2.x reports 0 right after a restore although the slot holds the restored session, so trusting it would overwrite that session. - The short Strata generation probe runs with thinking disabled, so it returns a visible reply.
Verified on Strata 0.2.6 (Qwen engine 0.1.50), Qwen3.8 Flash Next IQ3_XXS, RTX 4070 Ti 12 GB: READY on a freshly started engine; on a just-restored slot READY and the session kept (853 of 858 prompt tokens served from cache afterwards). Test suite: 549 tests OK.
No change to the proxy's runtime behaviour.