@pouchy_ai/companion-sdk 0.25.0 → 0.26.1
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 +33 -0
- package/README.md +7 -1
- package/dist/call.js +7 -6
- package/dist/client.d.ts +6 -8
- package/dist/client.js +7 -13
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +20 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,39 @@ a protocol bump is always called out explicitly here.
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.26.1] - 2026-07-12
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Docs: `/api/stt` now requires auth.** The audio-transcription relay the
|
|
20
|
+
`ingestKnowledge` guidance points at is no longer an open endpoint — send
|
|
21
|
+
your `pchy_…` access token as the bearer (any valid token works; the call
|
|
22
|
+
is metered like other companion ops). No SDK code paths changed; this
|
|
23
|
+
release updates the inline JSDoc guidance only.
|
|
24
|
+
- Server-side hardening (no client change needed): concurrent `input` posts
|
|
25
|
+
on one session now lose cleanly with the existing 409 `turn_pending` code
|
|
26
|
+
instead of double-running a turn — retry after the in-flight reply lands.
|
|
27
|
+
|
|
28
|
+
## [0.26.0] - 2026-07-11
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **Voice-call connect failures now honor the typed-error contract.** The
|
|
33
|
+
call helpers (`connectCall` / `startCall` / `openCompanionCall`) used to
|
|
34
|
+
reject with plain `Error`s for the connect-step failures, so they carried
|
|
35
|
+
no `.code` and couldn't be `switch`ed like every other SDK rejection.
|
|
36
|
+
They now reject with `CompanionError` and three new client-synthesized
|
|
37
|
+
codes: `call_unsupported` (non-browser environment — no WebRTC/mic),
|
|
38
|
+
`call_connect_failed` (mic-permission timeout, SDP exchange failed or
|
|
39
|
+
timed out; `status` carries the HTTP status when the exchange answered
|
|
40
|
+
non-2xx), and `call_dependency_missing` (the optional
|
|
41
|
+
`@elevenlabs/client` peer dependency isn't installed). Like
|
|
42
|
+
`reply_timeout`/`stream_unauthorized` these are SDK-synthesized and
|
|
43
|
+
deliberately NOT part of the server-mirrored `COMPANION_ERROR_CODES`
|
|
44
|
+
HTTP vocabulary. Browser-native `getUserMedia` rejections (e.g.
|
|
45
|
+
`NotAllowedError` on permission denial) still propagate untouched.
|
|
46
|
+
`CompanionError` itself is unchanged and keeps its import path.
|
|
47
|
+
|
|
15
48
|
## [0.25.0] - 2026-07-11
|
|
16
49
|
|
|
17
50
|
### Added
|
package/README.md
CHANGED
|
@@ -147,7 +147,13 @@ HTTP `code` vocabulary behind `CompanionError.code`, e.g. `missing_scope` /
|
|
|
147
147
|
`session_not_found` / `turn_pending`) — so you can assert the version, validate
|
|
148
148
|
the event vocabulary, or switch on error codes without hard-coding strings.
|
|
149
149
|
Every helper populates `CompanionError.code` when the server names a cause
|
|
150
|
-
(0.24.0 — previously only the POST-backed calls did).
|
|
150
|
+
(0.24.0 — previously only the POST-backed calls did). The SDK also synthesizes
|
|
151
|
+
a few client-side codes outside that HTTP vocabulary: `reply_timeout`
|
|
152
|
+
(awaitReply fallback gave up), `stream_unauthorized` (event-stream 401
|
|
153
|
+
exhausted reconnects), and — 0.26.0 — the voice connect-step codes
|
|
154
|
+
`call_unsupported` (no WebRTC/mic in this environment), `call_connect_failed`
|
|
155
|
+
(mic timeout / SDP exchange failed) and `call_dependency_missing`
|
|
156
|
+
(`@elevenlabs/client` not installed). Every
|
|
151
157
|
outbound payload has a named type; `ToolCallPayload` (`{ id, name, args }`) types
|
|
152
158
|
the `companion.tool_call` event, and `onToolCall`'s callback receives
|
|
153
159
|
`ToolCallEvent` — the payload plus `argsJson` (pre-parsed args).
|
package/dist/call.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
//
|
|
11
11
|
// Browser-only (needs WebRTC + getUserMedia). Returns a handle to hang up and to
|
|
12
12
|
// inject live context mid-call (the bridge the server uses for voiceRelevant).
|
|
13
|
+
import { CompanionError } from './errors.js';
|
|
13
14
|
/** Universal "host control" verbs the companion can call on ANY surface (web /
|
|
14
15
|
* game / app / hardware), regardless of provider. They're generic on purpose —
|
|
15
16
|
* the SPECIFIC intent rides in the string params, which YOUR surface interprets
|
|
@@ -147,7 +148,7 @@ const OPENAI_REALTIME_CALLS = 'https://api.openai.com/v1/realtime/calls';
|
|
|
147
148
|
/** Open a live call from pre-minted credentials. */
|
|
148
149
|
export async function openCompanionCall(creds, opts = {}, bridge) {
|
|
149
150
|
if (typeof navigator === 'undefined' || !navigator.mediaDevices) {
|
|
150
|
-
throw new
|
|
151
|
+
throw new CompanionError('connectCall is browser-only (needs WebRTC + microphone access)', 0, 'call_unsupported');
|
|
151
152
|
}
|
|
152
153
|
return creds.provider === 'openai-realtime'
|
|
153
154
|
? openOpenAICall(creds, opts, bridge)
|
|
@@ -176,7 +177,7 @@ async function getMicWithTimeout() {
|
|
|
176
177
|
.catch(() => { });
|
|
177
178
|
const timeout = new Promise((_, reject) => setTimeout(() => {
|
|
178
179
|
timedOut = true;
|
|
179
|
-
reject(new
|
|
180
|
+
reject(new CompanionError('microphone request timed out', 0, 'call_connect_failed'));
|
|
180
181
|
}, GUM_TIMEOUT_MS));
|
|
181
182
|
return Promise.race([micPromise, timeout]);
|
|
182
183
|
}
|
|
@@ -343,14 +344,14 @@ async function openOpenAICall(creds, opts, bridge) {
|
|
|
343
344
|
catch {
|
|
344
345
|
clearTimeout(sdpTimer);
|
|
345
346
|
cleanupPartial();
|
|
346
|
-
throw new
|
|
347
|
+
throw new CompanionError(sdpAbort.signal.aborted
|
|
347
348
|
? 'OpenAI Realtime SDP exchange timed out'
|
|
348
|
-
: 'OpenAI Realtime SDP exchange failed');
|
|
349
|
+
: 'OpenAI Realtime SDP exchange failed', 0, 'call_connect_failed');
|
|
349
350
|
}
|
|
350
351
|
clearTimeout(sdpTimer);
|
|
351
352
|
if (!res.ok) {
|
|
352
353
|
cleanupPartial();
|
|
353
|
-
throw new
|
|
354
|
+
throw new CompanionError(`OpenAI Realtime SDP exchange failed (${res.status})`, res.status, 'call_connect_failed');
|
|
354
355
|
}
|
|
355
356
|
await pc.setRemoteDescription({ type: 'answer', sdp: await res.text() });
|
|
356
357
|
let closed = false;
|
|
@@ -458,7 +459,7 @@ async function openConvaiCall(creds, opts, bridge) {
|
|
|
458
459
|
mod = await import('@elevenlabs/client');
|
|
459
460
|
}
|
|
460
461
|
catch {
|
|
461
|
-
throw new
|
|
462
|
+
throw new CompanionError("elevenlabs-convai call needs the optional peer dependency '@elevenlabs/client' — install it (npm i @elevenlabs/client)", 0, 'call_dependency_missing');
|
|
462
463
|
}
|
|
463
464
|
// App tools → EL client tools. NOTE: ElevenLabs only CALLS a client tool the
|
|
464
465
|
// agent already knows (declared on the agent config); a shared Convai agent
|
package/dist/client.d.ts
CHANGED
|
@@ -166,12 +166,10 @@ export declare function pouchyBrandIconUrl(baseUrl: string, size?: BrandIconSize
|
|
|
166
166
|
* a method before `connect()`). `code` is a stable machine-readable tag you can
|
|
167
167
|
* switch on, shared with the `control.error` stream event vocabulary:
|
|
168
168
|
* `'not_connected'`, `'missing_option'`, `'not_representative'`, or the server's
|
|
169
|
-
* own error code for an HTTP failure (when it sends one).
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
constructor(message: string, status: number, code?: string);
|
|
174
|
-
}
|
|
169
|
+
* own error code for an HTTP failure (when it sends one). Defined in errors.ts
|
|
170
|
+
* (so call.ts shares it cycle-free); re-exported here so the public import
|
|
171
|
+
* path is unchanged. */
|
|
172
|
+
export { CompanionError } from './errors.js';
|
|
175
173
|
/** The server's memory-consolidation diagnostic from `endSession()` / `close()`.
|
|
176
174
|
* `ok` is whether consolidation ran; `facts` is how many memories it wrote;
|
|
177
175
|
* `skipped` (when present) says why nothing was written
|
|
@@ -434,7 +432,8 @@ export declare class CompanionClient {
|
|
|
434
432
|
* materials" list with source attribution, exactly like a first-party upload.
|
|
435
433
|
* So the materials entry point can live in YOUR app, not just Pouchy's.
|
|
436
434
|
*
|
|
437
|
-
* Extract the text yourself (your own parser, or Pouchy's /api/stt for audio
|
|
435
|
+
* Extract the text yourself (your own parser, or Pouchy's /api/stt for audio —
|
|
436
|
+
* send your pchy_ token as the bearer; the relay is auth-gated and metered)
|
|
438
437
|
* and pass it here. Writes the user's SHARED knowledge, so the token must
|
|
439
438
|
* hold `memory.write:core` (the user's consent) — otherwise 403. Embeddings
|
|
440
439
|
* are computed on the user's next first-party open (eventually-consistent
|
|
@@ -581,4 +580,3 @@ export declare class CompanionClient {
|
|
|
581
580
|
private consume;
|
|
582
581
|
private sleep;
|
|
583
582
|
}
|
|
584
|
-
export {};
|
package/dist/client.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// kept in lockstep by protocol.drift.test.ts.
|
|
15
15
|
import { PROTOCOL_VERSION } from './protocol.js';
|
|
16
16
|
import { parseSse } from './sse.js';
|
|
17
|
+
import { CompanionError } from './errors.js';
|
|
17
18
|
import { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
|
|
18
19
|
/** Model tool arguments are model output — tolerate junk without throwing. */
|
|
19
20
|
function parseArgsJson(args) {
|
|
@@ -37,18 +38,10 @@ export function pouchyBrandIconUrl(baseUrl, size = 512) {
|
|
|
37
38
|
* a method before `connect()`). `code` is a stable machine-readable tag you can
|
|
38
39
|
* switch on, shared with the `control.error` stream event vocabulary:
|
|
39
40
|
* `'not_connected'`, `'missing_option'`, `'not_representative'`, or the server's
|
|
40
|
-
* own error code for an HTTP failure (when it sends one).
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
constructor(message, status, code) {
|
|
45
|
-
super(message);
|
|
46
|
-
this.name = 'CompanionError';
|
|
47
|
-
this.status = status;
|
|
48
|
-
if (code)
|
|
49
|
-
this.code = code;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
41
|
+
* own error code for an HTTP failure (when it sends one). Defined in errors.ts
|
|
42
|
+
* (so call.ts shares it cycle-free); re-exported here so the public import
|
|
43
|
+
* path is unchanged. */
|
|
44
|
+
export { CompanionError } from './errors.js';
|
|
52
45
|
const SEEN_CAP = 512;
|
|
53
46
|
export class CompanionClient {
|
|
54
47
|
opts;
|
|
@@ -649,7 +642,8 @@ export class CompanionClient {
|
|
|
649
642
|
* materials" list with source attribution, exactly like a first-party upload.
|
|
650
643
|
* So the materials entry point can live in YOUR app, not just Pouchy's.
|
|
651
644
|
*
|
|
652
|
-
* Extract the text yourself (your own parser, or Pouchy's /api/stt for audio
|
|
645
|
+
* Extract the text yourself (your own parser, or Pouchy's /api/stt for audio —
|
|
646
|
+
* send your pchy_ token as the bearer; the relay is auth-gated and metered)
|
|
653
647
|
* and pass it here. Writes the user's SHARED knowledge, so the token must
|
|
654
648
|
* hold `memory.write:core` (the user's consent) — otherwise 403. Embeddings
|
|
655
649
|
* are computed on the user's next first-party open (eventually-consistent
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Thrown by every helper on an HTTP or client-side failure. `status` is the
|
|
2
|
+
* HTTP status (0 for client-synthesized failures — timeouts, unsupported
|
|
3
|
+
* environments, missing optional deps); `code` is machine-switchable when
|
|
4
|
+
* the server (or the SDK itself) names a cause. */
|
|
5
|
+
export declare class CompanionError extends Error {
|
|
6
|
+
status: number;
|
|
7
|
+
code?: string;
|
|
8
|
+
constructor(message: string, status: number, code?: string);
|
|
9
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// CompanionError — the one error type every SDK helper rejects with (README
|
|
2
|
+
// contract: consumers `switch` on `.code` instead of string-matching prose).
|
|
3
|
+
// Lives in its own module so call.ts can use it without a runtime cycle
|
|
4
|
+
// through client.ts (client.ts re-exports it, keeping the public import path
|
|
5
|
+
// `@pouchy_ai/companion-sdk` → `CompanionError` unchanged).
|
|
6
|
+
/** Thrown by every helper on an HTTP or client-side failure. `status` is the
|
|
7
|
+
* HTTP status (0 for client-synthesized failures — timeouts, unsupported
|
|
8
|
+
* environments, missing optional deps); `code` is machine-switchable when
|
|
9
|
+
* the server (or the SDK itself) names a cause. */
|
|
10
|
+
export class CompanionError extends Error {
|
|
11
|
+
status;
|
|
12
|
+
code;
|
|
13
|
+
constructor(message, status, code) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'CompanionError';
|
|
16
|
+
this.status = status;
|
|
17
|
+
if (code)
|
|
18
|
+
this.code = code;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pouchy_ai/companion-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
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",
|