Elyra 0.9.41: Five Small Things That Make a Long Session Cheaper
Five changes in Elyra 0.9.41 that stop the Anthropic prompt cache from being thrown away, add /bug and /system, and make session resume close to instant.
The last two releases were about adding a new kind of model to Elyra. This one is about the model you already use, and the hundred small ways a long coding session leaks money and time without anyone noticing. None of the five changes here is dramatic on its own. Together they turn a three-hour session from something you babysit into something you can leave and come back to.
Let us go through them in the order you are most likely to feel them.
The cache you keep throwing away
Anthropic caches your prompt prefix: system prompt, tool definitions, conversation history. Read from cache, you pay a tenth of the input price. Rewrite it, you pay one and a quarter times. On a 100k-token session with Sonnet that is the difference between three cents and thirty-seven.
Two things silently threw that cache away in every previous Elyra release.
Toggling a tool
Anthropic's cache is hierarchical: tools, then system prompt, then messages. Change the tools block and everything below it is gone. Elyra let you toggle tools with /tools, extensions could call setActiveTools(), and each time it did exactly what you asked: removed the tool from the request. And with it, the entire cached conversation.
The fix is a distinction we should have had from the start. There is the set of tools the model may call, and the set of tools we send. They used to be the same thing. Now the sent set only grows within a session. Deactivate bash and its definition stays in the request and the system prompt; if the model tries to call it anyway, the agent blocks the call and says so:
Tool "bash" is currently disabled in this session. Do not call it again.
Active tools: read, grep.
Startup restrictions are unaffected. --no-tools and --tools read,grep still mean the model never sees the others, because they shape the initial set rather than toggling something mid-flight. If you prefer the old behaviour, "stableToolSet": false in your settings brings it back.
Going for coffee
The default Anthropic cache TTL is five minutes. Read the answer, think, type, and by the time you hit enter the cache is cold. Every pause over five minutes turned into a full rewrite.
Elyra now waits four minutes after a turn ends and sends a warm ping: the exact same request, plus a one-character user message, with max_tokens: 1. That read costs one cache read of the prefix and resets the clock. It does this up to twice per idle period, then gives up, because for a long break the one-hour TTL is the cheaper tool.
The ping never enters your conversation or session file. /session reports it separately:
Cost
Total: 0.4218
Cache warming: 2 pings, 0.0061
It only runs for Anthropic models with an API key. If you use a Claude subscription through OAuth, pings would burn rate-limit quota instead of dollars, so they are skipped. It only runs when the last request was at least 20k tokens; below that the maths does not work. And it is cancelled the instant you send a prompt, switch models, or abort. All of this lives under the cacheWarming setting.
One honest caveat: this shipped verified against Anthropic's documentation and our own test harness, not against a live billing statement. Watch your cacheRead numbers after a pause. If they are not what you expect, /bug is right there, which brings us to the next item.
Telling us when it breaks
There was no good way to report a problem from inside Elyra. You had to remember your version, your model, what you had been doing, and then go type it all into GitHub.
/bug edit tool fails on files with CRLF line endings
builds an issue body for you: Elyra version, runtime, OS, active model and thinking level, session message counts, the last eight tool calls, loaded extensions and any that failed to load, and the last error if the previous turn failed. You see the full preview first. Then you choose: open the prefilled GitHub form in your browser, copy the report to your clipboard, or cancel.
Before anything leaves your machine the report is redacted. Known key prefixes (sk-or-, sk-ant-, ghp_, AWS, Slack, Google), bearer tokens, KEY=value assignments, JWTs, and your home directory path are all masked. Nothing is sent until you submit on GitHub.
Changing the rules mid-game
Sometimes the instructions change halfway through. You switch to a release branch and nothing destructive should happen. The user starts writing in Norwegian. Tests must pass before every commit from now on.
The system prompt cannot help you here. It is fixed per request, and rewriting it means, again, throwing away the cache. What you want is an instruction that lands in the history at this point and applies from here on.
/system Do not touch files under vendor/ for the rest of this session
The instruction is persisted with the session, replayed on resume, summarized by compaction, and applies to every following turn. Extensions get the same thing:
elyra.on("tool_result", (event) => {
if (event.toolName === "bash" && /switched to branch 'release/.test(text(event))) {
elyra.sendSystemMessage(
"You are now on a release branch. Do not run migrations or force-push."
);
}
});
How it works under the hood is worth a paragraph, because it explains a design choice. No provider offers a portable mid-stream system role. OpenAI accepts developer messages; Anthropic, Bedrock, and Google accept only a single top-level system prompt. So the instruction travels as a user message wrapped in <system-reminder> tags, and the default system prompt tells the model to treat that wrapper with the same authority as the system prompt itself. It is the same approach Claude Code uses. It appends at the tail of history, so the cache stays warm.
Resuming without the wait
elyra --resume opens a picker of your past sessions. To build that picker, Elyra used to read every session file in the directory, parse every line, and concatenate the full text of every message, in case you wanted to search. With 47 sessions that took 150 milliseconds. Across all projects, 341 sessions, it took 1.2 seconds. Every time.
Now there is an index.json sidecar per sessions directory. Files whose modification time and size have not changed are served from the index; only new or changed files are parsed. Full message text is loaded the first time you type a search character, not at startup. Warm, the same 47 sessions list in about a millisecond and the 341 in 24.
--continue also stopped validating every file to find the newest one, and stopped parsing the chosen file twice.
The small fix
The image-model generator wrote unformatted output, and every npm publish regenerated it and left a whitespace-only diff in the working tree. It now emits what the formatter would have produced. This is the kind of thing that costs thirty seconds per release forever, and now it does not.
Upgrade
npm install -g @elyracode/coding-agent@latest
or
elyra update
Then, in a long Anthropic session, step away for five minutes, come back, and check /session. If you see two pings and a large cacheRead on the next turn, everything is working as designed. If you do not, you know the command.