@pouchy_ai/companion-sdk 0.22.0 → 0.23.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,33 @@ a protocol bump is always called out explicitly here.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.23.0] - 2026-07-11
16
+
17
+ ### Added
18
+
19
+ - **`onToolCall` hands you pre-parsed arguments.** The callback now receives a
20
+ `ToolCallEvent` — the `companion.tool_call` wire payload plus `argsJson`:
21
+ `args` pre-parsed as JSON (`undefined` when empty or malformed). Every tool
22
+ handler was re-implementing the same `try { JSON.parse(args) } catch {}`
23
+ boilerplate; now `const { item } = call.argsJson as {...}` just works, and the
24
+ raw `args` string is still there when you need the exact model output.
25
+ `ToolCallEvent` is exported. Backwards-compatible (the type extends
26
+ `ToolCallPayload`).
27
+
28
+ ### Docs
29
+
30
+ - The `control.error` stream-plane code vocabulary (`agent_error`,
31
+ `stream_unauthorized`) is now documented in the protocol/API references, the
32
+ README event tables, and pouchy.ai/sdk — previously the codes were emitted
33
+ but listed nowhere.
34
+ - The pouchy.ai/sdk CDN snippet now uses the canonical self-hosted bundle URL
35
+ (`https://pouchy.ai/sdk/companion-sdk.js`) and includes the
36
+ `@elevenlabs/client` import-map entry voice needs.
37
+ - `openCompanionCall` / `VoiceToolBridge` (the advanced voice primitive
38
+ `connectCall`/`startCall` wrap) get a pointer in the package README.
39
+
40
+ No wire-protocol change (`PROTOCOL_VERSION` stays `1`).
41
+
15
42
  ## [0.22.0] - 2026-07-11
16
43
 
17
44
  ### Added
package/README.md CHANGED
@@ -67,7 +67,7 @@ Subscribe with `on(type, fn)` (or `'*'`), or these typed convenience helpers:
67
67
  | --- | --- | --- |
68
68
  | `onMessage(fn)` | `companion.message` | the assistant reply (fires exactly once per turn, streaming or not) |
69
69
  | `onDelta(fn)` | *(POST-response SSE)* | **token streaming** — registering makes `sendText` stream the reply as it is generated: `fn(chunk, {reset?})` fires per text chunk (`reset` = clear the partial — that text was tool-call deliberation); `onMessage` then fires once with the authoritative final text (replay-deduplicated). Force per call with `sendText(text, { stream: true \| false })` |
70
- | `onToolCall(fn)` | `companion.tool_call` | the companion asks your app to run a declared tool → `sendToolResult` |
70
+ | `onToolCall(fn)` | `companion.tool_call` | the companion asks your app to run a declared tool → `sendToolResult`. The call carries `argsJson` — `args` pre-parsed as JSON (undefined on empty/malformed args) |
71
71
  | `onRender(fn)` | `companion.ui_action` | **Instant UI** — draw `payload.interface` (platform-neutral genui schema) with your own renderer (web / iOS / Android / CLI). Needs `ui.render` |
72
72
  | `onInterfaceUpdate(fn)` | `companion.ui_update` | live `{key,value}` update to an already-rendered panel (no rebuild) |
73
73
  | `onSocialMessage(fn)` | `companion.social_message` | inbound A2A friend message, delivered cross-app. Needs `social.message` |
@@ -77,7 +77,7 @@ Subscribe with `on(type, fn)` (or `'*'`), or these typed convenience helpers:
77
77
  | `onVoiceInject(fn)` | `companion.voice_inject` | `fn({text, speak})` — a `voiceRelevant` world-state line to say aloud during a live call; route `text` to your voice session when `speak` |
78
78
  | `onTyping(fn)` | `companion.typing` | activity indicator — `fn({active})` fires `true` when a turn starts working and `false` when it finishes / pauses, spanning the tool-loop / thinking phase before the first text delta. Drive a "typing…" state |
79
79
  | `onUsage(fn)` | `control.usage` | per-token metering echo. _(reserved — not emitted yet)_ |
80
- | `onError(fn)` | `control.error` | agent / stream errors |
80
+ | `onError(fn)` | `control.error` | agent / stream errors — `agent_error` (server-side turn failed after accept; safe to re-send) or the SDK-synthesized `stream_unauthorized` (stream 401 exhausted reconnects — refresh the token, `start()` again) |
81
81
 
82
82
  ### Instant UI
83
83
 
@@ -144,7 +144,12 @@ The wire-protocol runtime constants are exported too — `PROTOCOL_VERSION`,
144
144
  `INBOUND_TYPES`, and `OUTBOUND_TYPES` — so you can assert the version or
145
145
  enumerate/validate the event vocabulary without hard-coding strings. Every
146
146
  outbound payload has a named type; `ToolCallPayload` (`{ id, name, args }`) types
147
- the `companion.tool_call` event and `onToolCall`'s callback.
147
+ the `companion.tool_call` event, and `onToolCall`'s callback receives
148
+ `ToolCallEvent` — the payload plus `argsJson` (pre-parsed args).
149
+
150
+ For advanced voice hosts, the lower-level `openCompanionCall(client, options)`
151
+ primitive that `connectCall`/`startCall` wrap is exported too (bring-your-own
152
+ call lifecycle; see `docs/companion-integration-faq.md`).
148
153
 
