<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Svetozar Radojcin</title>
    <description>The latest articles on DEV Community by Svetozar Radojcin (@java_freepascal_dev).</description>
    <link>https://dev.to/java_freepascal_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3939868%2F5c813347-997d-4067-926d-2f65ec00c552.png</url>
      <title>DEV Community: Svetozar Radojcin</title>
      <link>https://dev.to/java_freepascal_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/java_freepascal_dev"/>
    <language>en</language>
    <item>
      <title>TabForge AI: a complete platform for building Java Web + AI apps</title>
      <dc:creator>Svetozar Radojcin</dc:creator>
      <pubDate>Tue, 11 Aug 2026 12:18:56 +0000</pubDate>
      <link>https://dev.to/java_freepascal_dev/tabforge-ai-a-complete-platform-for-building-java-web-ai-apps-4ian</link>
      <guid>https://dev.to/java_freepascal_dev/tabforge-ai-a-complete-platform-for-building-java-web-ai-apps-4ian</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TabForge AI closes that gap&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;p&gt;Here's the whole thing, top to bottom.&lt;/p&gt;

&lt;p&gt;## 1. Tabs as annotated beans — DynTabs&lt;/p&gt;

&lt;p&gt;You describe a tab; the framework handles opening, closing, lifecycle, and state. Each open tab gets its own isolated  CDI bean via a custom &lt;code&gt;@TabScoped&lt;/code&gt; scope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@Named&lt;/span&gt;
  &lt;span class="nd"&gt;@TabScoped&lt;/span&gt;
  &lt;span class="nd"&gt;@DynTab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"OrdersDynTab"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uniqueIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Orders"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Orders"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;includePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/WEB-INF/orders.xhtml"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;trackActivity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersBean&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseDyntabCdiBean&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// open the same tab twice → two independent instances&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
java&lt;br&gt;
  No manual navigation, no page-state juggling. Open a tab, get a bean; close it, it's gone.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A clean AI layer — EasyAI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One fluent entry point over LangChain4j. Chat, tools, agents, and structured extraction — provider-agnostic, so the  model behind it is a config detail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// A typed assistant with a business service exposed as tools&lt;/span&gt;
  &lt;span class="nc"&gt;OrdersAssistant&lt;/span&gt; &lt;span class="n"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EasyAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assistant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrdersAssistant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cancel order ORD-002"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You opt methods in as tools explicitly — no accidental exposure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@EasyTool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cancels an active order"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;cancelOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Deterministic pipelines — flow()&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="nc"&gt;EasyAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"understand"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;EasyAI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extract&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkStock"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;check&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"understand"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"place"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;place&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"understand"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userText&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your logic stays in plain Java. The LLM does one job: turn language into structure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ambient Activity Memory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@ActivityTracked&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BUSINESS_ACTION&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"view"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;entityType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"order"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entityIdParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"orderId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;viewOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the user can open an order and type "cancel this" — no id — and the assistant resolves "this" from what it just&lt;br&gt;
  saw them do.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The proactive assistant &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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: "&lt;em&gt;Looking at several Acme orders — want a quick account summary?&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SuggestionRule&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suggestion&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserActivityEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detect synchronously (cheap, predictable), phrase-and-push asynchronously, with a per-user cooldown so it's helpful  and never naggy. Deterministic code decides; the model is reserved for the one thing it's good at.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The UI, handled — pf-modern-template&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A self-contained PrimeFaces template: responsive layout, light/dark/dim themes, a transport-agnostic AI panel (chat +  live activity over SSE), a command palette, and now proactive suggestion chips. Drop-in — no build dependency.&lt;/p&gt;

&lt;p&gt;Getting started&lt;/p&gt;

&lt;p&gt;The fastest path is the starter — a pre-wired WAR you clone and deploy. Or add the library to an existing Jakarta EE 11+ project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.tabforgeai&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;tabforge-ai&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chat- and tools-only apps stay lean; RAG and vector-store integrations are optional add-ons you pull in only if you  use them.&lt;/p&gt;

