<p>Most of what a coding agent does is not writing code. It is deciding. Is this log line worth reading? Is this command about to delete something I care about? Which of these forty files actually matter for the bug I am chasing? Today those decisions all go through the same expensive model that writes your code, one paragraph of reasoning at a time.</p><p>Elyra 0.9.39 adds a different kind of model for a different kind of question. It also fixes a bug that quietly ate four models from the registry, and closes a small but annoying gap in the release process. Let us start with the interesting part.</p><h2>Meet Jev</h2><p>TypeSafe Jev is a decision model. It does not write prose. You hand it a piece of content and a typed question, and it hands back a probability. A yes/no question comes back as <code>0.97</code>. A multiple-choice question comes back as a distribution over the options. A “rate this on a scale” question comes back as an expected value plus the full spread.</p><p>It is fast (about a second) and cheap (around $0.00002 per call, which is roughly two cents per thousand). Input is billed at $0.042 per million tokens and output is free, because there is no output to speak of.</p><p>Our first instinct was to add it to the model list like any other provider. That turned out to be the wrong shape. Jev does not speak the chat-completions protocol. It has no messages, no tool calls, no streaming text. OpenRouter exposes it through a separate decisions endpoint with a request that looks like this:</p><pre><code class="language-json">{
  "model": "~typesafe/jev-latest",
  "state": "rm -rf node_modules dist",
  "questions": {
    "destructive": {
      "type": "noul",
      "instructions": "Would running this shell command destroy data or make a change that is hard to reverse?"
    }
  }
}
</code></pre><p>So instead of a model you can switch to, Jev arrives in Elyra as a tool the main model can call. The package is <code>@elyracode/jev-tools</code>.</p><pre><code class="language-bash">elyra install npm:@elyracode/jev-tools
</code></pre><p>It reuses whatever OpenRouter key you already have. If you have run <code>/login</code> and picked OpenRouter, you are done.</p><h2>The decide tool</h2><p>The tool takes a state and a list of questions. Three question types are supported.</p><p><code>noul</code> is yes/no with a probability. The name is odd but it is what the API calls it.</p><pre><code class="language-json">{
  "name": "refund",
  "type": "noul",
  "instructions": "Is the customer asking for their money back?",
  "criteria": {
    "true": "explicitly requests a refund, chargeback, or return of payment",
    "false": "complains, asks for help, or anything else"
  }
}
</code></pre><p><code>choice</code> picks one option and returns the full distribution, so you can see how confident it was.</p><pre><code class="language-json">{
  "name": "route",
  "type": "choice",
  "instructions": "Which kind of work does this request need?",
  "criteria": {
    "code": "write or change code in the repository",
    "research": "look something up on the web",
    "question": "answer from existing knowledge",
    "other": "none of the above"
  }
}
</code></pre><p><code>score</code> rates on an ordinal scale of two to ten labels, low to high.</p><pre><code class="language-json">{
  "name": "severity",
  "type": "score",
  "instructions": "How serious is this log line?",
  "criteria": ["debug", "info", "warning", "error", "critical"]
}
</code></pre><p>You can ask several questions about the same state in a single call, and it costs the same as asking one. The result comes back readable:</p><pre><code class="language-text">refund: yes (p=0.97)
route: code (p=0.81, conf=0.70)
  code=0.81, research=0.15, question=0.03, other=0.01
severity: 2.60/4 -&gt; error (conf=0.40)
  debug=0.05, info=0.10, warning=0.25, error=0.50, critical=0.10

