Pingerchips LogoPingerchips
Chat / SessionsReference

Client SDK

The browser-side chat API — PingerchipsChat, ChatSession, and View.

import { PingerchipsChat } from 'pingerchips-js/chat';

Also exported from pingerchips-js/chat: ChatSession, View, ActiveRun.

Building a React chat UI on the Vercel AI SDK? Use @pingerchips/ai instead — it gives you a useChat() transport with resumable streams. This page is the lower-level primitive.


PingerchipsChat

Constructor

new PingerchipsChat(appKey: string, options?: ChatOptions)

The first argument is the App Key string.

OptionTypeDescription
endpointstringWebSocket URL. Default wss://queue.pingerchips.com/socket (prod) / ws://localhost:4000/socket (dev).
realtimePingerchipsReuse an existing Pingerchips socket instead of opening a new one.
authEndpointstringURL your app exposes that mints a capability token (via POST /api/auth). The SDK POSTs { socket_id, thread_id, client_id } and expects { auth } back.
authCallbackfunctionAlternative to authEndpoint(params, done) => … where params is { socket_id, thread_id, client_id }. Call done(null, token) or return the token.
authHeadersobjectExtra headers on the authEndpoint request.
authParamsobjectExtra body fields on the authEndpoint request.
serializer / messageFormat"msgpack"Switch the socket to MessagePack binary frames.
reconnectAfterMs / rejoinAfterMsnumber | fnPhoenix socket backoff.
autoConnectbooleanfalse disconnects the socket immediately after construction.

connect(threadId, options?)

connect(threadId: string, options?: {
  clientId: string;      // required — identifies the human
  afterLogId?: number;   // resume: replay entries after this log_id
}): Promise<ChatSession>

Joins the thread channel as a human client and returns a ChatSession. Resolves the auth token via authEndpoint / authCallback first.

const chat = new PingerchipsChat('YOUR_APP_KEY', {
  authEndpoint: '/chat/auth',
});
const session = await chat.connect('thread-uuid', { clientId: 'user-42' });

connectAgent(threadId, options?)

connectAgent(threadId: string, options?: {
  secret: string;        // your App Secret — server-side only
  agentId: string;
  afterLogId?: number;
}): Promise<ChatSession>

Joins as an agent (authenticates with the App Secret directly). Prefer the Agent SDK for agent processes — it wraps this with a run lifecycle.


ChatSession

Returned by connect() / connectAgent(). Properties: threadId, appKey, view.

MethodDescription
cancel(runId)PromiseCancel an in-progress run.
push(event, payload)PromiseLow-level: push a raw channel event.
close()Leave the thread channel.
on(event, cb)thisSubscribe (delegates to view — see events below).
off(event, cb)thisUnsubscribe.

All reading and message-sending happens through session.view.


View

session.view — the read model plus the write commands for a thread.

Reading

MethodReturns
getMessages()Messages on the currently selected branch.
getChannelMessages()Every message the channel has seen (all branches).
runs()Array of RunInfo for runs in this thread.
hasOlder()boolean — is there history before the loaded window.
getStream(runId)The full accumulated token string for an active run ("" if none).
get logIdCurrent log position.

Sending

Each of these pushes to the channel and resolves to an ActiveRun (except loadOlder). IDs default to generated UUIDs.

MethodSignature
send(text, opts?)send(text, { parentId?, messageId?, runId?, role? })Promise<ActiveRun>
regenerate(messageId, opts?)regenerate(messageId, { runId?, parentId? })Promise<ActiveRun>
edit(messageId, content, opts?)edit(messageId, content, { newMessageId?, runId? })Promise<ActiveRun>
loadOlder(limit?)loadOlder(limit = 50)Promise<Message[]> — takes a count, walks back from the oldest loaded message
selectSibling(parentId, messageId)Switch the visible branch at a fork.
approveTool(runId, toolCallId)Promise — approve a suspended tool call.
rejectTool(runId, toolCallId)Promise — reject it.

Events

view.on(event, cb) / session.on(event, cb). There is no per-token callback — on "update", read view.getStream(runId) for the current text.

EventPayloadFires when
updateAny thread state change (new message, token flush, run status). Re-read view.getMessages() / getStream().
runRunInfoA run's info changes.
run:start{ runId, ownerClientId, startedAt }Agent starts a run.
run:end{ runId, reason, endedAt }Run terminates (reason: complete | cancelled | error). Followed by a state update with the finalised message.
run:suspend / run:suspended{ runId, toolCallId, toolName, args }Run pauses for tool approval (both event names fire).
run:resume{ runId }Run resumes.

ActiveRun

Returned by view.send() / regenerate() / edit().

Properties: runId, messageId, forkOf, regeneratesMessageId, serial, status.

MethodDescription
cancel()PromiseCancel this run.
toJSON(){ threadId, runId }

Putting it together

import { PingerchipsChat } from 'pingerchips-js/chat';

const chat = new PingerchipsChat('YOUR_APP_KEY', { authEndpoint: '/chat/auth' });
const session = await chat.connect('thread-uuid', { clientId: 'user-42' });
const { view } = session;

function render() {
  const msgs = view.getMessages();
  for (const run of view.runs()) {
    if (run.status === 'active') {
      // show the streaming bubble with the current text
      showStreaming(run.runId, view.getStream(run.runId));
    }
  }
  paint(msgs);
}

view.on('update', render);
render();

await view.send('What can you help me with?');

Message shape (wire)

interface Message {
  messageId:      string;   // client-minted UUID
  runId:          string;
  ownerClientId:  string;
  role:           'user' | 'assistant';
  content:        unknown;   // codec-defined; plain text by default
  parentId:       string | null;
  forkOf:         string | null;
  msgRegenerate:  string | null;
  status:         'streaming' | 'complete' | 'cancelled' | 'error';
  createdAt:      number;    // unix ms
}
interface RunInfo {
  runId:         string;
  ownerClientId: string;
  status:        'pending' | 'active' | 'suspended' | 'complete' | 'cancelled' | 'error';
  startedAt:     number;
  endedAt:       number | null;
  suspendedTool: { toolCallId: string; toolName: string; args: object } | null;
  parentId:      string | null;
  forkOf:        string | null;
  msgRegenerate: string | null;
}

On this page