@pouchy_ai/companion-sdk 0.19.1 → 0.20.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,17 @@ a protocol bump is always called out explicitly here.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.20.0] - 2026-07-11
16
+
17
+ ### Added
18
+
19
+ - `client.getWallet(): Promise<CompanionWallet>` and the `GET /api/companion/wallet`
20
+ endpoint — read the companion instance's OWN wallet (nonzero stablecoin
21
+ balances + total USD). Read-only and receive-only; requires the `wallet.read`
22
+ scope (or `wallet.spend`, which subsumes it). Shares its read path with the
23
+ conversational `get_wallet_balance` tool so the endpoint and the in-chat answer
24
+ can never diverge. New exported types `CompanionWallet` / `WalletBalance`.
25
+
15
26
  ## [0.19.1] - 2026-07-10
16
27
 
17
28
  ### Documentation
package/README.md CHANGED
@@ -72,11 +72,11 @@ Subscribe with `on(type, fn)` (or `'*'`), or these typed convenience helpers:
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` |
74
74
  | `onConfirmRequest(fn)` | `companion.confirm_request` | a sensitive-op approval request. **Platform session tokens** (your end users): show your own confirm card and resolve it with `confirmAction` — this is how confirm-gated custom skills (POST / credentialed) run. First-party user tokens: observe-only — approval stays first-party (where the `stepUp:true` biometric gate lives) |
75
- | `onAudio(fn)` | `companion.audio` | TTS clip (non-call modality) |
76
- | `onExpression(fn)` | `companion.expression` | avatar viseme / expression / gesture |
75
+ | `onAudio(fn)` | `companion.audio` | TTS clip (non-call modality). _(reserved — not emitted yet)_ |
76
+ | `onExpression(fn)` | `companion.expression` | avatar viseme / expression / gesture. _(reserved — not emitted yet)_ |
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
- | `onUsage(fn)` | `control.usage` | per-token metering echo |
79
+ | `onUsage(fn)` | `control.usage` | per-token metering echo. _(reserved — not emitted yet)_ |
80
80
  | `onError(fn)` | `control.error` | agent / stream errors |
81
81
 
82
82
  ### Instant UI
@@ -106,6 +106,7 @@ local/device skills).
106
106
  | `close()` | One-call teardown: `stop()` + `endSession()` — the text-session mirror of the call handle's `close()`. |
107
107
  | `startCall(opts)` / `connectCall(opts)` | Voice plane: raw credentials / fully-wired call handle (`call.close()` ends + folds the transcript into memory). |
108
108
  | `getAvatar()` / `brandIconUrl(size?)` | The user's avatar (VRM/portrait) and the Pouchy brand icon for your UI. |
109
+ | `getWallet()` | Read the instance's own wallet — `{ balances: [{ currency, amount }], totalUsd, currency }`. Read-only + receive-only; needs the `wallet.read` scope. |
109
110
 
110
111
  Tool calling beyond your own declared tools: `HOST_CONTROL_TOOLS` (universal
111
112
  verbs — `invoke_action`, `set_feature`, `set_value`, `navigate`, `highlight`)
package/dist/client.d.ts CHANGED
@@ -123,6 +123,23 @@ export interface CompanionAvatar {
123
123
  * built-ins today (reserved for custom previews / server-rendered thumbs). */
124
124
  imageUrl: string | null;
125
125
  }
126
+ /** One nonzero stablecoin balance line. */
127
+ export interface WalletBalance {
128
+ /** Stablecoin code (USDT / USDC / DAI / USD1). */
129
+ currency: string;
130
+ /** Decimal amount as a string (not atomic units), e.g. "5.83". */
131
+ amount: string;
132
+ }
133
+ /** Read-only snapshot of the companion instance's OWN wallet (receive-only —
134
+ * there is no way to move funds via the SDK). Requires the `wallet.read` scope. */
135
+ export interface CompanionWallet {
136
+ /** Nonzero per-currency balances (empty when the wallet is empty). */
137
+ balances: WalletBalance[];
138
+ /** Total across the balances; the allowed stablecoins are USD-pegged 1:1. */
139
+ totalUsd: number;
140
+ /** Denomination of `totalUsd` (always "USD" today). */
141
+ currency: string;
142
+ }
126
143
  /** Available sizes for the Pouchy brand icon (px). */
127
144
  export type BrandIconSize = 256 | 512 | 1024;
128
145
  /** Canonical URL of the official Pouchy brand icon at `size` px, derived from a
@@ -392,6 +409,12 @@ export declare class CompanionClient {
392
409
  * and the /models asset is CORS-enabled, so a cross-origin renderer can fetch
393
410
  * the VRM directly. Reflects the user's live choice (built-in or custom). */
394
411
  getAvatar(): Promise<CompanionAvatar>;
412
+ /** Read the companion instance's OWN wallet — nonzero stablecoin balances +
413
+ * total USD. Read-only and receive-only (spends go through the confirm-gated
414
+ * pay tools, never here). Requires the `wallet.read` scope (or `wallet.spend`,
415
+ * which subsumes it). Same data the companion gives when asked "what's my
416
+ * balance?". */
417
+ getWallet(): Promise<CompanionWallet>;
395
418
  /** Canonical URL of the official Pouchy brand icon (square PNG, transparent
396
419
  * background) at the given size. Static + public — needs no token — so it's
397
420
  * safe to drop straight into an `<img src>` for "powered by Pouchy" badges,
package/dist/client.js CHANGED
@@ -593,6 +593,25 @@ export class CompanionClient {
593
593
  imageUrl: json.imageUrl ?? null
594
594
  };
595
595
  }
596
+ /** Read the companion instance's OWN wallet — nonzero stablecoin balances +
597
+ * total USD. Read-only and receive-only (spends go through the confirm-gated
598
+ * pay tools, never here). Requires the `wallet.read` scope (or `wallet.spend`,
599
+ * which subsumes it). Same data the companion gives when asked "what's my
600
+ * balance?". */
601
+ async getWallet() {
602
+ const res = await this.doFetch(this.url('/api/companion/wallet'), {
603
+ headers: { Authorization: `Bearer ${this.opts.token}` }
604
+ });
605
+ const json = (await res.json().catch(() => null));
606
+ if (!res.ok || !json || json.ok === false) {
607
+ throw new CompanionError(json?.error || `wallet fetch failed (${res.status})`, res.status);
608
+ }
609
+ return {
610
+ balances: json.balances ?? [],
611
+ totalUsd: json.totalUsd ?? 0,
612
+ currency: json.currency ?? 'USD'
613
+ };
614
+ }
596
615
  /** Canonical URL of the official Pouchy brand icon (square PNG, transparent
597
616
  * background) at the given size. Static + public — needs no token — so it's
598
617
  * safe to drop straight into an `<img src>` for "powered by Pouchy" badges,
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, BrandIconSize, EndSessionResult } from './client.js';
3
+ export type { CompanionClientOptions, HelloAck, RecalledMemory, CompanionTurn, CallCredentials, CompanionToolDecl, 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';
@@ -2,7 +2,13 @@ export declare const PROTOCOL_VERSION: 1;
2
2
  /** App → Pouchy (control/data plane). */
3
3
  export declare const INBOUND_TYPES: readonly ["hello", "input.text", "context.event", "context.snapshot", "tool.result", "control.start_call", "control.end_call", "control.set_modalities", "control.ping"];
4
4
  export type InboundType = (typeof INBOUND_TYPES)[number];
5
- /** Pouchy → app (control/data plane). */
5
+ /** Pouchy → app (control/data plane).
6
+ * NOTE: three types are RESERVED — part of the vocabulary + typed handlers, but
7
+ * the server does NOT emit them yet, so `onAudio` / `onExpression` / `onUsage`
8
+ * are safe to register but won't fire until a producer ships. Don't build a UX
9
+ * that depends on them today. (companion.audio → non-call reply TTS;
10
+ * companion.expression → avatar viseme/expression cues; control.usage →
11
+ * per-turn usage-metering echo.) */
6
12
  export declare const OUTBOUND_TYPES: readonly ["hello.ack", "companion.message", "companion.audio", "companion.tool_call", "companion.ui_action", "companion.ui_update", "companion.expression", "companion.voice_inject", "companion.confirm_request", "companion.social_message", "companion.typing", "control.call_ready", "control.error", "control.usage"];
7
13
  export type OutboundType = (typeof OUTBOUND_TYPES)[number];
8
14
  export type MessageType = InboundType | OutboundType;
package/dist/protocol.js CHANGED
@@ -17,20 +17,26 @@ export const INBOUND_TYPES = [
17
17
  'control.set_modalities',
18
18
  'control.ping'
19
19
  ];
20
- /** Pouchy → app (control/data plane). */
20
+ /** Pouchy → app (control/data plane).
21
+ * NOTE: three types are RESERVED — part of the vocabulary + typed handlers, but
22
+ * the server does NOT emit them yet, so `onAudio` / `onExpression` / `onUsage`
23
+ * are safe to register but won't fire until a producer ships. Don't build a UX
24
+ * that depends on them today. (companion.audio → non-call reply TTS;
25
+ * companion.expression → avatar viseme/expression cues; control.usage →
26
+ * per-turn usage-metering echo.) */
21
27
  export const OUTBOUND_TYPES = [
22
28
  'hello.ack',
23
29
  'companion.message',
24
- 'companion.audio',
30
+ 'companion.audio', // reserved — not emitted yet
25
31
  'companion.tool_call',
26
32
  'companion.ui_action',
27
33
  'companion.ui_update',
28
- 'companion.expression',
34
+ 'companion.expression', // reserved — not emitted yet
29
35
  'companion.voice_inject',
30
36
  'companion.confirm_request',
31
37
  'companion.social_message',
32
38
  'companion.typing',
33
39
  'control.call_ready',
34
40
  'control.error',
35
- 'control.usage'
41
+ 'control.usage' // reserved — not emitted yet
36
42
  ];
package/dist/scopes.js CHANGED
@@ -21,7 +21,7 @@ export const COMPANION_SCOPES = [
21
21
  'memory.read:core', // read the shared global brain (SENSITIVE)
22
22
  'memory.write:core', // write into the shared global brain (SENSITIVE)
23
23
  'skills.execute', // run the user's installed skills (SENSITIVE)
24
- 'wallet.read', // READ-ONLY wallet: balance + own deposit address (receive-only; not sensitive)
24
+ 'wallet.read', // READ-ONLY wallet: balance + own deposit address (receive-only; not sensitive). Surfaced via the companion's wallet tools (ask it) — no standalone REST endpoint.
25
25
  'wallet.spend', // spend from the Care Wallet (SENSITIVE)
26
26
  'social.message', // message friends via A2A (SENSITIVE)
27
27
  // ── Representative ("on-behalf-of") plane ─────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/companion-sdk",
3
- "version": "0.19.1",
3
+ "version": "0.20.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",