Try it
Copy the code ↓- Data source
- Decision schema
- Model inference
- Structured result
2 Decision schema 4 typed questions
write_actionchoicesavemergenarrowdropsupportednoulprobability 0–1overreachscorefaithfulslight_stretchoverinterpretedduplicatenoulprobability 0–1
3 Model inference typesafe/jev-1.13
Waiting for a run.
4 Structured result
Raw JSON
Run the demo to see the JSON answers, the highlighted pick and the final action.
FLATKEY_API_KEY and run.curl -sS https://router.flatkey.ai/api/alpha/decisions \
-H "Authorization: Bearer $FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "Source:\nUser: I'\''m trying Rust this weekend for a small CLI side project. Work is still all TypeScript, and that won'\''t change anytime soon.\nAssistant: Nice, want a starter Cargo layout?\n\nCandidate memory: User is moving their team from TypeScript to Rust.\n\nExisting memory: User works mainly in TypeScript at their job.\n\nTask: write_action, supported, overreach, duplicate.",
"questions": {
"write_action": {
"type": "choice",
"instructions": "An agent wants to save the memory in `candidate`, extracted from the conversation in `source`. `existing` is the closest memory already stored. What should happen to the candidate?",
"criteria": {
"save": "The source clearly supports the candidate as written, and it adds something the existing memory does not already say",
"merge": "The source supports it, but it repeats or updates the existing memory, so it should be folded into that one",
"narrow": "Part of it is supported, but it generalizes or concludes more than the user actually said, so the wording must be narrowed",
"drop": "The source does not support it, or it came from a joke, a hypothetical, a quote of someone else, or small talk"
}
},
"supported": {
"type": "noul",
"instructions": "The conversation in `source` states, or directly implies, everything the `candidate` memory claims.",
"criteria": {
"true": "Every claim in the candidate can be pointed to in the source.",
"false": "At least one claim in the candidate is a guess, a generalization, or missing from the source."
}
},
"overreach": {
"type": "score",
"instructions": "How far does the candidate go beyond what the user actually said in `source`?",
"criteria": [
"faithful",
"slight_stretch",
"overinterpreted"
]
},
"duplicate": {
"type": "noul",
"instructions": "The `candidate` says the same thing as the `existing` memory, or only updates it."
}
}
}'// Node.js 18+ (built-in fetch). Run: FLATKEY_API_KEY=... node jev.mjs
const body = {
"model": "typesafe/jev-1.13",
"state": "Source:\nUser: I'm trying Rust this weekend for a small CLI side project. Work is still all TypeScript, and that won't change anytime soon.\nAssistant: Nice, want a starter Cargo layout?\n\nCandidate memory: User is moving their team from TypeScript to Rust.\n\nExisting memory: User works mainly in TypeScript at their job.\n\nTask: write_action, supported, overreach, duplicate.",
"questions": {
"write_action": {
"type": "choice",
"instructions": "An agent wants to save the memory in `candidate`, extracted from the conversation in `source`. `existing` is the closest memory already stored. What should happen to the candidate?",
"criteria": {
"save": "The source clearly supports the candidate as written, and it adds something the existing memory does not already say",
"merge": "The source supports it, but it repeats or updates the existing memory, so it should be folded into that one",
"narrow": "Part of it is supported, but it generalizes or concludes more than the user actually said, so the wording must be narrowed",
"drop": "The source does not support it, or it came from a joke, a hypothetical, a quote of someone else, or small talk"
}
},
"supported": {
"type": "noul",
"instructions": "The conversation in `source` states, or directly implies, everything the `candidate` memory claims.",
"criteria": {
"true": "Every claim in the candidate can be pointed to in the source.",
"false": "At least one claim in the candidate is a guess, a generalization, or missing from the source."
}
},
"overreach": {
"type": "score",
"instructions": "How far does the candidate go beyond what the user actually said in `source`?",
"criteria": [
"faithful",
"slight_stretch",
"overinterpreted"
]
},
"duplicate": {
"type": "noul",
"instructions": "The `candidate` says the same thing as the `existing` memory, or only updates it."
}
}
};
const res = await fetch("https://router.flatkey.ai/api/alpha/decisions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FLATKEY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`Jev request failed: ${res.status} ${await res.text()}`);
const { answers, usage } = await res.json();
console.log(JSON.stringify(answers, null, 2), usage);# Python 3.8+ (pip install requests). Run: FLATKEY_API_KEY=... python jev.py
import json, os, requests
body = json.loads(r"""{
"model": "typesafe/jev-1.13",
"state": "Source:\nUser: I'm trying Rust this weekend for a small CLI side project. Work is still all TypeScript, and that won't change anytime soon.\nAssistant: Nice, want a starter Cargo layout?\n\nCandidate memory: User is moving their team from TypeScript to Rust.\n\nExisting memory: User works mainly in TypeScript at their job.\n\nTask: write_action, supported, overreach, duplicate.",
"questions": {
"write_action": {
"type": "choice",
"instructions": "An agent wants to save the memory in `candidate`, extracted from the conversation in `source`. `existing` is the closest memory already stored. What should happen to the candidate?",
"criteria": {
"save": "The source clearly supports the candidate as written, and it adds something the existing memory does not already say",
"merge": "The source supports it, but it repeats or updates the existing memory, so it should be folded into that one",
"narrow": "Part of it is supported, but it generalizes or concludes more than the user actually said, so the wording must be narrowed",
"drop": "The source does not support it, or it came from a joke, a hypothetical, a quote of someone else, or small talk"
}
},
"supported": {
"type": "noul",
"instructions": "The conversation in `source` states, or directly implies, everything the `candidate` memory claims.",
"criteria": {
"true": "Every claim in the candidate can be pointed to in the source.",
"false": "At least one claim in the candidate is a guess, a generalization, or missing from the source."
}
},
"overreach": {
"type": "score",
"instructions": "How far does the candidate go beyond what the user actually said in `source`?",
"criteria": [
"faithful",
"slight_stretch",
"overinterpreted"
]
},
"duplicate": {
"type": "noul",
"instructions": "The `candidate` says the same thing as the `existing` memory, or only updates it."
}
}
}""")
res = requests.post(
"https://router.flatkey.ai/api/alpha/decisions",
headers={"Authorization": f"Bearer {os.environ['FLATKEY_API_KEY']}"},
json=body,
timeout=60,
)
res.raise_for_status()
data = res.json()
print(json.dumps(data["answers"], indent=2), data.get("usage"))
No Flatkey key yet? Get one for Jev 1.13 →
About this use case

Who / when
Audience
- teams building long-term memory for chat assistants and coding agents
- memory-layer builders who extract facts from conversations after every thread
- agent developers whose memory store keeps filling with guesses and near-duplicates
- anyone who wants cheap, logged checks before a write instead of a second LLM call
Triggers
- the assistant "remembers" that a user is switching stacks after one weekend experiment
- the same preference is stored five times in slightly different words
- a memory written from a joke or a hypothetical shows up in later answers as fact
- running a full LLM review on every candidate memory costs more than the thread itself
The workflow
- 01Candidate memoryThe source excerpt, the memory the extractor proposes, and the closest existing memory from your storeInput
- 02What to do?Save as new, merge into the existing one, narrow the wording, or drop itJev
- 03Supported?Yes/no probability that the source states or directly implies it, plus how far it stretchesJev
- 04Code gate≥ 0.75 write or merge, 0.4–0.75 narrow or hold in a shadow log, < 0.4 drop; log every decisionYour code
Question shape
{
"write_action": {
"type": "choice",
"instructions": "An agent wants to save the memory in `candidate`, extracted from the conversation in `source`. `existing` is the closest memory already stored. What should happen to the candidate?",
"criteria": {
"save": "The source clearly supports the candidate as written, and it adds something the existing memory does not already say",
"merge": "The source supports it, but it repeats or updates the existing memory, so it should be folded into that one",
"narrow": "Part of it is supported, but it generalizes or concludes more than the user actually said, so the wording must be narrowed",
"drop": "The source does not support it, or it came from a joke, a hypothetical, a quote of someone else, or small talk"
}
},
"supported": {
"type": "noul",
"instructions": "The conversation in `source` states, or directly implies, everything the `candidate` memory claims.",
"criteria": {
"true": "Every claim in the candidate can be pointed to in the source.",
"false": "At least one claim in the candidate is a guess, a generalization, or missing from the source."
}
},
"overreach": {
"type": "score",
"instructions": "How far does the candidate go beyond what the user actually said in `source`?",
"criteria": [
"faithful",
"slight_stretch",
"overinterpreted"
]
},
"duplicate": {
"type": "noul",
"instructions": "The `candidate` says the same thing as the `existing` memory, or only updates it."
}
}Gate policy
supported ≥ 0.75
Write it in code: save as a new memory, or merge into the existing one when write_action is merge or duplicate is high. Store the source message id with it.
0.4–0.75
Do not write the candidate as is: if write_action is narrow, ask the extractor for a narrower wording and check it again; otherwise hold it in a shadow log for review.
supported < 0.4
Drop it, and keep the rejected candidate with its probabilities in the log so you can tune the cutoffs later.
Failure modes
Pre-launch checks before you trust auto-routing:
- Writing every extracted fact straight to the store, so one weekend experiment becomes "user is switching stacks" in every later answer;
- Asking a single yes/no "is this a good memory?", which hides whether the problem is missing support, a stretch, or a duplicate;
- Skipping the duplicate check, so the same preference is stored many times and crowds out useful memories at retrieval time;
- Letting Jev rewrite the memory; Jev returns decisions, so the narrower wording should come from your extractor or LLM, then be checked again;
- Treating the probability as a verdict with no middle band, so borderline memories are silently saved or silently lost;
- Going live without a shadow period; log decisions next to what you write today first, and compare before you let the gate block writes;
FAQ
Why check a memory before writing it instead of cleaning up later?
A wrong memory starts shaping answers the moment it is stored. Checking at write time is cheap, because it is one small typed decision per candidate, and it keeps guesses, jokes and duplicates out of the store instead of hunting for them later.
Why four questions instead of one "keep it?" question?
Each one points to a different fix. Low support means drop, a stretch means narrow the wording, a duplicate means merge. One combined answer would tell you something is wrong but not what to do about it.
Can Jev write the narrower version of the memory?
No. Jev answers typed questions with probabilities; it does not generate text. Send narrow decisions back to your extractor or an LLM for a tighter wording, then run the new candidate through the same check before writing.
Where do the 0.4 and 0.75 cutoffs come from?
They are starting points, not rules. Run the check in shadow mode next to your current memory writes, compare the decisions with what a person would keep, and move the cutoffs until clear cases land in the right band and unclear ones land in the middle.
What does the Flatkey call look like?
POST https://router.flatkey.ai/api/alpha/decisions with Authorization: Bearer …, Content-Type: application/json, and a body of state (source, candidate, existing), model (typesafe/jev-1.13), and questions (choice / noul / score). This is not an OpenAI-compatible /v1 chat endpoint. Get a key for Jev 1.13 on Flatkey.
Can I bring my own API key?
Yes. Try the free demo above without signing in. To run it live on your own text, sign in with Google and connect a Flatkey or OpenRouter key (a quick test call runs when you save it). Manage or delete it anytime under Dashboard → BYOK. Keys are stored encrypted on the server, used only for runs you trigger, and never sent back to the browser. Need a Flatkey key? Get one for Jev 1.13 on Flatkey.
Is this the official TypeSafe Jev product page?
No. jev-llm.com is an independent research and practice site. Verify pricing, models and terms at the original TypeSafe sources.
Sources
Giving a Memory System Reflexes Since Jev launched, we’ve been exploring what its approach makes possible inside Nowledge Mem. The observations we shared along the way drew a lot of interest. This is the full follow-up to that
— @NowledgeMem View the post on X →
Wire this gate with your own key
Bring your own key through Flatkey / OpenRouter. Keep exact checks in code.