@pouchy_ai/companion-sdk 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -0
- package/LICENSE +41 -0
- package/README.md +136 -0
- package/dist/call.d.ts +59 -0
- package/dist/call.js +478 -0
- package/dist/client.d.ts +366 -0
- package/dist/client.js +716 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +17 -0
- package/dist/protocol.d.ts +124 -0
- package/dist/protocol.js +35 -0
- package/dist/sse.d.ts +12 -0
- package/dist/sse.js +38 -0
- package/dist/ws-transport.d.ts +21 -0
- package/dist/ws-transport.js +62 -0
- package/package.json +47 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import type { AudioClipPayload, CompanionEnvelope, ConfirmRequestPayload, ExpressionPayload, InterfaceUpdatePayload, OutboundType, RenderInterfacePayload, SocialMessagePayload, UsagePayload, WorldStateEvent } from './protocol.js';
|
|
2
|
+
/** Ergonomic world-state input: a WorldStateEvent with the CloudEvents plumbing
|
|
3
|
+
* (specversion / id / source) optional — sendWorldState fills them. */
|
|
4
|
+
export type WorldStateInput<D = unknown> = Omit<WorldStateEvent<D>, 'specversion' | 'id' | 'source'> & Partial<Pick<WorldStateEvent<D>, 'specversion' | 'id' | 'source'>>;
|
|
5
|
+
import { type CompanionCall, type CompanionCallOptions } from './call.js';
|
|
6
|
+
export interface CompanionClientOptions {
|
|
7
|
+
/** Origin of the Pouchy deployment, e.g. "https://www.pouchy.ai". */
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
/** A Pouchy access token (PAT). */
|
|
10
|
+
token: string;
|
|
11
|
+
/** Logical surface (one resumable session per surface), default "default". */
|
|
12
|
+
surface?: string;
|
|
13
|
+
/** Requested I/O modalities (intersected with the token's grant server-side). */
|
|
14
|
+
modalities?: string[];
|
|
15
|
+
/** Action types this surface can perform (declared at handshake). */
|
|
16
|
+
handles?: string[];
|
|
17
|
+
/** World-state kinds this surface emits (declared at handshake). */
|
|
18
|
+
contextKinds?: string[];
|
|
19
|
+
/** Tools the companion may ask this surface to perform (companion.tool_call). */
|
|
20
|
+
tools?: CompanionToolDecl[];
|
|
21
|
+
/** Static description of THIS app/game so the companion is grounded in where it
|
|
22
|
+
* is (drives reasoning + the call opener). Live state still flows via
|
|
23
|
+
* sendWorldState. */
|
|
24
|
+
appContext?: {
|
|
25
|
+
name?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
};
|
|
28
|
+
/** Open a REPRESENTATIVE ("on-behalf-of" / 代聊) session: the companion fields
|
|
29
|
+
* this VISITOR's messages on the owner's behalf — customer-service style —
|
|
30
|
+
* drawing only on screened owner context (persona + share-screened facts, plus
|
|
31
|
+
* the knowledge base when the owner's token grants `expose:knowledge`). Each
|
|
32
|
+
* visitor (stable opaque `id`, 8-64 url-safe chars that YOU keep stable per
|
|
33
|
+
* end-user) gets their own continuous thread. Requires the token's `represent`
|
|
34
|
+
* scope — without it the handshake is rejected (never silently downgraded to an
|
|
35
|
+
* owner-facing session). */
|
|
36
|
+
visitor?: {
|
|
37
|
+
id: string;
|
|
38
|
+
displayName?: string;
|
|
39
|
+
};
|
|
40
|
+
/** Injectable fetch (for Node < 18 / tests). Defaults to global fetch. */
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
/** Receive transport for the reply stream. 'sse' (default) works on every
|
|
43
|
+
* deploy; 'websocket' opts into the lower-latency WS plane when the host
|
|
44
|
+
* supports it (adapter-node / a WS gateway) and falls back to SSE if the
|
|
45
|
+
* socket can't open — so enabling it can never stop reply delivery. */
|
|
46
|
+
stream?: 'sse' | 'websocket';
|
|
47
|
+
/** Injectable WebSocket constructor (Node without a global WebSocket, or
|
|
48
|
+
* tests). Defaults to globalThis.WebSocket when present. */
|
|
49
|
+
webSocketImpl?: typeof WebSocket;
|
|
50
|
+
}
|
|
51
|
+
export interface HelloAck {
|
|
52
|
+
session: string;
|
|
53
|
+
grantedScopes: string[];
|
|
54
|
+
modalities: string[];
|
|
55
|
+
resumeCursor: number;
|
|
56
|
+
/** True when this is a representative (visitor-facing) session — i.e. a
|
|
57
|
+
* `visitor` was supplied and the token holds `represent`. */
|
|
58
|
+
representative: boolean;
|
|
59
|
+
/** Representative sessions only: whether this visitor is already paired with
|
|
60
|
+
* the owner (their two companions are friends). Lets you skip offering a
|
|
61
|
+
* "pair" affordance to an already-connected visitor. */
|
|
62
|
+
visitorPaired: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** A tool the embedding declares the companion may ask it to perform. */
|
|
65
|
+
export interface CompanionToolDecl {
|
|
66
|
+
name: string;
|
|
67
|
+
description?: string;
|
|
68
|
+
parameters?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/** Voice-plane credentials, discriminated by provider (see startCall). */
|
|
71
|
+
export type CallCredentials = {
|
|
72
|
+
provider: 'elevenlabs-convai';
|
|
73
|
+
/** EL conversation token (open WebRTC with @elevenlabs/client). */
|
|
74
|
+
token: string;
|
|
75
|
+
agentId: string;
|
|
76
|
+
/** Pass as overrides.agent.prompt.prompt when starting the EL session. */
|
|
77
|
+
instructions: string;
|
|
78
|
+
/** Server-resolved EL voice id (the user's cloned/localized voice). Pass
|
|
79
|
+
* as overrides.tts.voiceId so the call isn't a stock default voice. */
|
|
80
|
+
voice?: string;
|
|
81
|
+
/** Server-resolved agent language (ISO 639-1, e.g. 'zh'). Pass as
|
|
82
|
+
* overrides.agent.language so the call opens in the user's language. */
|
|
83
|
+
language?: string;
|
|
84
|
+
/** First-message override ('' = suppress the canned greeting so the call
|
|
85
|
+
* opens in-character / context-aware). Pass as overrides.agent.firstMessage. */
|
|
86
|
+
firstMessage?: string;
|
|
87
|
+
} | {
|
|
88
|
+
provider: 'openai-realtime';
|
|
89
|
+
/** Ephemeral client secret (ek_…); open WebRTC directly to OpenAI. */
|
|
90
|
+
clientSecret: string;
|
|
91
|
+
model: string;
|
|
92
|
+
voice: string;
|
|
93
|
+
expiresAt: number | null;
|
|
94
|
+
};
|
|
95
|
+
export interface RecalledMemory {
|
|
96
|
+
content: string;
|
|
97
|
+
category?: string;
|
|
98
|
+
kind?: string;
|
|
99
|
+
importance: number;
|
|
100
|
+
namespace: string;
|
|
101
|
+
createdAt?: string;
|
|
102
|
+
}
|
|
103
|
+
/** The user's current companion avatar (see getAvatar). */
|
|
104
|
+
export interface CompanionAvatar {
|
|
105
|
+
/** Companion display name (the user's chosen name), or null. */
|
|
106
|
+
name: string | null;
|
|
107
|
+
/** Persona archetype driving the default look/voice. */
|
|
108
|
+
archetype: 'girlfriend' | 'boyfriend' | 'pet';
|
|
109
|
+
/** The active model id (e.g. "default-gf-luna", or a custom id), or null. */
|
|
110
|
+
modelId: string | null;
|
|
111
|
+
/** Absolute URL to the VRM 3D model — the avatar itself. CORS-enabled so a
|
|
112
|
+
* cross-origin renderer can fetch it. Null only if resolution failed. */
|
|
113
|
+
vrmUrl: string | null;
|
|
114
|
+
/** Absolute URL to a flat 2D portrait when available; null for VRM-only
|
|
115
|
+
* built-ins today (reserved for custom previews / server-rendered thumbs). */
|
|
116
|
+
imageUrl: string | null;
|
|
117
|
+
}
|
|
118
|
+
/** Available sizes for the Pouchy brand icon (px). */
|
|
119
|
+
export type BrandIconSize = 256 | 512 | 1024;
|
|
120
|
+
/** Canonical URL of the official Pouchy brand icon at `size` px, derived from a
|
|
121
|
+
* deployment origin. Standalone (no client/token needed) so a surface can show
|
|
122
|
+
* the Pouchy mark before/without connecting. */
|
|
123
|
+
export declare function pouchyBrandIconUrl(baseUrl: string, size?: BrandIconSize): string;
|
|
124
|
+
export declare class CompanionError extends Error {
|
|
125
|
+
status: number;
|
|
126
|
+
constructor(message: string, status: number);
|
|
127
|
+
}
|
|
128
|
+
type Handler = (envelope: CompanionEnvelope) => void;
|
|
129
|
+
export declare class CompanionClient {
|
|
130
|
+
private readonly opts;
|
|
131
|
+
private readonly doFetch;
|
|
132
|
+
private _session;
|
|
133
|
+
private cursor;
|
|
134
|
+
private streaming;
|
|
135
|
+
private abort;
|
|
136
|
+
private readonly handlers;
|
|
137
|
+
private readonly seen;
|
|
138
|
+
private readonly pendingVoiceTools;
|
|
139
|
+
constructor(opts: CompanionClientOptions);
|
|
140
|
+
/** The active session id, or null before connect(). */
|
|
141
|
+
get sessionId(): string | null;
|
|
142
|
+
private url;
|
|
143
|
+
private jsonHeaders;
|
|
144
|
+
private requireSession;
|
|
145
|
+
private postJson;
|
|
146
|
+
/** Handshake: start or resume the session for this surface. */
|
|
147
|
+
connect(): Promise<HelloAck>;
|
|
148
|
+
/** Pair this representative session's VISITOR with the owner so their two
|
|
149
|
+
* companions become friends — unlocking the A2A plane (messaging, gifts,
|
|
150
|
+
* visiting) between them. Only valid in a representative session (the client
|
|
151
|
+
* was created with a `visitor`).
|
|
152
|
+
*
|
|
153
|
+
* Two-token consent: this client's PAT (the owner's) must hold `represent:pair`,
|
|
154
|
+
* and you pass the VISITOR's own Pouchy PAT — which must hold `social.message`
|
|
155
|
+
* — as proof + consent. The visitor must therefore also be a Pouchy user. */
|
|
156
|
+
pairVisitor(visitorToken: string): Promise<{
|
|
157
|
+
pairId: string | null;
|
|
158
|
+
}>;
|
|
159
|
+
/** Send a user text turn, optionally with images (data URLs) for a multimodal
|
|
160
|
+
* turn — images need the `files` scope. The reply arrives on the event stream
|
|
161
|
+
* as a `companion.message` — subscribe via onMessage()/start() to receive it. */
|
|
162
|
+
sendText(text: string, opts?: {
|
|
163
|
+
images?: string[];
|
|
164
|
+
}): Promise<{
|
|
165
|
+
seq: number | null;
|
|
166
|
+
}>;
|
|
167
|
+
/** Push live world-state — a single event or a batch. Retained events
|
|
168
|
+
* (retained:true) coalesce per kind; transient ones carry a salience.
|
|
169
|
+
*
|
|
170
|
+
* Ergonomic input: `specversion` / `id` / `source` are filled automatically
|
|
171
|
+
* (id → a fresh handle, source → this surface), so callers only supply the
|
|
172
|
+
* meaningful fields: `{ type, data, retained?, salience?, voiceRelevant? }`.
|
|
173
|
+
* A full CloudEvents envelope is still accepted (its fields win). */
|
|
174
|
+
sendWorldState(event: WorldStateInput | WorldStateInput[]): Promise<{
|
|
175
|
+
accepted: number;
|
|
176
|
+
dropped: number;
|
|
177
|
+
}>;
|
|
178
|
+
private randomId;
|
|
179
|
+
/** Open the voice plane. Returns realtime credentials to connect DIRECTLY to
|
|
180
|
+
* the voice provider (the Pouchy server is out of the audio path). Provider
|
|
181
|
+
* precedence is server-side: ElevenLabs Convai (primary) → OpenAI Realtime
|
|
182
|
+
* (fallback). Requires the `call` scope.
|
|
183
|
+
*
|
|
184
|
+
* - `elevenlabs-convai`: open the EL session with the returned `token` and
|
|
185
|
+
* pass `instructions` as `overrides.agent.prompt.prompt` (the EL agent runs
|
|
186
|
+
* EL's LLM, so the persona/memory snapshot rides in client-side).
|
|
187
|
+
* - `openai-realtime`: open WebRTC with the short-lived `clientSecret`
|
|
188
|
+
* (instructions are baked into the session server-side).
|
|
189
|
+
*/
|
|
190
|
+
startCall(opts?: {
|
|
191
|
+
voice?: string;
|
|
192
|
+
locale?: string;
|
|
193
|
+
}): Promise<CallCredentials>;
|
|
194
|
+
/** Open a live voice call end-to-end: mints credentials (startCall) and opens a
|
|
195
|
+
* WebRTC session directly to the provider — no first-party app code needed.
|
|
196
|
+
* Browser-only. Returns a handle to hang up + inject live context. EL Convai
|
|
197
|
+
* needs the optional '@elevenlabs/client' peer dependency; OpenAI Realtime has
|
|
198
|
+
* no extra deps. */
|
|
199
|
+
connectCall(opts?: CompanionCallOptions & {
|
|
200
|
+
voice?: string;
|
|
201
|
+
locale?: string;
|
|
202
|
+
}): Promise<CompanionCall>;
|
|
203
|
+
/** End the session: consolidate what happened into the companion's long-term
|
|
204
|
+
* memory, so it remembers this experience in future first-party conversations.
|
|
205
|
+
* Called automatically when a connectCall() handle is closed; call it directly
|
|
206
|
+
* when a non-voice session ends. Best-effort + idempotent (safe to call twice;
|
|
207
|
+
* uses keepalive so it survives a page unload). Pass the voice transcript when
|
|
208
|
+
* you have it (connectCall does this for you). */
|
|
209
|
+
endSession(opts?: {
|
|
210
|
+
transcript?: {
|
|
211
|
+
role: string;
|
|
212
|
+
text: string;
|
|
213
|
+
}[];
|
|
214
|
+
}): Promise<void>;
|
|
215
|
+
/** Report the result of a companion.tool_call this surface performed. When all
|
|
216
|
+
* of the turn's calls are reported, the companion resumes and the continuation
|
|
217
|
+
* (a companion.message or more tool calls) arrives on the event stream. */
|
|
218
|
+
sendToolResult(callId: string, result: {
|
|
219
|
+
ok?: boolean;
|
|
220
|
+
result?: unknown;
|
|
221
|
+
}): Promise<{
|
|
222
|
+
allDone: boolean;
|
|
223
|
+
}>;
|
|
224
|
+
/** A tool the live voice call asked the app to run. Fires the SAME
|
|
225
|
+
* companion.tool_call event the text flow uses (so the app's onToolCall +
|
|
226
|
+
* sendToolResult handle voice identically), and resolves with the result
|
|
227
|
+
* string fed back to the voice model. Times out so a missing result can't
|
|
228
|
+
* hang the call. */
|
|
229
|
+
private invokeVoiceTool;
|
|
230
|
+
/** Convenience: subscribe to tool-call requests from the companion. */
|
|
231
|
+
onToolCall(handler: (call: {
|
|
232
|
+
id: string;
|
|
233
|
+
name: string;
|
|
234
|
+
args: string;
|
|
235
|
+
}, envelope: CompanionEnvelope) => void): () => void;
|
|
236
|
+
/** Convenience: subscribe to errors the companion surfaces — server-side agent
|
|
237
|
+
* errors and stream failures (e.g. a permanent `stream_unauthorized`) both
|
|
238
|
+
* arrive as `control.error`. Returns an unsubscribe fn. */
|
|
239
|
+
onError(handler: (err: {
|
|
240
|
+
code: string;
|
|
241
|
+
message: string;
|
|
242
|
+
}, envelope: CompanionEnvelope) => void): () => void;
|
|
243
|
+
/** Recall the memory this token is authorized to see (ranked). */
|
|
244
|
+
recall(opts?: {
|
|
245
|
+
limit?: number;
|
|
246
|
+
}): Promise<RecalledMemory[]>;
|
|
247
|
+
/** Remember a fact (into this app's namespace unless `namespace` is given and
|
|
248
|
+
* the token has the core scope). Intimate-tier writes are rejected server-side. */
|
|
249
|
+
remember(fact: {
|
|
250
|
+
content: string;
|
|
251
|
+
importance?: number;
|
|
252
|
+
confidence?: number;
|
|
253
|
+
category?: string;
|
|
254
|
+
kind?: string;
|
|
255
|
+
namespace?: string;
|
|
256
|
+
sensitivity?: 'public' | 'personal';
|
|
257
|
+
}): Promise<{
|
|
258
|
+
cloudId: string;
|
|
259
|
+
namespace: string;
|
|
260
|
+
}>;
|
|
261
|
+
/** Ingest a DOCUMENT into the user's knowledge base. Unlike `remember` (one
|
|
262
|
+
* short fact), this distils already-extracted text — a PDF body, a meeting
|
|
263
|
+
* transcript, notes — into a headline summary PLUS full-text chunks, each
|
|
264
|
+
* embedded and semantically recallable, and surfaces it in the user's "My
|
|
265
|
+
* materials" list with source attribution, exactly like a first-party upload.
|
|
266
|
+
* So the materials entry point can live in YOUR app, not just Pouchy's.
|
|
267
|
+
*
|
|
268
|
+
* Extract the text yourself (your own parser, or Pouchy's /api/stt for audio)
|
|
269
|
+
* and pass it here. Writes the user's SHARED knowledge, so the token must
|
|
270
|
+
* hold `memory.write:core` (the user's consent) — otherwise 403. Embeddings
|
|
271
|
+
* are computed on the user's next first-party open (eventually-consistent
|
|
272
|
+
* semantic recall). `kind` is a free label for the source type (pdf / audio /
|
|
273
|
+
* note / …) used only for display + the materials icon. */
|
|
274
|
+
ingestKnowledge(doc: {
|
|
275
|
+
text: string;
|
|
276
|
+
name: string;
|
|
277
|
+
kind?: string;
|
|
278
|
+
locale?: string;
|
|
279
|
+
}): Promise<{
|
|
280
|
+
ok: boolean;
|
|
281
|
+
summary: string;
|
|
282
|
+
chunks: number;
|
|
283
|
+
}>;
|
|
284
|
+
/** Convenience over `ingestKnowledge`: hand over a RAW file and Pouchy
|
|
285
|
+
* understands it server-side before ingesting — no parser/transcriber/vision on
|
|
286
|
+
* your side. Pass a `data:<mime>;base64,…` URL. Supported today: **PDF**
|
|
287
|
+
* (server-side text extraction), **audio/video** (Whisper transcript), and
|
|
288
|
+
* **image** (server-side vision caption — an objective description becomes the
|
|
289
|
+
* material). For other types, extract the text yourself and call
|
|
290
|
+
* `ingestKnowledge`. Same `memory.write:core` requirement and "My materials"
|
|
291
|
+
* integration as `ingestKnowledge`. */
|
|
292
|
+
ingestFile(file: {
|
|
293
|
+
dataUrl: string;
|
|
294
|
+
name: string;
|
|
295
|
+
kind?: string;
|
|
296
|
+
locale?: string;
|
|
297
|
+
}): Promise<{
|
|
298
|
+
ok: boolean;
|
|
299
|
+
summary: string;
|
|
300
|
+
chunks: number;
|
|
301
|
+
}>;
|
|
302
|
+
/** Fetch the user's CURRENT companion avatar so the embedding can render the
|
|
303
|
+
* same virtual human Pouchy shows. The avatar is a VRM 3D model (`vrmUrl`,
|
|
304
|
+
* load it with a VRM/glTF renderer e.g. three-vrm); `imageUrl` is a flat 2D
|
|
305
|
+
* portrait when one exists (null today for built-in models). URLs are absolute
|
|
306
|
+
* and the /models asset is CORS-enabled, so a cross-origin renderer can fetch
|
|
307
|
+
* the VRM directly. Reflects the user's live choice (built-in or custom). */
|
|
308
|
+
getAvatar(): Promise<CompanionAvatar>;
|
|
309
|
+
/** Canonical URL of the official Pouchy brand icon (square PNG, transparent
|
|
310
|
+
* background) at the given size. Static + public — needs no token — so it's
|
|
311
|
+
* safe to drop straight into an `<img src>` for "powered by Pouchy" badges,
|
|
312
|
+
* the companion's app tile, etc. Sizes: 256 | 512 | 1024 (default 512). */
|
|
313
|
+
brandIconUrl(size?: BrandIconSize): string;
|
|
314
|
+
/** Subscribe to outbound events of a given type, or "*" for all. Returns an
|
|
315
|
+
* unsubscribe function. */
|
|
316
|
+
on(type: OutboundType | '*', handler: Handler): () => void;
|
|
317
|
+
/** Convenience: subscribe to assistant text replies. */
|
|
318
|
+
onMessage(handler: (text: string, envelope: CompanionEnvelope) => void): () => void;
|
|
319
|
+
/** Convenience: subscribe to Instant UI render surfaces (companion.ui_action).
|
|
320
|
+
* The host draws `payload.interface` with its OWN renderer — a web component, a
|
|
321
|
+
* native iOS/Android view tree, a CLI/TUI — all consuming the same
|
|
322
|
+
* platform-neutral genui schema. If the panel set `reportChanges`, feed the
|
|
323
|
+
* user's edits back via `sendText`/`sendWorldState` to continue the turn. */
|
|
324
|
+
onRender(handler: (payload: RenderInterfacePayload, envelope: CompanionEnvelope) => void): () => void;
|
|
325
|
+
/** Convenience: subscribe to TTS audio clips for the reply (companion.audio,
|
|
326
|
+
* non-call modality) — play `url`, or resolve `ref` your way. */
|
|
327
|
+
onAudio(handler: (payload: AudioClipPayload, envelope: CompanionEnvelope) => void): () => void;
|
|
328
|
+
/** Convenience: subscribe to avatar cues (companion.expression) — viseme /
|
|
329
|
+
* expression / gesture for an embed rendering the companion's body. */
|
|
330
|
+
onExpression(handler: (payload: ExpressionPayload, envelope: CompanionEnvelope) => void): () => void;
|
|
331
|
+
/** Convenience: subscribe to the per-token metering echo (control.usage) for a
|
|
332
|
+
* usage / billing view. */
|
|
333
|
+
onUsage(handler: (payload: UsagePayload, envelope: CompanionEnvelope) => void): () => void;
|
|
334
|
+
/** Convenience: OBSERVE sensitive-op confirmation requests
|
|
335
|
+
* (companion.confirm_request) — a payment, skill run, or friend message the
|
|
336
|
+
* companion wants the user to approve. Use it to surface the pending approval
|
|
337
|
+
* in your UI (e.g. "open Pouchy to approve"). NOTE: an embed cannot approve —
|
|
338
|
+
* approval is authed as the first-party Pouchy user (their app / a hosted
|
|
339
|
+
* confirm page), which is also where a biometric (Face ID / passkey) gate
|
|
340
|
+
* lives. This handler is observe-only. */
|
|
341
|
+
onConfirmRequest(handler: (payload: ConfirmRequestPayload, envelope: CompanionEnvelope) => void): () => void;
|
|
342
|
+
/** Convenience: subscribe to live updates of an already-rendered Instant UI
|
|
343
|
+
* panel (companion.ui_update). Apply each `{ key, value }` to the panel's state
|
|
344
|
+
* bag and re-evaluate bound displays — no rebuild. */
|
|
345
|
+
onInterfaceUpdate(handler: (payload: InterfaceUpdatePayload, envelope: CompanionEnvelope) => void): () => void;
|
|
346
|
+
/** Convenience: subscribe to inbound A2A messages from the user's paired
|
|
347
|
+
* friends (companion.social_message), delivered cross-app to any embed whose
|
|
348
|
+
* token holds `social.message`. Surface them in your own UI / notify the user. */
|
|
349
|
+
onSocialMessage(handler: (payload: SocialMessagePayload, envelope: CompanionEnvelope) => void): () => void;
|
|
350
|
+
/** Open the event stream (auto-reconnecting with the resume cursor). Uses the
|
|
351
|
+
* WebSocket plane when opted in + available, else SSE. */
|
|
352
|
+
start(): void;
|
|
353
|
+
/** WebSocket receive loop. Resolves when the socket closes; on any failure
|
|
354
|
+
* while still streaming it falls back to the SSE loop, so opting into WS can
|
|
355
|
+
* only ever DEGRADE to SSE, never drop replies. Input is still sent over
|
|
356
|
+
* REST (sendInput) — receiving is the latency-sensitive half. */
|
|
357
|
+
private wsLoop;
|
|
358
|
+
/** Stop the event stream. */
|
|
359
|
+
stop(): void;
|
|
360
|
+
private emit;
|
|
361
|
+
private handleFrame;
|
|
362
|
+
private loop;
|
|
363
|
+
private consume;
|
|
364
|
+
private sleep;
|
|
365
|
+
}
|
|
366
|
+
export {};
|