# We built a bidirectional context loop between web apps and AI agents

> Source: <https://dev.to/java_freepascal_dev/we-built-a-bidirectional-context-loop-between-web-apps-and-ai-agents-2mfg>
> Published: 2026-06-25 13:39:49+00:00

Source code: [Tabforge AI](https://github.com/tabforgeai/tabforge-ai)

Most AI integrations in business apps feel the same:

You send a prompt → the model returns an answer → you try to glue it into your app.

It works, but it always feels slightly disconnected from what’s actually happening in the UI.

The AI doesn’t really know what the user is doing.

And the app doesn’t really know what the AI just did.

We ran into that problem while building EasyAI / TabForge, and ended up with something we didn’t originally set out to build:

a bidirectional context loop between the application and the AI runtime.

**The problem:** AI in apps is stateless

Even when you pass “context”, it’s usually:

But the real state lives elsewhere:

So you end up doing things like:

It works, but it’s fragile.

**

Ambient Activity Memory (App → AI)**

The first thing we added was a way for the application to continuously describe what is happening inside it.

Not as logs.Not as analytics.

But as structured semantic events tied to actual UI actions.

So instead of:

*“here is an order id”*

the system already knows:

Now when the user says:

*“cancel this order”*

there is no ambiguity about what “this” refers to. The AI doesn’t guess context.It already has it.

**EasyAIEvent (AI → App)**

Once the AI started understanding the app state, the next obvious question was:

*what does the AI give back to the application?*

Not just a final answer, but the execution itself.

So every agent run can optionally emit a structured event stream:

`.withEventListener(event -> {`

log.info("[{}] {} — {}", event.source(), event.phase(), event.title());

})

No framework coupling.No HTTP assumptions.No UI dependencies.

Just a pure event stream that your app can consume however it wants.

*What this enables (more interesting part)*

Once you have both directions:

App → AI

The system knows what the user is doing.

AI → App

The system exposes what the agent is doing. You end up with something simple but powerful:

*a closed loop between UI state and AI execution state*

**

Why this matters in practice**

This removes a bunch of glue code that usually creeps into AI integrations:

Instead:

**Important design choice**

The event model is intentionally framework-agnostic:

(source, phase, status, title, detail, toolName, sequence, timestamp)

It does not know anything about:

HTTP, WebSockets, SSE, UI frameworks

That part is left to the application.

We ship a minimal example that maps the event stream to a real-time UI panel using SSE, but it stays outside the core library.

**Where this is going**

The interesting part is not the event system itself. It’s what becomes possible when:

You start to move from *“AI calls inside an app”* toward something closer to:

*AI as a participant in application execution, not just a function you call*

**If you strip everything away**

At its core, this is all we tried to solve:

*How do we make AI systems aware of application state without coupling them to the UI?*

And the answer turned out to be:

**Don’t pass state. Stream it in both directions.**

If you want to explore it:

GitHub: [https://github.com/tabforgeai/tabforge-ai](https://github.com/tabforgeai/tabforge-ai)

Full example: [https://github.com/tabforgeai/tabforge-ai-demo](https://github.com/tabforgeai/tabforge-ai-demo)
