> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cedarcopilot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscribing State to Input

> Automatically add state to the agent input context

Cedar's `useSubscribeStateToAgentContext` function allows you to automatically make any Cedar-registered state available to AI agents as context. This enables agents to understand your app's current state and provide more relevant, contextual responses.

> **Prerequisite** – The state you want to subscribe **must first be registered** in Cedar using either [`useCedarState`](/state-access/agentic-state-access#useCedarState) *or* [`useRegisterState`](/state-access/agentic-state-access#useRegisterState-Hook).

## useSubscribeStateToAgentContext Overview

The `useSubscribeStateToAgentContext` function subscribes to local state changes and automatically updates the agent's input context whenever the state changes. This means your AI agent always has access to the most up-to-date information from your application, including any Zod schemas defined for the state.

### Function Signature

```typescript theme={null}
function useSubscribeStateToAgentContext<T>(
	stateKey: string,
	mapFn: (state: T) => Record<string, any>,
	options?: {
		icon?: ReactNode | ((item: ElementType<T>) => ReactNode);
		color?: string;
		labelField?: string | ((item: ElementType<T>) => string);
		order?: number;
		showInChat?: boolean | ((entry: ContextEntry) => boolean);
		collapse?:
			| boolean
			| number
			| {
					threshold: number;
					label?: string;
					icon?: ReactNode;
			  };
	}
): void;
```

### Parameters

* **`stateKey: string`** - The registered state key that we want to subscribe to
* **`mapFn: (state: T) => Record<string, any>`** - Function that maps your state to context entries
* **`options`** (optional) - Configuration for visual representation and label extraction:
  * `icon?: ReactNode | ((item: ElementType<T>) => ReactNode)` - Icon to display for this context. Can be a static React element or a function that returns an icon based on each item
  * `color?: string` - Hex color for visual styling
  * `labelField?: string | ((item: ElementType<T>) => string)` - How to extract labels from your data. Can be a field name or a function
  * `order?: number` - Display order for context badges (lower numbers appear first)
  * `showInChat?: boolean | ((entry: ContextEntry) => boolean)` - Whether to show context badges in chat. Can be a boolean or a function to filter specific entries (default = true)
  * `collapse?: boolean | number | { threshold: number; label?: string; icon?: ReactNode }` - Collapse multiple entries into a single badge. Can be boolean (default threshold 5), number (custom threshold), or object with full configuration

## Basic Usage Example

Here's a simple example with a todo list:

```tsx theme={null}
import { useCedarState, useSubscribeStateToAgentContext } from 'cedar-os';
import { CheckCircle } from 'lucide-react';

function TodoApp() {
	// 1) Register the state in Cedar
	const [todos, setTodos] = useCedarState(
		'todos',
		[
			{ id: 1, text: 'Buy groceries', completed: false },
			{ id: 2, text: 'Walk the dog', completed: true },
		],
		'Todo items'
	);

	// 2) Subscribe that state to input context
	useSubscribeStateToAgentContext(
		'todos',
		(todoList) => ({
			todos: todoList, // Key 'todos' will be available to the agent
		}),
		{
			icon: <CheckCircle />,
			color: '#10B981', // Green color
			labelField: 'text', // Use the 'text' field as label for each todo
		}
	);

	return (
		<div>
			<TodoList todos={todos} onToggle={setTodos} />
			<ChatInput /> {/* Agent can now see todos in context */}
		</div>
	);
}
```

## Complex State Example

Here's a more advanced example from the Product Roadmap demo:

```tsx theme={null}
import { useRegisterState, useSubscribeStateToAgentContext } from 'cedar-os';
import { Box } from 'lucide-react';
import { Node } from 'reactflow';

interface FeatureNodeData {
	title: string;
	status: 'planned' | 'in-progress' | 'completed';
	priority: 'low' | 'medium' | 'high';
	description?: string;
}

function SelectedNodesPanel() {
	const [selected, setSelected] = useState<Node<FeatureNodeData>[]>([]);

	// Register the react-flow selection array in Cedar
	useRegisterState({
		key: 'selectedNodes',
		value: selected,
		description: 'Currently selected nodes in the canvas',
	});

	// Subscribe selected nodes to input context
	useSubscribeStateToAgentContext(
		'selectedNodes',
		(nodes: Node<FeatureNodeData>[]) => ({
			selectedNodes: nodes.map((node) => ({
				id: node.id,
				title: node.data.title,
				status: node.data.status,
				priority: node.data.priority,
				description: node.data.description,
			})),
		}),
		{
			// Dynamic icons based on status
			icon: (node) => {
				switch (node.status) {
					case 'completed':
						return '✅';
					case 'in-progress':
						return '🔄';
					case 'planned':
						return '📋';
					default:
						return <Box />;
				}
			},
			color: '#8B5CF6', // Purple color for selected nodes
			labelField: 'title', // Use title field for labels
			// Only show high priority items in chat
			showInChat: (entry) => {
				const node = entry.data;
				return node.priority === 'high';
			},
			// Collapse when more than 3 nodes selected
			collapse: {
				threshold: 3,
				label: '{count} Selected Features',
				icon: <Box />,
			},
		}
	);

	// Update selection when user selects nodes
	useOnSelectionChange({
		onChange: ({ nodes }) => setSelected(nodes),
	});

	return (
		<div>
			<h4>Selected Nodes</h4>
			{selected.map((node) => (
				<div key={node.id}>{node.data.title}</div>
			))}
		</div>
	);
}
```

## Using the labelField Option

The `labelField` option allows you to specify how labels should be extracted from your data. This is especially useful when your data has custom field names or when you need custom label logic.

### labelField as a String

Specify which field to use as the label:

```tsx theme={null}
const [users, setUsers] = useState([
	{ userId: 'u1', fullName: 'John Doe', email: 'john@example.com' },
	{ userId: 'u2', fullName: 'Jane Smith', email: 'jane@example.com' },
]);

useSubscribeStateToAgentContext(
	users,
	(userList) => ({ activeUsers: userList }),
	{
		labelField: 'fullName', // Will use fullName as the label
		icon: <User />,
		color: '#3B82F6',
	}
);
```

### labelField as a Function

Use a function for custom label generation:

```tsx theme={null}
const [products, setProducts] = useState([
	{ sku: 'P001', name: 'Laptop', price: 999, inStock: true },
	{ sku: 'P002', name: 'Mouse', price: 29, inStock: false },
]);

useSubscribeStateToAgentContext(
	products,
	(productList) => ({ inventory: productList }),
	{
		labelField: (product) =>
			`${product.name} (${product.inStock ? 'In Stock' : 'Out of Stock'})`,
		icon: <Package />,
		color: '#10B981',
	}
);
```

## Dynamic Icons and Conditional Display

Cedar supports dynamic behavior for both icons and chat display through function-based options. This allows you to customize the appearance and visibility of context entries based on their actual data.

### Dynamic Icon Functions

The `icon` option can accept a function that receives each item and returns an appropriate icon:

```tsx theme={null}
import { CheckCircle, Clock, AlertCircle, Archive } from 'lucide-react';

const [tasks, setTasks] = useState([
	{ id: 1, title: 'Design Review', status: 'completed' },
	{ id: 2, title: 'Code Implementation', status: 'in-progress' },
	{ id: 3, title: 'Testing', status: 'pending' },
	{ id: 4, title: 'Documentation', status: 'archived' },
]);

useSubscribeStateToAgentContext(
	'tasks',
	(taskList) => ({ activeTasks: taskList }),
	{
		labelField: 'title',
		color: '#3B82F6',
		// Dynamic icon based on task status
		icon: (task) => {
			switch (task.status) {
				case 'completed':
					return <CheckCircle className='text-green-500' />;
				case 'in-progress':
					return <Clock className='text-blue-500' />;
				case 'pending':
					return <AlertCircle className='text-yellow-500' />;
				case 'archived':
					return <Archive className='text-gray-500' />;
				default:
					return <Clock />;
			}
		},
	}
);
```

### Conditional Chat Display with showInChat

The `showInChat` option can be a function that determines whether specific entries should appear as badges in the chat UI:

```tsx theme={null}
const [products, setProducts] = useState([
	{ id: 1, name: 'Laptop', category: 'electronics', inStock: true },
	{ id: 2, name: 'Notebook', category: 'office', inStock: false },
	{ id: 3, name: 'Phone', category: 'electronics', inStock: true },
]);

useSubscribeStateToAgentContext(
	'products',
	(productList) => ({ inventory: productList }),
	{
		labelField: 'name',
		icon: <Package />,
		color: '#10B981',
		// Only show products that are in stock in the chat UI
		showInChat: (entry) => {
			const product = entry.data;
			return product.inStock === true;
		},
	}
);
```

### Advanced Dynamic Configuration Example

Here's a comprehensive example combining dynamic icons, conditional display, and collapsing from the Product Roadmap demo:

```tsx theme={null}
import { Box } from 'lucide-react';

interface FeatureNodeData {
	title: string;
	status: 'done' | 'in progress' | 'planned' | 'backlog';
	priority: 'high' | 'medium' | 'low';
}

const [selectedNodes, setSelectedNodes] = useState<Node<FeatureNodeData>[]>([]);

useSubscribeStateToAgentContext(
	'selectedNodes',
	(nodes) => ({ selectedNodes: nodes }),
	{
		// Dynamic icons based on node status
		icon: (node) => {
			const status = node?.data?.status;
			switch (status) {
				case 'done':
					return '✅';
				case 'in progress':
					return '🔄';
				case 'planned':
					return '📋';
				case 'backlog':
					return '📝';
				default:
					return <Box />;
			}
		},
		color: '#8B5CF6',
		labelField: (node) => node?.data?.title,
		// Only show nodes that are not in backlog status in chat context
		showInChat: (entry) => {
			const node = entry.data;
			return node?.data?.status !== 'backlog';
		},
		order: 2,
		// Collapse into a single badge when more than 5 nodes are selected
		collapse: {
			threshold: 5,
			label: '{count} Selected Nodes',
			icon: <Box />,
		},
	}
);
```

### Function Parameter Types

When using function-based options, the parameters are strongly typed:

<Tabs>
  <Tab title="Icon Function">
    ```tsx theme={null}
    icon: (item: ElementType<T>) => ReactNode
    ```

    * **`item`** - Individual item from your data array (or the single item if not an array)
    * **Returns** - Any valid React node (JSX element, string, emoji, etc.)
  </Tab>

  <Tab title="showInChat Function">
    ```tsx theme={null}
    showInChat: (entry: ContextEntry) => boolean
    ```

    * **`entry`** - Complete context entry with `id`, `source`, `data`, and `metadata`
    * **`entry.data`** - Your original data item
    * **Returns** - `true` to show the badge, `false` to hide it
  </Tab>

  <Tab title="labelField Function">
    ```tsx theme={null}
    labelField: (item: ElementType<T>) => string
    ```

    * **`item`** - Individual item from your data array
    * **Returns** - String label to display for this item
  </Tab>
</Tabs>

### Best Practices for Dynamic Options

1. **Performance**: Dynamic functions are called for each item, so keep them lightweight
2. **Consistency**: Use consistent logic patterns across your application
3. **Fallbacks**: Always provide fallback values for unexpected data
4. **Type Safety**: Leverage TypeScript for better development experience

```tsx theme={null}
// ✅ Good: Lightweight with fallbacks
icon: (task) => {
	const status = task?.status || 'unknown';
	const iconMap = {
		completed: '✅',
		pending: '⏳',
		failed: '❌',
		unknown: '❓',
	};
	return iconMap[status];
},

// ❌ Avoid: Heavy computations or API calls
icon: (task) => {
	// Don't do expensive operations here
	const complexCalculation = performHeavyComputation(task);
	return getIconFromAPI(complexCalculation); // Async operations won't work
},
```

### Single Values Support

`useSubscribeInputContext` now supports single values (not just arrays):

```tsx theme={null}
const [count, setCount] = useState(42);
const [isEnabled, setIsEnabled] = useState(true);
const [userName, setUserName] = useState('Alice');

// Subscribe a number
useSubscribeStateToAgentContext(count, (value) => ({ itemCount: value }), {
	icon: <Hash />,
	color: '#F59E0B',
});

// Subscribe a boolean
useSubscribeStateToAgentContext(
	isEnabled,
	(value) => ({ featureEnabled: value }),
	{
		icon: <ToggleLeft />,
		color: '#10B981',
	}
);

// Subscribe a string with custom label
useSubscribeStateToAgentContext(userName, (value) => ({ currentUser: value }), {
	labelField: (name) => `User: ${name}`,
	icon: <User />,
	color: '#8B5CF6',
});
```

## Controlling Display Order

The `order` property allows you to control the display order of context badges in the UI. This is useful when you have multiple context subscriptions and want to prioritize their visibility.

### How Order Works

* **Lower numbers appear first**: `order: 1` will appear before `order: 10`
* **Default behavior**: Items without an order are treated as having the maximum order value
* **Stable sorting**: Items with the same order maintain their original relative position

### Basic Order Example

```tsx theme={null}
function PrioritizedContext() {
	const [criticalAlerts, setCriticalAlerts] = useState([]);
	const [normalTasks, setNormalTasks] = useState([]);
	const [archivedItems, setArchivedItems] = useState([]);

	// Critical alerts appear first (order: 1)
	useSubscribeStateToAgentContext(
		criticalAlerts,
		(alerts) => ({ criticalAlerts: alerts }),
		{
			icon: <AlertCircle />,
			color: '#EF4444',
			order: 1, // Highest priority - appears first
		}
	);

	// Normal tasks appear second (order: 10)
	useSubscribeStateToAgentContext(
		normalTasks,
		(tasks) => ({ activeTasks: tasks }),
		{
			icon: <CheckCircle />,
			color: '#3B82F6',
			order: 10, // Medium priority
		}
	);

	// Archived items appear last (order: 100)
	useSubscribeStateToAgentContext(
		archivedItems,
		(items) => ({ archivedItems: items }),
		{
			icon: <Archive />,
			color: '#6B7280',
			order: 100, // Low priority - appears last
		}
	);

	return <ChatInput />;
}
```

### Complex Order Example with Mention Providers

When combining `useSubscribeInputContext` with mention providers, you can create a well-organized context display:

```tsx theme={null}
import { useStateBasedMentionProvider } from 'cedar-os';

function OrganizedWorkspace() {
	const [selectedNodes, setSelectedNodes] = useState([]);
	const [user, setUser] = useState(null);

	// User context appears first (order: 1)
	useSubscribeStateToAgentContext(
		user,
		(userData) => ({ currentUser: userData }),
		{
			icon: <User />,
			color: '#8B5CF6',
			labelField: 'name',
			order: 1, // User info always visible first
		}
	);

	// Selected items appear second (order: 5)
	useSubscribeStateToAgentContext(
		selectedNodes,
		(nodes) => ({ selectedNodes: nodes }),
		{
			icon: <Box />,
			color: '#F59E0B',
			labelField: 'title',
			order: 5, // Selection context after user
		}
	);

	// Mention provider for all nodes (order: 10)
	useStateBasedMentionProvider({
		stateKey: 'nodes',
		trigger: '@',
		labelField: 'title',
		description: 'All nodes',
		icon: <Box />,
		color: '#3B82F6',
		order: 10, // Available nodes after selections
	});

	// Mention provider for connections (order: 20)
	useStateBasedMentionProvider({
		stateKey: 'edges',
		trigger: '@',
		labelField: (edge) => `${edge.source} → ${edge.target}`,
		description: 'Connections',
		icon: <ArrowRight />,
		color: '#10B981',
		order: 20, // Connections appear last
	});

	return <ChatInput />;
}
```

### Order Best Practices

1. **Use consistent spacing**: Leave gaps between order values (1, 10, 20) to allow for future insertions
2. **Group related contexts**: Give similar contexts adjacent order values
3. **Prioritize by importance**: Most relevant context should have lower order values
4. **Document your ordering**: Comment why certain items have specific orders

```tsx theme={null}
// Order schema for our app:
// 1-9: User and session data (critical context)
// 10-19: Current selections and active state
// 20-49: General application state
// 50-99: Historical or computed data
// 100+: Low priority or debug information

useSubscribeStateToAgentContext('sessionData', mapper, { order: 1 }); // Critical
useSubscribeStateToAgentContext('selectedItems', mapper, { order: 10 }); // Active
useSubscribeStateToAgentContext('appState', mapper, { order: 20 }); // General
useSubscribeStateToAgentContext('history', mapper, { order: 50 }); // Historical
useSubscribeStateToAgentContext('debugInfo', mapper, { order: 100 }); // Debug
```

## Collapsing Multiple Entries

The `collapse` option allows you to automatically collapse multiple context entries into a single badge when the number of entries exceeds a threshold. This is particularly useful for managing UI clutter when dealing with large datasets.

### Collapse Configuration Types

The `collapse` option supports three different configuration formats:

<Tabs>
  <Tab title="Boolean (Default)">
    Set to `true` to enable collapsing with default settings:

    ```tsx theme={null}
    useSubscribeStateToAgentContext(
    	'selectedItems',
    	(items) => ({ selectedItems: items }),
    	{
    		collapse: true, // Uses default threshold of 5
    		icon: <Box />,
    		color: '#3B82F6',
    	}
    );
    ```

    When `collapse: true`, items will collapse into a single badge when more than 5 entries are present.
  </Tab>

  <Tab title="Number (Custom Threshold)">
    Specify a custom threshold number:

    ```tsx theme={null}
    useSubscribeStateToAgentContext(
    	'notifications',
    	(notifications) => ({ notifications }),
    	{
    		collapse: 10, // Collapse when more than 10 notifications
    		icon: <Bell />,
    		color: '#F59E0B',
    	}
    );
    ```

    This will collapse entries when the count exceeds the specified number.
  </Tab>

  <Tab title="Object (Full Configuration)">
    Provide complete control over the collapsed appearance:

    ```tsx theme={null}
    useSubscribeStateToAgentContext(
    	'selectedNodes',
    	(nodes) => ({ selectedNodes: nodes }),
    	{
    		collapse: {
    			threshold: 5,
    			label: '{count} Selected Nodes', // {count} will be replaced with actual count
    			icon: <Box />,
    		},
    		color: '#8B5CF6',
    	}
    );
    ```

    The object format allows you to customize the collapsed badge's label and icon.
  </Tab>
</Tabs>

### Collapse Label Templates

When using the object format, the `label` field supports template variables:

* **`{count}`** - Replaced with the actual number of entries
* **Static text** - Any other text is displayed as-is

```tsx theme={null}
// Examples of label templates
collapse: {
	threshold: 3,
	label: '{count} Items', // Displays "5 Items" when 5 entries
}

collapse: {
	threshold: 8,
	label: 'Multiple Selections ({count})', // Displays "Multiple Selections (12)"
}

collapse: {
	threshold: 5,
	label: 'Batch Selection', // Static label, no count shown
}
```

### Real-World Collapse Examples

<CodeGroup>
  ```tsx Shopping Cart theme={null}
  const [cartItems, setCartItems] = useState([
  	{ id: 1, name: 'Laptop', price: 999 },
  	{ id: 2, name: 'Mouse', price: 29 },
  	{ id: 3, name: 'Keyboard', price: 79 },
  	// ... more items
  ]);

  useSubscribeStateToAgentContext(
  'cart',
  (items) => ({ cartItems: items }),
  {
  labelField: 'name',
  icon: <ShoppingCart />,
  color: '#10B981',
  // Collapse when cart has more than 3 items
  collapse: {
  threshold: 3,
  label: '{count} Items in Cart',
  icon: <ShoppingBag />,
  },
  }
  );

  ```

  ```tsx Task Management theme={null}
  const [selectedTasks, setSelectedTasks] = useState([]);

  useSubscribeStateToAgentContext(
  	'selectedTasks',
  	(tasks) => ({ selectedTasks: tasks }),
  	{
  		labelField: 'title',
  		icon: <CheckSquare />,
  		color: '#6366F1',
  		// Simple numeric threshold
  		collapse: 7, // Collapse when more than 7 tasks selected
  	}
  );
  ```

  ```tsx File Selection theme={null}
  const [selectedFiles, setSelectedFiles] = useState([]);

  useSubscribeStateToAgentContext(
  	'selectedFiles',
  	(files) => ({ selectedFiles: files }),
  	{
  		labelField: 'filename',
  		icon: <File />,
  		color: '#EF4444',
  		// Boolean with default behavior
  		collapse: true, // Uses default threshold of 5
  	}
  );
  ```
</CodeGroup>

### Dynamic Collapse Behavior

The collapse feature is dynamic and responsive to state changes:

```tsx theme={null}
function DynamicCollapseExample() {
	const [items, setItems] = useState([]);

	useSubscribeStateToAgentContext(
		'dynamicItems',
		(itemList) => ({ dynamicItems: itemList }),
		{
			collapse: {
				threshold: 4,
				label: 'Multiple Items ({count})',
				icon: <Package />,
			},
			icon: <Box />,
			color: '#8B5CF6',
		}
	);

	// When items.length <= 4: Shows individual badges
	// When items.length > 4: Shows single collapsed badge
	return (
		<div>
			<button onClick={() => setItems([...items, { id: Date.now() }])}>
				Add Item
			</button>
			<ChatInput /> {/* Badges automatically collapse/expand */}
		</div>
	);
}
```

<Tip>
  **Performance Tip**: Collapsing is particularly useful for performance when
  dealing with large datasets, as it reduces the number of DOM elements rendered
  in the chat UI while still providing all the data to the agent.
</Tip>

### Default Label Extraction

When no `labelField` is specified, the function looks for labels in this order:

1. `title` field
2. `label` field
3. `name` field
4. `id` field
5. String representation of the value

When using `useSubscribeInputContext`, entries are automatically marked with `source: 'subscription'`.

## Output Behavior and Structure

### What Gets Sent to the Agent

When the agent receives context from `useSubscribeStateToAgentContext`, it gets a simplified structure containing only the essential data:

```json theme={null}
{
	"contextKey": {
		"source": "subscription",
		"data": {
			/* your actual data */
		}
	}
}
```

<Info>
  The agent only receives the `source` and `data` fields. Visual metadata like
  `icon`, `color`, and `label` are used only for UI display and are not sent to
  the agent.
</Info>

### Array Behavior

The output structure depends on how you pass data to `useSubscribeStateToAgentContext`:

<Tabs>
  <Tab title="Single Value → Single Entry">
    When your `mapFn` returns a single non-array value, it's stored as a single context entry:

    ```tsx theme={null}
    // Input: Single object
    useSubscribeStateToAgentContext('user', (userData) => ({
      currentUser: userData, // Single object
    }));

    // Agent receives:
    {
      "currentUser": {
        "source": "subscription",
        "data": { "id": "123", "name": "John Doe" }
      }
    }
    ```
  </Tab>

  <Tab title="Array → Array of Entries">
    When your `mapFn` returns an array, it's preserved as an array of context entries:

    ```tsx theme={null}
    // Input: Array
    useSubscribeStateToAgentContext('todos', (todoList) => ({
      todos: todoList, // Array of todos
    }));

    // Agent receives:
    {
      "todos": [
        { "source": "subscription", "data": { "id": 1, "text": "Buy groceries" }},
        { "source": "subscription", "data": { "id": 2, "text": "Walk dog" }}
      ]
    }
    ```
  </Tab>

  <Tab title="Single-Item Array → Array">
    Even single-item arrays are preserved as arrays:

    ```tsx theme={null}
    // Input: Array with one item
    useSubscribeStateToAgentContext('selected', (selection) => ({
      selectedItems: [selection[0]], // Single-item array
    }));

    // Agent receives:
    {
      "selectedItems": [
        { "source": "subscription", "data": { "id": "item1", "title": "Selected Item" }}
      ]
    }
    ```
  </Tab>
</Tabs>

### Practical Examples

Here are real-world examples showing the input and output:

<CodeGroup>
  ```tsx React Component theme={null}
  // Example 1: User profile (single value)
  const [user] = useState({ id: '123', name: 'Alice', role: 'admin' });

  useSubscribeStateToAgentContext('user', (userData) => ({
  currentUser: userData, // Single object
  }), { labelField: 'name' });

  ```

  ```json Agent Context theme={null}
  {
    "currentUser": {
      "source": "subscription",
      "data": {
        "id": "123",
        "name": "Alice",
        "role": "admin"
      }
    }
  }
  ```
</CodeGroup>

<CodeGroup>
  ```tsx React Component   theme={null}
  // Example 2: Shopping cart (array)
  const [cart] = useState([
    { id: 'p1', name: 'Laptop', price: 999 },
    { id: 'p2', name: 'Mouse', price: 29 }
  ]);

  useSubscribeStateToAgentContext('cart', (items) => ({
  cartItems: items, // Array
  }), { labelField: 'name' });

  ```

  ```json Agent Context theme={null}
  {
    "cartItems": [
      {
        "source": "subscription",
        "data": { "id": "p1", "name": "Laptop", "price": 999 }
      },
      {
        "source": "subscription",
        "data": { "id": "p2", "name": "Mouse", "price": 29 }
      }
    ]
  }
  ```
</CodeGroup>

<CodeGroup>
  ```tsx React Component theme={null}
  // Example 3: Single selected item (preserved as array)
  const [selected] = useState([
    { id: 'node1', title: 'Important Feature', status: 'planned' }
  ]);

  useSubscribeStateToAgentContext('selection', (nodes) => ({
  selectedNodes: nodes, // Single-item array
  }), { labelField: 'title' });

  ```

  ```json Agent Context theme={null}
  {
    "selectedNodes": [
      {
        "source": "subscription",
        "data": {
          "id": "node1",
          "title": "Important Feature",
          "status": "planned"
        }
      }
    ]
  }
  ```
</CodeGroup>

<Tip>
  **Why preserve array structure?** This allows the agent to understand whether
  you're working with a single item or a collection, even when that collection
  has only one item. This distinction can be important for generating
  appropriate responses.
</Tip>

### Multiple Context Keys

When you return multiple keys from your `mapFn`, each follows the same behavior rules:

```tsx theme={null}
useSubscribeStateToAgentContext('appState', (state) => ({
	currentUser: state.user, // Single object → single entry
	activeTasks: state.tasks, // Array → array of entries
	selectedItems: [state.selected], // Single-item array → array
	preferences: state.prefs, // Single object → single entry
}));

// Agent receives all four keys with appropriate structures
```

## Multiple State Subscriptions

You can subscribe multiple pieces of state:

```tsx theme={null}
function MyApp() {
	const [user, setUser] = useState(null);
	const [preferences, setPreferences] = useState({});
	const [currentPage, setCurrentPage] = useState('/dashboard');

	// Subscribe user data
	useSubscribeStateToAgentContext(
		user,
		(userData) => ({
			currentUser: userData
				? {
						id: userData.id,
						name: userData.name,
						role: userData.role,
				  }
				: null,
		}),
		{
			icon: <User />,
			color: '#3B82F6',
		}
	);

	// Subscribe preferences
	useSubscribeStateToAgentContext(
		preferences,
		(prefs) => ({
			userPreferences: prefs,
		}),
		{
			icon: <Settings />,
			color: '#6B7280',
		}
	);

	// Subscribe navigation state
	useSubscribeStateToAgentContext(
		currentPage,
		(page) => ({
			currentPage: {
				path: page,
				timestamp: new Date().toISOString(),
			},
		}),
		{
			icon: <Navigation />,
			color: '#F59E0B',
		}
	);

	return <YourAppContent />;
}
```

## Best Practices

### 1. Transform Sensitive Data

Don't expose sensitive information to the agent:

```tsx theme={null}
useSubscribeStateToAgentContext('userProfile', (profile) => ({
	user: {
		name: profile.name,
		tier: profile.subscriptionTier,
		preferences: profile.preferences,
		// Don't include: email, password, tokens, etc.
	},
}));
```

### 2. Use Meaningful Keys

Choose descriptive keys for your context:

```tsx theme={null}
useSubscribeStateToAgentContext('shoppingCart', (cart) => ({
	shoppingCart: cart.items, // Clear and descriptive
	cartTotal: cart.total,
	cartItemCount: cart.items.length,
}));
```

### 3. Optimize Large Data Sets

For large data sets, consider filtering or summarizing:

```tsx theme={null}
useSubscribeStateToAgentContext(allTransactions, (transactions) => ({
	recentTransactions: transactions
		.slice(0, 10) // Only last 10 transactions
		.map((t) => ({
			id: t.id,
			amount: t.amount,
			date: t.date,
			// Exclude detailed metadata
		})),
	transactionSummary: {
		total: transactions.length,
		totalAmount: transactions.reduce((sum, t) => sum + t.amount, 0),
	},
}));
```

## Visual Customization

The `options` parameter allows you to customize how the context appears in the UI:

```tsx theme={null}
import { Star, AlertCircle, CheckCircle } from 'lucide-react';

// Different colors and icons for different priorities
useSubscribeStateToAgentContext(
	highPriorityTasks,
	(tasks) => ({ highPriorityTasks: tasks }),
	{
		icon: <AlertCircle />,
		color: '#EF4444', // Red for high priority
	}
);

useSubscribeStateToAgentContext(
	completedTasks,
	(tasks) => ({ completedTasks: tasks }),
	{
		icon: <CheckCircle />,
		color: '#10B981', // Green for completed
	}
);

useSubscribeStateToAgentContext(
	starredItems,
	(items) => ({ starredItems: items }),
	{
		icon: <Star />,
		color: '#F59E0B', // Yellow for starred
	}
);
```

## Integration with Chat Input

The subscribed context automatically becomes available to your AI agent when using Cedar's chat components:

```tsx theme={null}
import { ChatInput } from 'cedar-os-components';

function MyChat() {
	// Your useSubscribeInputContext calls here...

	return (
		<div>
			{/* Context is automatically included when user sends messages */}
			<ChatInput placeholder='Ask me about your todos, selected nodes, or anything else...' />
		</div>
	);
}
```

The agent will receive the context in a structured format and can reference it when generating responses, making the conversation more contextual and relevant to your application's current state.
