@pouchy_ai/companion-sdk 0.14.0 → 0.15.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,7 +12,20 @@ a protocol bump is always called out explicitly here.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
- ## [0.14.0] - 2026-07-06
15
+ ## [0.15.0] - 2026-07-08
16
+
17
+ ### Added
18
+
19
+ - **Typing / activity indicator** — a new `companion.typing` event and a
20
+ `client.onTyping(({ active }) => …)` convenience. The server emits
21
+ `active:true` when a turn starts working and `active:false` when it emits
22
+ its reply or pauses for an app tool result, so it spans the whole turn —
23
+ including the tool-loop / thinking phase BEFORE the first text delta, which
24
+ `onDelta` can't observe. Drive a "typing…" affordance uniformly across
25
+ streaming and non-streaming replies. Additive and non-breaking (receivers
26
+ ignore unknown types); the terminal `companion.message` stays authoritative.
27
+ Wire protocol stays `PROTOCOL_VERSION 1` (an added outbound type is
28
+ backwards-compatible). New payload type: `TypingPayload { active: boolean }`.
16
29
 
17
30
  ### Added
18
31
 
package/README.md CHANGED
@@ -70,6 +70,7 @@ Subscribe with `on(type, fn)` (or `'*'`), or these typed convenience helpers:
70
70
  | `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) |
71
71
  | `onAudio(fn)` | `companion.audio` | TTS clip (non-call modality) |
72
72
  | `onExpression(fn)` | `companion.expression` | avatar viseme / expression / gesture |
73
+ | `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 |
73
74
  | `onUsage(fn)` | `control.usage` | per-token metering echo |
74
75
  | `onError(fn)` | `control.error` | agent / stream errors |
75
76
 
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AudioClipPayload, CompanionEnvelope, ConfirmRequestPayload, ExpressionPayload, InterfaceUpdatePayload, OutboundType, PendingConfirm, RenderInterfacePayload, SocialMessagePayload, UsagePayload, WorldStateEvent } from './protocol.js';
1
+ import type { AudioClipPayload, CompanionEnvelope, ConfirmRequestPayload, ExpressionPayload, InterfaceUpdatePayload, OutboundType, PendingConfirm, RenderInterfacePayload, SocialMessagePayload, TypingPayload, UsagePayload, 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'>>;
@@ -401,6 +401,12 @@ export declare class CompanionClient {
401
401
  * friends (companion.social_message), delivered cross-app to any embed whose
402
402
  * token holds `social.message`. Surface them in your own UI / notify the user. */
403
403
  onSocialMessage(handler: (payload: SocialMessagePayload, envelope: CompanionEnvelope) => void): () => void;
404
+ /** Convenience: subscribe to the companion's activity indicator
405
+ * (companion.typing). Fires `active:true` when a turn starts working and
406
+ * `active:false` when it finishes or pauses for a tool result — spanning the
407
+ * tool-loop / thinking phase before the first text delta. Drive a "typing…"
408
+ * affordance from it: `client.onTyping(({ active }) => setTyping(active))`. */
409
+ onTyping(handler: (payload: TypingPayload, envelope: CompanionEnvelope) => void): () => void;
404
410
  /** Open the event stream (auto-reconnecting with the resume cursor). Uses the
405
411
  * WebSocket plane when opted in + available, else SSE. */
406
412
  start(): void;
package/dist/client.js CHANGED
@@ -643,6 +643,18 @@ export class CompanionClient {
643
643
  handler(p, env);
644
644
  });
645
645
  }
646
+ /** Convenience: subscribe to the companion's activity indicator
647
+ * (companion.typing). Fires `active:true` when a turn starts working and
648
+ * `active:false` when it finishes or pauses for a tool result — spanning the
649
+ * tool-loop / thinking phase before the first text delta. Drive a "typing…"
650
+ * affordance from it: `client.onTyping(({ active }) => setTyping(active))`. */
651
+ onTyping(handler) {
652
+ return this.on('companion.typing', (env) => {
653
+ const p = env.payload;
654
+ if (p && typeof p === 'object' && typeof p.active === 'boolean')
655
+ handler(p, env);
656
+ });
657
+ }
646
658
  /** Open the event stream (auto-reconnecting with the resume cursor). Uses the
647
659
  * WebSocket plane when opted in + available, else SSE. */
648
660
  start() {
package/dist/index.d.ts CHANGED
@@ -3,6 +3,6 @@ export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js
3
3
  export type { CompanionClientOptions, HelloAck, RecalledMemory, CallCredentials, CompanionToolDecl, WorldStateInput, CompanionAvatar, BrandIconSize } 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 type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, PendingConfirm, AudioClipPayload, ExpressionPayload, UsagePayload } from './protocol.js';
6
+ export type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, PendingConfirm, AudioClipPayload, ExpressionPayload, TypingPayload, UsagePayload } from './protocol.js';
7
7
  /** Create a companion client. */
8
8
  export declare function createCompanion(opts: CompanionClientOptions): CompanionClient;
@@ -3,7 +3,7 @@ export declare const PROTOCOL_VERSION: 1;
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
5
  /** Pouchy → app (control/data plane). */
6
- 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", "control.call_ready", "control.error", "control.usage"];
6
+ 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
7
  export type OutboundType = (typeof OUTBOUND_TYPES)[number];
8
8
  export type MessageType = InboundType | OutboundType;
9
9
  /** The single envelope every control/data-plane message shares. */
@@ -120,6 +120,16 @@ export interface SocialMessagePayload {
120
120
  /** ISO timestamp of delivery. */
121
121
  createdAt: string;
122
122
  }
123
+ /** Payload of a `companion.typing` event — an activity indicator bracketing the
124
+ * model work of a turn: `active:true` when the companion starts working,
125
+ * `active:false` when it emits its reply or pauses for an app tool result. It
126
+ * spans the whole turn — the tool-loop / thinking phase BEFORE the first text
127
+ * delta, which `onDelta` can't observe — so a host can show a "typing…" state
128
+ * uniformly across streaming and non-streaming replies. Advisory: a lost event
129
+ * never affects the turn; the terminal `companion.message` is authoritative. */
130
+ export interface TypingPayload {
131
+ active: boolean;
132
+ }
123
133
  /** A live world-state event the app streams in. `retained:true` = latest-value
124
134
  * state (coalesced); `retained:false` = a transient moment carrying `salience`
125
135
  * and optional `voiceRelevant`. */
package/dist/protocol.js CHANGED
@@ -29,6 +29,7 @@ export const OUTBOUND_TYPES = [
29
29
  'companion.voice_inject',
30
30
  'companion.confirm_request',
31
31
  'companion.social_message',
32
+ 'companion.typing',
32
33
  'control.call_ready',
33
34
  'control.error',
34
35
  'control.usage'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/companion-sdk",
3
- "version": "0.14.0",
3
+ "version": "0.15.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",