deja

agent
Guvenlik Denetimi
Basarisiz
Health Uyari
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 7 GitHub stars
Code Basarisiz
  • spawnSync — Synchronous process spawning in bench/tasks/fix-cart/check.mjs
  • spawnSync — Synchronous process spawning in bench/tasks/rename-units/check.mjs
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Déjà vu for your coding agent: cuts ~30% of the input bill on real Claude Code sessions with cache-safe dedup and ski-rental context decay. Zero deps.

README.md

deja

ci
deja
runtime deps
license

Your coding agent pays for the same context over and over. deja stops that, and doesn't break your prompt cache doing it.

npx dejactx audit      # what did stale context cost you? (reads local history, sends nothing)
npx dejactx claude     # run Claude Code through deja

The npm package is dejactx, not deja. The deja package on npm is an unrelated project, so never run npx deja or npm i deja. Installing dejactx globally (npm i -g dejactx) gives you the deja command used below.

On 51 real Claude Code sessions (2,922 API requests), replayed request by request with exact prompt-cache billing:

strategy input bill average prompt
as it happened — 95.8k tokens
eager pruning (what context pruners do) −15.8% 56.8k
deja −30.7% 61.5k

It also means fewer forgetful moments. In this history, Claude Code auto-compacted its context 9 times, each time with about 137k tokens in context. With deja, the same moments would have held about 77k, so compaction (and the detail it throws away) comes much later.

(Full benchmarks and methodology.)

deja audit output: input bill cut by 30%, eager pruning only 14%

Run npx dejactx audit to get this table for your own history, and npx dejactx audit --share for a card you can post.


The problem nobody measures

Every API request an agent makes resends the entire conversation. A 400-line npm test log the agent glanced at once rides along in every request after that. In the sessions above, tool output alone was 1.3M tokens when counted once, but 76M tokens actually sent, because each result was resent about 58 times on average.

Prompt caching makes those resends cheaper (0.1× price), but not free. They also fill the context window, which triggers lossy auto-compaction sooner.

The obvious fix is to prune old tool output. It backfires. The prompt cache matches on an exact prefix, so editing an old message invalidates everything after it, and the whole tail is re-billed at 1.25×. Prune eagerly and you keep paying for cache rewrites. In the replay, eager pruning that waits 20 requests before pruning cost 22% more than doing nothing at all:

output counts as stale after eager pruning deja
5 requests −15.8% −30.7%
10 requests +0.2% −27.0%
20 requests +21.6% −21.9%

(Reproduce with npx dejactx audit --min-age 20.)

How deja works

deja is a local proxy between your agent and the Anthropic API (ANTHROPIC_BASE_URL). It has two engines.

1. Dedup: back-references that never touch the cache

Each new tool result is compressed against everything already visible in the conversation, LZ77-style, with the conversation itself as the dictionary. When the agent re-reads a file it just edited, only the changed lines go through:

⟪deja: 212 lines; 205 unchanged ones are shown as ⟨references⟩ to identical text still earlier in your context.⟫
  1-39	⟨same as Read src/app.ts lines 1-39⟩
    40	  const retries = 5;
    41	  const backoff = exponential(250);
 42-212	⟨same as Read src/app.ts lines 40-210⟩

The key property is that the rewrite of message k is a pure function of messages 0..k. Earlier messages come out byte-identical on every request, so the provider's prompt cache keeps hitting. Line-number gutters are matched separately from content, so an insertion that shifts every later line still matches.

Verified on the live API. Here's Claude Code 2.1 through deja, reading two files and then re-reading both. These are the usage numbers Anthropic returned:

request tool results deduped by deja cache read cache write
1 0 0 11,217 16,817
2 2 0 28,034 13,906
3 4 2 41,940 533

Request 3 read the entire previous prompt from cache (28,034 + 13,906 = 41,940). The only new tokens written were the two re-reads, which came through as references. The model answered correctly from the first reads. (Full verification log, including a live eviction test.)

2. Decay: cache-aware eviction (ski rental)

