@pouchy_ai/companion-sdk - v0.1.0
    Preparing search index...

    Interface CompanionClientOptions

    interface CompanionClientOptions {
        appContext?: { description?: string; name?: string };
        baseUrl: string;
        contextKinds?: string[];
        debug?: boolean | ((event: CompanionDebugEvent) => void);
        fetch?: {
            (input: URL | RequestInfo, init?: RequestInit): Promise<Response>;
            (input: string | URL | Request, init?: RequestInit): Promise<Response>;
        };
        handles?: string[];
        modalities?: string[];
        onAuthError?: () => string | Promise<string | null> | null;
        outboxStore?: OutboxStore;
        queueOffline?: boolean;
        replayPendingToolCalls?: boolean;
        requestTimeoutMs?: number;
        stream?: "websocket" | "sse";
        surface?: string;
        token: string;
        tools?: CompanionToolDecl[];
        visitor?: { displayName?: string; id: string };
        webSocketImpl?: {
            CLOSED: 3;
            CLOSING: 2;
            CONNECTING: 0;
            OPEN: 1;
            prototype: WebSocket;
            new (url: string | URL, protocols?: string | string[]): WebSocket;
        };
    }
    Index
    appContext?: { description?: string; name?: string }

    Static description of THIS app/game so the companion is grounded in where it is (drives reasoning + the call opener). Live state still flows via sendWorldState.

    baseUrl: string

    Origin of the Pouchy deployment, e.g. "https://pouchy.ai".

    contextKinds?: string[]

    World-state kinds this surface emits (declared at handshake).

    debug?: boolean | ((event: CompanionDebugEvent) => void)

    Instrumentation (0.32.0). true logs structured events via console.debug('[pouchy-sdk]', …); a function receives every CompanionDebugEvent (HTTP request/response, delivered envelope, stream-state transition, synthesized error) for your own logger / devtools / test recorder. Events never carry the token or any header — method, path, status, timing, envelope type/id only. Zero cost when unset.

    fetch?: {
        (input: URL | RequestInfo, init?: RequestInit): Promise<Response>;
        (input: string | URL | Request, init?: RequestInit): Promise<Response>;
    }

    Injectable fetch (for Node < 18 / tests). Defaults to global fetch.

    Type Declaration

      • (input: URL | RequestInfo, init?: RequestInit): Promise<Response>
      • Parameters

        • input: URL | RequestInfo
        • Optionalinit: RequestInit

        Returns Promise<Response>

      • (input: string | URL | Request, init?: RequestInit): Promise<Response>
      • Parameters

        • input: string | URL | Request
        • Optionalinit: RequestInit

        Returns Promise<Response>

    handles?: string[]

    Action types this surface can perform (declared at handshake). Stored but never consumed server-side today — a client-side capability hint whose one SDK effect is opting a tool-less embed into the auto-offered host-control verbs on connectCall() voice sessions. It does NOT register tools for text turns: a tool you want called on a text turn must be declared in tools.

    modalities?: string[]

    Requested I/O modalities (intersected with the token's grant server-side).

    onAuthError?: () => string | Promise<string | null> | null

    Called when a request or the event stream is rejected with 401 (expired / revoked token — session tokens live 1h by default). Return a fresh token (e.g. re-minted by your backend via POST /v1/sessions) and the client retries transparently; return null to give up, surfacing the failure exactly as without this hook. Scope denials (403 missing_scope) are configuration errors and never trigger it. Alternative: call setToken() proactively on your own refresh schedule.

    outboxStore?: OutboxStore

    Persistence for the offline queue (see queueOffline). Defaults to localStorage when available (privacy-mode / quota failures fall back silently), else an in-memory page-lifetime store. The key is pouchy-outbox:${surface}, so each surface keeps its own queue.

    queueOffline?: boolean

    Opt-in offline send queue (0.34.0). A sendText that fails at the NETWORK level (fetch rejected before any HTTP response, or navigator.onLine is false) is queued instead of lost, and replayed FIFO once the server is reachable again — after a successful connect(), on every stream reconnect, or via flushOutbox(). Replays reuse the ORIGINAL turnId: the server dedupes completed turnIds (10 min TTL, last 8 per session), so a retry after an ambiguous failure can never double-run a turn. HTTP error responses (4xx/5xx incl. 429) are NEVER queued — the server saw those; they keep the normal throw semantics. Default false.

    replayPendingToolCalls?: boolean

    Replay of pause-orphaned tool calls (0.37.0). When connect() resumes a session whose turn is paused on tool calls THIS surface never answered (the page reloaded / crashed mid-pause), the handshake carries them as pendingToolCalls and the client re-emits each to your onToolCall handler with replayed: true — post the results as usual and the paused turn completes instead of being abandoned. Register onToolCall BEFORE calling connect()/start() to receive them. The result apply is idempotent per call id, but the APP-side effect may not be: if a tool has side effects, treat call.id as an idempotency key when replayed is set. Pass false to disable the re-emit — the calls stay readable on the HelloAck.pendingToolCalls you get from connect(). Default true.

    requestTimeoutMs?: number

    Deadline for a request to produce RESPONSE HEADERS, in ms (0.35.0). Bounds every HTTP call the client makes (connect, sendText, recall, …) so a server that accepts the TCP connection but never answers can't hang a helper forever — previously only sendText({ awaitReply }) had any deadline. Reading a response BODY (the SSE event stream, a streaming sendText reply) is never bounded by this — the timer is cleared the moment headers arrive. Composes with any per-call AbortSignal (whichever fires first wins); the timeout rejects as a status-0 CompanionError with code 'request_timeout'. Default 30_000; 0 disables.

    stream?: "websocket" | "sse"

    Receive transport for the reply stream. 'sse' (default) works on every deploy; 'websocket' opts into the lower-latency WS plane when the host supports it (adapter-node / a WS gateway) and falls back to SSE if the socket can't open — so enabling it can never stop reply delivery.

    surface?: string

    Logical surface (one resumable session per surface), default "default".

    token: string

    A Pouchy access token — normally a session token from POST /v1/sessions; a PAT (pchy_…) for personal integrations.

    Tools the companion may ask this surface to perform (companion.tool_call).

    visitor?: { displayName?: string; id: string }

    Open a REPRESENTATIVE ("on-behalf-of" / 代聊) session: the companion fields this VISITOR's messages on the owner's behalf — customer-service style — drawing only on screened owner context (persona + share-screened facts, plus the knowledge base when the owner's token grants expose:knowledge). Each visitor (stable opaque id, 8-64 url-safe chars that YOU keep stable per end-user) gets their own continuous thread. Requires the token's represent scope — without it the handshake is rejected (never silently downgraded to an owner-facing session).

    webSocketImpl?: {
        CLOSED: 3;
        CLOSING: 2;
        CONNECTING: 0;
        OPEN: 1;
        prototype: WebSocket;
        new (url: string | URL, protocols?: string | string[]): WebSocket;
    }

    Injectable WebSocket constructor (Node without a global WebSocket, or tests). Defaults to globalThis.WebSocket when present.