Yada: an agent harness that keeps the application in charge
Yada is an open-source TypeScript runtime for adding conversational agents to existing products. It handles model communication, tool orchestration, conversation state, approvals and recovery, while the application keeps ownership of its business rules, permissions and data. Yada runs in production inside TalonHR, the hiring platform where it began.
At a glance
- What it is: an agent harness written in TypeScript, released under the MIT license
- Models: Anthropic, OpenAI, Gemini, DeepSeek, Ollama and llama.cpp
- Capabilities: typed functions, MCP servers, HTTP APIs, databases and Agent Skills
- Storage: SQLite, PostgreSQL or MySQL
- Clients: a service API, a browser client and a themeable chat widget; Flutter apps use the same API
- First deployment: TalonHR (Go backend, Next.js web app, Flutter mobile apps)
Why Yada exists
TalonHR brings applicants, employers, recruiters and coaches into one platform. As it grew, useful workflows became harder to find. We paused feature work to see where people were getting stuck, simplified the interface, and began exploring a different way in: letting people describe what they wanted to do.
That raised a harder engineering question. How can an agent act on a user's behalf while respecting the application's permissions, asking before it changes anything, and recovering when a request or a worker fails?
Answering it inside TalonHR alone would have produced a feature. I wanted a runtime. So I built Yada as a standalone harness, with TalonHR as its first production deployment rather than its only home.
The application stays in charge
Yada is built on one principle: the agent should never become a second place where business rules live.
TalonHR's Go services continue to own recruitment workflows, authorization and data. Yada owns everything around the model: talking to providers, orchestrating tools, holding conversation state, managing approvals and recovering from failure. The application exposes specific capabilities as tools, and Yada can only act through those boundaries.
Writing Yada in TypeScript gave it direct access to the model SDKs, MCP tooling, schema validation and browser integrations an agent layer needs. The cost is one more runtime to deploy and operate. The return is that agent development moves independently of the backend, and the same harness can sit in front of applications written in any language.
From conversation to controlled execution
A model response on its own does nothing. An agent has to discover tools, validate inputs, execute permitted calls and use the results to keep going.
Yada runs that work through a bounded execution loop. Tools are filtered for the authenticated identity, arguments are validated against their schemas, and authorization is checked before anything runs. Limits on model rounds, tool calls, failures, concurrency and output size stop a single request from expanding without end. Reads can run concurrently; writes go through a separate approval path.
Because providers and capabilities are pluggable, an application's integration with Yada survives a change of model or the addition of a new tool.
Approvals bound to the exact operation
When Yada proposes a write, it first prepares a preview along with the version of the state that preview depends on. Approval is bound to the authenticated user and tenant, the exact operation and arguments, that state version and an expiry. Just before execution, Yada checks authorization and state again. An approval cannot be stretched to cover a different action, a different user or a world that has since moved on.
Recovery that never blindly replays a write
The hardest case is a write that may have succeeded even though its response never arrived. Retrying could repeat a real business operation.
Yada treats this as part of the execution model, not an edge case. Every operation carries an identifier the owning backend uses for idempotency. Once execution has started, an uncertain outcome is checked against an authoritative receipt instead of being replayed. If the outcome still cannot be established, the operation stays unresolved and is flagged for investigation.
That boundary is deliberate. Yada coordinates the action, but the backend still has to enforce business permissions, apply state checks atomically and keep reliable receipts. No runtime can supply those guarantees on behalf of an external system, and Yada does not pretend to.
Surviving interruptions
A deployment, a crashed worker or a dropped connection should not send every task back to the start. Yada persists conversations, checkpoints, approvals and recovery state, and uses renewable worker leases so competing workers cannot overwrite each other's progress.
On recovery, Yada reuses completed tool results and rechecks permissions for pending work. A model request that was interrupted before its response was checkpointed may need to run again; an uncertain write takes the reconciliation path described above.
Integrating Yada
A service API separates execution from the interface. It supports run submission, persisted events, cancellation and approval decisions, so a submitted run keeps going after the client disconnects and the interface can reconnect to pick up its progress.
On the web, the browser client and an accessible, themeable chat widget handle the front end. A Flutter client talks to the same API. To get started, a starter command assembles an application with typed tools, storage, skills and a chat widget.
Testing agent behaviour
Yada ships with repeatable evaluation scenarios that check expected outcomes, tool calls and execution behaviour. A deterministic fake provider exercises the runtime without calling a real model, which makes regression testing fast and stable; runs against live providers show how real models behave. These checks catch regressions. Whether a conversation is actually useful is still a question for users.
Where Yada stands
Yada is live in TalonHR as a conversational entry point into supported workflows. The open-source repository includes the runtime, provider adapters, storage implementations, MCP integrations, the service API, browser components, examples and documentation.
The TalonHR rollout is deliberately phased. The next step is learning which requests people complete, where clarification helps, where conversations fail, and where the traditional interface is still the better route. It is too early to claim a measured improvement in task completion or time saved.
What building Yada taught me
The tool loop is the easy part of an agent. What decides whether it can be trusted with real work is everything around it: where the application boundary sits, how approvals are bound, and what happens when an outcome is uncertain. That is the part Yada was built to get right.
Explore Yada · View the MIT-licensed source · Read the engineering story