### Clone Repository and Install Dependencies Source: https://github.com/langchain-ai/agent-inbox/blob/main/README.md Clone the Agent Inbox repository and install its dependencies using yarn. Ensure Node.js and yarn are installed beforehand. ```bash git clone https://github.com/langchain-ai/agent-inbox.git cd agent-inbox yarn install ``` -------------------------------- ### ThreadsProvider Setup and Usage Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Sets up the ThreadsProvider at the app root and demonstrates consuming its context via useThreadsContext to manage thread data and invoke actions. It automatically re-fetches threads based on URL parameters and agent inbox changes. ```tsx import { ThreadsProvider, useThreadsContext } from "@/components/agent-inbox/contexts/ThreadContext"; import { HumanResponse } from "@/components/agent-inbox/types"; // --- Provider setup (once, at app root) --- function App() { return ( ); } // --- Consumer: read threads and invoke actions --- function MyInboxUI() { const { loading, threadData, hasMoreThreads, agentInboxes, fetchThreads, fetchSingleThread, sendHumanResponse, ignoreThread, clearThreadData, addAgentInbox, deleteAgentInbox, changeAgentInbox, updateAgentInbox, } = useThreadsContext(); // Fetch interrupted threads const loadInterrupted = () => fetchThreads("interrupted"); // Send a streamed response and consume events const submitAndStream = async (threadId: string, response: HumanResponse) => { const stream = sendHumanResponse(threadId, [response], { stream: true }); if (!stream) return; for await (const chunk of stream) { if (chunk.data?.event === "on_chain_start") { console.log("Node executing:", chunk.data.metadata?.langgraph_node); } if (chunk.event === "error") { console.error("Stream error:", chunk.data); } } }; // Non-streaming response const submitSync = (threadId: string, response: HumanResponse) => { const run = sendHumanResponse(threadId, [response]); run?.then((r) => console.log("Run created:", r.run_id)); }; return loading ?

Loading...

