How Context-Awareness Works in Agent-Native to Expose UI State

Agent-Native maintains real-time context-awareness by synchronizing UI navigation state to a shared application-state store, allowing agents to read the current screen via the navigation and __url__ keys while commanding navigation changes through atomic navigate commands.

Context-awareness in Agent-Native bridges the gap between AI agents and dynamic user interfaces. According to the BuilderIO/agent-native repository, the framework implements a bidirectional synchronization pattern that exposes semantic UI state to agents while allowing them to steer navigation programmatically. This architecture ensures the agent always operates with an accurate view of what the user is currently viewing.

The Four Pillars of Context-Awareness

The context-awareness pattern defined in SKILL.md consists of four tightly-coupled components that give the agent a reliable, real-time view of the UI.

The UI writes a compact, semantic snapshot of the current screen to the navigation key on every route change. This object typically includes the view name, selected IDs, active tabs, and other route-specific metadata. The agent reads this value before acting to understand the current context.

Source: [SKILL.md – Core pattern 1](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md#L24-L28).

Current URL (__url__ key)

The framework automatically injects the full URL—including pathname, search, hash, and searchParams—into the __url__ key. This allows the agent to implement filter-by-URL logic without duplicating URL data inside the navigation object.

Source: [SKILL.md – Core pattern 2](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md#L68-L73).

The view-screen Action

A server-side action that reads both navigation and __url__, fetches relevant domain data, and returns a JSON snapshot representing the user's visible screen. This acts as the "agent's eyes," providing a rich context block that the agent can consume as <current-screen>.

Source: [view-screen.ts](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/templates/workspace-core/actions/view-screen.ts).

The navigate Command

A one-shot command the agent writes to the navigate key in the application state. The UI consumes this command, performs the navigation, and then deletes the entry. This enables the agent to programmatically steer the user interface.

Source: [SKILL.md – Core pattern 4](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md#L13-L19).

Writing UI State to the Application Store

The UI-side implementation uses the useNavigationState hook shipped with every template. This hook internally calls useAgentRouteState, which writes the navigation key on every route change and listens for incoming navigate commands.

// app/hooks/use-navigation-state.ts
import { useAgentRouteState } from "@agent-native/core/client";
import { TAB_ID } from "@/lib/tab-id";

export function useNavigationState() {
  useAgentRouteState({
    browserTabId: TAB_ID,
    requestSource: TAB_ID,
    getNavigationState: ({ pathname, searchParams }) => ({
      view: pathname === "/" ? "home" : pathname.slice(1),
      label: searchParams.get("label"),
    }),
    getCommandPath: (command: any) => command.path ?? "/",
  });
}

The requestSource: TAB_ID parameter tags each write with a unique identifier. This tagging allows the UI to distinguish its own writes from those originating from the agent or other tabs, preventing unnecessary re-renders.

Source: lines 30-47 of use-navigation-state.ts as referenced in SKILL.md.

Reading State from the Agent

Agents interact with the application state through explicit read and write operations.

Reading Navigation

To inspect the current UI context, the agent calls readAppState targeting the navigation key:

import { readAppState } from "@agent-native/core/application-state";

const navigation = await readAppState("navigation");
// Example output: { view: "thread", threadId: "abc123", label: "important" }

While the agent receives this data automatically via the <current-screen> block, explicit reads are useful for complex logic that requires checking context before executing actions.

Source: lines 52-56 of SKILL.md.

Commanding Navigation

To change the UI state, the agent writes a navigate command:

import { writeAppState } from "@agent-native/core/application-state";

await writeAppState("navigate", { view: "inbox", threadId: "abc123" });

The UI's useAgentRouteState consumes this command, routes the application accordingly, and then removes the entry from the store.

Source: lines 19-24 of SKILL.md.

Implementing the view-screen Action

The canonical view-screen implementation demonstrates how to assemble a comprehensive screen snapshot. Located in actions/view-screen.ts, this script reads the raw navigation state, augments it with domain-specific data, and returns a structured JSON object.

// actions/view-screen.ts
import { readAppState } from "@agent-native/core/application-state";

export default async function main() {
  const navigation = await readAppState("navigation");
  const url = (await readAppState("__url__")) as {
    searchParams?: Record<string, string>;
  } | null;
  const screen: Record<string, unknown> = { navigation };

  if (url?.searchParams) {
    screen.activeFilters = url.searchParams;
  }

  // Fetch domain data based on current view
  if (navigation?.view === "inbox") {
    const emails = await fetchEmailList(navigation.label);
    screen.emailList = emails;
  }
  
  if (navigation?.threadId) {
    const thread = await fetchThread(navigation.threadId);
    screen.thread = thread;
  }

  console.log(JSON.stringify(screen, null, 2));
}

This action serves as the primary mechanism for exposing rich UI context to the agent beyond simple route metadata.

Source: lines 82-99 of SKILL.md.

Preventing UI Jitter with Source Filtering

When the application state updates, the UI must ignore its own writes to prevent feedback loops. Agent-Native solves this through source filtering in the synchronization layer.

The useDbSync hook accepts an ignoreSource parameter configured with the tab's unique ID:

// app/root.tsx
import { TAB_ID } from "@/lib/tab-id";

useDbSync({
  queryClient,
  ignoreSource: TAB_ID,   // ignore writes originating from this tab
});

This configuration prevents the UI from reacting to its own navigation writes, eliminating needless refetches and ensuring that UI updates are driven only by external changes—such as those from other tabs, server-side scripts, or the agent.

Source: lines 44-51 of SKILL.md.

Complete End-to-End Flow

The context-awareness mechanism operates through a deterministic sequence:

  1. User navigates → Router change triggers useNavigationState to write to the navigation key.
  2. Agent reads → The agent receives <current-screen> (auto-injected) or explicitly reads navigation to understand the context.
  3. Agent enriches → If needed, the agent calls the view-screen action to fetch a detailed snapshot of visible data.
  4. Agent commands → The agent writes a navigate command to application_state using writeAppState.
  5. UI executesuseAgentRouteState consumes the command, performs the navigation, and removes the entry.
  6. Synchronization → All writes are tagged with their source, and useDbSync filters out self-writes to prevent race conditions.

Summary

  • Agent-Native exposes UI state through a shared application-state store containing the navigation and __url__ keys.
  • The useNavigationState hook (using useAgentRouteState) writes semantic navigation data on every route change.
  • The view-screen action assembles rich screen snapshots by combining navigation metadata with domain-specific data fetches.
  • Agents command navigation by writing to the navigate key, which the UI consumes and executes atomically.
  • Source tagging via requestSource and the ignoreSource parameter in useDbSync prevents UI jitter and feedback loops.

Frequently Asked Questions

How does the agent know what the user is currently viewing?

The agent accesses the current UI context through the navigation key in the application-state store, which contains a semantic snapshot of the active view, selected IDs, and other route metadata. Additionally, the __url__ key provides the full URL including search parameters. The agent can also call the view-screen action to retrieve a comprehensive JSON snapshot of the visible screen including domain-specific data.

What prevents the UI from entering an infinite loop when writing navigation state?

Agent-Native implements jitter prevention through source tagging. When the UI writes to the application state, it includes a requestSource identifier (typically TAB_ID). The useDbSync hook is configured with an ignoreSource parameter set to the same ID, causing the UI to ignore writes that originate from itself. This ensures the UI only reacts to external changes from the agent or other tabs.

Can the agent navigate the user to a different screen?

Yes, the agent can programmatically control navigation by writing a command to the navigate key using writeAppState("navigate", { view: "inbox", ... }). The UI's useAgentRouteState hook listens for this key, executes the navigation using the provided path, and then deletes the command from the store. This creates a clean, one-shot command pattern that avoids race conditions.

Where is the context-awareness pattern documented in the source code?

The canonical specification lives in packages/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md. This file defines the four core patterns: the navigation key structure, the __url__ injection, the view-screen action implementation, and the navigate command protocol. Example implementations are also provided in app/hooks/use-navigation-state.ts and actions/view-screen.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →