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

> Discover how Agent-Native achieves context-awareness by synchronizing UI state to a shared store, enabling agents to read and command navigation seamlessly.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: internals
- Published: 2026-06-28

---

**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`](https://github.com/BuilderIO/agent-native/blob/main/SKILL.md) consists of four tightly-coupled components that give the agent a reliable, real-time view of the UI.

### Navigation State (`navigation` key)

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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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/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`](https://github.com/BuilderIO/agent-native/blob/main/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.

```tsx
// 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`](https://github.com/BuilderIO/agent-native/blob/main/use-navigation-state.ts) as referenced in [`SKILL.md`](https://github.com/BuilderIO/agent-native/blob/main/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:

```ts
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`](https://github.com/BuilderIO/agent-native/blob/main/SKILL.md).

### Commanding Navigation

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

```ts
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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/actions/view-screen.ts), this script reads the raw navigation state, augments it with domain-specific data, and returns a structured JSON object.

```ts
// 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`](https://github.com/BuilderIO/agent-native/blob/main/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:

```tsx
// 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`](https://github.com/BuilderIO/agent-native/blob/main/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 executes** → `useAgentRouteState` 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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/app/hooks/use-navigation-state.ts) and [`actions/view-screen.ts`](https://github.com/BuilderIO/agent-native/blob/main/actions/view-screen.ts).