Over the past year I’ve worked with a lot of LLM orchestration frameworks. Most of them follow the same pattern: heavy dependencies, sprawling abstractions, and a configuration-heavy API that locks you in before you’ve written a single useful line of code. I wanted something different — something that felt like writing normal TypeScript but could express complex agent behaviour.
That’s why I built Flowneer.
What is it?
Flowneer is a tiny (~3 kB gzipped), zero-dependency TypeScript flow builder designed for LLM agents. The core idea is a fluent chainable API that lets you describe a pipeline of steps, branches, loops, and parallel executions in a way that reads almost like prose.
const flow = flowneer()
.startWith(fetchUserContext)
.then(callLLM)
.branch(
(ctx) => ctx.needsMoreInfo,
(yes) => yes.then(askClarifyingQuestion).loop(() => stillNeedsInfo),
(no) => no.then(generateFinalResponse),
)
.parallel([saveToMemory, logToAnalytics])
.stream(outputToUser);
await flow.run(initialContext);
Why I built it
I was building a side project that needed a ReAct-style agent loop — observe, think, act, repeat. Every framework I tried either required me to define everything through JSON config files, or pulled in half the npm registry as dependencies.
I wanted to write the orchestration logic in plain TypeScript, see exactly what was happening, and be able to unit-test each step in isolation. None of the existing tools gave me that combination.
The technical challenges
Getting the types right
The hardest part was the TypeScript type system. Flowneer passes a context object through every step, and each step can read and write to it. I wanted TypeScript to track the shape of the context as it flows through the pipeline — so if a step adds a summary field, subsequent steps can reference ctx.summary with full autocompletion.
Making this work with branching (where different branches might produce different shapes) without losing type safety took a lot of iteration. I ended up settling on a generic Context type that each step can intersect with, trading some inference convenience for correctness.
The plugin system
I wanted Flowneer to be extensible without imposing structure on users who don’t need it. The plugin API uses lifecycle hooks — beforeStep, afterStep, onError, onComplete — so you can intercept any point in the flow.
This unlocked building the preset packages: @flowneer/react for ReAct-style agent loops, @flowneer/memory for attaching a memory store, and @flowneer/tools for structured tool calling. Each preset is just a plugin — no core modifications needed.
Keeping it small
The 3 kB budget was self-imposed but important to me. Every feature proposal had to justify itself against that limit. Streaming support was the closest call — I nearly didn’t include it, but the ability to .stream() directly to a UI felt too useful to cut.
What I learned
The most valuable insight from this project was that orchestration logic is just control flow. Branches, loops, parallelism — these are problems TypeScript already solves. The real value a library like Flowneer adds is the consistent context passing, the plugin hooks, and the structured error handling. Everything else is sugar.
I also learned a lot about keeping the bundle tiny. Tree-shaking alone isn’t enough — you have to design the API so that unused features are never imported in the first place.
Where it’s going
Flowneer is on v0.9.x and under heavy development. Upcoming work includes better tooling for debugging flow execution, a visualizer for inspecting the pipeline at runtime, and more community-contributed presets.
If you’re building LLM agents in TypeScript and want full control over the orchestration without committing to a heavyweight framework, give it a try. I’d love feedback on the API design.