We built a bidirectional context loop between web apps and AI agents The Tabforge AI team developed a bidirectional context loop between web applications and AI agents, enabling continuous state streaming rather than stateless prompt-response interactions. The system uses structured semantic events from the app to the AI and an event stream from the AI back to the app, creating a closed loop that eliminates fragile glue code. This framework-agnostic approach allows AI to participate in application execution without coupling to the UI. 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