: ; } ``` -------------------------------- ### Python Example: Using the interrupt function Source: https://github.com/langchain-ai/agent-inbox/blob/main/README.md Demonstrates how to create and send a `HumanInterrupt` request and process the `HumanResponse` in Python. Ensure the `interrupt` function is imported from `langgraph.types`. ```python from typing import TypedDict, Literal, Optional, Union from langgraph.types import interrupt def my_graph_function(state: MyGraphState): # Extract the last tool call from the `messages` field in the state tool_call = state["messages"][-1].tool_calls[0] # Create an interrupt request: HumanInterrupt = { "action_request": { "action": tool_call['name'], "args": tool_call['args'] }, "config": { "allow_ignore": True, "allow_respond": True, "allow_edit": False, "allow_accept": False }, "description": _generate_email_markdown(state) # Generate a detailed markdown description. } # Send the interrupt request, and extract the first response. # The Agent Inbox will always respond with a list of `HumanResponse` objects, although # at this time only a single object will be returned. response = interrupt(request)[0] if response['type'] == "response": # Do something with the response # ...rest of function ``` -------------------------------- ### TypeScript Example: Using the interrupt function Source: https://github.com/langchain-ai/agent-inbox/blob/main/README.md Shows how to use the `interrupt` function to send a `HumanInterrupt` and receive a `HumanResponse` in TypeScript. Import `interrupt`, `HumanInterrupt`, and `HumanResponse` from `@langchain/langgraph`. ```typescript import { interrupt } from "@langchain/langgraph"; import { HumanInterrupt, HumanResponse } from "@langchain/langgraph/prebuilt"; function myGraphFunction(state: MyGraphState) { // Extract the last tool call from the `messages` field in the state const toolCall = state.messages[state.messages.length - 1].tool_calls[0]; // Create an interrupt const request: HumanInterrupt = { action_request: { action: toolCall.name, args: toolCall.args }, config: { allow_ignore: true, allow_respond: true, allow_edit: false, allow_accept: false }, description: _generateEmailMarkdown(state) // Generate a detailed markdown description. }; // Send the interrupt request, and extract the first response. // The Agent Inbox will always respond with an array of `HumanResponse` objects, although // at this time only a single object will be returned. const response = interrupt(request)[0]; if (response.type === "response") { // Do something with the response } // ...rest of function }; ``` -------------------------------- ### createClient Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Creates a configured `@langchain/langgraph-sdk` Client instance. It wraps the LangGraph SDK `Client` constructor with deployment URL and optional API key. The key is sent as an `x-api-key` header. Local deployments (non-HTTPS / localhost) do not require an API key. ```APIDOC ## `createClient` **Creates a configured `@langchain/langgraph-sdk` Client instance.** Wraps the LangGraph SDK `Client` constructor with deployment URL and optional API key. The key is sent as `x-api-key` header. Local deployments (non-HTTPS / localhost) do not require an API key. ```typescript import { createClient } from "@/lib/client"; // For a deployed LangGraph Platform instance const deployedClient = createClient({ deploymentUrl: "https://my-agent.default.us.langgraph.app", langchainApiKey: "lsv2_pt_abc123...", }); // For a local LangGraph server (no key required) const localClient = createClient({ deploymentUrl: "http://localhost:2024", langchainApiKey: undefined, }); // Direct SDK usage after creation const threads = await deployedClient.threads.search({ status: "interrupted", offset: 0, limit: 10, metadata: { graph_id: "my_graph" }, }); const state = await deployedClient.threads.getState("thread-uuid"); // Resume an interrupted thread const run = await deployedClient.runs.create("thread-uuid", "my_graph", { command: { resume: [{ type: "accept", args: null }] }, }); // Streaming resume const stream = deployedClient.runs.stream("thread-uuid", "my_graph", { command: { resume: [{ type: "response", args: "Please proceed." }] }, streamMode: "events", }); for await (const event of stream) { console.log(event); } ``` ``` -------------------------------- ### Create LangGraph Client Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Use `createClient` to instantiate a LangGraph SDK client. Provide the deployment URL and an optional API key for deployed instances. Local deployments do not require an API key. The key is sent as an `x-api-key` header. ```typescript import { createClient } from "@/lib/client"; // For a deployed LangGraph Platform instance const deployedClient = createClient({ deploymentUrl: "https://my-agent.default.us.langgraph.app", langchainApiKey: "lsv2_pt_abc123...", }); // For a local LangGraph server (no key required) const localClient = createClient({ deploymentUrl: "http://localhost:2024", langchainApiKey: undefined, }); // Direct SDK usage after creation const threads = await deployedClient.threads.search({ status: "interrupted", offset: 0, limit: 10, metadata: { graph_id: "my_graph" }, }); const state = await deployedClient.threads.getState("thread-uuid"); // Resume an interrupted thread const run = await deployedClient.runs.create("thread-uuid", "my_graph", { command: { resume: [{ type: "accept", args: null }] }, }); // Streaming resume const stream = deployedClient.runs.stream("thread-uuid", "my_graph", { command: { resume: [{ type: "response", args: "Please proceed." }] }, streamMode: "events", }); for await (const event of stream) { console.log(event); } ``` -------------------------------- ### Fetch Deployment Info from LangGraph Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Fetches metadata from a LangGraph deployment's `/info` endpoint to retrieve `project_id` and `tenant_id`. Returns `null` on any error. Used for generating stable inbox IDs. ```typescript import { fetchDeploymentInfo } from "@/components/agent-inbox/utils"; const info = await fetchDeploymentInfo("https://my-agent.default.us.langgraph.app"); if (info) { console.log(info.host.project_id); // "a1b2c3d4-மையில்" console.log(info.host.tenant_id); // "t9z8y7..." console.log(info.host.revision_id); // "rev_abc" console.log(info.flags.langsmith); // true } // Generate the inbox ID in the new format if (info?.host.project_id) { const inboxId = `${info.host.project_id}:my_graph`; // => "a1b2c3d4-...:my_graph" } ``` -------------------------------- ### AddAgentInboxDialog Component Usage Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Demonstrates different ways to use the AddAgentInboxDialog component, including standard triggering, hiding the built-in trigger, and providing an API key for deployed graph onboarding. The LangSmith API key field appears automatically under specific URL and deployment conditions. ```tsx import { AddAgentInboxDialog } from "@/components/agent-inbox/components/add-agent-inbox-dialog"; import { useState } from "react"; function SettingsPanel() { const [apiKey, setApiKey] = useState(""); return (
{/* Standard trigger button */} {/* Hide built-in trigger (controlled externally) */} {/* With API key field for deployed graph onboarding */} setApiKey(e.target.value)} /> {/* The LangSmith API key field appears automatically when: - no_inboxes_found=true is in the URL, AND - the entered deployment URL is a deployed (HTTPS, non-localhost) URL */}
); } // First-time user flow (triggered automatically): // URL: /?no_inboxes_found=true → dialog opens with welcome message // After adding an inbox: → no_inboxes_found param is cleared, page reloads ``` -------------------------------- ### Convert LangChain Messages to UI and OpenAI Formats Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Use `convertLangchainMessages` for rendering in `@assistant-ui/react` and `convertToOpenAIFormat` for LLM API calls. Both functions require importing from '@/lib/convert_messages'. ```typescript import { convertLangchainMessages, convertToOpenAIFormat, getMessageType, } from "@/lib/convert_messages"; import { HumanMessage, AIMessage, ToolMessage, SystemMessage } from "@langchain/core/messages"; const messages = [ new SystemMessage("You are a helpful assistant."), new HumanMessage("What's the weather?"), new AIMessage({ content: "", tool_calls: [{ id: "tc_1", name: "get_weather", args: { city: "NYC" } }] }), new ToolMessage({ content: "72°F and sunny", tool_call_id: "tc_1", name: "get_weather" }), new AIMessage("It's 72°F and sunny in NYC."), ]; // For @assistant-ui/react rendering const uiMessages = messages.map(m => convertLangchainMessages(m)); // [ // { role: "system", content: [{ type: "text", text: "You are a helpful assistant." }] }, // { role: "user", content: [{ type: "text", text: "What's the weather?" }] }, // { role: "assistant", content: [{ type: "tool-call", toolCallId: "tc_1", toolName: "get_weather", args: {...} }, { type: "text", text: "" }] }, // { role: "tool", toolCallId: "tc_1", toolName: "get_weather", result: "72°F and sunny" }, // { role: "assistant", content: [{ type: "text", text: "It's 72°F and sunny in NYC." }] }, // ] // For OpenAI API calls const openAiMessages = messages.map(convertToOpenAIFormat); // [ // { role: "system", content: "You are a helpful assistant." }, // { role: "user", content: "What's the weather?" }, // { role: "assistant", content: "" }, // { role: "tool", toolName: "get_weather", result: "72°F and sunny" }, // { role: "assistant", content: "It's 72°F and sunny in NYC." }, // ] // Safely detect message type from any LangChain message variant const type = getMessageType(new HumanMessage("hi")); // => "human" ``` -------------------------------- ### Create Default Human Response and Detect Changes Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Builds the initial `HumanResponseWithEdits[]` array and determines the default submit type. It also initializes a ref for change detection. Priority for `defaultSubmitType` is `accept` > `response` > `edit`. Use `haveArgsChanged` to check if arguments have been modified. ```typescript import { createDefaultHumanResponse } from "@/components/agent-inbox/utils"; import { HumanInterrupt } from "@/components/agent-inbox/types"; import { useRef } from "react"; const interrupt: HumanInterrupt = { action_request: { action: "draft_email", args: { subject: "Hello", body: "World" } }, config: { allow_accept: true, allow_edit: true, allow_respond: false, allow_ignore: true }, }; const initialValues = { current: {} as Record }; const { responses, defaultSubmitType, hasAccept } = createDefaultHumanResponse( [interrupt], initialValues ); // responses => [ // { type: "edit", args: { action: "draft_email", args: {...} }, acceptAllowed: true, editsMade: false }, // { type: "ignore", args: null }, // { type: "accept", args: null }, // ] // defaultSubmitType => "accept" // hasAccept => true // initialValues.current => { subject: "Hello", body: "World" } // Check if args have been changed by the user import { haveArgsChanged } from "@/components/agent-inbox/utils"; const changed = haveArgsChanged( { subject: "Hi there", body: "World" }, initialValues.current ); // changed => true (subject differs) ``` -------------------------------- ### Manage URL Search Parameters with useQueryParams Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Use this hook to read and update URL search parameters without full page navigation. It leverages Next.js `router.replace` and reads directly from `window.location.href` to prevent stale closure issues. ```tsx import { useQueryParams } from "@/components/agent-inbox/hooks/use-query-params"; import { INBOX_PARAM, OFFSET_PARAM, LIMIT_PARAM, AGENT_INBOX_PARAM, VIEW_STATE_THREAD_QUERY_PARAM, } from "@/components/agent-inbox/constants"; function PaginationControls() { const { searchParams, updateQueryParams, getSearchParam } = useQueryParams(); const currentOffset = Number(getSearchParam(OFFSET_PARAM) ?? "0"); const currentLimit = Number(getSearchParam(LIMIT_PARAM) ?? "10"); // Advance one page const nextPage = () => updateQueryParams(OFFSET_PARAM, String(currentOffset + currentLimit)); // Go back one page const prevPage = () => updateQueryParams(OFFSET_PARAM, String(Math.max(0, currentOffset - currentLimit))); // Change both offset and limit atomically const setPageSize = (limit: number) => updateQueryParams([OFFSET_PARAM, LIMIT_PARAM], ["0", String(limit)]); // Open a thread detail view const openThread = (threadId: string) => updateQueryParams(VIEW_STATE_THREAD_QUERY_PARAM, threadId); // Navigate back to inbox list (removes the thread param) const closeThread = () => updateQueryParams(VIEW_STATE_THREAD_QUERY_PARAM); // no value = delete return (
Offset: {currentOffset}
); } ``` -------------------------------- ### Construct LangSmith Studio URL Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Generates a LangSmith Studio URL for inspecting a thread. Handles both deployed and local graphs, appending `baseUrl` as a query parameter for local deployments. ```typescript import { constructOpenInStudioURL } from "@/components/agent-inbox/utils"; import { AgentInbox } from "@/components/agent-inbox/types"; // Deployed graph const deployedInbox: AgentInbox = { id: "proj-uuid:my_graph", graphId: "my_graph", deploymentUrl: "https://my-agent.default.us.langgraph.app", tenantId: "tenant-uuid", selected: true, createdAt: "2024-01-01T00:00:00Z", }; const deployedUrl = constructOpenInStudioURL(deployedInbox, "thread-abc-123"); // => "https://smith.langchain.com/studio/thread?organizationId=tenant-uuid&hostProjectId=proj-uuid&threadId=thread-abc-123" // Local graph const localInbox: AgentInbox = { id: "local-uuid", graphId: "my_graph", deploymentUrl: "http://localhost:2024", selected: true, createdAt: "2024-01-01T00:00:00Z", }; const localUrl = constructOpenInStudioURL(localInbox, "thread-abc-123"); // => "https://smith.langchain.com/studio/thread?threadId=thread-abc-123&baseUrl=http%3A%2F%2Flocalhost%3A2024" window.open(localUrl, "_blank"); ``` -------------------------------- ### Integrate AgentInbox Component Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Integrates the AgentInbox component into your application. Ensure it is wrapped within ThreadsProvider and Suspense for proper context and loading state management. Optionally, provide a generic type for typed thread state. ```tsx "use client"; import { AgentInbox } from "@/components/agent-inbox"; import { ThreadsProvider } from "@/components/agent-inbox/contexts/ThreadContext"; import { Suspense } from "react"; export default function Page() { return ( Loading...}>
{/* Optionally pass custom ThreadValues generic for typed thread state */} />
); } // URL states managed automatically: // List view: /?agent_inbox=&inbox=interrupted&offset=0&limit=10 // Thread view: /?agent_inbox=&inbox=interrupted&offset=0&limit=10&view_state_thread_id= ``` -------------------------------- ### SSR-Safe Browser localStorage with useLocalStorage Source: https://context7.com/langchain-ai/agent-inbox/llms.txt This hook provides a server-side rendering-safe wrapper around the browser's `localStorage`. It guards all calls with `typeof window === "undefined"` to prevent errors in Next.js server components or during SSR. It returns `undefined` or `null` on the server or in test environments. ```tsx import { useLocalStorage } from "@/components/agent-inbox/hooks/use-local-storage"; import { LANGCHAIN_API_KEY_LOCAL_STORAGE_KEY } from "@/components/agent-inbox/constants"; function ApiKeyManager() { const { getItem, setItem, removeItem } = useLocalStorage(); // Store the LangSmith API key const saveKey = (key: string) => setItem(LANGCHAIN_API_KEY_LOCAL_STORAGE_KEY, key); // Read it back (returns null if not set, undefined if SSR) const loadKey = (): string | null | undefined => getItem(LANGCHAIN_API_KEY_LOCAL_STORAGE_KEY); // Clear it const clearKey = () => removeItem(LANGCHAIN_API_KEY_LOCAL_STORAGE_KEY); // Custom JSON storage pattern const storeObject = (data: Record) => setItem("my_key", JSON.stringify(data)); const loadObject = (fallback: T): T => { const raw = getItem("my_key"); if (!raw) return fallback; try { return JSON.parse(raw) as T; } catch { return fallback; } }; return (
); } ``` -------------------------------- ### Run Inbox ID Backfill Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Migrates existing inbox IDs to the projectId:graphId format. Runs once per user session. Use `forceInboxBackfill` to re-run unconditionally. ```typescript import { runInboxBackfill, forceInboxBackfill, isBackfillCompleted, clearBackfillFlag, } from "@/components/agent-inbox/utils/backfill"; // Normal startup: runs only if not yet completed const result = await runInboxBackfill(); if (result.success) { console.log("Updated inboxes:", result.updatedInboxes); // Each deployed inbox now has id: "projectId:graphId" } // Force re-run (useful after adding a new inbox or debugging) const forced = await forceInboxBackfill(); console.log("Force backfill succeeded:", forced.success); // Check status console.log("Backfill done?", isBackfillCompleted()); // true after first successful run // Reset for testing clearBackfillFlag(); console.log("Backfill done?", isBackfillCompleted()); // false // Browser console helpers (exposed on window) // window.resetInboxData() — shows debug info and optionally clears all inbox data // window.forceBackfill() — same as forceInboxBackfill() ``` -------------------------------- ### useInboxes Source: https://context7.com/langchain-ai/agent-inbox/llms.txt Hook for full CRUD management of agent inboxes stored in localStorage. Runs a one-time backfill on mount to migrate inbox IDs to the `projectId:graphId` format for deployed graphs. All changes are persisted to `localStorage` key `inbox:agent_inboxes` and reflected in URL query params. ```APIDOC ## `useInboxes` **Hook for full CRUD management of agent inboxes stored in localStorage.** Runs a one-time backfill on mount to migrate inbox IDs to the `projectId:graphId` format for deployed graphs. All changes are persisted to `localStorage` key `inbox:agent_inboxes` and reflected in URL query params. ```tsx import { useInboxes } from "@/components/agent-inbox/hooks/use-inboxes"; import { AgentInbox } from "@/components/agent-inbox/types"; function InboxManager() { const { agentInboxes, // AgentInbox[] — reactive list from localStorage addAgentInbox, // Add and auto-select a new inbox deleteAgentInbox, // Remove by id; selects first remaining changeAgentInbox, // Select a different inbox (updates URL + localStorage) updateAgentInbox, // Edit an existing inbox in-place } = useInboxes(); // Add a new local-dev inbox const handleAdd = () => { addAgentInbox({ id: crypto.randomUUID(), graphId: "my_email_agent", deploymentUrl: "http://localhost:2024", name: "Local Email Agent", selected: false, createdAt: new Date().toISOString(), }); }; // Switch to a different inbox (resets offset/limit/inbox tab) const handleSwitch = (id: string) => changeAgentInbox(id); // Update an existing inbox (e.g., rename) const handleRename = (inbox: AgentInbox) => { updateAgentInbox({ ...inbox, name: "Renamed Agent" }); }; return (
{agentInboxes.map(inbox => (
{inbox.name ?? inbox.graphId}
))}
); } ``` ``` -------------------------------- ### Manage Agent Inboxes with `useInboxes` Hook Source: https://context7.com/langchain-ai/agent-inbox/llms.txt The `useInboxes` hook provides full CRUD management for agent inboxes stored in localStorage. It performs a one-time backfill on mount to migrate inbox IDs to the `projectId:graphId` format for deployed graphs. All changes are persisted to `localStorage` key `inbox:agent_inboxes` and reflected in URL query params. ```tsx import { useInboxes } from "@/components/agent-inbox/hooks/use-inboxes"; import { AgentInbox } from "@/components/agent-inbox/types"; function InboxManager() { const { agentInboxes, // AgentInbox[] — reactive list from localStorage addAgentInbox, // Add and auto-select a new inbox deleteAgentInbox, // Remove by id; selects first remaining changeAgentInbox, // Select a different inbox (updates URL + localStorage) updateAgentInbox, // Edit an existing inbox in-place } = useInboxes(); // Add a new local-dev inbox const handleAdd = () => { addAgentInbox({ id: crypto.randomUUID(), graphId: "my_email_agent", deploymentUrl: "http://localhost:2024", name: "Local Email Agent", selected: false, createdAt: new Date().toISOString(), }); }; // Switch to a different inbox (resets offset/limit/inbox tab) const handleSwitch = (id: string) => changeAgentInbox(id); // Update an existing inbox (e.g., rename) const handleRename = (inbox: AgentInbox) => { updateAgentInbox({ ...inbox, name: "Renamed Agent" }); }; return (
{agentInboxes.map(inbox => (
{inbox.name ?? inbox.graphId}
))}
); } ``` -------------------------------- ### Manage User Interaction State with useInterruptedActions Source: https://context7.com/langchain-ai/agent-inbox/llms.txt This hook manages all user interaction state for a single interrupted thread. It initializes human response state, determines the default submit type, and provides handlers for submission, ignoring, and resolving. Use this hook to stream graph execution after submission and refetch thread state on completion. ```tsx import useInterruptedActions from "@/components/agent-inbox/hooks/use-interrupted-actions"; import { InterruptedThreadData, ThreadData } from "@/components/agent-inbox/types"; import { useState } from "react"; function InterruptHandler({ threadData }: { threadData: InterruptedThreadData }) { const [data, setData] = useState(threadData); const { handleSubmit, handleIgnore, handleResolve, humanResponse, setHumanResponse, selectedSubmitType, setSelectedSubmitType, loading, streaming, streamFinished, currentNode, hasEdited, hasAddedResponse, acceptAllowed, isIgnoreAllowed, supportsMultipleMethods, initialHumanInterruptEditValue, } = useInterruptedActions({ threadData, setThreadData: setData }); return (
{streaming &&

Executing node: {currentNode}...

} {streamFinished &&

Graph run complete.

} {supportsMultipleMethods && (
)} {selectedSubmitType === "response" && (