Skip to main content
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
});
OptionTypeDefaultDescription
baseUrlstringrequiredAPI base URL
apiKeystringProject API key (sk_live_... or sk_test_...)
apiPrefixstringauto/v1 for cloud, /api/v1 for self-hosted
fetchtypeof fetchglobalThis.fetchCustom 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:
PropertyTypeDescription
sessionIdstring | nullSession ID from the x-session-id response header
abortedbooleanWhether abort() was called
askUserAskUserPayload | nullStructured questions when finish_reason is "ask_user"
missionPreviewMissionPreviewPayload | nullProposed 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

FieldTypeDescription
messagesChatCompletionMessage[]Conversation history
agentstringTarget agent name (omit for orchestrator)
sessionIdstringResume an existing session
streambooleanEnable streaming (set automatically by the SDK)
modelstringIgnored — 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:
StatusDescription
connectingOpening the SSE connection
connectedConnected and receiving events
reconnectingConnection lost, retrying with exponential backoff
disconnectedManually disconnected via disconnect()
errorConnection 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:
EventPayload
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.