&lt;p&gt;The philosophy&lt;/p&gt;

&lt;p&gt;One idea runs through all of it: let deterministic code decide, and reserve the model for the irreducible — language. That's what makes AI in a serious enterprise app predictable, testable, and safe.&lt;/p&gt;

&lt;p&gt;Proactive UX just arrived, first-class, in the Java stack.&lt;br&gt;
&lt;a href="https://github.com/tabforgeai/tabforge-ai" rel="noopener noreferrer"&gt;&lt;br&gt;
The library&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/tabforgeai/tabforge-ai-demo" rel="noopener noreferrer"&gt;demo app&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/tabforgeai/tabforge-ai-starter-" rel="noopener noreferrer"&gt;ready to use starter&lt;/a&gt;&lt;br&gt;
&lt;a href="https://youtu.be/qNDD9mfFEJk" rel="noopener noreferrer"&gt;See it in action&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All OpenSource&lt;/p&gt;

</description>
      <category>ai</category>
      <category>java</category>
      <category>software</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Teaching an AI to read a relational database — including Oracle APEX</title>
      <dc:creator>Svetozar Radojcin</dc:creator>
      <pubDate>Wed, 22 Jul 2026 07:49:26 +0000</pubDate>
      <link>https://dev.to/java_freepascal_dev/teaching-an-ai-to-read-a-relational-database-including-oracle-apex-4c5p</link>
      <guid>https://dev.to/java_freepascal_dev/teaching-an-ai-to-read-a-relational-database-including-oracle-apex-4c5p</guid>
      <description>&lt;p&gt;A few months back I started building a  tool to solve a problem that kept&lt;br&gt;
annoying me: when you want an AI assistant to help with an old database, you end  up as a human clipboard — copying schema, pasting stored procedures, describing  foreign keys by hand. The AI never actually &lt;em&gt;sees&lt;/em&gt; the database.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Legacy SQL Architect MCP&lt;/strong&gt; — an &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP&lt;/a&gt;  server that connects Claude (or any MCP-compatible client) directly to your  PostgreSQL, SQL Server, or Oracle database, in strict &lt;strong&gt;read-only&lt;/strong&gt; mode. The AI  inspects the real thing instead of guessing from what you paste.&lt;/p&gt;

&lt;p&gt;## The core idea&lt;/p&gt;

&lt;p&gt;Once connected, the assistant can use a set of tools to explore the database on  its own:&lt;/p&gt;

&lt;p&gt;| Tool | What it does |&lt;br&gt;
  |------|--------------|&lt;br&gt;
  | &lt;code&gt;inspect_schema&lt;/code&gt; | Tables, columns, types, primary &amp;amp; foreign keys |&lt;br&gt;
  | &lt;code&gt;data_sampler&lt;/code&gt; | Sample rows, with automatic masking of sensitive columns |&lt;br&gt;
  | &lt;code&gt;get_procedure_source&lt;/code&gt; | Full source of stored procedures and functions |&lt;br&gt;
  | &lt;code&gt;query_plan_expert&lt;/code&gt; | Execution plan analysis, full-scan detection |&lt;br&gt;
  | &lt;code&gt;dependency_graph&lt;/code&gt; | FK chains, trigger → procedure call chains, view deps |&lt;br&gt;
  | &lt;code&gt;generate_mermaid_erd&lt;/code&gt; | An ER diagram in Mermaid |&lt;br&gt;
  | &lt;code&gt;generate_documentation&lt;/code&gt; | Complete Markdown docs for the whole schema |&lt;br&gt;
  | &lt;code&gt;find_impact&lt;/code&gt; | Everything that depends on a given table |&lt;br&gt;
  | &lt;code&gt;generate_java_dao&lt;/code&gt; | Ready-to-compile Java Entity + Repository classes |&lt;/p&gt;

&lt;p&gt;So you can say things like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Inspect the schema, sample the orders table, read the procedures that touch  it, and tell me what would break if I rename &lt;code&gt;order_status&lt;/code&gt;."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;…and the assistant actually goes and looks, then answers with references to real   objects. Read-only by design — it physically cannot INSERT, UPDATE, or DELETE.&lt;/p&gt;