Stale tool output, and the bodies of files the agent wrote long ago, can be replaced with small stubs that say how to get the content back ("run the tool again", "read the file"). The real question is when to do it:

  • Keeping D stale tokens costs rent: D × 0.1 on every request.
  • Evicting them changes the prompt at position p, so the suffix S after p must be re-cached once. That's a purchase of 1.25 × (S − D) − 0.1 × S.

This is the classic ski-rental problem. deja pays rent until the rent paid equals the purchase price, then buys. In the textbook version (one item, a fixed purchase price) that rule is never worse than 2× the best schedule chosen in hindsight, without knowing how long the session will last. Real sessions have many items and a purchase price that moves, so deja doesn't claim a proof there; an end-to-end test checks the 2× bound on randomized sessions instead. And when you've been idle longer than the cache TTL, the cache is already gone, so eviction is free and deja does it immediately.

Evictions are sticky and persisted, so after an eviction the new prefix is stable and cacheable again.

How deja compares

approach what it does prompt cache the catch
auto-compaction (built into agents) summarises the conversation when the window fills rebuilt from scratch lossy, and only kicks in at the limit
Claude Code's unchanged re-read check (built in) answers a Read of an unchanged file with "unchanged since last read" preserved exact repeats of Read only: misses edited files, partial ranges and repeated Bash output; complementary to deja
Anthropic context editing (clear_tool_uses, server-side) clears the oldest tool results once the prompt passes a token threshold broken at the clear point you pick fixed thresholds (trigger, keep, clear_at_least) yourself; no idle-cache awareness or dedup; your agent must send the setting
eager context pruning drops old tool results as soon as they're old broken on every prune can cost more than doing nothing (see above)
TTL-only pruning prunes only after the cache has expired preserved misses every saving inside an active session
output filters (shorter test/build logs) shrink tool output before the agent sees it unaffected lossy at the source; complementary to deja
repo maps / code search tools help the agent read less unaffected complementary to deja
deja exact back-references and cost-modelled eviction preserved, or rebuilt only when it pays off stubs are lossy by design, but the content is re-creatable

Install and use

Requires Node 20+. Every command works two ways: npx dejactx <command> with nothing installed, or deja <command> after npm i -g dejactx.

# See what you'd save, from your local Claude Code history (~/.claude/projects)
npx dejactx audit
npx dejactx audit --since 7d --project my-app
npx dejactx audit --tune          # find the best --min-age for how *you* work

# Run Claude Code through deja: deja's flags go before `claude`, Claude Code's after it
npx dejactx claude
npx dejactx claude --resume
npx dejactx --min-age 10 claude --model opus

# Or run the proxy yourself and point any Anthropic-API agent at it
npx dejactx start            # http://127.0.0.1:8787
export ANTHROPIC_BASE_URL=http://127.0.0.1:8787

# Wrap any other agent
npx dejactx run -- aider --model sonnet

# What has deja saved so far?
npx dejactx stats

Live savings in Claude Code's status line

Install globally and let deja configure it:

npm i -g dejactx
deja setup statusline      # adds the statusLine entry to ~/.claude/settings.json (with a backup)

Sessions started with deja claude then show something like deja ↓45.6k tok · $0.42 · 3 stubbed at the bottom of the screen.

OpenAI-compatible agents

deja also understands the OpenAI Chat Completions format, so it works with Cline, Roo, OpenCode, aider, Continue, OpenRouter, vLLM or Ollama, anything that lets you set a base URL:

npx dejactx run --openai -- aider --model gpt-5           # wraps the agent
npx dejactx start --upstream https://openrouter.ai/api     # or point the agent's base URL at http://127.0.0.1:8787/v1

For providers with no prompt cache at all (most local models), add --uncached: every request is billed in full there, so stale output is evicted as soon as it goes stale. Replaying the same 51 sessions that way, deja cuts 41.3% of input tokens (deja audit --uncached).

flag meaning
--no-decay dedup only: never stub anything
--off forward requests untouched (for A/B comparisons)
--openai wrap an OpenAI-compatible agent (sets OPENAI_BASE_URL)
--uncached the upstream has no prompt cache: evict eagerly
--min-age <n> requests before output counts as stale (default 5)
--upstream <url> forward somewhere else (defaults to $ANTHROPIC_BASE_URL, so deja can sit in front of a corporate gateway)

