@pouchy_ai/companion-sdk 0.18.1 → 0.19.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 CHANGED
@@ -12,6 +12,31 @@ a protocol bump is always called out explicitly here.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.19.1] - 2026-07-10
16
+
17
+ ### Documentation
18
+
19
+ - README gains a client-method reference table (memory ingest, history,
20
+ setModalities, ping, pendingConfirms/confirmAction incl. the
21
+ exec_failed/retryable retry contract, endSession skipped reasons, close(),
22
+ startCall/connectCall, getAvatar/brandIconUrl) plus a note on
23
+ HOST_CONTROL_TOOLS and AVATAR_VISUAL_TOOLS (declare-or-silent-no-op).
24
+ No code change.
25
+
26
+ ## [0.19.0] - 2026-07-10
27
+
28
+ ### Added
29
+
30
+ - **Typed scope vocabulary exported from the package.** `COMPANION_SCOPES` /
31
+ `CompanionScope`, `COMPANION_MODALITIES` / `CompanionModality`,
32
+ `SENSITIVE_SCOPES`, `REPRESENT_SCOPES`, `DEFAULT_SCOPES`,
33
+ `DEFAULT_MODALITIES`, and the helpers `isSensitiveScope()`,
34
+ `isRepresentScope()`, `hasScope(granted, required)`. Configure keys, gate
35
+ features on `hello.ack.grantedScopes`, and explain 403s with compile-checked
36
+ scope strings instead of prose-copied literals. Mirrors the server vocabulary
37
+ and is guarded by the same drift test as the wire protocol. Additive — no
38
+ wire-protocol change.
39
+
15
40
  ## [0.18.1] - 2026-07-08
16
41
 
17
42
  ### Changed
package/README.md CHANGED
@@ -92,6 +92,52 @@ Reference renderers ship in [`examples/`](../../examples): `web-instant-ui` (van
92
92
  `native-instant-ui` (SwiftUI + Jetpack Compose), and `cli-skills` (terminal +
93
93
  local/device skills).
94
94
 
95
+ ## Client methods (beyond the quickstart)
96
+
97
+ | Method | What it does |
98
+ |---|---|
99
+ | `recall(query)` / `remember(text)` | Read / write the token-scoped memory namespace. |
100
+ | `ingestKnowledge(text, opts?)` / `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
+ | `setModalities([...])` | Switch active I/O mid-session (within the token's grant). |
103
+ | `ping()` | Keepalive for long-idle embeds. |
104
+ | `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
+ | `endSession()` | Distill the session into durable memory now; returns `{ skipped?: 'no_session' \| 'no_content' \| 'throttled' }`. |
106
+ | `close()` | One-call teardown: `stop()` + `endSession()` — the text-session mirror of the call handle's `close()`. |
107
+ | `startCall(opts)` / `connectCall(opts)` | Voice plane: raw credentials / fully-wired call handle (`call.close()` ends + folds the transcript into memory). |
108
+ | `getAvatar()` / `brandIconUrl(size?)` | The user's avatar (VRM/portrait) and the Pouchy brand icon for your UI. |
109
+
110
+ Tool calling beyond your own declared tools: `HOST_CONTROL_TOOLS` (universal
111
+ verbs — `invoke_action`, `set_feature`, `set_value`, `navigate`, `highlight`)
112
+ are offered automatically once you declare `tools`/`handles`, and
113
+ `AVATAR_VISUAL_TOOLS` (`play_gesture` / `play_expression`) let avatar-rendering
114
+ embeds receive expression cues — declare them or they no-op silently. Full
115
+ contract: `docs/companion-host-control.md`.
116
+
117
+ ## Scopes (typed)
118
+
119
+ The full capability-scope vocabulary ships as typed constants — no more
120
+ copying scope strings from prose:
121
+
122
+ ```ts
123
+ import {
124
+ COMPANION_SCOPES, SENSITIVE_SCOPES, DEFAULT_SCOPES,
125
+ hasScope, isSensitiveScope, type CompanionScope
126
+ } from '@pouchy_ai/companion-sdk';
127
+
128
+ const ack = await c.connect();
129
+ if (!hasScope(ack.grantedScopes, 'skills.execute')) {
130
+ // hide the "run a skill" affordance instead of eating a 403
131
+ }
132
+ ```
133
+
134
+ `COMPANION_SCOPES` lists every scope a key can carry; `SENSITIVE_SCOPES` are
135
+ the ones that are never on by default (money / skills / social / core memory /
136
+ the representative plane); `DEFAULT_SCOPES` is what a fresh key gets. Also
137
+ exported: `COMPANION_MODALITIES`, `REPRESENT_SCOPES`, `DEFAULT_MODALITIES`,
138
+ `isRepresentScope()`. The list mirrors the server vocabulary and is guarded by
139
+ the SDK's drift test.
140
+
95
141
  ## Representative mode (代聊)
96
142
 
97
143
  Pass a `visitor` and the session flips from **owner-facing** to **representative**:
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js
3
3
  export type { CompanionClientOptions, HelloAck, RecalledMemory, CompanionTurn, CallCredentials, CompanionToolDecl, WorldStateInput, CompanionAvatar, 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
+ export { COMPANION_SCOPES, COMPANION_MODALITIES, SENSITIVE_SCOPES, REPRESENT_SCOPES, DEFAULT_SCOPES, DEFAULT_MODALITIES, isSensitiveScope, isRepresentScope, hasScope } from './scopes.js';
7
+ export type { CompanionScope, CompanionModality } from './scopes.js';
6
8
  export type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, PendingConfirm, AudioClipPayload, ExpressionPayload, TypingPayload, VoiceInjectPayload, UsagePayload } from './protocol.js';
7
9
  /** Create a companion client. */
8
10
  export declare function createCompanion(opts: CompanionClientOptions): CompanionClient;
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import { CompanionClient } from './client.js';
12
12
  export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
13
13
  export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES, AVATAR_VISUAL_TOOLS, AVATAR_VISUAL_TOOL_NAMES } from './call.js';
