chains of thought,minus the thought.
a typed typescript framework for composing calls to jev into decision graphs. jev doesn't write essays. it answers with probabilities, in milliseconds. jevchain routes on them and keeps the receipts.
"my toaster whispers my name at 3am."
- 01routefront-desk
- paranormal0.98
- repair0.02
- billing0.00
- 02emitbooked: one (1) exorcist
ask
one typed question. pick one, rate it, is it true. back comes a distribution, not a paragraph.
route
the answer picks the path. forget a branch and tsc tells you before production does.
trace
every run keeps the road taken, the roads not taken, and the numbers that decided between them.
const desk = route("front-desk", {
ask: choice("Which team should handle this ticket?", {
repair: "an ordinary mechanical or electrical fault",
billing: "payments, refunds, warranties",
paranormal: "behaviour no appliance can do",
}),
lowConfidence: { below: 0.4, then: emit("a human will read this. probably dave.") },
branches: {
repair: emit("technician booked"),
billing: emit("forwarded to billing"),
paranormal: emit("booked: one (1) exorcist"),
},
});
const { trace } = await createJev().run(desk, "My toaster whispers my name at 3am.");
trace.spans[0].decision.summary;
// 'Went to "paranormal" with 98%, a landslide over "repair" at 2% (confidence 0.97).'the haunted appliance desk from the readme, trimmed. the last line is the trace explaining itself, templated from the numbers. using an llm for that part would be a bit rich.
why it exists
for a week in september 2026, ai twitter answered every question with "just jev it." ticket routing? jev it. could this meeting have been an email? jev it. is seven even? this site jevs that. so i took the bit literally and gave it types.
it holds up better than it should. stack enough small, typed decisions and you have a program. a lot of agent pipelines are this, plus more tokens and a paragraph to parse at the end.
decomposition, again
most of what's in agentic atlas comes down to decomposition: small steps, narrow jobs, typed contracts, explicit routes, a record you can read after. jevchain is that at its smallest. each step is one question. the interesting work is in how they compose.
if a step only needs a decision, it doesn't need an agent.
what's in the box
- the trace is the product. the runtime emits events and folds them with
reduceTrace, the same function the studio draws from. the live animation and the saved trace can't disagree. - roads not taken. every branch keeps the probability it lost with.
- exhaustive routes. a choice between billing, bug and vibes answers
"billing" | "bug" | "vibes". a route missing"bug"doesn't compile. - batching for free. same state, same tick, one request. the trace counts calls and requests separately, so you can see what it saved.
- chains are data. a node is its own json.
fromJSON(toJSON(desk))is the same chain. - share links, no backend. the run rides in the url hash, which never reaches a server. good for traces of someone's group chat.
- zero dependencies. typescript and fetch.
the studio
run chains against the real api and watch the path light up. click a node for its distributions. read the run back as a numbered story. compare two inputs and find where they split. build one by hand and export typescript.
five examples, silly on purpose: haunted appliance support, group chat drama triage, should i text them back, could this meeting be an email, pull request horoscopes. each one teaches a real pattern.
what's rough
the rate limiter lives in memory, one per server. share links get long when traces do. saved runs live in your browser and nowhere else. typescript export leaves step code as todo stubs. bring your own code.