&lt;p&gt;## The new part: Oracle APEX&lt;/p&gt;

&lt;p&gt;This is the release I want to talk about, and I'll be honest about how it came to be. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm not an APEX developer.&lt;/strong&gt; I've never shipped a production APEX app. But APEX  performance problems are notoriously hard to see, and it turns out Oracle exposes a &lt;em&gt;lot&lt;/em&gt; about an APEX application through its own dictionary views —&lt;br&gt;
  &lt;code&gt;APEX_APPLICATIONS&lt;/code&gt;,&lt;code&gt;APEX_APPLICATION_PAGE_REGIONS&lt;/code&gt;,&lt;code&gt;APEX_APPLICATION_LOVS&lt;/code&gt;, &lt;code&gt;APEX_WORKSPACE_ACTIVITY_LOG&lt;/code&gt;, &lt;code&gt;APEX_DEBUG_MESSAGES&lt;/code&gt;, and friends. The answer to "why is this page slow?" is often sitting right there.&lt;/p&gt;

&lt;p&gt;So I studied those views and turned them into six tools that hand that insight to  the AI in a form it can reason about. Every one was verified against a &lt;strong&gt;live  APEX 26.1 instance&lt;/strong&gt; before shipping.&lt;/p&gt;

&lt;p&gt;### &lt;code&gt;inspect_apex_performance&lt;/code&gt;&lt;br&gt;
  An overview of an application: pages, regions with SQL, LOVs, validations,  processes — plus the &lt;strong&gt;slowest pages&lt;/strong&gt; by real user activity, and a list of  recommendations (slow pages, &lt;code&gt;SELECT *&lt;/code&gt; regions, LOVs with no &lt;code&gt;WHERE&lt;/code&gt; clause,   unconditional processes, …).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Run inspect_apex_performance on app 102 and show me the 5 slowest pages and why."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;get_apex_source&lt;/code&gt;&lt;br&gt;
  The APEX counterpart to &lt;code&gt;get_procedure_source&lt;/code&gt;: it extracts the embedded code   across 12 component types (regions, processes, computations, validations, items,  branches, dynamic actions, LOVs, authorizations, …). It introspects each view's  columns first, so it tolerates dictionary drift between APEX versions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Get the source of every SQL region on page 3 and summarize what they query."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;inspect_apex_debug&lt;/code&gt;&lt;br&gt;
  Reads &lt;code&gt;APEX_DEBUG_MESSAGES&lt;/code&gt;, reconstructs page views by grouping on the page-view  id, finds the slowest ones, and drills into the individual steps — so you can see  &lt;em&gt;where inside a page render&lt;/em&gt; the time actually went.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Which page views in the last 2 days were slowest, and what step dominated?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;apex_config_audit&lt;/code&gt;&lt;br&gt;
  A static anti-pattern audit — no runtime data needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pagination schemes that force a &lt;code&gt;COUNT(*)&lt;/code&gt; ("row N of Z")&lt;/li&gt;