149
154
  ## Representative mode (代聊)
150
155
 
package/dist/client.d.ts CHANGED
@@ -75,6 +75,13 @@ export interface CompanionToolDecl {
75
75
  description?: string;
76
76
  parameters?: Record<string, unknown>;
77
77
  }
78
+ /** What `onToolCall` hands your handler: the `companion.tool_call` wire payload
79
+ * plus `argsJson` — `args` pre-parsed as JSON. `undefined` when `args` is
80
+ * empty or not valid JSON; keep reading the raw `args` string when you need
81
+ * the exact model output. */
82
+ export interface ToolCallEvent extends ToolCallPayload {
83
+ argsJson?: unknown;
84
+ }
78
85
  /** Voice-plane credentials, discriminated by provider (see startCall). */
79
86
  export type CallCredentials = {
80
87
  provider: 'elevenlabs-convai';
@@ -332,8 +339,11 @@ export declare class CompanionClient {
332
339
  * string fed back to the voice model. Times out so a missing result can't
333
340
  * hang the call. */
334
341
  private invokeVoiceTool;
335
- /** Convenience: subscribe to tool-call requests from the companion. */
336
- onToolCall(handler: (call: ToolCallPayload, envelope: CompanionEnvelope) => void): () => void;
342
+ /** Convenience: subscribe to tool-call requests from the companion. The call
343
+ * carries the wire payload plus `argsJson` `args` pre-parsed as JSON
344
+ * (undefined when empty or malformed), so handlers don't each re-implement
345
+ * the same try/catch around `JSON.parse(args)`. */
346
+ onToolCall(handler: (call: ToolCallEvent, envelope: CompanionEnvelope) => void): () => void;
337
347
  /** Convenience: subscribe to errors the companion surfaces — server-side agent
338
348
  * errors and stream failures (e.g. a permanent `stream_unauthorized`) both
339
349
  * arrive as `control.error`. Returns an unsubscribe fn. */
package/dist/client.js CHANGED
@@ -15,6 +15,17 @@
15
15
  import { PROTOCOL_VERSION } from './protocol.js';
16
16
  import { parseSse } from './sse.js';
17
17
  import { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
18
+ /** Model tool arguments are model output — tolerate junk without throwing. */
19
+ function parseArgsJson(args) {
20
+ if (!args.trim())
21
+ return undefined;
22
+ try {
23
+ return JSON.parse(args);
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
18
29
  /** Canonical URL of the official Pouchy brand icon at `size` px, derived from a
19
30
  * deployment origin. Standalone (no client/token needed) so a surface can show
20
31
  * the Pouchy mark before/without connecting. */
@@ -501,12 +512,16 @@ export class CompanionClient {
501
512
  });
502
513
  });
503
514
  }
504
- /** Convenience: subscribe to tool-call requests from the companion. */
515
+ /** Convenience: subscribe to tool-call requests from the companion. The call
516
+ * carries the wire payload plus `argsJson` — `args` pre-parsed as JSON
517
+ * (undefined when empty or malformed), so handlers don't each re-implement
518
+ * the same try/catch around `JSON.parse(args)`. */
505
519
  onToolCall(handler) {
506
520
  return this.on('companion.tool_call', (env) => {
507
521
  const p = env.payload;
508
522
  if (typeof p?.id === 'string' && typeof p?.name === 'string') {
509
- handler({ id: p.id, name: p.name, args: typeof p.args === 'string' ? p.args : '' }, env);
523
+ const args = typeof p.args === 'string' ? p.args : '';
524
+ handler({ id: p.id, name: p.name, args, argsJson: parseArgsJson(args) }, env);
510
525
  }
511
526
  });
512
527
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CompanionClient, type CompanionClientOptions } from './client.js';
2
2
  export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
3
- export type { CompanionClientOptions, HelloAck, RecalledMemory, CompanionTurn, CallCredentials, CompanionToolDecl, WorldStateInput, CompanionAvatar, CompanionWallet, WalletBalance, BrandIconSize, EndSessionResult } from './client.js';
3
+ export type { CompanionClientOptions, HelloAck, RecalledMemory, CompanionTurn, CallCredentials, CompanionToolDecl, ToolCallEvent, WorldStateInput, CompanionAvatar, CompanionWallet, WalletBalance, BrandIconSize, EndSessionResult } from './client.js';
4
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 { COMPANION_SCOPES, COMPANION_MODALITIES, SENSITIVE_SCOPES, REPRESENT_SCOPES, DEFAULT_SCOPES, DEFAULT_MODALITIES, isSensitiveScope, isRepresentScope, hasScope } from './scopes.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pouchy_ai/companion-sdk",
3
- "version": "0.22.0",
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.",
3
+ "version": "0.23.0",
4
+ "description": "Embed the Pouchy companion \u2014 chat, voice, tools, memory, live world-state, instant UI, and agent-to-agent messaging \u2014 in any app, game, or site.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "homepage": "https://pouchy.ai",