The @polpo-ai/react package provides type-safe React hooks with real-time SSE updates, built on top of @polpo-ai/client.
For non-React environments (Node.js scripts, Vue, Svelte, server-side), use @polpo-ai/client directly. The React SDK re-exports all client types and classes for convenience.
Installation
npm install @polpo-ai/react
This automatically installs @polpo-ai/client as a dependency. Peer dependencies: React 18 or 19.
PolpoProvider
Wrap your app to provide Polpo context:
import { PolpoProvider } from "@polpo-ai/react";
function App() {
return (
<PolpoProvider
baseUrl="http://localhost:3000"
projectId="my-project"
apiKey="optional-key"
>
<MyApp />
</PolpoProvider>
);
}
Props
| Prop | Type | Required | Description |
|---|
baseUrl | string | yes | Polpo server URL |
projectId | string | yes | Project ID |
apiKey | string | no | API key for authentication |
children | ReactNode | yes | Child components |
autoConnect | boolean | no | Auto-connect to SSE on mount (default: true) |
eventFilter | string[] | no | Glob patterns to filter SSE events |
usePolpo
Access the Polpo client and connection status.
const { client, connectionStatus } = usePolpo();
Returns
| Field | Type | Description |
|---|
client | PolpoClient | HTTP client with request deduplication |
connectionStatus | "connected" | "connecting" | "disconnected" | SSE connection state |
useTasks
List tasks with real-time updates.
const { tasks, isLoading, error, createTask, deleteTask, retryTask, refetch } = useTasks();
Returns
| Field | Type | Description |
|---|
tasks | Task[] | All tasks (auto-updated via SSE) |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
createTask | (opts) => Promise<Task> | Create a new task |
deleteTask | (id) => Promise<void> | Delete a task |
retryTask | (id) => Promise<void> | Retry a failed task |
refetch | () => Promise<void> | Force refetch |
useTask
Single task details with actions.
const { task, isLoading, error, updateTask, deleteTask, killTask, reassessTask, retryTask } = useTask("task-1");
Parameters
| Param | Type | Description |
|---|
taskId | string | Task ID to subscribe to |
Returns
| Field | Type | Description |
|---|
task | Task | undefined | Task object |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
updateTask | (opts) => Promise<Task> | Update description, assignment, expectations |
deleteTask | () => Promise<void> | Delete the task |
killTask | () => Promise<void> | Kill running agent |
reassessTask | () => Promise<void> | Re-run assessment |
retryTask | () => Promise<void> | Retry failed task |
useMissions
List missions with management actions.
const { missions, isLoading, createMission, updateMission, executeMission, resumeMission, abortMission, deleteMission, refetch } = useMissions();
Returns
| Field | Type | Description |
|---|
missions | Mission[] | All missions |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
createMission | (opts) => Promise<Mission> | Save new mission |
updateMission | (id, opts) => Promise<Mission> | Update mission metadata |
executeMission | (id) => Promise<{ tasks: Task[] }> | Execute a draft mission |
resumeMission | (id, opts?) => Promise | Resume failed mission |
abortMission | (id) => Promise<{ aborted: number }> | Abort active mission |
deleteMission | (id) => Promise<void> | Delete mission |
refetch | () => Promise<void> | Force refetch |
useMission
Single mission with actions and completion report.
const { mission, report, isLoading, error, updateMission, executeMission, resumeMission, abortMission, deleteMission } = useMission("mission-1");
Parameters
| Param | Type | Description |
|---|
missionId | string | Mission ID to subscribe to |
Returns
| Field | Type | Description |
|---|
mission | Mission | undefined | Mission object |
report | MissionReport | undefined | Completion report (populated from mission:completed SSE event) |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
updateMission | (opts) => Promise<Mission> | Update mission metadata |
executeMission | () => Promise<{ tasks: Task[] }> | Execute the mission |
resumeMission | (opts?) => Promise | Resume failed mission |
abortMission | () => Promise<{ aborted: number }> | Abort active mission |
deleteMission | () => Promise<void> | Delete mission |
useAgents
Team and agent management.
const { agents, team, isLoading, addAgent, removeAgent, renameTeam, refetch } = useAgents();
Returns
| Field | Type | Description |
|---|
agents | AgentConfig[] | All agents |
team | Team | null | Team info |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
addAgent | (opts) => Promise<void> | Register new agent |
removeAgent | (name) => Promise<void> | Remove agent |
renameTeam | (name) => Promise<void> | Rename the team |
refetch | () => Promise<void> | Force refetch |
useAgent
Single agent by name.
const { agent, isLoading, error, refetch } = useAgent("backend-dev");
Parameters
| Param | Type | Description |
|---|
name | string | Agent name |
Returns
| Field | Type | Description |
|---|
agent | AgentConfig | null | Agent configuration |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
refetch | () => Promise<void> | Force refetch |
useProcesses
Active agent processes.
const { processes, isLoading, error, refetch } = useProcesses();
Returns
| Field | Type | Description |
|---|
processes | AgentProcess[] | Active processes with PID, activity, alive status |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
refetch | () => Promise<void> | Force refetch |
AgentProcess fields:
agentName, pid, taskId, startedAt, alive
activity: { lastTool, lastFile, lastUpdate, toolCalls, filesCreated, filesEdited, summary }
useEvents
SSE event stream.
const { events } = useEvents(["task:*", "assessment:*"], 100);
Parameters
| Param | Type | Default | Description |
|---|
filter | string[] | — | Glob patterns to filter events |
maxEvents | number | 200 | Max events to keep in buffer |
Returns
| Field | Type | Description |
|---|
events | Event[] | Buffered events (newest first) |
useStats
Aggregated statistics. Returns null when not yet connected.
const stats = useStats();
// stats: { pending, running, done, failed, queued } | null
useMemory
Project memory access.
const { memory, isLoading, saveMemory, refetch } = useMemory();
Returns
| Field | Type | Description |
|---|
memory | { exists: boolean, content: string } | null | Memory state |
isLoading | boolean | Initial load in progress |
saveMemory | (content: string) => Promise<void> | Save memory |
refetch | () => Promise<void> | Force refetch |
useSkills
Discover available skills in the project.
const { skills, isLoading, error, refetch } = useSkills();
Returns
| Field | Type | Description |
|---|
skills | SkillInfo[] | Available skills discovered from .polpo/skills/ and other locations |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
refetch | () => Promise<void> | Force refetch |
useNotifications
Notification records and sending.
const { notifications, stats, sendNotification, loading, refetch } = useNotifications({
limit: 50,
status: "sent",
channel: "slack",
});
Parameters
| Param | Type | Description |
|---|
opts.limit | number | Max notifications to fetch |
opts.status | string | Filter by status |
opts.channel | string | Filter by channel |
Returns
| Field | Type | Description |
|---|
notifications | NotificationRecord[] | Notification records |
stats | NotificationStats | null | Aggregate stats (sent, failed, pending counts) |
sendNotification | (req) => Promise<SendNotificationResult> | Send a notification |
loading | boolean | Initial load in progress |
refetch | () => void | Force refetch |
useApprovals
Approval gate management.
const { approvals, pending, approve, reject, loading, refetch } = useApprovals();
Parameters
| Param | Type | Description |
|---|
status | ApprovalStatus | Optional: filter by approval status |
Returns
| Field | Type | Description |
|---|
approvals | ApprovalRequest[] | All approval requests |
pending | ApprovalRequest[] | Only pending approvals |
approve | (id, opts?) => Promise<void> | Approve a request (opts: { resolvedBy?, note? }) |
reject | (id, feedback, resolvedBy?) => Promise<void> | Reject a request |
loading | boolean | Initial load in progress |
refetch | () => void | Force refetch |
usePlaybooks
Playbook discovery and execution.
const { playbooks, loading, getPlaybook, runPlaybook, refetch } = usePlaybooks();
Returns
| Field | Type | Description |
|---|
playbooks | PlaybookInfo[] | Available playbooks |
loading | boolean | Initial load in progress |
getPlaybook | (name) => Promise<PlaybookDefinition> | Fetch full playbook definition |
runPlaybook | (name, params?) => Promise<PlaybookRunResult> | Execute a playbook |
refetch | () => void | Force refetch |
useSessions
Chat session management.
const { sessions, activeSessionId, setActiveSessionId, getMessages, renameSession, deleteSession } = useSessions();
Returns
| Field | Type | Description |
|---|
sessions | ChatSession[] | All chat sessions |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
activeSessionId | string | null | Currently selected session |
setActiveSessionId | (id: string | null) => void | Select a session |
getMessages | (sessionId: string) => Promise<ChatMessage[]> | Fetch messages for a session |
renameSession | (sessionId: string, title: string) => Promise<void> | Rename a session |
deleteSession | (sessionId: string) => Promise<void> | Delete a session |
refetch | () => Promise<void> | Force refetch |
useTaskActivity
Per-task activity history from run logs.
const { entries, isLoading, error, refetch } = useTaskActivity("task-1");
// With auto-polling
const { entries } = useTaskActivity("task-1", { pollIntervalMs: 5000 });
Parameters
| Param | Type | Description |
|---|
taskId | string | null | Task ID to fetch activity for. Pass null to skip. |
options.pollIntervalMs | number | Auto-poll interval in ms (optional) |
Returns
| Field | Type | Description |
|---|
entries | RunActivityEntry[] | Activity log entries (tool calls, file edits, summaries) |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
refetch | () => Promise<void> | Force refetch |
useAuthStatus
Per-provider authentication status: config keys, environment variables, and OAuth profiles (metadata only — tokens are never exposed).
const { authStatus, isLoading, error, refetch } = useAuthStatus();
Returns
| Field | Type | Description |
|---|
authStatus | AuthStatusResponse | null | Per-provider auth health |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
refetch | () => Promise<void> | Force refetch |
AuthStatusResponse.providers is a Record<string, ProviderAuthInfo> with:
| Field | Type | Description |
|---|
hasEnvKey | boolean | API key set via environment variable |
envVar | string? | Environment variable name |
profiles | AuthProfileMeta[] | OAuth / API key profiles |
oauthAvailable | boolean | Whether OAuth login is supported |
oauthProviderName | string? | Human-readable OAuth provider name |
oauthFlow | string? | OAuth flow description |
Each AuthProfileMeta includes id, type ("oauth" | "api_key"), email, status ("active" | "cooldown" | "billing_disabled" | "expired"), expiry, and cooldown details.
useLogs
Event log sessions and entries.
const { sessions, isLoading, getLogEntries, refetch } = useLogs();
Returns
| Field | Type | Description |
|---|
sessions | LogSession[] | All log sessions |
isLoading | boolean | Initial load in progress |
error | Error | null | Fetch error |
getLogEntries | (sessionId: string) => Promise<LogEntry[]> | Fetch entries for a log session |
refetch | () => Promise<void> | Force refetch |
Imports
// Full import (hooks + re-exported client types)
import { PolpoProvider, useTasks, useMissions } from "@polpo-ai/react";
import type { Task, Mission } from "@polpo-ai/react";
// Individual hook imports
import { useTasks } from "@polpo-ai/react/hooks/use-tasks";
// Use the client SDK directly for non-React code
import { PolpoClient } from "@polpo-ai/client";
All types and classes from @polpo-ai/client are re-exported from @polpo-ai/react for backward compatibility.
Architecture
The SDK uses useSyncExternalStore for push-based state management:
@polpo-ai/client — Framework-agnostic HTTP client, SSE manager, reactive store
PolpoProvider — Wires client + SSE + store into React context
- 24 hooks — Thin wrappers that subscribe to the store and expose mutation callbacks
- Memoized selectors — Multi-key cached selectors per hook
- Microtask batching —
queueMicrotask for batched re-renders
The React SDK depends on @polpo-ai/client (auto-installed) and has React as a peer dependency. Both packages are tree-shakeable with sideEffects: false.