&lt;li&gt;reports with unbounded / very high max-rows&lt;/li&gt;
&lt;li&gt;frequently-used LOVs with no caching&lt;/li&gt;
&lt;li&gt;pages with too many server-side dynamic actions&lt;/li&gt;
&lt;li&gt;pages crowded with many SQL regions&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Audit app 102 for configuration anti-patterns."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;apex_sql_runtime_stats&lt;/code&gt;&lt;br&gt;
  Correlates the app to its real runtime cost by reading &lt;code&gt;V$SQL&lt;/code&gt; for the app's  parsing schema — buffer gets, elapsed time, executions — and flags the hot SQL.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What's the most expensive SQL this app has actually run? Order by buffer gets  per execution."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;### &lt;code&gt;apex_explain_batch&lt;/code&gt;&lt;br&gt;
  Runs &lt;code&gt;EXPLAIN PLAN&lt;/code&gt; across an app's SQL regions, SQL LOVs, and Exists-type validations at once, then flags the red lines: &lt;code&gt;TABLE ACCESS FULL&lt;/code&gt;,&lt;br&gt;
  &lt;code&gt;MERGE JOIN CARTESIAN&lt;/code&gt;, &lt;code&gt;INDEX FULL SCAN&lt;/code&gt;. (It handles APEX &lt;code&gt;&amp;amp;SUBSTITUTION.&lt;/code&gt;  tokens by binding them, so the plans actually parse.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Explain-plan all the SQL in app 102 and list anything doing a full scan or a  Cartesian join."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;## None of this is magic&lt;/p&gt;

&lt;p&gt;It's all sitting in Oracle's own dictionary. What the tool does is connect the  dots and hand them to the AI in a form it can reason about — grounded in your  actual application, not a generic "best practices" lecture.&lt;/p&gt;

&lt;p&gt;## An honest ask&lt;/p&gt;

&lt;p&gt;Because I'm &lt;em&gt;not&lt;/em&gt; an APEX expert, I'd genuinely value review from people who are.  Am I reading the right views? Is there a metric you always check first that I'm   ignoring? Did I get something wrong? You know this platform far better than I do.&lt;/p&gt;

&lt;p&gt;It's open source and free (Apache 2.0), with native installers for Windows and   Linux (deb + rpm) that bundle their own Java runtime — no JDK needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/tabforgeai/legacy-sql-architect-mcp" rel="noopener noreferrer"&gt;https://github.com/tabforgeai/legacy-sql-architect-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it against a real APEX app, I'd love to hear what it found — or where it fell short.&lt;/p&gt;

</description>
      <category>oracle</category>
      <category>apex</category>
      <category>ai</category>
      <category>database</category>
    </item>
    <item>
      <title>We built a bidirectional context loop between web apps and AI agents</title>
      <dc:creator>Svetozar Radojcin</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:39:49 +0000</pubDate>
      <link>https://dev.to/java_freepascal_dev/we-built-a-bidirectional-context-loop-between-web-apps-and-ai-agents-2mfg</link>
      <guid>https://dev.to/java_freepascal_dev/we-built-a-bidirectional-context-loop-between-web-apps-and-ai-agents-2mfg</guid>
      <description>&lt;p&gt;Source code: &lt;a href="https://github.com/tabforgeai/tabforge-ai" rel="noopener noreferrer"&gt;Tabforge AI&lt;/a&gt;&lt;br&gt;
Most AI integrations in business apps feel the same:&lt;/p&gt;

&lt;p&gt;You send a prompt → the model returns an answer → you try to glue it into your app.&lt;/p&gt;

&lt;p&gt;It works, but it always feels slightly disconnected from what’s actually happening in the UI.&lt;/p&gt;

&lt;p&gt;The AI doesn’t really know what the user is doing.&lt;/p&gt;

&lt;p&gt;And the app doesn’t really know what the AI just did.&lt;/p&gt;

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

&lt;p&gt;a bidirectional context loop between the application and the AI runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; AI in apps is stateless&lt;/p&gt;

&lt;p&gt;Even when you pass “context”, it’s usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a string&lt;/li&gt;
&lt;li&gt;a JSON blob&lt;/li&gt;
&lt;li&gt;or a manually assembled prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real state lives elsewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which record is open&lt;/li&gt;
&lt;li&gt;what tab the user is on&lt;/li&gt;
&lt;li&gt;what action just happened&lt;/li&gt;
&lt;li&gt;what step in a workflow the user is in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you end up doing things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passing IDs around&lt;/li&gt;
&lt;li&gt;re-sending state on every request&lt;/li&gt;
&lt;li&gt;rebuilding context for every AI call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works, but it’s fragile.&lt;br&gt;
**&lt;br&gt;
Ambient Activity Memory (App → AI)**&lt;/p&gt;

&lt;p&gt;The first thing we added was a way for the application to continuously describe what is happening inside it.&lt;/p&gt;

&lt;p&gt;Not as logs.Not as analytics.&lt;br&gt;
But as structured semantic events tied to actual UI actions.&lt;/p&gt;

&lt;p&gt;So instead of:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“here is an order id”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;the system already knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user is currently viewing Order #248&lt;/li&gt;
&lt;li&gt;user just switched from payment tab to details tab&lt;/li&gt;
&lt;li&gt;user just triggered refund flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now when the user says:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“cancel this order”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;there is no ambiguity about what “this” refers to. The AI doesn’t guess context.It already has it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EasyAIEvent (AI → App)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the AI started understanding the app state, the next obvious question was:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;what does the AI give back to the application?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not just a final answer, but the execution itself.&lt;/p&gt;

&lt;p&gt;So every agent run can optionally emit a structured event stream:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;started&lt;/li&gt;
&lt;li&gt;planning&lt;/li&gt;
&lt;li&gt;tool calls&lt;/li&gt;
&lt;li&gt;progress updates&lt;/li&gt;
&lt;li&gt;completion
This is exposed via a simple hook:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;.withEventListener(event -&amp;gt; {&lt;br&gt;
    log.info("[{}] {} — {}", event.source(), event.phase(), event.title());&lt;br&gt;
})&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No framework coupling.No HTTP assumptions.No UI dependencies.&lt;br&gt;
Just a pure event stream that your app can consume however it wants.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What this enables (more interesting part)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you have both directions:&lt;br&gt;
  App → AI&lt;/p&gt;

&lt;p&gt;The system knows what the user is doing.&lt;br&gt;
   AI → App&lt;/p&gt;

&lt;p&gt;The system exposes what the agent is doing. You end up with something simple but powerful:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;a closed loop between UI state and AI execution state&lt;/em&gt;&lt;br&gt;
**&lt;br&gt;
Why this matters in practice**&lt;/p&gt;

&lt;p&gt;This removes a bunch of glue code that usually creeps into AI integrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passing IDs back and forth&lt;/li&gt;
&lt;li&gt;manually constructing prompts&lt;/li&gt;
&lt;li&gt;debugging “what did the agent actually do?”&lt;/li&gt;
&lt;li&gt;rebuilding context on every request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app continuously emits context&lt;/li&gt;
&lt;li&gt;the AI continuously emits execution state&lt;/li&gt;
&lt;li&gt;both stay decoupled, but synchronized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important design choice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The event model is intentionally framework-agnostic:&lt;br&gt;
(source, phase, status, title, detail, toolName, sequence, timestamp)&lt;/p&gt;

&lt;p&gt;It does not know anything about:&lt;/p&gt;

&lt;p&gt;HTTP, WebSockets, SSE, UI frameworks&lt;/p&gt;

&lt;p&gt;That part is left to the application.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where this is going&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The interesting part is not the event system itself. It’s what becomes possible when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app knows what the user is doing&lt;/li&gt;
&lt;li&gt;the AI knows what it is doing&lt;/li&gt;
&lt;li&gt;and both sides share the same runtime context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You start to move from &lt;em&gt;“AI calls inside an app”&lt;/em&gt; toward something closer to:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI as a participant in application execution, not just a function you call&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you strip everything away&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At its core, this is all we tried to solve:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How do we make AI systems aware of application state without coupling them to the UI?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And the answer turned out to be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t pass state. Stream it in both directions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to explore it:&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=Nm5RPV49Qck" rel="noopener noreferrer"&gt;The AI assistant knows what user doing in the app&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=qNDD9mfFEJk" rel="noopener noreferrer"&gt;AI streams his work details to the app&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/tabforgeai/tabforge-ai" rel="noopener noreferrer"&gt;https://github.com/tabforgeai/tabforge-ai&lt;/a&gt;&lt;br&gt;
Full example: &lt;a href="https://github.com/tabforgeai/tabforge-ai-demo" rel="noopener noreferrer"&gt;https://github.com/tabforgeai/tabforge-ai-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