[typesafe/jev-1.13 · 120 tok · $0.000005]
</code></pre><h2>What it is good for</h2><p>The pattern is: many small judgements, where spending a paragraph of frontier-model reasoning on each one would be wasteful.</p><p><strong>Triage.</strong> You have a hundred failing test names and want the ones that look like real regressions rather than flakes. Ask the agent to run them through <code>decide</code> with a <code>noul</code> question and only read the ones above 0.7.</p><p><strong>Filtering.</strong> <code>tail -f app.log</code> is too noisy. Ask the agent to score each line for “user-visible failure” and surface the top of the scale.</p><p><strong>Routing.</strong> Before deciding whether to search the web, read the codebase, or just answer, the agent can ask Jev which kind of request this is. The answer arrives before the main model has finished thinking about it.</p><p><strong>Classification at scale.</strong> Labelling issues, sorting feedback, tagging commits by Conventional Commit type. Jev handles a thousand of these for two cents.</p><h2>What it is not good for</h2><p>Anything that needs generated text. Jev filters and ranks; it does not summarize or explain. Its context window is about 32K tokens, so hand it the paragraph, not the whole file. And it reads your criteria literally. If you ask “does this contain actionable information” it will say yes to a spam message offering a free course, because a free course is, technically, actionable. Spell out both sides.</p><h2>The bash gate</h2><p>The example we kept coming back to during development was destructive shell commands. The agent runs <code>git reset --hard</code> or <code>rm -rf</code> and you find out afterwards. Existing guards are regex lists, which catch <code>rm -rf</code> and miss <code>find . -delete</code>, <code>truncate</code>, <code>DROP TABLE</code>, and <code>git push --force</code> to a shared branch.</p><p>So jev-tools ships an opt-in gate. When enabled, every bash call is first sent to Jev with the question above. The command and the working directory are the state; the criteria list what counts as destructive (deletes, drops, force-pushes, discarding uncommitted work, broad permission changes, running as root) and what does not (reads, builds, tests, installs, anything git or a package manager can undo).</p><p>If the probability clears the threshold, Elyra asks:</p><pre><code class="language-text">Jev: 92% likely destructive

  git push --force origin main

Run this command?
</code></pre><p>In print or RPC mode, where there is nobody to ask, the command is blocked instead.</p><p>Turn it on with <code>elyra --jev-gate</code>, <code>JEV_BASH_GATE=1</code>, or <code>/jev gate on</code> mid-session. The threshold defaults to 0.7 and is tunable with <code>JEV_BASH_GATE_THRESHOLD</code>. It is off by default because it adds about a second to every shell command, and on a build-and-test loop that adds up.</p><p>Two design choices are worth spelling out. First, the gate fails open. If the API is down or the key is missing, it logs a warning and lets the command through. Blocking all shell access because a classifier is unreachable would be worse than the problem it solves. Second, and for the same reason, this is a second opinion, not a security boundary. Treat it like a colleague glancing over your shoulder, not like a sandbox.</p><h2>The four models that went missing</h2><p>The other change in this release is a fix. Elyra generates its model registry from three upstream sources, one of which is <a target="_blank" rel="noopener noreferrer nofollow" href="http://models.dev">models.dev</a>. This week <a target="_blank" rel="noopener noreferrer nofollow" href="http://models.dev">models.dev</a> renamed the Kimi For Coding provider from <code>kimi-for-coding</code> to <code>kimi-code-plan-cn</code> and <code>kimi-code-plan-global</code>. Our generator looked up the old key, found nothing, and silently produced a registry with zero kimi-coding models.</p><p>The interesting part is how it surfaced. The <code>KnownProvider</code> type still listed <code>"kimi-coding"</code>, but the generated <code>MODELS</code> object no longer had that key, so <code>(typeof MODELS)[TProvider]</code> stopped type-checking. Fifteen errors, none of them in the file anyone had touched. The generator now reads the new key with the old one as fallback, and <code>k3</code>, <code>k3-256k</code>, <code>kimi-for-coding</code>, and <code>kimi-for-coding-highspeed</code> are back.</p><p>We also caught a related nuisance while releasing: the image-model generator wrote <code>JSON.stringify</code> output that biome then reformatted, so every <code>npm publish</code> left a whitespace-only diff in the working tree. It now emits formatted output directly. Small thing, but it was going to bite on every release forever.</p><h2>Everything else</h2><p>The model registry got its usual refresh. OpenRouter dropped <code>mistralai/mistral-large-2512</code> and <code>stealth/union-alpha</code>, and added <code>zai/glm-5.3-flashx</code>, <code>deepseek/deepseek-v4-flash-0731:free</code>, <code>alibaba/qwen3.8-omni-flash</code>, and <code>unbiased/pareto</code>, alongside pricing and context updates.</p><p>All packages are on 0.9.39. Upgrade with:</p><pre><code class="language-bash">npm install -g @elyracode/coding-agent@latest
elyra install npm:@elyracode/jev-tools
or elyra update
</code></pre><p></p><p>Then try it:</p><pre><code class="language-text">&gt; Before we start, use decide to rate each of these five TODO comments
&gt; for how likely they are to be a real bug rather than a nice-to-have.
</code></pre><p>A caveat to close on. OpenRouter’s decisions endpoint is marked alpha, and there is no official specification for it yet; the request shape here comes from reading community clients. If the endpoint moves, <code>JEV_BASE_URL</code> lets you point the extension somewhere else without waiting for a release. We will keep an eye on it.</p>