Inside your own agent

Building on the Anthropic or OpenAI SDK, or the Claude Agent SDK's underlying client? Use deja in-process instead of as a proxy:

import Anthropic from "@anthropic-ai/sdk";
import { dejaFetch } from "dejactx";

const client = new Anthropic({ fetch: dejaFetch() });

dejaFetch rewrites Messages, Chat Completions and Responses API calls exactly as the proxy does, and passes everything else through. The engines (transformRequest, decide, CacheBiller, …) are exported too. See docs/api.md.

To send one request untouched, add the header x-deja: off. deja strips the header and forwards the original bytes. A running proxy also answers GET /deja/health with its upstream and running totals.

Safety

  • Fails open. If deja can't parse or transform a request, it forwards the original bytes.
  • No content is stored. The ledger (~/.deja/ledger.jsonl) records token counts only, and decay state records tool-use ids only. See docs/data.md for every field.
  • Nothing leaves your machine except the request your agent was already making.
  • Errors are never compressed. A failing tool result is always shown verbatim.
  • Everything is recoverable. References point at text still in context. Stubs only replace content the environment can recreate (re-run the command, re-read the file), and they keep the first and last lines.
  • No dependencies at runtime.

FAQ

Does this break prompt caching?
Dedup can't: it never edits earlier messages. Decay does edit them, but only when the ski-rental rule says the rewrite has paid for itself, or when the cache has already expired. The audit bills every strategy with an exact-prefix cache model, so any cache breakage would show up as cost.

Will the model get confused?
References are plain text the model reads like any other annotation, and they point at content that's still in context. Stubs tell the model exactly how to get the content back. Decay only touches output that has sat unused for several requests, never touches user answers, subagent reports or skills, and keeps a file's contents while the agent is still editing it. In the replay, a quarter of the outputs deja would stub were called again later anyway, without deja. deja stats tracks the same rate live, and npm run bench:quality runs real coding tasks with deja on and off and compares how many pass (details).

What about Claude Code's own compaction?
It still works. deja just pushes it further away. Compaction summaries are lossy, while deja's references are exact.

How accurate are the audit numbers?
They're estimates. Tokens come from a fast local counter that undercounts (0.71× the provider's figure on a live Claude Code prompt), so dollar amounts are conservative. Costs come from a model of Anthropic's prompt caching: 5-minute TTL, 1.25× writes, 0.1× reads, a 20k-token system prompt. The percentages compare strategies under the same estimator and cache model, which makes them the robust part. deja stats reports what the live proxy actually removed, alongside the provider's own cache numbers.

Does it work with Codex CLI?
Codex talks to the OpenAI Responses API, which deja supports. Point Codex's provider base URL at http://127.0.0.1:8787/v1 after deja start --upstream https://api.openai.com, or try deja run --openai -- codex.

Can I use it in CI?
Yes. See docs/ci.md for headless runs and GitHub Actions.

Something looks off.
See docs/troubleshooting.md, and run deja doctor.

Status and roadmap

deja is new (0.1). The engines are fuzz-tested, the cost model is checked against the hindsight optimum, and the proxy has been verified live against the Anthropic API. The audit numbers come from one developer's history, though, so run npx dejactx audit on yours and share the summary.

Next up:

  • deja audit for other agents' transcripts (Codex, Cline, OpenCode)
  • per-tool stub formats (git diff, package managers, test runners)
  • a calibrated token estimator, using the provider counts deja already records
  • an npm release

Development

npm install
npm test          # vitest
npm run build     # tsc → dist/
node dist/cli.js audit

The design, with the cost model and the ski-rental analysis, is in docs/design.md. The interesting code:

  • src/core/match.ts: the LZ77-style line matcher
  • src/anthropic/transform.ts: the prefix-deterministic request rewrite
  • src/decay/policy.ts: ski-rental eviction
  • src/audit/billing.ts: the exact-prefix prompt-cache billing model
  • src/audit/replay.ts: strategy replays over real transcripts

License

MIT

Yorumlar (0)

Sonuc bulunamadi