14
+ export { COMPANION_SCOPES, COMPANION_MODALITIES, SENSITIVE_SCOPES, REPRESENT_SCOPES, DEFAULT_SCOPES, DEFAULT_MODALITIES, isSensitiveScope, isRepresentScope, hasScope } from './scopes.js';
14
15
  /** Create a companion client. */
15
16
  export function createCompanion(opts) {
16
17
  return new CompanionClient(opts);
@@ -0,0 +1,24 @@
1
+ /** Every capability scope a Personal Access Token / OAuth grant can carry. */
2
+ export declare const COMPANION_SCOPES: readonly ["chat", "voice", "call", "files", "events.subscribe", "worldstate.write", "ui.render", "memory.read:app", "memory.write:app", "memory.read:core", "memory.write:core", "skills.execute", "wallet.read", "wallet.spend", "social.message", "represent", "expose:knowledge", "expose:facts", "represent:pair", "represent:remember"];
3
+ export type CompanionScope = (typeof COMPANION_SCOPES)[number];
4
+ /** I/O modalities a key can carry alongside its scopes. */
5
+ export declare const COMPANION_MODALITIES: readonly ["text", "voice", "call", "files"];
6
+ export type CompanionModality = (typeof COMPANION_MODALITIES)[number];
7
+ /** Sensitive scopes — never granted by default; the user opts in at key
8
+ * creation, and execution still passes a confirm on a Pouchy-controlled
9
+ * surface (see `companion.confirm_request`). */
10
+ export declare const SENSITIVE_SCOPES: ReadonlySet<CompanionScope>;
11
+ /** The representative ("on-behalf-of") plane — a distinct axis from the
12
+ * execution scopes: one lets the companion ACT for the owner, the other lets
13
+ * it REPRESENT the owner to an outside visitor. `represent` is the umbrella;
14
+ * the `expose:*` / `represent:*` scopes only mean anything alongside it. */
15
+ export declare const REPRESENT_SCOPES: ReadonlySet<CompanionScope>;
16
+ /** What a freshly-generated key carries unless the user opts into more: a
17
+ * chat/play companion confined to its own app memory namespace. */
18
+ export declare const DEFAULT_SCOPES: readonly CompanionScope[];
19
+ export declare const DEFAULT_MODALITIES: readonly CompanionModality[];
20
+ export declare function isSensitiveScope(s: CompanionScope): boolean;
21
+ export declare function isRepresentScope(s: CompanionScope): boolean;
22
+ /** Does a granted scope set (e.g. `hello.ack.grantedScopes`) satisfy a
23
+ * required scope? Exact-match for v1 (no hierarchical wildcards). */
24
+ export declare function hasScope(granted: readonly string[], required: CompanionScope): boolean;
package/dist/scopes.js ADDED
@@ -0,0 +1,85 @@
1
+ // Companion capability scopes — the SDK's self-contained copy (no $lib
2
+ // imports; same doctrine as protocol.ts). The server's source of truth is
3
+ // src/lib/types/companion-scopes.ts; protocol.drift.test.ts fails if the two
4
+ // diverge — extend both together.
5
+ //
6
+ // Why the SDK exports these: a host app configuring a key, gating a feature
7
+ // on `hello.ack.grantedScopes`, or explaining a `403 missing scope` needs the
8
+ // vocabulary as types and constants, not prose. Typos in scope strings become
9
+ // compile errors instead of runtime 403s.
10
+ /** Every capability scope a Personal Access Token / OAuth grant can carry. */
11
+ export const COMPANION_SCOPES = [
12
+ 'chat', // text turns
13
+ 'voice', // TTS / STT
14
+ 'call', // realtime WebRTC voice session
15
+ 'files', // multimodal file in/out (vision / image)
16
+ 'events.subscribe', // receive the outbound companion event stream
17
+ 'worldstate.write', // push inbound live world-state / context events
18
+ 'ui.render', // let the companion render Instant UI surfaces on the host (display + forms; not sensitive)
19
+ 'memory.read:app', // read this app's own memory namespace
20
+ 'memory.write:app', // write into this app's own memory namespace
21
+ 'memory.read:core', // read the shared global brain (SENSITIVE)
22
+ 'memory.write:core', // write into the shared global brain (SENSITIVE)
23
+ 'skills.execute', // run the user's installed skills (SENSITIVE)
24
+ 'wallet.read', // READ-ONLY wallet: balance + own deposit address (receive-only; not sensitive)
25
+ 'wallet.spend', // spend from the Care Wallet (SENSITIVE)
26
+ 'social.message', // message friends via A2A (SENSITIVE)
27
+ // ── Representative ("on-behalf-of") plane ─────────────────────────────
28
+ 'represent', // open visitor-facing representative sessions (SENSITIVE)
29
+ 'expose:knowledge', // let the representative draw on the owner's knowledge base (SENSITIVE)
30
+ 'expose:facts', // widen exposed facts past the identity/preference floor (SENSITIVE)
31
+ 'represent:pair', // let a representative pair a visitor (also a Pouchy user) as a friend (SENSITIVE)
32
+ 'represent:remember' // let a representative keep durable per-visitor notes across visits (SENSITIVE)
33
+ ];
34
+ /** I/O modalities a key can carry alongside its scopes. */
35
+ export const COMPANION_MODALITIES = ['text', 'voice', 'call', 'files'];
36
+ /** Sensitive scopes — never granted by default; the user opts in at key
37
+ * creation, and execution still passes a confirm on a Pouchy-controlled
38
+ * surface (see `companion.confirm_request`). */
39
+ export const SENSITIVE_SCOPES = new Set([
40
+ 'memory.read:core',
41
+ 'memory.write:core',
42
+ 'skills.execute',
43
+ 'wallet.spend',
44
+ 'social.message',
45
+ 'represent',
46
+ 'expose:knowledge',
47
+ 'expose:facts',
48
+ 'represent:pair',
49
+ 'represent:remember'
50
+ ]);
51
+ /** The representative ("on-behalf-of") plane — a distinct axis from the
52
+ * execution scopes: one lets the companion ACT for the owner, the other lets
53
+ * it REPRESENT the owner to an outside visitor. `represent` is the umbrella;
54
+ * the `expose:*` / `represent:*` scopes only mean anything alongside it. */
55
+ export const REPRESENT_SCOPES = new Set([
56
+ 'represent',
57
+ 'expose:knowledge',
58
+ 'expose:facts',
59
+ 'represent:pair',
60
+ 'represent:remember'
61
+ ]);
62
+ /** What a freshly-generated key carries unless the user opts into more: a
63
+ * chat/play companion confined to its own app memory namespace. */
64
+ export const DEFAULT_SCOPES = [
65
+ 'chat',
66
+ 'voice',
67
+ 'call',
68
+ 'files',
69
+ 'events.subscribe',
70
+ 'worldstate.write',
71
+ 'memory.read:app',
72
+ 'memory.write:app'
73
+ ];
74
+ export const DEFAULT_MODALITIES = ['text', 'voice'];
75
+ export function isSensitiveScope(s) {
76
+ return SENSITIVE_SCOPES.has(s);
77
+ }
78
+ export function isRepresentScope(s) {
79
+ return REPRESENT_SCOPES.has(s);
80
+ }
81
+ /** Does a granted scope set (e.g. `hello.ack.grantedScopes`) satisfy a
82
+ * required scope? Exact-match for v1 (no hierarchical wildcards). */
83
+ export function hasScope(granted, required) {
84
+ return granted.includes(required);
85
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/companion-sdk",
3
- "version": "0.18.1",
3
+ "version": "0.19.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",