AI can write the code. Making it hold up under real use is the part that has not changed. Kyo is the foundation that takes that on, with layered safety from the first line of code to crash recovery, so what you build survives errors, restarts, and real traffic.
val checkout: Receipt < (Async & Abort[Declined]) =
for
auth <- bank.authorize(card, total)
done <- orders.record(auth)
yield done
// every effect and failure, in the typeThe demo works. Then an error slips through unnoticed, a restart wipes the night's work, or a little real traffic shows up and the whole thing buckles. The distance between software that works once and software you can depend on is the gap most projects fall into, and the more of the code an AI wrote, the more the ground underneath has to hold.
Almost every module compiles to all four targets from one source, so the choice below is where your program runs, not which features you get. The same APIs come with it; only the runtime underneath changes.
The mature platform most production already runs on, where the work-stealing scheduler spreads fibers across every core. It has the widest library reach of the four.
One source runs in the browser and on Node, so a web UI, an edge function, and a backend can share the same code and the same APIs.
Compiles to a standalone binary that starts in milliseconds with no runtime to install, for command-line tools and long-running services alike.
The Scala.js WasmGC backend runs the same code at near-native speed, in the browser or any Wasm host.
And the engine under all of it: compiled code, an effect runtime built around allocation discipline, and an adaptive work-stealing scheduler that keeps every core fed. One process holds thousands of concurrent computations, where the default stack of the AI ecosystem scales by adding worker fleets. Latency and throughput per process are the serving bill, and the serving bill is what decides whether you can afford to run what you built.
Not a dozen libraries that each break in their own way. One import brings the whole vocabulary, and adding a module adds its types to that same import with nothing new to wire. Describe a type once with derives Schema and you get JSON, Protobuf, Ion, and YAML codecs, validation, typed field access, and a diff you can serialize, send, and replay, all from that one definition, so the parts you assemble already speak the same language instead of needing glue between them.
One foundation carries your code from the first line you write to a crash and back, and it catches mistakes earlier at every step: as you write, as it compiles, as it runs, and when it fails anyway. You opt into none of it. There are no conventions to follow, no annotations to add, and no cleanup to wire, because the safe path is the default the API hands you, whether a developer wrote the code or an AI agent generated it.
The shapes that break a program cannot be expressed in the first place, so you spend your time on logic instead of guarding against invalid states. The UI layer turns the wrong markup back right where you write it: an SVG primitive is not an HTML child, and the compiler says so before it reaches the screen.
div(span("ok")) // compiles
div(Svg.circle(...)) // does not: an SVG primitive is not an HTML childThe same check reaches the request: an auth filter does not compile until the route declares the header it reads, then hands the verified user to the next handler as a typed field.
Every effect a piece of code uses, and every way it can fail, is part of its type:
def charge(card: Card, amount: Money): Receipt < (Async & Abort[Declined])This function can suspend, and it can be declined. Nothing else. Forget to handle that failure and it does not compile, because the unhandled effect stays in the type until you deal with it. Every rejection points at the exact line, so whoever wrote it, a person or a model, corrects it in a step instead of a rewrite.
Work started together is torn down together, and anything acquired in a scope, a connection, a file, a handle, is released exactly once: on success, on failure, and on interruption alike.
The scheduler keeps every task moving on a 10ms slice and catches a blocking call by watching it stop using the CPU, the socket reads and native I/O that still look like running threads to every other runtime. It sizes its own pool to the machine and sheds load by a stable per-user decision before the queue can OOM, and flaky calls retry on a policy and time out cleanly instead of hanging.
Long-running work records each step before the next begins, so the process can go down anywhere and resume from the last completed step, replaying nothing it already finished.
It does not even need the same machine: each run is held on a time-limited lease, so a stalled executor's work is claimed by another and carried forward. Even a one-hour sleep survives a restart, and a half-finished transaction unwinds itself in reverse. No separate workflow server to stand up; the durability is part of the same foundation as the rest.
The code an AI writes is held by the same APIs and contracts as yours, and the agent it builds runs inside the same runtime and durability: a failed tool call is a typed error, a multi-step run is a durable workflow, a runaway loop is preempted on its time slice. Nothing about AI is a special case.
The same type system that checks your code checks your agents.
Kyo doesn't make bugs impossible. What it does is take the failure modes that wreck most projects off the table, and catch many of the rest before they ship.
Adopting Kyo is not a rewrite. Bring it in next to the code you run today, one call at a time, and the two compose in a single program. Failures and cancellation cross the boundary in both directions, so nothing leaks when you mix them.
Call into Kyo from your effect type and run Kyo as either, both ways. Failure and interruption semantics are preserved across the boundary, so a cancel on one side cancels the other.
Install the work-stealing scheduler as the execution context your service already runs on, a drop-in for Cats Effect, ZIO, Pekko, or Finagle. Future-based code gets the same time-slicing and blocking detection underneath, on a pool that sizes itself to the machine.
Not every module needs the scheduler. kyo-prelude handles typed errors, environment, and state in place with no runtime, and kyo-data, kyo-schema, and kyo-parse are plain libraries.
The whole foundation is one import away. import kyo.* brings the entire vocabulary, and adding a module adds its types to that same import.
// Kyo composes with ZIO and Cats Effect, both ways
val fromZio: User < (Abort[E] & Async) = ZIOs.get(loadUser)
val toZio: ZIO[Any, E, Receipt] = ZIOs.run(checkout)
val fromCats: User < Async = Cats.get(cachedUser)
val toCats: IO[Receipt] = Cats.run(checkout)This project exists because of a specific belief about what software is for and who gets to build it. Read the manifesto.