TabForge AI: a complete platform for building Java Web + AI apps TabForge AI, a new platform for building AI-powered Java web applications on Jakarta EE and PrimeFaces, was released. It provides a multi-tab UI shell, a provider-agnostic AI layer built on LangChain4j, and features such as tool-calling agents, structured extraction, and proactive suggestions. The platform includes a library, live demo, starter project, and a drop-in UI template. Modern AI UX — chat panels, tool-calling agents, assistants that remember context and even suggest your next step — has lived in JavaScript SaaS for years. The Java enterprise stack has been left doing it the hard way. TabForge AI closes that gap . It's a complete platform for building AI-powered web apps on Jakarta EE + PrimeFaces — from the multi-tab UI shell down to a clean, provider-agnostic AI layer. Library, live demo, starter project, and a drop-in UI template — all shipped. Here's the whole thing, top to bottom. 1. Tabs as annotated beans — DynTabs You describe a tab; the framework handles opening, closing, lifecycle, and state. Each open tab gets its own isolated CDI bean via a custom @TabScoped scope. @Named @TabScoped @DynTab name = "OrdersDynTab", uniqueIdentifier = "Orders", title = "Orders", includePage = "/WEB-INF/orders.xhtml", trackActivity = true public class OrdersBean extends BaseDyntabCdiBean { // open the same tab twice → two independent instances } java No manual navigation, no page-state juggling. Open a tab, get a bean; close it, it's gone. One fluent entry point over LangChain4j. Chat, tools, agents, and structured extraction — provider-agnostic, so the model behind it is a config detail. // A typed assistant with a business service exposed as tools OrdersAssistant ai = EasyAI.assistant OrdersAssistant.class .withTools orderService .build ; String reply = ai.ask "cancel order ORD-002" ; You opt methods in as tools explicitly — no accidental exposure: @EasyTool "Cancels an active order" public String cancelOrder String orderId { ... } Agents are powerful but unpredictable. When you want a repeatable, testable process, flow lets you own the steps and call the model only at the edges that actually need language: php EasyAI.flow .step "understand", ctx - EasyAI.extract OrderRequest.class .from ctx.inputText .step "checkStock", ctx - inventory.check ctx.get "understand", OrderRequest.class .step "place", ctx - orders.place ctx.get "understand", OrderRequest.class .build .run userText ; Your logic stays in plain Java. The LLM does one job: turn language into structure. The framework quietly records what the user does in the app — opening a record, running a search — and makes that timeline available to the assistant. So deixis just works: @ActivityTracked type = BUSINESS ACTION, verb = "view", entityType = "order", entityIdParams = "orderId" public String viewOrder String orderId { ... } Now the user can open an order and type "cancel this" — no id — and the assistant resolves "this" from what it just saw them do. This is the piece you normally only see in Copilot, Gmail's Smart Compose, or Notion AI — and almost never as a first-class pattern in a Java web framework. Built on Ambient Memory, the app can offer the next useful step before you ask. Open two orders for the same customer, and a dismissible chip appears: " Looking at several Acme orders — want a quick account summary? " The important part: it's not a black-box agent watching you. A small, deterministic rule — plain Java you write and unit-test — decides if and what to suggest. The model only phrases the sentence. public interface SuggestionRule { Optional