@pouchy_ai/companion-sdk 0.20.0 → 0.22.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 +47 -0
- package/README.md +10 -3
- package/dist/client.d.ts +29 -7
- package/dist/client.js +76 -14
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/protocol.d.ts +12 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,53 @@ a protocol bump is always called out explicitly here.
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.22.0] - 2026-07-11
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Runtime protocol constants are now exported.** `PROTOCOL_VERSION`,
|
|
20
|
+
`INBOUND_TYPES`, and `OUTBOUND_TYPES` are re-exported from the package entry —
|
|
21
|
+
previously only their *types* were reachable. A host can now assert the
|
|
22
|
+
protocol version or enumerate/validate the event vocabulary at runtime without
|
|
23
|
+
hard-coding the strings.
|
|
24
|
+
- **`ToolCallPayload` type.** The `companion.tool_call` payload
|
|
25
|
+
(`{ id, name, args }`) now has a named, exported interface, so a consumer using
|
|
26
|
+
the generic `on('companion.tool_call', …)` gets a typed payload — parity with
|
|
27
|
+
`RenderInterfacePayload`, `ConfirmRequestPayload`, and the other payload types.
|
|
28
|
+
`onToolCall`'s callback is typed against it.
|
|
29
|
+
|
|
30
|
+
No wire-protocol change (`PROTOCOL_VERSION` stays `1`); this is a purely additive
|
|
31
|
+
type/export surface, hence the minor bump.
|
|
32
|
+
|
|
33
|
+
## [0.21.0] - 2026-07-11
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **Token refresh:** `client.setToken(token)` swaps the bearer used by every
|
|
38
|
+
subsequent request and stream reconnect, and the new `onAuthError` client
|
|
39
|
+
option refreshes on demand — a 401 (REST **or** event stream) calls it; return
|
|
40
|
+
a fresh token (e.g. re-minted via `POST /v1/sessions`) and the client retries
|
|
41
|
+
transparently, return `null` to surface the failure exactly as before.
|
|
42
|
+
Concurrent 401s share one refresh; the stream loop caps consecutive
|
|
43
|
+
refresh-and-reject cycles at 3 so a bad hook can't hot-loop. Previously a
|
|
44
|
+
1-hour session token expiring mid-embed silently stopped reply delivery
|
|
45
|
+
(`stream_unauthorized`) with no recovery short of rebuilding the client.
|
|
46
|
+
- **Semantic recall:** `recall({ query })` — the server has always supported
|
|
47
|
+
`?q=` semantic search over the token-visible facts; the client now exposes it.
|
|
48
|
+
Without `query` the ranking is importance/recency, as before.
|
|
49
|
+
- **Machine-readable error codes:** companion API error responses now carry a
|
|
50
|
+
stable `code` (`missing_token` / `invalid_token` / `missing_scope` /
|
|
51
|
+
`invalid_request` / `session_not_found` / `turn_pending` / `no_pending_tools` /
|
|
52
|
+
`unknown_call` / `payload_too_large` / `forbidden` / `unavailable`), so
|
|
53
|
+
`CompanionError.code` is finally switchable for HTTP failures — the type
|
|
54
|
+
always promised it; the server now delivers it. The vocabulary is append-only.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- README method table showed pre-0.18 signatures for `recall` / `remember` /
|
|
59
|
+
`ingestKnowledge` / `history` (positional args instead of the actual object
|
|
60
|
+
args). Corrected to match the shipped API.
|
|
61
|
+
|
|
15
62
|
## [0.20.0] - 2026-07-11
|
|
16
63
|
|
|
17
64
|
### Added
|
package/README.md
CHANGED
|
@@ -96,11 +96,12 @@ local/device skills).
|
|
|
96
96
|
|
|
97
97
|
| Method | What it does |
|
|
98
98
|
|---|---|
|
|
99
|
-
| `recall(query)` / `remember(
|
|
100
|
-
| `ingestKnowledge(text,
|
|
101
|
-
| `history(limit?)` | Fetch the session transcript — restore chat on reload. |
|
|
99
|
+
| `recall({ query?, limit? })` / `remember({ content, … })` | Read / write the token-scoped memory namespace. `query` runs SEMANTIC search over the facts server-side (embedding-based, ranked fallback); omit it for the plain importance/recency ranking. |
|
|
100
|
+
| `ingestKnowledge({ text, name, … })` / `ingestFile(file)` | Push documents into the user's knowledge base ("My materials"); needs `memory.write:core`. |
|
|
101
|
+
| `history({ limit? })` | Fetch the session transcript — restore chat on reload. |
|
|
102
102
|
| `setModalities([...])` | Switch active I/O mid-session (within the token's grant). |
|
|
103
103
|
| `ping()` | Keepalive for long-idle embeds. |
|
|
104
|
+
| `setToken(token)` | Swap the bearer for every subsequent request + stream reconnect — session tokens expire (1h default), so call this when your backend re-mints one. Prefer the `onAuthError` constructor option to refresh on demand instead. |
|
|
104
105
|
| `pendingConfirms()` / `confirmAction(id, approve)` | List / resolve pending sensitive-action confirms (platform session tokens). On `{ status: 'exec_failed', retryable: true }` you MAY re-call the same confirmId — idempotent actions only. |
|
|
105
106
|
| `endSession()` | Distill the session into durable memory now; returns `{ skipped?: 'no_session' \| 'no_content' \| 'throttled' }`. |
|
|
106
107
|
| `close()` | One-call teardown: `stop()` + `endSession()` — the text-session mirror of the call handle's `close()`. |
|
|
@@ -139,6 +140,12 @@ exported: `COMPANION_MODALITIES`, `REPRESENT_SCOPES`, `DEFAULT_MODALITIES`,
|
|
|
139
140
|
`isRepresentScope()`. The list mirrors the server vocabulary and is guarded by
|
|
140
141
|
the SDK's drift test.
|
|
141
142
|
|
|
143
|
+
The wire-protocol runtime constants are exported too — `PROTOCOL_VERSION`,
|
|
144
|
+
`INBOUND_TYPES`, and `OUTBOUND_TYPES` — so you can assert the version or
|
|
145
|
+
enumerate/validate the event vocabulary without hard-coding strings. Every
|
|
146
|
+
outbound payload has a named type; `ToolCallPayload` (`{ id, name, args }`) types
|
|
147
|
+
the `companion.tool_call` event and `onToolCall`'s callback.
|
|
148
|
+
|
|
142
149
|
## Representative mode (代聊)
|
|
143
150
|
|
|
144
151
|
Pass a `visitor` and the session flips from **owner-facing** to **representative**:
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AudioClipPayload, CompanionEnvelope, ConfirmRequestPayload, ExpressionPayload, InterfaceUpdatePayload, OutboundType, PendingConfirm, RenderInterfacePayload, SocialMessagePayload, TypingPayload, UsagePayload, VoiceInjectPayload, WorldStateEvent } from './protocol.js';
|
|
1
|
+
import type { AudioClipPayload, CompanionEnvelope, ConfirmRequestPayload, ExpressionPayload, InterfaceUpdatePayload, OutboundType, PendingConfirm, RenderInterfacePayload, SocialMessagePayload, ToolCallPayload, TypingPayload, UsagePayload, VoiceInjectPayload, WorldStateEvent } from './protocol.js';
|
|
2
2
|
/** Ergonomic world-state input: a WorldStateEvent with the CloudEvents plumbing
|
|
3
3
|
* (specversion / id / source) optional — sendWorldState fills them. */
|
|
4
4
|
export type WorldStateInput<D = unknown> = Omit<WorldStateEvent<D>, 'specversion' | 'id' | 'source'> & Partial<Pick<WorldStateEvent<D>, 'specversion' | 'id' | 'source'>>;
|
|
@@ -47,6 +47,14 @@ export interface CompanionClientOptions {
|
|
|
47
47
|
/** Injectable WebSocket constructor (Node without a global WebSocket, or
|
|
48
48
|
* tests). Defaults to globalThis.WebSocket when present. */
|
|
49
49
|
webSocketImpl?: typeof WebSocket;
|
|
50
|
+
/** Called when a request or the event stream is rejected with 401 (expired /
|
|
51
|
+
* revoked token — session tokens live 1h by default). Return a fresh token
|
|
52
|
+
* (e.g. re-minted by your backend via POST /v1/sessions) and the client
|
|
53
|
+
* retries transparently; return null to give up, surfacing the failure
|
|
54
|
+
* exactly as without this hook. Scope denials (403 `missing_scope`) are
|
|
55
|
+
* configuration errors and never trigger it. Alternative: call `setToken()`
|
|
56
|
+
* proactively on your own refresh schedule. */
|
|
57
|
+
onAuthError?: () => string | null | Promise<string | null>;
|
|
50
58
|
}
|
|
51
59
|
export interface HelloAck {
|
|
52
60
|
session: string;
|
|
@@ -190,6 +198,19 @@ export declare class CompanionClient {
|
|
|
190
198
|
constructor(opts: CompanionClientOptions);
|
|
191
199
|
/** The active session id, or null before connect(). */
|
|
192
200
|
get sessionId(): string | null;
|
|
201
|
+
/** Swap the bearer token used by every subsequent request and stream
|
|
202
|
+
* reconnect. Session tokens expire (default 1h) — call this when your
|
|
203
|
+
* backend re-mints one, or wire `onAuthError` to do it on demand. */
|
|
204
|
+
setToken(token: string): void;
|
|
205
|
+
private refreshing;
|
|
206
|
+
/** Run opts.onAuthError (deduped), apply the fresh token, and report whether
|
|
207
|
+
* the caller should retry. Never throws — a failed refresh means "surface
|
|
208
|
+
* the original 401". */
|
|
209
|
+
private tryRefreshToken;
|
|
210
|
+
/** doFetch with one transparent retry after a 401-triggered token refresh.
|
|
211
|
+
* Everything except the stream loop (which has its own auth handling) goes
|
|
212
|
+
* through here. */
|
|
213
|
+
private fetchAuthed;
|
|
193
214
|
private url;
|
|
194
215
|
private jsonHeaders;
|
|
195
216
|
private requireSession;
|
|
@@ -312,11 +333,7 @@ export declare class CompanionClient {
|
|
|
312
333
|
* hang the call. */
|
|
313
334
|
private invokeVoiceTool;
|
|
314
335
|
/** Convenience: subscribe to tool-call requests from the companion. */
|
|
315
|
-
onToolCall(handler: (call:
|
|
316
|
-
id: string;
|
|
317
|
-
name: string;
|
|
318
|
-
args: string;
|
|
319
|
-
}, envelope: CompanionEnvelope) => void): () => void;
|
|
336
|
+
onToolCall(handler: (call: ToolCallPayload, envelope: CompanionEnvelope) => void): () => void;
|
|
320
337
|
/** Convenience: subscribe to errors the companion surfaces — server-side agent
|
|
321
338
|
* errors and stream failures (e.g. a permanent `stream_unauthorized`) both
|
|
322
339
|
* arrive as `control.error`. Returns an unsubscribe fn. */
|
|
@@ -324,9 +341,14 @@ export declare class CompanionClient {
|
|
|
324
341
|
code: string;
|
|
325
342
|
message: string;
|
|
326
343
|
}, envelope: CompanionEnvelope) => void): () => void;
|
|
327
|
-
/** Recall the memory this token is authorized to see
|
|
344
|
+
/** Recall the memory this token is authorized to see. Without `query` the
|
|
345
|
+
* facts come back ranked by importance/recency; with `query` the server
|
|
346
|
+
* runs semantic search over them (embedding-based, falls back to ranked
|
|
347
|
+
* when embeddings are unavailable) — e.g.
|
|
348
|
+
* `recall({ query: 'food preferences', limit: 10 })`. */
|
|
328
349
|
recall(opts?: {
|
|
329
350
|
limit?: number;
|
|
351
|
+
query?: string;
|
|
330
352
|
}): Promise<RecalledMemory[]>;
|
|
331
353
|
/** Fetch this session's recent conversation turns (oldest→newest) so a
|
|
332
354
|
* reconnecting embed can restore its transcript. Distinct from `recall`
|
package/dist/client.js
CHANGED
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
// await c.sendText('how am I doing?');
|
|
9
9
|
//
|
|
10
10
|
// Pure, dependency-free, isomorphic (browser + Node 18+ fetch). The transport is
|
|
11
|
-
// an internal detail
|
|
12
|
-
//
|
|
11
|
+
// an internal detail: the default REST/SSE plane, or an opt-in WebSocket plane
|
|
12
|
+
// (ws-transport.ts) that degrades back to SSE on failure — both sit behind this
|
|
13
|
+
// same API. Protocol types are a self-contained copy of the server's contract,
|
|
14
|
+
// kept in lockstep by protocol.drift.test.ts.
|
|
13
15
|
import { PROTOCOL_VERSION } from './protocol.js';
|
|
14
16
|
import { parseSse } from './sse.js';
|
|
15
17
|
import { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
|
|
@@ -63,6 +65,45 @@ export class CompanionClient {
|
|
|
63
65
|
get sessionId() {
|
|
64
66
|
return this._session;
|
|
65
67
|
}
|
|
68
|
+
/** Swap the bearer token used by every subsequent request and stream
|
|
69
|
+
* reconnect. Session tokens expire (default 1h) — call this when your
|
|
70
|
+
* backend re-mints one, or wire `onAuthError` to do it on demand. */
|
|
71
|
+
setToken(token) {
|
|
72
|
+
if (!token)
|
|
73
|
+
throw new CompanionError('setToken: token is required', 0, 'missing_option');
|
|
74
|
+
this.opts.token = token;
|
|
75
|
+
}
|
|
76
|
+
// In-flight onAuthError call, shared so concurrent 401s trigger ONE refresh.
|
|
77
|
+
refreshing = null;
|
|
78
|
+
/** Run opts.onAuthError (deduped), apply the fresh token, and report whether
|
|
79
|
+
* the caller should retry. Never throws — a failed refresh means "surface
|
|
80
|
+
* the original 401". */
|
|
81
|
+
async tryRefreshToken() {
|
|
82
|
+
const refresh = this.opts.onAuthError;
|
|
83
|
+
if (!refresh)
|
|
84
|
+
return false;
|
|
85
|
+
this.refreshing ??= Promise.resolve()
|
|
86
|
+
.then(refresh)
|
|
87
|
+
.catch(() => null)
|
|
88
|
+
.finally(() => {
|
|
89
|
+
this.refreshing = null;
|
|
90
|
+
});
|
|
91
|
+
const token = await this.refreshing;
|
|
92
|
+
if (!token)
|
|
93
|
+
return false;
|
|
94
|
+
this.opts.token = token;
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
/** doFetch with one transparent retry after a 401-triggered token refresh.
|
|
98
|
+
* Everything except the stream loop (which has its own auth handling) goes
|
|
99
|
+
* through here. */
|
|
100
|
+
async fetchAuthed(url, init) {
|
|
101
|
+
const res = await this.doFetch(url, init);
|
|
102
|
+
if (res.status !== 401 || !(await this.tryRefreshToken()))
|
|
103
|
+
return res;
|
|
104
|
+
init.headers.Authorization = `Bearer ${this.opts.token}`;
|
|
105
|
+
return this.doFetch(url, init);
|
|
106
|
+
}
|
|
66
107
|
url(path) {
|
|
67
108
|
return this.opts.baseUrl.replace(/\/+$/, '') + path;
|
|
68
109
|
}
|
|
@@ -75,7 +116,7 @@ export class CompanionClient {
|
|
|
75
116
|
return this._session;
|
|
76
117
|
}
|
|
77
118
|
async postJson(path, body) {
|
|
78
|
-
const res = await this.
|
|
119
|
+
const res = await this.fetchAuthed(this.url(path), {
|
|
79
120
|
method: 'POST',
|
|
80
121
|
headers: this.jsonHeaders(),
|
|
81
122
|
body: JSON.stringify(body)
|
|
@@ -151,7 +192,7 @@ export class CompanionClient {
|
|
|
151
192
|
* dedups by id). Falls back to buffered semantics if the server ever
|
|
152
193
|
* responds with plain JSON (e.g. an old deployment). */
|
|
153
194
|
async sendTextStreaming(sessionId, body) {
|
|
154
|
-
const res = await this.
|
|
195
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/session/${sessionId}/input`), {
|
|
155
196
|
method: 'POST',
|
|
156
197
|
headers: this.jsonHeaders(),
|
|
157
198
|
body: JSON.stringify({ ...body, stream: true })
|
|
@@ -347,7 +388,7 @@ export class CompanionClient {
|
|
|
347
388
|
.slice(-60)
|
|
348
389
|
.map((t) => ({ role: t.role, text: t.text.slice(0, 500) }));
|
|
349
390
|
try {
|
|
350
|
-
const res = await this.
|
|
391
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/session/${id}/end`), {
|
|
351
392
|
method: 'POST',
|
|
352
393
|
headers: this.jsonHeaders(),
|
|
353
394
|
body: JSON.stringify({ transcript }),
|
|
@@ -481,10 +522,20 @@ export class CompanionClient {
|
|
|
481
522
|
}, env);
|
|
482
523
|
});
|
|
483
524
|
}
|
|
484
|
-
/** Recall the memory this token is authorized to see
|
|
525
|
+
/** Recall the memory this token is authorized to see. Without `query` the
|
|
526
|
+
* facts come back ranked by importance/recency; with `query` the server
|
|
527
|
+
* runs semantic search over them (embedding-based, falls back to ranked
|
|
528
|
+
* when embeddings are unavailable) — e.g.
|
|
529
|
+
* `recall({ query: 'food preferences', limit: 10 })`. */
|
|
485
530
|
async recall(opts) {
|
|
486
|
-
const
|
|
487
|
-
|
|
531
|
+
const params = new URLSearchParams();
|
|
532
|
+
if (opts?.limit)
|
|
533
|
+
params.set('limit', String(opts.limit));
|
|
534
|
+
if (opts?.query?.trim())
|
|
535
|
+
params.set('q', opts.query.trim());
|
|
536
|
+
const query = params.toString();
|
|
537
|
+
const qs = query ? `?${query}` : '';
|
|
538
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/memory${qs}`), {
|
|
488
539
|
headers: { Authorization: `Bearer ${this.opts.token}` }
|
|
489
540
|
});
|
|
490
541
|
const json = (await res.json().catch(() => null));
|
|
@@ -501,7 +552,7 @@ export class CompanionClient {
|
|
|
501
552
|
async history(opts) {
|
|
502
553
|
const id = this.requireSession();
|
|
503
554
|
const qs = opts?.limit ? `?limit=${encodeURIComponent(opts.limit)}` : '';
|
|
504
|
-
const res = await this.
|
|
555
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/session/${encodeURIComponent(id)}/history${qs}`), { headers: { Authorization: `Bearer ${this.opts.token}` } });
|
|
505
556
|
const json = (await res.json().catch(() => null));
|
|
506
557
|
if (!res.ok || !json || json.ok === false) {
|
|
507
558
|
throw new CompanionError(json?.error || `history failed (${res.status})`, res.status);
|
|
@@ -514,7 +565,7 @@ export class CompanionClient {
|
|
|
514
565
|
* returned. Requires a live session. */
|
|
515
566
|
async setModalities(modalities) {
|
|
516
567
|
const id = this.requireSession();
|
|
517
|
-
const res = await this.
|
|
568
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/session/${encodeURIComponent(id)}/modalities`), {
|
|
518
569
|
method: 'POST',
|
|
519
570
|
headers: {
|
|
520
571
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -533,7 +584,7 @@ export class CompanionClient {
|
|
|
533
584
|
* it). Cheap; call it on a timer for a background tab. Requires a live session. */
|
|
534
585
|
async ping() {
|
|
535
586
|
const id = this.requireSession();
|
|
536
|
-
const res = await this.
|
|
587
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/session/${encodeURIComponent(id)}/ping`), { method: 'POST', headers: { Authorization: `Bearer ${this.opts.token}` } });
|
|
537
588
|
if (!res.ok) {
|
|
538
589
|
const json = (await res.json().catch(() => null));
|
|
539
590
|
throw new CompanionError(json?.error || `ping failed (${res.status})`, res.status);
|
|
@@ -578,7 +629,7 @@ export class CompanionClient {
|
|
|
578
629
|
* and the /models asset is CORS-enabled, so a cross-origin renderer can fetch
|
|
579
630
|
* the VRM directly. Reflects the user's live choice (built-in or custom). */
|
|
580
631
|
async getAvatar() {
|
|
581
|
-
const res = await this.
|
|
632
|
+
const res = await this.fetchAuthed(this.url('/api/companion/avatar'), {
|
|
582
633
|
headers: { Authorization: `Bearer ${this.opts.token}` }
|
|
583
634
|
});
|
|
584
635
|
const json = (await res.json().catch(() => null));
|
|
@@ -599,7 +650,7 @@ export class CompanionClient {
|
|
|
599
650
|
* which subsumes it). Same data the companion gives when asked "what's my
|
|
600
651
|
* balance?". */
|
|
601
652
|
async getWallet() {
|
|
602
|
-
const res = await this.
|
|
653
|
+
const res = await this.fetchAuthed(this.url('/api/companion/wallet'), {
|
|
603
654
|
headers: { Authorization: `Bearer ${this.opts.token}` }
|
|
604
655
|
});
|
|
605
656
|
const json = (await res.json().catch(() => null));
|
|
@@ -715,7 +766,7 @@ export class CompanionClient {
|
|
|
715
766
|
* session tokens only, like `confirmAction`. */
|
|
716
767
|
async pendingConfirms() {
|
|
717
768
|
const sessionId = this.requireSession();
|
|
718
|
-
const res = await this.
|
|
769
|
+
const res = await this.fetchAuthed(this.url(`/api/companion/session/${encodeURIComponent(sessionId)}/confirm`), { headers: { Authorization: `Bearer ${this.opts.token}` } });
|
|
719
770
|
const json = (await res.json().catch(() => null));
|
|
720
771
|
if (!res.ok || !json || json.ok === false) {
|
|
721
772
|
throw new CompanionError(json?.error || `pending confirms failed (${res.status})`, res.status);
|
|
@@ -924,11 +975,21 @@ export class CompanionClient {
|
|
|
924
975
|
async loop() {
|
|
925
976
|
const id = this.requireSession();
|
|
926
977
|
let backoff = 500;
|
|
978
|
+
let authRefreshes = 0; // consecutive 401→refresh cycles without a healthy connect
|
|
927
979
|
while (this.streaming) {
|
|
928
980
|
this.abort = new AbortController();
|
|
929
981
|
try {
|
|
930
982
|
const res = await this.doFetch(this.url(`/api/companion/session/${id}/stream?cursor=${this.cursor}`), { headers: { Authorization: `Bearer ${this.opts.token}` }, signal: this.abort.signal });
|
|
931
983
|
if (res.status === 401 || res.status === 403) {
|
|
984
|
+
// 401 = the token itself is bad — usually an EXPIRED session token
|
|
985
|
+
// (they live ~1h). With an onAuthError hook this is recoverable:
|
|
986
|
+
// refresh and reconnect instead of going permanently silent. The
|
|
987
|
+
// counter stops a hot loop when the hook keeps returning a token
|
|
988
|
+
// the server keeps rejecting.
|
|
989
|
+
if (res.status === 401 && authRefreshes < 3 && (await this.tryRefreshToken())) {
|
|
990
|
+
authRefreshes += 1;
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
932
993
|
// Permanent: the token can't subscribe (most often it lacks the
|
|
933
994
|
// `events.subscribe` scope; also revoked / wrong user). Retrying is
|
|
934
995
|
// futile and would silently swallow every reply — surface it as a
|
|
@@ -955,6 +1016,7 @@ export class CompanionClient {
|
|
|
955
1016
|
continue;
|
|
956
1017
|
}
|
|
957
1018
|
backoff = 500; // healthy connection resets backoff
|
|
1019
|
+
authRefreshes = 0;
|
|
958
1020
|
await this.consume(res.body);
|
|
959
1021
|
}
|
|
960
1022
|
catch {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_
|
|
|
5
5
|
export type { CompanionCall, CompanionCallOptions, VoiceToolBridge } from './call.js';
|
|
6
6
|
export { COMPANION_SCOPES, COMPANION_MODALITIES, SENSITIVE_SCOPES, REPRESENT_SCOPES, DEFAULT_SCOPES, DEFAULT_MODALITIES, isSensitiveScope, isRepresentScope, hasScope } from './scopes.js';
|
|
7
7
|
export type { CompanionScope, CompanionModality } from './scopes.js';
|
|
8
|
-
export type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, PendingConfirm, AudioClipPayload, ExpressionPayload, TypingPayload, VoiceInjectPayload, UsagePayload } from './protocol.js';
|
|
8
|
+
export type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, PendingConfirm, ToolCallPayload, AudioClipPayload, ExpressionPayload, TypingPayload, VoiceInjectPayload, UsagePayload } from './protocol.js';
|
|
9
|
+
export { PROTOCOL_VERSION, INBOUND_TYPES, OUTBOUND_TYPES } from './protocol.js';
|
|
9
10
|
/** Create a companion client. */
|
|
10
11
|
export declare function createCompanion(opts: CompanionClientOptions): CompanionClient;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Companion SDK — public entry. Embed the Pouchy companion in a third-party
|
|
2
|
-
// surface (web / app / game / hardware) over the REST/SSE plane
|
|
2
|
+
// surface (web / app / game / hardware) over the REST/SSE plane (with an
|
|
3
|
+
// optional WebSocket transport that degrades to SSE).
|
|
3
4
|
//
|
|
4
5
|
// import { createCompanion } from '$lib/companion-sdk';
|
|
5
6
|
// const c = createCompanion({ baseUrl: 'https://pouchy.ai', token: PAT });
|
|
@@ -12,6 +13,9 @@ import { CompanionClient } from './client.js';
|
|
|
12
13
|
export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
|
|
13
14
|
export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
|
|
14
15
|
export { COMPANION_SCOPES, COMPANION_MODALITIES, SENSITIVE_SCOPES, REPRESENT_SCOPES, DEFAULT_SCOPES, DEFAULT_MODALITIES, isSensitiveScope, isRepresentScope, hasScope } from './scopes.js';
|
|
16
|
+
// Runtime protocol constants — so a host can assert PROTOCOL_VERSION or
|
|
17
|
+
// enumerate/validate the event vocabulary without hard-coding the strings.
|
|
18
|
+
export { PROTOCOL_VERSION, INBOUND_TYPES, OUTBOUND_TYPES } from './protocol.js';
|
|
15
19
|
/** Create a companion client. */
|
|
16
20
|
export function createCompanion(opts) {
|
|
17
21
|
return new CompanionClient(opts);
|
package/dist/protocol.d.ts
CHANGED
|
@@ -76,6 +76,18 @@ export interface PendingConfirm {
|
|
|
76
76
|
/** Epoch ms when the companion asked. */
|
|
77
77
|
createdAt: number;
|
|
78
78
|
}
|
|
79
|
+
/** Payload of a `companion.tool_call` event — the companion is asking the host
|
|
80
|
+
* to run one of the app-declared tools it was given at `hello`. `args` is the
|
|
81
|
+
* raw JSON string the model produced (parse it yourself); reply by posting the
|
|
82
|
+
* result back with `client.sendToolResult(id, …)`. */
|
|
83
|
+
export interface ToolCallPayload {
|
|
84
|
+
/** Correlation id — echo it back on the tool result. */
|
|
85
|
+
id: string;
|
|
86
|
+
/** The declared tool name the model chose. */
|
|
87
|
+
name: string;
|
|
88
|
+
/** The model's arguments as a raw JSON string. */
|
|
89
|
+
args: string;
|
|
90
|
+
}
|
|
79
91
|
/** Payload of a `companion.ui_update` event — a live change to an
|
|
80
92
|
* already-rendered Instant UI panel: write each `{ key, value }` into the
|
|
81
93
|
* panel's state bag (re-evaluating templates / progress / `when`), without
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pouchy_ai/companion-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|