Skip to content

Shared State (Tandem)

Open in ChatGPT Open in Claude

While AI Actions let the agent perform tasks, Shared State gives the agent eyes and hands inside your application. It creates a two-way sync between your app’s UI state (like a form or settings panel) and the agent. The agent reads the current state of your UI in real time and writes changes back to it.

  • Contextual awareness — the agent sees exactly what the user sees, so it can give relevant help without asking clarifying questions.
  • Interactive guidance — instead of just describing steps, the agent can highlight fields, pre-fill values, and walk users through complex workflows.
  • Seamless task automation — the agent can update your UI directly, turning multi-step processes into a single conversation.
  • Your App → Agent: your app calls shareState() whenever the UI changes, keeping the agent’s picture of the current screen up to date.
  • Agent → Your App: when the agent decides to act, it pushes updates through the handler you registered, and your UI re-renders instantly.
flowchart LR
  subgraph Your App
      UI[UI Component]
      SF["shareState(key, state)"]
  end
  subgraph Foldspace
      AG[Agent]
  end
  UI -- "User types ➜ setForm()" --> SF
  SF -- "State snapshot" --> AG
  AG -- "Agent updates ➜ handler(key, next)" --> UI

Two methods on the agent instance manage the whole process.

shareState(key, state, handler, stateDescription)

Section titled “shareState(key, state, handler, stateDescription)”

Establish and update synchronized state. Call it once on initialization, then again every time the user-side state changes.

ParameterTypeRequiredDescription
keystringYesA unique string identifier for this piece of state (e.g. "contact_form").
stateobjectYesA snapshot of the current UI state object (e.g. { name: "", email: "" }).
handler(key: string, nextState: any) => voidNoOptional, but required to let the agent push updates back to your UI. Pass it for two-way sync.
stateDescriptionstringNoA free-form string that adds context about the state. Can describe the type/model, usage instructions, or provide a human-readable explanation.
persistAcrossPagesbooleanNo
// Initial sync: Register the state key, initial data, and the handler
const agentKey = "YOUR_AGENT_KEY";
const agent = window.foldspace?.agent(agentKey);
agent.current?.shareState(stateKey, form, handleStateChange, stateDescription);

stateDescription is optional but gives context to both developers and the agent. Though concise by design, it can carry different kinds of guidance:

  • Naming conventions: e.g. "All keys in this state should be snake_case".
  • Model structure hints: e.g. "If 'items' is an empty array, each entry should contain { id: string, quantity: number }".
  • Purpose description: e.g. "Tracks the shipping address details required for checkout".

stateDescription enriches the state with descriptive or instructional metadata, so the UI and the agent interpret the state consistently.

Tell the agent to stop tracking a piece of state. Call it for cleanup when a component unmounts or the user navigates away, preventing memory leaks.

ParameterTypeRequiredDescription
keystringYesThe unique string identifier for the state you wish to clear.

Synchronize a React form component’s state with the agent using hooks.

import { useEffect, useRef, useState } from "react";
type FormState = {
name: string;
email: string;
company?: string;
};
interface Props {
agentKey: string;
}
export default function SharedStateForm({ agentKey }: Props) {
// 1. Local UI state is kept minimal.
const [form, setForm] = useState<FormState>({ name: "", email: "", company: "" });
// 2. The agent instance is created once and stored in a ref.
const agentRef = useRef<any | null>(null);
const stateKey = "contact_form";
const stateDescription = `
type FormState = {
name: string;
email: string;
company?: string;
};
interface Props {
agentKey: string;
}`;
// 3. `useEffect` handles the entire lifecycle: setup, subscription, and cleanup.
useEffect(() => {
// Initialize the agent
agentRef.current = window.foldspace?.agent(agentKey);
// Agent -> UI: Define the handler to receive updates FROM the agent
const handleStateChange = (key: string, nextState: unknown) => {
if (key === stateKey) {
setForm(nextState as FormState); // Update local state
}
};
// Initial sync: Register the state key, initial data, and the handler
agentRef.current?.shareState(stateKey, form, handleStateChange, stateDescription);
// Cleanup function: Tell the agent to stop tracking on unmount
return () => {
agentRef.current?.clearState(stateKey);
agentRef.current = null;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [agentKey]);
// 4. UI -> Agent: Call this on user input to keep the agent's snapshot fresh.
const updateField = <K extends keyof FormState>(key: K, value: FormState[K]) => {
const next = { ...form, [key]: value };
setForm(next);
// Send the fresh snapshot to the agent
agentRef.current?.shareState(stateKey, next);
};
// 5. Render your form inputs and call updateField on change.
return <>...</>;
}
sequenceDiagram
  participant App as Your App
  participant SDK as Foldspace SDK
  participant Agent as Agent
  App->>SDK: shareState("form", data, handler)
  SDK->>Agent: State snapshot
  Note right of Agent: Agent reads form context
  Agent-->>SDK: Updated state
  SDK-->>App: handler("form", nextState)
  App->>App: setForm(nextState)
  • AI Actions: give your agent custom actions that automate workflows and return context-aware responses.
  • Knowledge Base: connect a knowledge base for accurate, context-rich answers.