@pear-protocol/agent-sdk
v0.4.1
Published
TypeScript client for the Pear **agent chat** API — the conversational assistant (pair-trade ideas, market data, and confirm-before-write trade execution).
Readme
@pear-protocol/agent-sdk
TypeScript client for the Pear agent chat API — the conversational assistant (pair-trade ideas, market data, and confirm-before-write trade execution).
@pear-protocol/agent-sdk— framework-agnostic core (AgentChatClient).@pear-protocol/agent-sdk/react— optional React hooks (useAgentChat,useAgentSessions).
Published to public npm (same registry as the org's other @pear-protocol
packages — no .npmrc or token needed). The wire types are a single source of
truth shared with the backend, so they can't silently drift.
Install
pnpm add @pear-protocol/agent-sdk zod
# only if you use the React hooks:
pnpm add reactzod@^4.1.0 is a required peer dependency (the SDK ships zod schemas at
runtime). react@>=18 is required only for the /react hooks.
Quickstart (core)
import { AgentChatClient } from "@pear-protocol/agent-sdk";
const client = new AgentChatClient({
// Inject your env — the SDK NEVER reads it.
baseUrl: import.meta.env.VITE_AGENT_PEAR_API_URL, // Vite
// baseUrl: process.env.NEXT_PUBLIC_AGENT_PEAR_API_URL, // Next.js
getToken: () => auth.accessToken, // returns the freshest JWT; called per request
tokenVersion: "v2", // "v2" wallet JWT (default) | "v3"
});
const { id } = await client.createSession();
for await (const ev of client.streamChat({ sessionId: id, message: "Long ETH short BTC?" })) {
if (ev.type === "token") process.stdout.write(ev.delta);
if (ev.type === "done" && ev.result.pendingAction) {
// A money-moving trade was STAGED, not executed — render Confirm/Cancel.
await client.confirmTicket(ev.result.pendingAction.ticketId);
// ...or: await client.cancelTicket(ev.result.pendingAction.ticketId);
}
}streamChat yields token | thinking | status | sources events, then a
synthetic { type: "done", result }. Token text is delta-only — accumulate
ev.delta. The SSE wire has no terminal done frame; the SDK assembles one. If
the backend emits a mid-stream error, streamChat throws an AgentChatError.
Aborting the stream (the hook's stop(), or your own AbortSignal) also stops
generation server-side — the backend detects the disconnect and winds the
worker job down at the next safe boundary (never mid-trade-write).
There's also a sync client.sendMessage({ sessionId, message }) returning a
ChatResult, and sessions/history methods (listSessions,
listSessionSummaries, createSession, getSession, deleteSession,
getMessages).
Both streamChat and sendMessage require a surface
(pear_v3 | pear_pro | base_mini | web) declaring which Pear client you
are — the API rejects requests without a valid one. The agent applies
per-surface universe and execution policy — e.g. base_mini surfaces only SYMM
assets and is analysis-only. (useAgentChat defaults to pear_v3; set it via
the returned setSurface.) The Telegram surfaces are resolved server-side and
are not client-declarable.
React
import { useAgentChat } from "@pear-protocol/agent-sdk/react";
function Chat({ client }) {
const {
messages, sendMessage, isStreaming, status, stop,
pendingAction, confirmTicket, cancelTicket, mode, setMode, surface, setSurface,
} = useAgentChat({ client });
// render `messages`; if `pendingAction`, show Confirm/Cancel buttons wired to
// confirmTicket(pendingAction.ticketId) / cancelTicket(pendingAction.ticketId)
}useAgentChat uses plain React state (no forced data lib). useAgentSessions(client)
is a minimal session-list hook; apps using TanStack Query can skip it and call the
client directly.
Auth
getToken() returns whatever bearer token the agent API accepts (today a v2
HL-issued JWT). The SDK sends Authorization: Bearer <token> +
X-Auth-Token-Version. It does NOT own sign-in — wire it to your wallet/auth
flow. v3 callers are identity-only (no wallet → trade tickets disabled).
Errors
confirmTicket / cancelTicket throw typed errors: TicketExpiredError (410),
ForbiddenError (403, not your ticket), ConflictError (409, already handled),
AuthError (401). All extend AgentChatError (carries .status).
Wallet linking (trade authority)
The agent can only execute trades for a linked wallet. Linking is self-service — the caller's JWT is the authorization, and the Pear API key is minted server-side (it never exists in the browser):
const { linked } = await client.getLinkStatus(); // backend-verified — don't cache
if (!linked) await client.linkWallet(); // idempotent; {force} re-mints, {code} binds Telegram
await client.unlinkWallet(); // agent loses trade authorityReact apps can use useWalletLink(client) from ./react —
{ linked, isWorking, error, link, unlink, refresh } (linked is null
while the initial status fetch is in flight).
Trade tickets (confirm-before-write)
A turn that would move money returns a pendingAction
({ ticketId, action, consequence, options: ["confirm","cancel"] }) instead of
executing. Render it, then call confirmTicket(ticketId) (executes) or
cancelTicket(ticketId) (discards). setTradeConfirmations(false) opts a user out
of the confirm step entirely.
