@pouchy_ai/companion-sdk 0.10.0 → 0.11.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 CHANGED
@@ -12,6 +12,40 @@ a protocol bump is always called out explicitly here.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.11.0] - 2026-07-02
16
+
17
+ ### Added
18
+
19
+ - **`AVATAR_VISUAL_TOOLS` / `AVATAR_VISUAL_TOOL_NAMES` are now exported from the
20
+ package entry point.** `docs/companion-host-control.md` has documented these
21
+ since the avatar-emote work, but the public `index.ts` never re-exported them —
22
+ hosts that wanted to filter or inspect the avatar visual tool set had to
23
+ reach into internal module paths. Additive; no behavior change.
24
+
25
+ ### Docs
26
+
27
+ - The protocol document now reflects the shipped transport reality: REST + SSE
28
+ is the live control/data plane, `stream: 'websocket'` is a client-side option
29
+ that automatically falls back to SSE (the server WebSocket endpoint is not
30
+ yet available), and the server agent loop is live. The voice and poker
31
+ integration guides now install the SDK from npm instead of a local build /
32
+ bundled tarball.
33
+
34
+ ## [0.10.1] - 2026-06-29
35
+
36
+ ### Fixed
37
+
38
+ - **Voice calls no longer drop on a transient network blip.** A WebRTC
39
+ `connectionState` of `disconnected` is transient per spec — a brief Wi-Fi
40
+ hiccup or cell handoff routinely hits it and recovers to `connected` on its
41
+ own. `connectCall()` previously tore the call down the instant `disconnected`
42
+ appeared, killing calls that would have healed. It now holds a short grace
43
+ window (6s) and only tears down if the connection is still `disconnected` when
44
+ it elapses; recovery to `connected` cancels the teardown. `failed` / `closed`
45
+ remain immediately fatal, and a genuine silent drop still tears down (so the
46
+ mic can't keep recording). No surface change — `connectCall` signature,
47
+ events, payloads, and scopes are unchanged.
48
+
15
49
  ## [0.10.0] - 2026-06-28
16
50
 
17
51
  Completes the SDK surface for all six companion capabilities, with Instant UI and
@@ -82,5 +116,8 @@ self-hosted/CDN build; this entry captures the surface that ships publicly.
82
116
  - **Versioned wire protocol.** `PROTOCOL_VERSION = 1`, kept in lockstep with the
83
117
  server by `protocol.drift.test.ts` (fails CI on divergence).
84
118
 
85
- [Unreleased]: https://github.com/oviswang/Pouchy/compare/companion-sdk-v0.9.0...HEAD
119
+ [Unreleased]: https://github.com/oviswang/Pouchy/compare/companion-sdk-v0.11.0...HEAD
120
+ [0.11.0]: https://github.com/oviswang/Pouchy/compare/companion-sdk-v0.10.1...companion-sdk-v0.11.0
121
+ [0.10.1]: https://github.com/oviswang/Pouchy/compare/companion-sdk-v0.10.0...companion-sdk-v0.10.1
122
+ [0.10.0]: https://github.com/oviswang/Pouchy/compare/companion-sdk-v0.9.0...companion-sdk-v0.10.0
86
123
  [0.9.0]: https://github.com/oviswang/Pouchy/releases/tag/companion-sdk-v0.9.0
package/dist/call.js CHANGED
@@ -302,10 +302,26 @@ async function openOpenAICall(creds, opts, bridge) {
302
302
  }
303
303
  await pc.setRemoteDescription({ type: 'answer', sdp: await res.text() });
304
304
  let closed = false;
305
+ // `disconnected` is TRANSIENT per the WebRTC spec — a brief Wi-Fi hiccup or a
306
+ // cell handoff routinely drops to `disconnected` and recovers to `connected`
307
+ // on its own. Tearing down the instant it appears killed calls that would have
308
+ // healed (field report: SDK voice "动不动就掉线"). Give it a grace window to
309
+ // come back; only if it's STILL disconnected when the timer fires do we tear
310
+ // down — so the mic still can't record forever on a genuine silent drop (the
311
+ // original concern). `failed` / `closed` stay immediately fatal.
312
+ const DISCONNECT_GRACE_MS = 6000;
313
+ let disconnectTimer = null;
314
+ const clearDisconnectTimer = () => {
315
+ if (disconnectTimer) {
316
+ clearTimeout(disconnectTimer);
317
+ disconnectTimer = null;
318
+ }
319
+ };
305
320
  const teardown = () => {
306
321
  if (closed)
307
322
  return;
308
323
  closed = true;
324
+ clearDisconnectTimer();
309
325
  if (micUnmuteTimer) {
310
326
  clearTimeout(micUnmuteTimer);
311
327
  micUnmuteTimer = null;
@@ -320,13 +336,35 @@ async function openOpenAICall(creds, opts, bridge) {
320
336
  mic.getTracks().forEach((t) => t.stop());
321
337
  };
322
338
  pc.onconnectionstatechange = () => {
323
- if (['failed', 'disconnected', 'closed'].includes(pc.connectionState)) {
324
- // Tear down BEFORE surfacing the error. On a silent transport drop
325
- // (Wi-Fi→cellular, ICE failure, server expiry) the embedder may never
326
- // call close(), so without this the mic keeps recording indefinitely.
327
- // Mirrors the first-party realtime/connection.ts fail() ordering.
339
+ const st = pc.connectionState;
340
+ if (st === 'connected') {
341
+ // (Re)connected cancel any pending disconnect teardown.
342
+ clearDisconnectTimer();
343
+ return;
344
+ }
345
+ if (st === 'failed' || st === 'closed') {
346
+ // Genuinely dead — tear down immediately, BEFORE surfacing the error, so a
347
+ // silent transport drop can't leave the mic recording (mirrors the
348
+ // first-party realtime/connection.ts fail() ordering).
349
+ clearDisconnectTimer();
328
350
  teardown();
329
- opts.onError?.(new Error(`call ${pc.connectionState}`));
351
+ opts.onError?.(new Error(`call ${st}`));
352
+ return;
353
+ }
354
+ if (st === 'disconnected') {
355
+ // Hold a grace window; a recovery to `connected` (handled above) cancels
356
+ // the timer. Still disconnected when it fires → tear down.
357
+ if (disconnectTimer || closed)
358
+ return;
359
+ disconnectTimer = setTimeout(() => {
360
+ disconnectTimer = null;
361
+ if (closed)
362
+ return;
363
+ if (pc.connectionState === 'disconnected') {
364
+ teardown();
365
+ opts.onError?.(new Error('call disconnected'));
366
+ }
367
+ }, DISCONNECT_GRACE_MS);
330
368
  }
331
369
  };
332
370
  return {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CompanionClient, type CompanionClientOptions } from './client.js';
2
2
  export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
3
3
  export type { CompanionClientOptions, HelloAck, RecalledMemory, CallCredentials, CompanionToolDecl, WorldStateInput, CompanionAvatar, BrandIconSize } from './client.js';
4
- export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES } from './call.js';
4
+ export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
5
5
  export type { CompanionCall, CompanionCallOptions, VoiceToolBridge } from './call.js';
6
6
  export type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, AudioClipPayload, ExpressionPayload, UsagePayload } from './protocol.js';
7
7
  /** Create a companion client. */
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // await c.sendText('what should I do?');
11
11
  import { CompanionClient } from './client.js';
12
12
  export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
13
- export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES } from './call.js';
13
+ export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
14
14
  /** Create a companion client. */
15
15
  export function createCompanion(opts) {
16
16
  return new CompanionClient(opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/companion-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Embed the Pouchy companion — chat, voice, tools, memory, live world-state, instant UI, and agent-to-agent messaging — in any app, game, or site.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -21,7 +21,12 @@
21
21
  },
22
22
  "main": "./dist/index.js",
23
23
  "types": "./dist/index.d.ts",
24
- "files": ["dist", "README.md", "CHANGELOG.md", "LICENSE"],
24
+ "files": [
25
+ "dist",
26
+ "README.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
25
30
  "sideEffects": false,
26
31
  "scripts": {
27
32
  "build": "tsc -p tsconfig.json && node scripts/fix-esm-extensions.mjs",
@@ -40,7 +45,13 @@
40
45
  "esbuild": "^0.24.0",
41
46
  "typescript": "^5.5.0"
42
47
  },
43
- "keywords": ["pouchy", "ai-companion", "voice", "agent", "sdk"],
48
+ "keywords": [
49
+ "pouchy",
50
+ "ai-companion",
51
+ "voice",
52
+ "agent",
53
+ "sdk"
54
+ ],
44
55
  "publishConfig": {
45
56
  "access": "public"
46
57
  }