@pinecall/skills 0.1.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.
Files changed (68) hide show
  1. package/README.md +65 -0
  2. package/build.mjs +204 -0
  3. package/package.json +29 -0
  4. package/skills/pinecall-concepts/SKILL.md +41 -0
  5. package/skills/pinecall-concepts/references/concepts/agents-and-channels.md +155 -0
  6. package/skills/pinecall-concepts/references/concepts/deployment-topologies.md +120 -0
  7. package/skills/pinecall-concepts/references/concepts/hot-reload.md +119 -0
  8. package/skills/pinecall-concepts/references/concepts/philosophy.md +100 -0
  9. package/skills/pinecall-concepts/references/concepts/server-vs-client-llm.md +119 -0
  10. package/skills/pinecall-examples/SKILL.md +59 -0
  11. package/skills/pinecall-examples/references/examples/browser-widget.md +206 -0
  12. package/skills/pinecall-examples/references/examples/chat-bot.md +184 -0
  13. package/skills/pinecall-examples/references/examples/headless-agent.md +121 -0
  14. package/skills/pinecall-examples/references/examples/index.md +183 -0
  15. package/skills/pinecall-examples/references/examples/multi-channel-bot.md +173 -0
  16. package/skills/pinecall-examples/references/examples/outbound-dispatch.md +109 -0
  17. package/skills/pinecall-examples/references/examples/turn-detection.md +150 -0
  18. package/skills/pinecall-guides/SKILL.md +68 -0
  19. package/skills/pinecall-guides/references/guides/call-ringing.md +149 -0
  20. package/skills/pinecall-guides/references/guides/conversation-history.md +377 -0
  21. package/skills/pinecall-guides/references/guides/dev-mode.md +130 -0
  22. package/skills/pinecall-guides/references/guides/events.md +677 -0
  23. package/skills/pinecall-guides/references/guides/human-takeover.md +184 -0
  24. package/skills/pinecall-guides/references/guides/inbound-voice.md +201 -0
  25. package/skills/pinecall-guides/references/guides/knowledge-bases.md +166 -0
  26. package/skills/pinecall-guides/references/guides/live-listening.md +199 -0
  27. package/skills/pinecall-guides/references/guides/multi-tenant.md +158 -0
  28. package/skills/pinecall-guides/references/guides/outbound-calls.md +279 -0
  29. package/skills/pinecall-guides/references/guides/sse-streaming.md +207 -0
  30. package/skills/pinecall-guides/references/guides/testing-agents.md +272 -0
  31. package/skills/pinecall-guides/references/guides/tools-and-functions.md +254 -0
  32. package/skills/pinecall-guides/references/guides/webrtc-browser.md +200 -0
  33. package/skills/pinecall-guides/references/guides/whatsapp.md +370 -0
  34. package/skills/pinecall-guides/references/guides/ws-streaming.md +235 -0
  35. package/skills/pinecall-quickstart/SKILL.md +54 -0
  36. package/skills/pinecall-quickstart/references/index.md +123 -0
  37. package/skills/pinecall-quickstart/references/quickstart.md +185 -0
  38. package/skills/pinecall-reference/SKILL.md +43 -0
  39. package/skills/pinecall-reference/references/reference/cli.md +578 -0
  40. package/skills/pinecall-reference/references/reference/events.md +366 -0
  41. package/skills/pinecall-reference/references/reference/llm-providers.md +263 -0
  42. package/skills/pinecall-reference/references/reference/rest-api.md +122 -0
  43. package/skills/pinecall-reference/references/reference/session-limits.md +119 -0
  44. package/skills/pinecall-reference/references/reference/stt-providers.md +174 -0
  45. package/skills/pinecall-reference/references/reference/tts-providers.md +149 -0
  46. package/skills/pinecall-sdk-api/SKILL.md +56 -0
  47. package/skills/pinecall-sdk-api/references/api/agent.md +328 -0
  48. package/skills/pinecall-sdk-api/references/api/call.md +324 -0
  49. package/skills/pinecall-sdk-api/references/api/pinecall.md +186 -0
  50. package/skills/pinecall-sdk-api/references/api/reply-stream.md +148 -0
  51. package/skills/pinecall-security/SKILL.md +37 -0
  52. package/skills/pinecall-security/references/security.md +138 -0
  53. package/skills/pinecall-web-chat/SKILL.md +38 -0
  54. package/skills/pinecall-web-chat/references/web/chat/chat-session.md +178 -0
  55. package/skills/pinecall-web-chat/references/web/chat/overview.md +98 -0
  56. package/skills/pinecall-web-components/SKILL.md +37 -0
  57. package/skills/pinecall-web-components/references/web/components/overview.md +128 -0
  58. package/skills/pinecall-web-voice/SKILL.md +40 -0
  59. package/skills/pinecall-web-voice/references/web/core/datachannel-protocol.md +149 -0
  60. package/skills/pinecall-web-voice/references/web/core/overview.md +70 -0
  61. package/skills/pinecall-web-voice/references/web/core/state-and-phases.md +153 -0
  62. package/skills/pinecall-web-voice/references/web/core/voice-session.md +279 -0
  63. package/skills/pinecall-web-widget/SKILL.md +41 -0
  64. package/skills/pinecall-web-widget/references/web/widget/overview.md +67 -0
  65. package/skills/pinecall-web-widget/references/web/widget/props.md +291 -0
  66. package/skills/pinecall-web-widget/references/web/widget/theming.md +131 -0
  67. package/skills/pinecall-web-widget/references/web/widget/tools-api.md +381 -0
  68. package/skills/pinecall-web-widget/references/web/widget/use-voice-session-hook.md +130 -0
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: "@pinecall/web/core"
3
+ description: "Framework-agnostic WebRTC voice session client. Zero dependencies."
4
+ ---
5
+
6
+ # @pinecall/web/core
7
+
8
+ The browser-side counterpart to `@pinecall/sdk`. A framework-agnostic WebRTC client that handles the audio transport, mic access, and DataChannel events. Works with React, Vue, Svelte, vanilla JS, or any framework.
9
+
10
+ ```bash
11
+ npm install @pinecall/web
12
+ ```
13
+
14
+ > Zero runtime dependencies. Browser-only (requires `RTCPeerConnection` and `getUserMedia`).
15
+
16
+ ## What it does
17
+
18
+ `@pinecall/web/core` is what the browser uses to talk to a Pinecall agent over WebRTC. It:
19
+
20
+ - Fetches a short-lived token from your backend (or the voice server, in convenience mode)
21
+ - Requests microphone access via `getUserMedia`
22
+ - Opens a peer connection to `voice.pinecall.io`
23
+ - Exposes the conversation as a reactive state object + an event stream
24
+
25
+ It does **not** render UI. For a drop-in React widget with an animated orb, use [`@pinecall/web`](/web/widget/overview) (which is built on top of `@pinecall/web/core`).
26
+
27
+ ## Quick start
28
+
29
+ ```typescript
30
+ import { VoiceSession } from "@pinecall/web/core";
31
+
32
+ const session = new VoiceSession({ agent: "mara" });
33
+
34
+ session.addEventListener("message", (e) => {
35
+ console.log(`${e.detail.message.role}: ${e.detail.message.text}`);
36
+ });
37
+
38
+ await session.connect();
39
+
40
+ // later
41
+ session.disconnect();
42
+ ```
43
+
44
+ That's it. The session manages the connection lifecycle, mic, and transcript. You get a stream of structured events you can wire into any UI.
45
+
46
+ ## How it fits with the other packages
47
+
48
+ ![Web package architecture](/assets/diagrams/web-package-arch.png)
49
+
50
+ - **You write**: agent logic in Node.js with `@pinecall/sdk`
51
+ - **You drop in**: `@pinecall/web/core` (or `@pinecall/web`) in the browser
52
+ - **The voice server**: handles the audio pipeline (STT, LLM, TTS, VAD) and forwards events both ways
53
+
54
+ ## Two interfaces: state + events
55
+
56
+ `@pinecall/web/core` exposes the session two ways. Use whichever fits your framework:
57
+
58
+ | Style | API | Best for |
59
+ |---|---|---|
60
+ | **Reactive state** | `session.subscribe(cb)` + `session.getState()` | React's `useSyncExternalStore`, Vue's `ref`, Svelte stores |
61
+ | **Event listeners** | `session.addEventListener("message" \| "phase" \| "event" \| ...)` | Vanilla JS, imperative code, observability |
62
+
63
+ You can mix them in the same app — they share the same underlying state machine.
64
+
65
+ ## What's next
66
+
67
+ - [`VoiceSession` class](/web/core/voice-session) — constructor, methods, options
68
+ - [State, phases, and transcripts](/web/core/state-and-phases) — the reactive state model
69
+ - [DataChannel protocol](/web/core/datachannel-protocol) — every event the server emits
70
+ - [`@pinecall/web`](/web/widget/overview) — the React widget built on `@pinecall/web/core`
@@ -0,0 +1,153 @@
1
+ ---
2
+ title: "State and Phases"
3
+ description: "The reactive state model: status, phases, transcript messages, and lifecycles."
4
+ ---
5
+
6
+ # State and Phases
7
+
8
+ The `VoiceSession` exposes a single reactive state object via `getState()`. This page covers what's in it and how it changes over the lifetime of a call.
9
+
10
+ ## State shape
11
+
12
+ ```typescript
13
+ interface VoiceSessionState {
14
+ status: "idle" | "connecting" | "connected" | "error";
15
+ error: string | null;
16
+ isMuted: boolean;
17
+ phase: "idle" | "listening" | "speaking" | "pause" | "thinking";
18
+ userSpeaking: boolean;
19
+ agentSpeaking: boolean;
20
+ duration: number; // seconds since connected
21
+ messages: TranscriptMessage[];
22
+ toolCalls: ToolUI[]; // active tool UI entries (only for tracked tools)
23
+ idleWarning: number | null; // seconds until idle timeout (null = no warning)
24
+ }
25
+ ```
26
+
27
+ | Field | Meaning |
28
+ |---|---|
29
+ | `status` | The connection lifecycle. `idle` → `connecting` → `connected` → (`idle` on disconnect, or `error`). |
30
+ | `error` | Populated when `status === "error"`. Always check this when handling errors. |
31
+ | `isMuted` | Mic state. Mirrors `setMuted()` / `toggleMute()`. |
32
+ | `phase` | What the conversation is doing right now (see below). |
33
+ | `userSpeaking` | `true` between `speech.started` and `speech.ended` events. Use for live waveform UIs. |
34
+ | `agentSpeaking` | `true` while TTS is playing. |
35
+ | `duration` | Seconds since `status` became `connected`. Updates every second. |
36
+ | `messages` | Full transcript — user and bot turns. See [Transcript messages](#transcript-messages) below. |
37
+ | `idleWarning` | When the server emits `session.idleWarning`, this holds the seconds remaining until timeout. `null` when no warning is active. |
38
+
39
+ ## Call phases
40
+
41
+ `phase` tells you what the conversation is doing **right now**. It's the field you'll bind to UI state most often (orb color, animation, status label).
42
+
43
+ | Phase | Meaning | Triggered by |
44
+ |---|---|---|
45
+ | `idle` | Not in a call | Initial state, after disconnect |
46
+ | `listening` | Mic is hot, waiting for speech | Connection established; after bot finishes; after `turn.resumed` |
47
+ | `speaking` | Agent is speaking (TTS playing) | First `bot.word` event |
48
+ | `thinking` | Processing user input, waiting for LLM | `user.message` (STT final), `turn.end` |
49
+ | `pause` | Turn detection pause — user may still be talking | `turn.pause` (brief silence detected) |
50
+
51
+ Typical flow during one exchange:
52
+
53
+ ![Call conversation phases](/assets/diagrams/call-phase-flow.png)
54
+
55
+ ## Transcript messages
56
+
57
+ The `messages` array contains the full conversation history. Each message is structured:
58
+
59
+ ```typescript
60
+ interface TranscriptMessage {
61
+ id: number;
62
+ role: "user" | "bot" | "system";
63
+ text: string;
64
+ isInterim?: boolean; // user only: STT is still processing
65
+ speaking?: boolean; // bot only: TTS is playing this message
66
+ interrupted?: boolean; // bot only: user barged in
67
+ messageId?: string; // bot only: server-assigned ID
68
+ toolCallId?: string; // system only: tool call ID (for updating with result)
69
+ }
70
+ ```
71
+
72
+ Messages mutate in place as STT refines, words stream in, and the bot finishes speaking — they don't get replaced. That means if you bind to `messages` reactively, the right entry will update.
73
+
74
+ ### User message lifecycle
75
+
76
+ ```
77
+ user.speaking → { role: "user", text: "Hola", isInterim: true }
78
+ text updates as STT refines...
79
+ user.speaking → { role: "user", text: "Hola que", isInterim: true }
80
+ user.message → { role: "user", text: "Hola, ¿qué tal?", isInterim: false }
81
+ ```
82
+
83
+ If you're rendering a transcript, render `isInterim: true` messages with reduced opacity or a "typing" indicator so the user sees that the STT is still processing.
84
+
85
+ ### Bot message lifecycle (word-by-word)
86
+
87
+ ```
88
+ bot.speaking → { role: "bot", text: "", speaking: true, messageId: "abc" }
89
+ bot.word → text: "Hello"
90
+ bot.word → text: "Hello there"
91
+ bot.word → text: "Hello there how"
92
+ bot.word → text: "Hello there how are"
93
+ bot.word → text: "Hello there how are you"
94
+ bot.finished → { speaking: false, text: "Hello there, how are you?" }
95
+ ```
96
+
97
+ `bot.speaking` arrives with the full intended `text`, but the widget intentionally starts with `text: ""` and builds word-by-word so the on-screen captions stay in sync with the audio.
98
+
99
+ `bot.finished` may include a polished final text (with proper punctuation that the per-word stream doesn't have).
100
+
101
+ ### Interrupted bot
102
+
103
+ When the user barges in mid-utterance:
104
+
105
+ ```
106
+ bot.word → text: "Hello there how"
107
+ bot.interrupted → { speaking: false, interrupted: true }
108
+ ```
109
+
110
+ Render interrupted messages with a visual marker (e.g. `⚡` icon, ellipsis, gray border) so users see the bot was cut off rather than just suddenly stopping.
111
+
112
+ ## Subscribing to changes
113
+
114
+ The state object is **stable by identity** — `getState()` returns the same reference until something changes. This is what makes it safe for React's `useSyncExternalStore`:
115
+
116
+ ```typescript
117
+ session.subscribe(() => {
118
+ const next = session.getState(); // new reference only if state changed
119
+ // ...
120
+ });
121
+ ```
122
+
123
+ For more targeted updates, subscribe to specific events:
124
+
125
+ ```typescript
126
+ session.addEventListener("phase", (e) => {
127
+ // only fires when phase actually changes
128
+ document.body.dataset.phase = e.detail.phase;
129
+ });
130
+ ```
131
+
132
+ ## Driving UI from `phase` and `agentSpeaking`
133
+
134
+ A common pattern: bind your "orb" or status visual to `phase` for the overall mode, and use `agentSpeaking` for a faster-reacting animation layer.
135
+
136
+ ```typescript
137
+ const orb = document.getElementById("orb");
138
+
139
+ session.subscribe(() => {
140
+ const { phase, agentSpeaking, idleWarning } = session.getState();
141
+
142
+ orb.dataset.phase = phase; // CSS handles per-phase styling
143
+ orb.classList.toggle("speaking", agentSpeaking);
144
+ orb.classList.toggle("idle-warning", idleWarning !== null);
145
+ });
146
+ ```
147
+
148
+ The `@pinecall/web` package follows exactly this pattern — see [its theming guide](/web/widget/theming) for the full set of CSS classes.
149
+
150
+ ## What's next
151
+
152
+ - [DataChannel protocol](/web/core/datachannel-protocol) — the raw events that drive state changes
153
+ - [`VoiceSession` class](/web/core/voice-session) — methods and constructor
@@ -0,0 +1,279 @@
1
+ ---
2
+ title: "VoiceSession"
3
+ description: "The core class: constructor, methods, and framework integration patterns."
4
+ ---
5
+
6
+ # VoiceSession
7
+
8
+ The main class exported by `@pinecall/web/core`. Manages the WebRTC peer connection, mic stream, DataChannel events, and a reactive state object.
9
+
10
+ ## Constructor
11
+
12
+ ```typescript
13
+ new VoiceSession(options)
14
+ ```
15
+
16
+ | Option | Type | Required | Description |
17
+ |---|---|---|---|
18
+ | `agent` | `string` | ✅ | Agent ID to connect to |
19
+ | `server` | `string` | — | API base URL (default: `https://voice.pinecall.io`) |
20
+ | `config` | `Record<string, unknown>` | — | Session config overrides (voice, STT, language, greeting) |
21
+ | `metadata` | `Record<string, unknown>` | — | Metadata passed to the agent (visible as `call.metadata` server-side) |
22
+
23
+ The constructor does **not** open a connection. Call `connect()` when you want the call to start.
24
+
25
+ ```typescript
26
+ const session = new VoiceSession({
27
+ agent: "mara",
28
+ config: {
29
+ voice: "elevenlabs/sarah",
30
+ stt: { provider: "deepgram", model: "nova-3", language: "es" },
31
+ language: "es",
32
+ greeting: "¡Hola! ¿En qué puedo ayudarte?",
33
+ },
34
+ });
35
+ ```
36
+
37
+ The `config` object uses Pinecall's shortcut syntax — same format the server SDK accepts. See [STT Providers](/reference/stt-providers) and [TTS Providers](/reference/tts-providers).
38
+
39
+ ## Methods
40
+
41
+ ### `connect()`
42
+
43
+ Opens the WebRTC connection. Returns a `Promise<void>` that resolves when the connection is established.
44
+
45
+ ```typescript
46
+ await session.connect();
47
+ ```
48
+
49
+ Internally it:
50
+
51
+ 1. Fetches a short-lived token from `GET /webrtc/token?agent_id=<agent>`
52
+ 2. Fetches ICE servers from `GET /webrtc/ice-servers` (falls back to Google STUN)
53
+ 3. Requests microphone access via `getUserMedia`
54
+ 4. Creates `RTCPeerConnection`, adds the mic track, opens a DataChannel
55
+ 5. Generates an SDP offer, gathers ICE candidates
56
+ 6. Sends the offer to `POST /webrtc/offer` with the token
57
+ 7. Applies the remote SDP answer → connection established
58
+
59
+ State transitions: `idle` → `connecting` → `connected` (or `error`).
60
+
61
+ ### `disconnect()`
62
+
63
+ Closes the connection, stops the mic, clears timers. State returns to `idle`. The `messages` array is preserved.
64
+
65
+ ```typescript
66
+ session.disconnect();
67
+ ```
68
+
69
+ ### `toggleMute()` / `setMuted(muted)`
70
+
71
+ Mute or unmute the mic. Both disable the local audio track **and** send `{ action: "mute" | "unmute" }` over the DataChannel so the server stops processing audio too.
72
+
73
+ ```typescript
74
+ session.toggleMute();
75
+ session.setMuted(true);
76
+ ```
77
+
78
+ ### `getState()`
79
+
80
+ Returns the current state snapshot. The returned object is **stable by identity** — it only changes when state mutates, which makes it safe for React's `useSyncExternalStore`.
81
+
82
+ ```typescript
83
+ const { status, phase, messages, isMuted, duration } = session.getState();
84
+ ```
85
+
86
+ See [State and Phases](/web/core/state-and-phases) for the full shape.
87
+
88
+ ### `subscribe(listener)`
89
+
90
+ Subscribes to all state changes. Returns an unsubscribe function. Designed to plug directly into reactive frameworks.
91
+
92
+ ```typescript
93
+ const unsubscribe = session.subscribe(() => {
94
+ console.log(session.getState());
95
+ });
96
+
97
+ // later
98
+ unsubscribe();
99
+ ```
100
+
101
+ ### `destroy()`
102
+
103
+ Disconnects, clears all subscribers, and marks the instance unusable. Call this on component unmount.
104
+
105
+ ```typescript
106
+ session.destroy();
107
+ ```
108
+
109
+ ### `configure(config)`
110
+
111
+ Sends a mid-call configuration update over the DataChannel. The server hot-swaps providers without disconnecting. Use this for live language/voice/STT switching during an active call.
112
+
113
+ ```typescript
114
+ session.configure({
115
+ voice: "elevenlabs/george",
116
+ stt: { provider: "deepgram", model: "nova-3", language: "es" },
117
+ language: "es",
118
+ });
119
+ ```
120
+
121
+ > Only works on a connected session. For pre-connect config updates use `updateOptions()`.
122
+
123
+ ### `updateOptions(patch)`
124
+
125
+ Updates options **before** the next `connect()` call. No effect on an already-connected session.
126
+
127
+ ```typescript
128
+ session.updateOptions({
129
+ config: {
130
+ voice: "elevenlabs/valentina",
131
+ language: "es",
132
+ greeting: "¡Hola!",
133
+ },
134
+ });
135
+
136
+ await session.connect(); // uses the new config
137
+ ```
138
+
139
+ ## Events (EventTarget)
140
+
141
+ `VoiceSession` extends `EventTarget`. Listen with `addEventListener`:
142
+
143
+ | Event | `detail` | When |
144
+ |---|---|---|
145
+ | `status` | `{ status }` | Connection status changed |
146
+ | `phase` | `{ phase }` | Call phase changed (listening, speaking, thinking, etc.) |
147
+ | `message` | `{ message }` | New transcript message added or existing one updated |
148
+ | `error` | `{ error }` | An error occurred |
149
+ | `change` | `{ state }` | Any state mutation (most general) |
150
+ | `event` | raw payload | Every raw DataChannel event from the server |
151
+
152
+ ```typescript
153
+ session.addEventListener("message", (e) => {
154
+ const msg = e.detail.message;
155
+ if (msg.role === "user" && !msg.isInterim) console.log("User:", msg.text);
156
+ });
157
+
158
+ session.addEventListener("event", (e) => {
159
+ // raw — see DataChannel protocol page for the full catalog
160
+ if (e.detail.event === "llm.toolCall") {
161
+ console.log("Tool calls:", e.detail.tool_calls);
162
+ }
163
+ });
164
+ ```
165
+
166
+ The `event` listener is the power-user escape hatch. Every JSON message from the server's DataChannel is forwarded as-is. Use it for things the state machine doesn't expose: tool calls, audio metrics, custom events.
167
+
168
+ ## Framework patterns
169
+
170
+ ### Vanilla JS
171
+
172
+ ```typescript
173
+ import { VoiceSession } from "@pinecall/web/core";
174
+
175
+ const session = new VoiceSession({ agent: "florencia" });
176
+ const btn = document.getElementById("call-btn");
177
+ const transcript = document.getElementById("transcript");
178
+
179
+ btn.onclick = async () => {
180
+ if (session.getState().status === "connected") {
181
+ session.disconnect();
182
+ btn.textContent = "Start Call";
183
+ } else {
184
+ await session.connect();
185
+ btn.textContent = "End Call";
186
+ }
187
+ };
188
+
189
+ session.addEventListener("message", (e) => {
190
+ const msg = e.detail.message;
191
+ const div = document.createElement("div");
192
+ div.className = msg.role;
193
+ div.textContent = `${msg.role}: ${msg.text}`;
194
+ transcript.appendChild(div);
195
+ });
196
+
197
+ session.addEventListener("phase", (e) => {
198
+ document.body.dataset.phase = e.detail.phase;
199
+ });
200
+ ```
201
+
202
+ ### React (`useSyncExternalStore`)
203
+
204
+ ```tsx
205
+ import { useSyncExternalStore, useCallback, useState, useEffect } from "react";
206
+ import { VoiceSession } from "@pinecall/web/core";
207
+
208
+ function useVoiceSession(agent: string) {
209
+ const [session] = useState(() => new VoiceSession({ agent }));
210
+
211
+ const state = useSyncExternalStore(
212
+ useCallback((cb) => session.subscribe(cb), [session]),
213
+ () => session.getState(),
214
+ );
215
+
216
+ useEffect(() => () => session.destroy(), [session]);
217
+
218
+ return { ...state, session };
219
+ }
220
+ ```
221
+
222
+ > If you're using React and want a ready-made widget instead of building UI, use [`@pinecall/web`](/web/widget/overview) — it wraps this pattern and ships an animated orb UI.
223
+
224
+ ### Vue 3
225
+
226
+ ```typescript
227
+ import { ref, onUnmounted } from "vue";
228
+ import { VoiceSession } from "@pinecall/web/core";
229
+
230
+ export function useVoiceSession(agent: string) {
231
+ const session = new VoiceSession({ agent });
232
+ const state = ref(session.getState());
233
+
234
+ session.subscribe(() => {
235
+ state.value = session.getState();
236
+ });
237
+
238
+ onUnmounted(() => session.destroy());
239
+
240
+ return { state, session };
241
+ }
242
+ ```
243
+
244
+ ### Svelte
245
+
246
+ ```typescript
247
+ import { readable } from "svelte/store";
248
+ import { VoiceSession } from "@pinecall/web/core";
249
+
250
+ export function createVoiceSession(agent: string) {
251
+ const session = new VoiceSession({ agent });
252
+
253
+ const state = readable(session.getState(), (set) => {
254
+ return session.subscribe(() => set(session.getState()));
255
+ });
256
+
257
+ return { state, session };
258
+ }
259
+ ```
260
+
261
+ ## TypeScript types
262
+
263
+ All types are exported from the package:
264
+
265
+ ```typescript
266
+ import type {
267
+ VoiceSessionOptions,
268
+ VoiceSessionState,
269
+ SessionStatus, // "idle" | "connecting" | "connected" | "error"
270
+ CallPhase, // "idle" | "listening" | "speaking" | "pause" | "thinking"
271
+ TranscriptMessage,
272
+ } from "@pinecall/web/core";
273
+ ```
274
+
275
+ ## What's next
276
+
277
+ - [State and phases](/web/core/state-and-phases) — the reactive state model in detail
278
+ - [DataChannel protocol](/web/core/datachannel-protocol) — every event the server emits
279
+ - [`@pinecall/web`](/web/widget/overview) — the React widget built on top
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: pinecall-web-widget
3
+ description: >-
4
+ @pinecall/web React widget — VoiceWidget props, theming, useVoiceSession hook, client tools. Use when the user is building, configuring, or debugging with @pinecall/sdk. Keywords: react widget, voicewidget, useVoiceSession, theming, props, client tools, @pinecall/web.
5
+ license: MIT
6
+ ---
7
+
8
+ # @pinecall/web (React Widget)
9
+
10
+ @pinecall/web React widget — VoiceWidget props, theming, useVoiceSession hook, client tools.
11
+
12
+ This skill bundles the official Pinecall documentation for **@pinecall/web (React Widget)**. The
13
+ table below indexes every page; open the `references/…` file for the full text
14
+ (loaded on demand). Source of truth: <https://docs.pinecall.io>.
15
+
16
+ | Page | What it covers | Open |
17
+ |------|----------------|------|
18
+ | **@pinecall/web** | Drop-in React voice widget with animated orb UI, live transcript, themes, and multi-language support. | [`references/web/widget/overview.md`](references/web/widget/overview.md) · [docs](https://docs.pinecall.io/web/widget/overview) |
19
+ | **Props** | Every prop the VoiceWidget accepts — including token security, tools, theming, and multi-language. | [`references/web/widget/props.md`](references/web/widget/props.md) · [docs](https://docs.pinecall.io/web/widget/props) |
20
+ | **Theming** | Theme presets, CSS variables, and full customization of the orb and transcript UI. | [`references/web/widget/theming.md`](references/web/widget/theming.md) · [docs](https://docs.pinecall.io/web/widget/theming) |
21
+ | **useVoiceSession hook** | Build a fully custom voice UI without giving up the widget's session management. | [`references/web/widget/use-voice-session-hook.md`](references/web/widget/use-voice-session-hook.md) · [docs](https://docs.pinecall.io/web/widget/use-voice-session-hook) |
22
+ | **Tools API** | Render interactive UI in response to LLM tool calls. Buttons, forms, pickers — all synced to the conversation. | [`references/web/widget/tools-api.md`](references/web/widget/tools-api.md) · [docs](https://docs.pinecall.io/web/widget/tools-api) |
23
+
24
+
25
+ ## House rules — always apply
26
+
27
+ - **Example defaults** (use these exact strings unless the user asks otherwise):
28
+ `stt: "deepgram/flux"`, `llm: "openai/gpt-5-chat-latest"`, `voice: "elevenlabs/sarah"`.
29
+ **NEVER use `deepgram/nova-2`** — it is not supported. Use `deepgram/nova-3`
30
+ only for languages Flux doesn't support (e.g. Arabic).
31
+ - **Turn detection & VAD are auto-derived from the STT provider — never set
32
+ `turnDetection` or `vad` manually.** Flux → native turns + native VAD;
33
+ every other STT → `smart_turn` + `silero`.
34
+ - **Greeting**: inbound → `greeting` field in `pc.agent()`; outbound → `greeting`
35
+ field in `agent.dial()`. It is sugar for `call.say()` in `call.started`.
36
+ - **Auth**: `new Pinecall()` reads `PINECALL_API_KEY` from env and auto-connects.
37
+ - Full documentation: <https://docs.pinecall.io>
38
+
39
+ ---
40
+ *Generated from `sdk/docs/` by `@pinecall/skills` — do not edit by hand; edit the
41
+ docs and re-run `node build.mjs`.*
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: "@pinecall/web"
3
+ description: "Drop-in React voice widget with animated orb UI, live transcript, themes, and multi-language support."
4
+ ---
5
+
6
+ # @pinecall/web
7
+
8
+ A React voice widget for Pinecall agents. Animated orb, live transcript, theme presets, multi-language selector, and an interactive tools API for rendering UI in response to LLM tool calls.
9
+
10
+ ```bash
11
+ npm install @pinecall/web react react-dom
12
+ ```
13
+
14
+ > Built on top of [`@pinecall/web/core`](/web/core/overview). React ≥18 is a peer dependency.
15
+
16
+ ## Quick start
17
+
18
+ ```tsx
19
+ import { VoiceWidget } from "@pinecall/web";
20
+
21
+ export default function App() {
22
+ return <VoiceWidget agent="mara" name="Mara" />;
23
+ }
24
+ ```
25
+
26
+ That's it. The widget renders a floating orb in the bottom-right corner. Click to start a voice call, click again to end it. The orb animates through phases (idle → connecting → listening → speaking → thinking) and shows a live transcript bubble above.
27
+
28
+ ## What you get out of the box
29
+
30
+ - **Animated orb** with breathing rings, pulse states, and per-phase colors
31
+ - **Live transcript** rendered as chat bubbles next to the orb
32
+ - **5 theme presets** (`dark`, `midnight`, `aurora`, `sunset`, `light`) — plus full CSS variable overrides
33
+ - **Multi-language pill selector** with hot-swap mid-call
34
+ - **Token security** via `tokenProvider` — API keys never leave your server
35
+ - **Idle warning state** when the user goes silent too long
36
+ - **Interactive Tools API** for rendering UI in response to LLM tool calls
37
+ - **`useVoiceSession()` hook** for building completely custom UIs
38
+
39
+ ## Standalone components
40
+
41
+ The package also exports standalone components for building custom multi-channel experiences:
42
+
43
+ | Export | Purpose |
44
+ |---|---|
45
+ | `ContactHub` | Multi-channel contact menu (voice, chat, WhatsApp, Call Me) |
46
+ | `ChatView` | Embedded LLM text chat with streaming markdown |
47
+ | `useVoiceSession()` | Headless hook — build your own UI from scratch |
48
+ | `useAgentInfo()` | Fetch agent channel info for auto-discovery |
49
+
50
+ These are **not** wired into `<VoiceWidget>` — you compose them yourself in your app's UI.
51
+
52
+ ## When to use what
53
+
54
+ | You want to... | Use |
55
+ |---|---|
56
+ | Drop a voice button on your site | `<VoiceWidget />` |
57
+ | Build a fully custom UI in React | `useVoiceSession()` hook |
58
+ | Build a fully custom UI in Vue/Svelte/vanilla | [`@pinecall/web/core`](/web/core/overview) directly |
59
+ | Render interactive UI from agent tool calls | `<VoiceWidget>` + `tools` prop or `useVoice()` + `trackedTools` |
60
+ | Add multi-channel contact menu | Import `ContactHub` and compose it in your layout |
61
+
62
+ ## What's next
63
+
64
+ - [Props reference](/web/widget/props) — every prop with type and default
65
+ - [Theming](/web/widget/theming) — presets, CSS variables, custom themes
66
+ - [`useVoiceSession` hook](/web/widget/use-voice-session-hook) — for custom UIs
67
+ - [Tools API](/web/widget/tools-api) — render interactive components from tool calls