The @polpo-ai/sdk package provides a typed HTTP client, SSE streaming, and chat completions for interacting with Polpo from any TypeScript or JavaScript environment.
Install
npm install @polpo-ai/sdk
Quick start
import { PolpoClient } from "@polpo-ai/sdk";
const client = new PolpoClient({
baseUrl: "https://api.polpo.sh",
apiKey: "sk_live_...", // your project API key
});
// Talk to an agent
const stream = client.chatCompletionsStream({
agent: "backend-dev",
messages: [{ role: "user", content: "Refactor the auth module" }],
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
console.log("\nSession:", stream.sessionId);
Configuration
const client = new PolpoClient({
baseUrl: "https://api.polpo.sh", // Polpo
apiKey: "sk_live_...", // project API key
});
| Option | Type | Default | Description |
|---|
baseUrl | string | required | API base URL |
apiKey | string | — | Project API key (sk_live_... or sk_test_...) |
apiPrefix | string | auto | /v1 for cloud, /api/v1 for self-hosted |
fetch | typeof fetch | globalThis.fetch | Custom fetch implementation |
The SDK auto-detects the API prefix based on the base URL. Cloud (*.polpo.sh) uses /v1, self-hosted uses /api/v1. Override with apiPrefix if needed.
Chat completions
The completions API is OpenAI-compatible. Target a specific agent with the agent field, or omit it to talk to the orchestrator.
Streaming
const stream = client.chatCompletionsStream({
agent: "researcher",
messages: [{ role: "user", content: "Find recent papers on RAG" }],
sessionId: "existing-session-id", // optional — resume a conversation
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
// Metadata available after streaming
console.log("Session ID:", stream.sessionId);
console.log("Aborted?", stream.aborted);
The ChatCompletionStream is an AsyncIterable that also exposes metadata:
| Property | Type | Description |
|---|
sessionId | string | null | Session ID from the x-session-id response header |
aborted | boolean | Whether abort() was called |
askUser | AskUserPayload | null | Structured questions when finish_reason is "ask_user" |
missionPreview | MissionPreviewPayload | null | Proposed mission when finish_reason is "mission_preview" |
Aborting a stream
const stream = client.chatCompletionsStream({ agent: "dev", messages });
setTimeout(() => stream.abort(), 10_000); // cancel after 10s
for await (const chunk of stream) {
// loop exits cleanly when aborted
}
Non-streaming
const response = await client.chatCompletions({
agent: "backend-dev",
messages: [{ role: "user", content: "Explain the auth flow" }],
});
console.log(response.choices[0].message.content);
Request fields
| Field | Type | Description |
|---|
messages | ChatCompletionMessage[] | Conversation history |
agent | string | Target agent name (omit for orchestrator) |
sessionId | string | Resume an existing session |
stream | boolean | Enable streaming (set automatically by the SDK) |
model | string | Ignored — Polpo uses the agent’s configured model |
Sessions
Sessions persist conversation history across multiple completions calls.
// List all sessions
const { sessions } = await client.getSessions();
// Get messages for a session
const { session, messages } = await client.getSessionMessages("session-id");
// Rename a session
await client.renameSession("session-id", "Auth refactor discussion");
// Delete a session
await client.deleteSession("session-id");
Agents
// List all agents
const agents = await client.getAgents();
// Get a specific agent
const agent = await client.getAgent("backend-dev");
// Add a new agent
await client.addAgent({
name: "qa-engineer",
role: "Writes and runs tests",
model: "anthropic:claude-sonnet-4-5",
allowedTools: ["read", "write", "edit", "bash", "glob", "grep"],
});
// Update an agent
await client.updateAgent("qa-engineer", { maxTurns: 200 });
// Remove an agent
await client.removeAgent("qa-engineer");
// List teams
const teams = await client.getTeams();
Memory
// Shared memory (all agents)
const { content } = await client.getMemory();
await client.saveMemory("Updated project context...");
// Per-agent memory
const agentMem = await client.getAgentMemory("backend-dev");
await client.saveAgentMemory("backend-dev", "Prefers functional patterns...");
Vault
Manage encrypted credentials. The API never exposes raw secret values in list operations.
// Save a credential
await client.saveVaultEntry({
agent: "emailer",
service: "smtp",
type: "smtp",
label: "Company SMTP",
credentials: { host: "smtp.company.com", user: "bot@company.com", pass: "..." },
});
// List entries (metadata only — no secret values)
const entries = await client.listVaultEntries("emailer");
// Update specific fields (merge, not replace)
await client.patchVaultEntry("emailer", "smtp", {
credentials: { pass: "new-password" },
});
// Remove
await client.removeVaultEntry("emailer", "smtp");
Skills
// List skills with agent assignments
const skills = await client.getSkills();
// Assign a skill to an agent
await client.assignSkill("code-review", "backend-dev");
// Unassign
await client.unassignSkill("code-review", "backend-dev");
Real-time events (SSE)
Subscribe to live events from your project using the EventSourceManager.
import { EventSourceManager } from "@polpo-ai/sdk";
const events = new EventSourceManager({
url: client.getEventsUrl(), // includes auth
onEvent: (event) => {
console.log(`[${event.event}]`, event.data);
},
onStatusChange: (status) => {
console.log("Connection:", status);
},
});
events.connect();
// Later...
events.disconnect();
Connection status
The onStatusChange callback receives one of:
| Status | Description |
|---|
connecting | Opening the SSE connection |
connected | Connected and receiving events |
reconnecting | Connection lost, retrying with exponential backoff |
disconnected | Manually disconnected via disconnect() |
error | Connection failed |
Filtering events
Pass event type prefixes to getEventsUrl to receive only specific events:
const url = client.getEventsUrl(["agent:", "session:"]);
Event types
Events follow the pattern category:action. Key categories for agents:
| Event | Payload |
|---|
agent:spawned | { taskId, agentName, taskTitle } |
agent:finished | { taskId, agentName, exitCode, duration, sessionId } |
agent:activity | { taskId, agentName, tool, file, summary } |
session:created | { sessionId, title } |
message:added | { sessionId, messageId, role } |
Error handling
All methods throw PolpoApiError on failure:
import { PolpoApiError } from "@polpo-ai/sdk";
try {
await client.getAgent("nonexistent");
} catch (err) {
if (err instanceof PolpoApiError) {
console.log(err.message); // human-readable message
console.log(err.code); // "NOT_FOUND", "AUTH_REQUIRED", etc.
console.log(err.statusCode); // HTTP status
}
}
Request deduplication
Concurrent identical GET requests are automatically deduplicated. If you call getAgents() twice before the first resolves, only one HTTP request is made and both callers receive the same result.