@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,178 @@
1
+ ---
2
+ title: "ChatSession API"
3
+ description: "Full reference for ChatSession (vanilla) and usePinecallChat (React)."
4
+ ---
5
+
6
+ # `ChatSession` API
7
+
8
+ The `ChatSession` class is the framework-agnostic core. For React there's also the `usePinecallChat` hook which wraps the same class with `useSyncExternalStore`.
9
+
10
+ ## `ChatSession` (vanilla)
11
+
12
+ ### Constructor
13
+
14
+ ```typescript
15
+ import { ChatSession } from "@pinecall/web/chat";
16
+
17
+ const chat = new ChatSession({ agent: "florencia" });
18
+ ```
19
+
20
+ | Option | Type | Required | Description |
21
+ |---|---|---|---|
22
+ | `agent` | `string` | ✅ | Agent slug (e.g. `"florencia"`, `"dev-berna-florencia"`) |
23
+ | `server` | `string` | — | Voice server URL (default: `https://voice.pinecall.io`) |
24
+
25
+ ### Methods
26
+
27
+ | Method | Description |
28
+ |---|---|
29
+ | `connect()` | Connect — fetches token, opens WebSocket |
30
+ | `disconnect()` | Close the WebSocket connection |
31
+ | `destroy()` | Disconnect + clear all subscribers. Do not reuse. |
32
+ | `send(text)` | Send a text message to the agent |
33
+ | `setContext(key, value)` | Inject / update / clear keyed context in the LLM prompt |
34
+ | `getState()` | Read-only snapshot of current state |
35
+ | `subscribe(cb)` | Subscribe to state changes (returns unsubscribe) |
36
+
37
+ ### Events (`EventTarget`)
38
+
39
+ | Event | `detail` | When |
40
+ |---|---|---|
41
+ | `status` | `{ status }` | Connection status changed |
42
+ | `message` | `{ message }` | New or updated message |
43
+ | `error` | `{ error }` | Error occurred |
44
+ | `change` | `{ state }` | Any state mutation (most general) |
45
+ | `event` | raw payload | Every raw server event |
46
+
47
+ ### State shape
48
+
49
+ ```typescript
50
+ interface ChatSessionState {
51
+ status: "idle" | "connecting" | "connected" | "error" | "destroyed";
52
+ error: string | null;
53
+ messages: ChatMessage[];
54
+ typing: boolean; // true while bot is streaming a response
55
+ streamingText: string; // partial text of the current bot response
56
+ sessionId: string | null;
57
+ }
58
+
59
+ interface ChatMessage {
60
+ id: number;
61
+ role: "user" | "bot";
62
+ text: string;
63
+ messageId?: string; // server-assigned ID (bot messages)
64
+ isStreaming?: boolean; // true while bot is still streaming
65
+ }
66
+ ```
67
+
68
+ ### Reactive subscribe pattern
69
+
70
+ Works with any reactive system — MobX, signals, Vue refs, Svelte stores:
71
+
72
+ ```typescript
73
+ const unsubscribe = chat.subscribe(() => {
74
+ const state = chat.getState();
75
+ console.log("Messages:", state.messages.length);
76
+ console.log("Typing:", state.typing);
77
+ });
78
+
79
+ // clean up
80
+ unsubscribe();
81
+ ```
82
+
83
+ ### Injecting dynamic context
84
+
85
+ Same pattern as `@pinecall/web`'s `setContext()` — inject live UI state into the LLM's system prompt:
86
+
87
+ ```typescript
88
+ chat.setContext("cart", JSON.stringify({
89
+ items: ["Corte de cabello", "Tinte"],
90
+ total: 85.00,
91
+ }));
92
+
93
+ // clear a context key
94
+ chat.setContext("cart", null);
95
+ ```
96
+
97
+ The agent's system prompt picks this up automatically:
98
+
99
+ ```
100
+ ## UI Context
101
+ ### cart
102
+ {"items":["Corte de cabello","Tinte"],"total":85.00}
103
+ ```
104
+
105
+ ## `usePinecallChat` (React)
106
+
107
+ React-only hook exported from `@pinecall/web/chat/react`. Wraps `ChatSession` with `useSyncExternalStore` for efficient rendering. Session is created once on mount and destroyed on unmount.
108
+
109
+ ### Quick usage
110
+
111
+ ```tsx
112
+ import { usePinecallChat } from "@pinecall/web/chat/react";
113
+
114
+ function Chat() {
115
+ const { messages, send, connected, typing, streamingText } = usePinecallChat({
116
+ agent: "florencia",
117
+ });
118
+
119
+ if (!connected) return <p>Connecting...</p>;
120
+
121
+ return (
122
+ <div>
123
+ {messages.map((m) => (
124
+ <p key={m.id}>
125
+ <strong>{m.role}:</strong> {m.text}
126
+ {m.isStreaming && "▊"}
127
+ </p>
128
+ ))}
129
+ {typing && <p>Bot is typing: {streamingText}▊</p>}
130
+ <input
131
+ placeholder="Type a message..."
132
+ onKeyDown={(e) => {
133
+ if (e.key === "Enter") {
134
+ send(e.currentTarget.value);
135
+ e.currentTarget.value = "";
136
+ }
137
+ }}
138
+ />
139
+ </div>
140
+ );
141
+ }
142
+ ```
143
+
144
+ ### Hook options
145
+
146
+ | Option | Type | Default | Description |
147
+ |---|---|---|---|
148
+ | `agent` | `string` | **required** | Agent ID |
149
+ | `server` | `string` | `"https://voice.pinecall.io"` | Voice server URL |
150
+ | `autoConnect` | `boolean` | `true` | Connect on mount automatically |
151
+
152
+ ### Hook return
153
+
154
+ | Field | Type | Description |
155
+ |---|---|---|
156
+ | `messages` | `ChatMessage[]` | All messages in the conversation |
157
+ | `send` | `(text: string) => void` | Send a text message |
158
+ | `connected` | `boolean` | `true` when connected to the server |
159
+ | `typing` | `boolean` | `true` while the bot is streaming |
160
+ | `streamingText` | `string` | Partial text of the current bot response |
161
+ | `error` | `string \| null` | Current error, if any |
162
+ | `setContext` | `(key, value) => void` | Inject dynamic context into the LLM prompt |
163
+ | `connect` | `() => void` | Manually connect (if `autoConnect: false`) |
164
+ | `disconnect` | `() => void` | Manually disconnect |
165
+
166
+ ## Protocol
167
+
168
+ What happens under the hood:
169
+
170
+ ![Chat WebSocket protocol sequence](/assets/diagrams/chat-protocol-sequence.png)
171
+
172
+ ## Related packages
173
+
174
+ | Package | Description |
175
+ |---|---|
176
+ | [`@pinecall/sdk`](/api/pinecall) | Server-side SDK — agent, call, tools, channels |
177
+ | [`@pinecall/web/core`](/web/core/overview) | WebRTC voice session (framework-agnostic) |
178
+ | [`@pinecall/web`](/web/widget/overview) | React voice widget with animated orb |
@@ -0,0 +1,98 @@
1
+ ---
2
+ title: "@pinecall/web/chat"
3
+ description: "Text chat client for Pinecall voice agents. Framework-agnostic core + React hook."
4
+ ---
5
+
6
+ # @pinecall/web/chat
7
+
8
+ Text chat client for Pinecall agents. The chat counterpart to `@pinecall/web/core` — same agents, same prompts, same tools, but text-only over WebSocket instead of audio over WebRTC.
9
+
10
+ ```bash
11
+ npm install @pinecall/web
12
+ ```
13
+
14
+ > **Browser-only.** Uses native `WebSocket` and `EventTarget` APIs. Works in any modern browser, bundler, or SSR-hydrated app.
15
+
16
+ ## What it does
17
+
18
+ `@pinecall/web/chat` lets your browser talk to a Pinecall agent in plain text. It:
19
+
20
+ - Fetches a short-lived token from the voice server
21
+ - Opens a WebSocket to `/chat/ws`
22
+ - Sends user messages, receives streamed bot responses (token-by-token)
23
+ - Exposes the conversation as reactive state + events
24
+ - Supports the same `setContext` mechanism as `@pinecall/web` for syncing UI state
25
+
26
+ It does **not** include UI. For React you get a hook (`usePinecallChat`). For Vue, Svelte, or vanilla JS you use the `ChatSession` class directly.
27
+
28
+ ## When to use it
29
+
30
+ | | Use |
31
+ |---|---|
32
+ | You want voice with UI rendering | [`@pinecall/web`](/web/widget/overview) |
33
+ | You want voice with no UI assumptions | [`@pinecall/web/core`](/web/core/overview) |
34
+ | **You want text chat** | **`@pinecall/web/chat`** |
35
+ | You want to embed both voice + chat | Use both packages on the same agent |
36
+
37
+ The same agent (`pc.agent("florencia", ...)`) can have a `webrtc` channel **and** a `chat` channel — `@pinecall/web/chat` connects to the chat channel. Same prompt, same tools, same conversation logic.
38
+
39
+ ## Quick start (vanilla)
40
+
41
+ ```typescript
42
+ import { ChatSession } from "@pinecall/web/chat";
43
+
44
+ const chat = new ChatSession({ agent: "florencia" });
45
+
46
+ chat.addEventListener("message", (e) => {
47
+ const m = e.detail.message;
48
+ console.log(`${m.role}: ${m.text}`);
49
+ });
50
+
51
+ await chat.connect();
52
+ chat.send("Hola, quiero reservar un turno");
53
+ ```
54
+
55
+ ## Quick start (React)
56
+
57
+ ```tsx
58
+ import { usePinecallChat } from "@pinecall/web/chat/react";
59
+
60
+ function Chat() {
61
+ const { messages, send, connected, typing } = usePinecallChat({ agent: "florencia" });
62
+
63
+ if (!connected) return <p>Connecting...</p>;
64
+
65
+ return (
66
+ <div>
67
+ {messages.map((m) => (
68
+ <p key={m.id}>
69
+ <strong>{m.role}:</strong> {m.text}
70
+ {m.isStreaming && "▊"}
71
+ </p>
72
+ ))}
73
+ {typing && <p>Bot is typing…</p>}
74
+ <input
75
+ placeholder="Type a message..."
76
+ onKeyDown={(e) => {
77
+ if (e.key === "Enter") {
78
+ send(e.currentTarget.value);
79
+ e.currentTarget.value = "";
80
+ }
81
+ }}
82
+ />
83
+ </div>
84
+ );
85
+ }
86
+ ```
87
+
88
+ React is an **optional** peer dependency — the React subpath (`@pinecall/web/chat/react`) is only loaded if you import it.
89
+
90
+ ## How it fits with the rest
91
+
92
+ ![Chat architecture](/assets/diagrams/chat-architecture.png)
93
+
94
+ The agent's chat channel routes through the same LLM pipeline as voice — including tool calling. Anything you've built for the voice agent (tools, prompt, hot-reload, multi-tenant) works on the chat channel without changes.
95
+
96
+ ## What's next
97
+
98
+ - [`ChatSession` API](/web/chat/chat-session) — full reference for both vanilla and React
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: pinecall-web-components
3
+ description: >-
4
+ @pinecall/web web components — framework-agnostic custom elements. Use when the user is building, configuring, or debugging with @pinecall/sdk. Keywords: web components, custom element, framework-agnostic widget.
5
+ license: MIT
6
+ ---
7
+
8
+ # @pinecall/web (Web Components)
9
+
10
+ @pinecall/web web components — framework-agnostic custom elements.
11
+
12
+ This skill bundles the official Pinecall documentation for **@pinecall/web (Web Components)**. 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
+ | **Web Components** | Framework-agnostic <pinecall-orb>, <pinecall-modal> and <pinecall-chat> Custom Elements — voice and text in any framework, no React required. | [`references/web/components/overview.md`](references/web/components/overview.md) · [docs](https://docs.pinecall.io/web/components/overview) |
19
+
20
+
21
+ ## House rules — always apply
22
+
23
+ - **Example defaults** (use these exact strings unless the user asks otherwise):
24
+ `stt: "deepgram/flux"`, `llm: "openai/gpt-5-chat-latest"`, `voice: "elevenlabs/sarah"`.
25
+ **NEVER use `deepgram/nova-2`** — it is not supported. Use `deepgram/nova-3`
26
+ only for languages Flux doesn't support (e.g. Arabic).
27
+ - **Turn detection & VAD are auto-derived from the STT provider — never set
28
+ `turnDetection` or `vad` manually.** Flux → native turns + native VAD;
29
+ every other STT → `smart_turn` + `silero`.
30
+ - **Greeting**: inbound → `greeting` field in `pc.agent()`; outbound → `greeting`
31
+ field in `agent.dial()`. It is sugar for `call.say()` in `call.started`.
32
+ - **Auth**: `new Pinecall()` reads `PINECALL_API_KEY` from env and auto-connects.
33
+ - Full documentation: <https://docs.pinecall.io>
34
+
35
+ ---
36
+ *Generated from `sdk/docs/` by `@pinecall/skills` — do not edit by hand; edit the
37
+ docs and re-run `node build.mjs`.*
@@ -0,0 +1,128 @@
1
+ ---
2
+ title: "Web Components"
3
+ description: "Framework-agnostic <pinecall-orb>, <pinecall-modal> and <pinecall-chat> Custom Elements — voice and text in any framework, no React required."
4
+ ---
5
+
6
+ # Web Components
7
+
8
+ `@pinecall/web` ships native **Custom Elements** so you can drop a Pinecall voice agent into **any** framework — React, Vue, Svelte, Angular, Astro, or plain HTML — with no React dependency. They wrap the same vanilla [`VoiceSession`](/web/core/overview) as the React widget, render in Shadow DOM, and are SSR-safe.
9
+
10
+ | Import | Element | Needs React |
11
+ |---|---|---|
12
+ | `@pinecall/web/orb` | `<pinecall-orb>` — a click-to-talk voice orb | ❌ |
13
+ | `@pinecall/web/modal` | `<pinecall-modal>` — a glass call modal (orb **or** wave visual, live captions, transcript, text-during-call) | ❌ |
14
+ | `@pinecall/web/chatbox` | `<pinecall-chat>` — a docked chatbox (text chat that can escalate to a voice call) | ❌ |
15
+ | `@pinecall/web/orb/react` | `<Orb>` — thin React wrapper | ✅ |
16
+ | `@pinecall/web/modal/react` | `<CallModal>` — thin React wrapper | ✅ |
17
+ | `@pinecall/web/chatbox/react` | `<ChatBox>` — thin React wrapper | ✅ |
18
+
19
+ ```bash
20
+ npm install @pinecall/web
21
+ ```
22
+
23
+ ## Quick start (any framework)
24
+
25
+ ```html
26
+ <pinecall-orb agent="mara" name="Mara" preset="midnight"></pinecall-orb>
27
+ <pinecall-modal agent="mara" name="Mara" visual="wave"></pinecall-modal>
28
+
29
+ <script type="module">
30
+ import "@pinecall/web/orb"; // registers <pinecall-orb>
31
+ import "@pinecall/web/modal"; // registers <pinecall-modal>
32
+
33
+ // Functions/objects can't be HTML attributes — set them as PROPERTIES:
34
+ const modal = document.querySelector("pinecall-modal");
35
+ modal.tokenProvider = async () => (await fetch("/api/token")).json();
36
+ modal.addEventListener("pinecall:status", (e) => console.log(e.detail));
37
+ </script>
38
+ ```
39
+
40
+ > Importing the module auto-registers the element (guarded and idempotent; a no-op during SSR). You can also call `definePinecallOrb()` / `definePinecallModal()` explicitly.
41
+
42
+ ## Props, properties & events
43
+
44
+ **Attributes** (primitives): `agent`, `server`, `name`, `label`, `preset` (`dark` · `midnight` · `aurora` · `sunset` · `light`), `avatar`, and on the modal `visual` (`orb` | `wave`).
45
+
46
+ **Properties** (functions/objects — set in JS, not as attributes): `config`, `metadata`, `tokenProvider`, `theme`.
47
+
48
+ **Events**: `pinecall:status` (detail = status), `pinecall:transcript` (detail = messages), `pinecall:error` (detail = message), and on the modal/chatbox `pinecall:open` / `pinecall:close`.
49
+
50
+ **Imperative API**: `connect()`, `disconnect()`, `toggleMute()`, `setMuted()`, `configure()`, `sendText()`, `getState()`; the modal also has `open()` / `close()`.
51
+
52
+ ## `<pinecall-modal>`
53
+
54
+ A call modal with a launcher button (FAB). Open it and it starts a WebRTC call.
55
+
56
+ - **`visual="orb"`** — animated orb + status + a single live caption.
57
+ - **`visual="wave"`** — a waveform driven by **real `audio.metrics`**, a quoted live caption, an activity sub-status (e.g. `transcribing · Deepgram`), and a Ring → Listen → Think → Speak stepper.
58
+ - The **keyboard button** flips to a transcript view: chat bubbles of the whole conversation plus a text input — type during the call and the agent answers in voice and text (via `sendText`).
59
+ - Controls: mute (pauses the local mic), hang up, keyboard.
60
+
61
+ ## `<pinecall-orb>` — `opens`
62
+
63
+ The orb is a launcher. Its `opens` attribute controls what a click does:
64
+
65
+ - `opens="inline"` (default) — connect right there, with live captions beside the orb.
66
+ - `opens="modal"` — open a `<pinecall-modal>`.
67
+ - `opens="chat"` — open a `<pinecall-chat>` chatbox.
68
+
69
+ The launched element's own FAB is suppressed (`no-fab`) so the orb stays the single launcher.
70
+
71
+ ## `<pinecall-chat>` — docked chatbox
72
+
73
+ A traditional web-chat: a launcher bubble opens a panel with message bubbles and a text input.
74
+
75
+ - **Text-first** (backed by `ChatSession`) with a **call button** that escalates to a WebRTC voice call (`VoiceSession`) — talk and/or type in the same panel. The conversation **continues** across the switch (the prior transcript is carried into the new session).
76
+ - `greeting` attribute — a first bot bubble shown on open (client-side; text chat has no server-pushed greeting). `auto-call` — start directly in a voice call. `no-call` — hide the call button (pure text chat).
77
+ - Its **`tokenProvider` is channel-aware**: `(channel: "chat" | "webrtc") => { token, server }`, so one backend function mints the right token for each transport.
78
+
79
+ ```html
80
+ <pinecall-chat agent="florencia" name="Florencia" greeting="Hi! How can I help?"></pinecall-chat>
81
+ <script type="module">
82
+ import "@pinecall/web/chatbox";
83
+ document.querySelector("pinecall-chat").tokenProvider =
84
+ async (channel) => (await fetch(`/api/token?channel=${channel}`)).json();
85
+ </script>
86
+ ```
87
+
88
+ > **Chat greeting:** unlike voice, a text chat is not greeted by the server (`call.say` doesn't reach chat WebSocket clients). Use the `greeting` attribute for an instant client-side first message.
89
+
90
+ ## Theming
91
+
92
+ Theming uses the same `--vw-*` custom properties as the widget (they inherit through the Shadow boundary), plus modal-specific ones. Use the `preset` attribute, the `theme` property, or plain CSS on the element:
93
+
94
+ ```css
95
+ pinecall-modal {
96
+ --vw-color-accent: 205, 88, 178; /* magenta — also tints the card */
97
+ --pm-user: #34d399; /* your transcript color */
98
+ --pm-bot: #ffd166; /* agent transcript color */
99
+ }
100
+ ```
101
+
102
+ The card gradient and speaker colors derive from the theme accent by default, so each preset themes the whole modal.
103
+
104
+ ## React wrappers
105
+
106
+ In React, prefer the wrappers — they bind object/function props cleanly (no refs) and map events to callbacks:
107
+
108
+ ```tsx
109
+ import { Orb } from "@pinecall/web/orb/react";
110
+ import { CallModal } from "@pinecall/web/modal/react";
111
+
112
+ <Orb agent="mara" name="Mara"
113
+ tokenProvider={async () => (await fetch("/api/token")).json()}
114
+ onStatus={(s) => console.log(s)} />
115
+
116
+ <CallModal agent="mara" name="Mara" visual="wave"
117
+ tokenProvider={async () => (await fetch("/api/token")).json()} />
118
+ ```
119
+
120
+ ## Tokens
121
+
122
+ Same model as everything else — mint a short-lived token from your backend and hand it back via `tokenProvider`. See [WebRTC in the Browser](/guides/webrtc-browser) and [Security](/security).
123
+
124
+ ## Related
125
+
126
+ - [`@pinecall/web` (React widget)](/web/widget/overview)
127
+ - [`@pinecall/web/core` — VoiceSession](/web/core/overview)
128
+ - [`@pinecall/web/chat` — text chat](/web/chat/overview)
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: pinecall-web-voice
3
+ description: >-
4
+ @pinecall/web/core — browser WebRTC voice (VoiceSession, state & phases, DataChannel protocol). Use when the user is building, configuring, or debugging with @pinecall/sdk. Keywords: webrtc, voice session, browser voice, datachannel, @pinecall/web/core.
5
+ license: MIT
6
+ ---
7
+
8
+ # @pinecall/web/core (Voice)
9
+
10
+ @pinecall/web/core — browser WebRTC voice (VoiceSession, state & phases, DataChannel protocol).
11
+
12
+ This skill bundles the official Pinecall documentation for **@pinecall/web/core (Voice)**. 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/core** | Framework-agnostic WebRTC voice session client. Zero dependencies. | [`references/web/core/overview.md`](references/web/core/overview.md) · [docs](https://docs.pinecall.io/web/core/overview) |
19
+ | **VoiceSession** | The core class: constructor, methods, and framework integration patterns. | [`references/web/core/voice-session.md`](references/web/core/voice-session.md) · [docs](https://docs.pinecall.io/web/core/voice-session) |
20
+ | **State and Phases** | The reactive state model: status, phases, transcript messages, and lifecycles. | [`references/web/core/state-and-phases.md`](references/web/core/state-and-phases.md) · [docs](https://docs.pinecall.io/web/core/state-and-phases) |
21
+ | **DataChannel Protocol** | The raw WebRTC DataChannel protocol — every server event and every client command. | [`references/web/core/datachannel-protocol.md`](references/web/core/datachannel-protocol.md) · [docs](https://docs.pinecall.io/web/core/datachannel-protocol) |
22
+
23
+
24
+ ## House rules — always apply
25
+
26
+ - **Example defaults** (use these exact strings unless the user asks otherwise):
27
+ `stt: "deepgram/flux"`, `llm: "openai/gpt-5-chat-latest"`, `voice: "elevenlabs/sarah"`.
28
+ **NEVER use `deepgram/nova-2`** — it is not supported. Use `deepgram/nova-3`
29
+ only for languages Flux doesn't support (e.g. Arabic).
30
+ - **Turn detection & VAD are auto-derived from the STT provider — never set
31
+ `turnDetection` or `vad` manually.** Flux → native turns + native VAD;
32
+ every other STT → `smart_turn` + `silero`.
33
+ - **Greeting**: inbound → `greeting` field in `pc.agent()`; outbound → `greeting`
34
+ field in `agent.dial()`. It is sugar for `call.say()` in `call.started`.
35
+ - **Auth**: `new Pinecall()` reads `PINECALL_API_KEY` from env and auto-connects.
36
+ - Full documentation: <https://docs.pinecall.io>
37
+
38
+ ---
39
+ *Generated from `sdk/docs/` by `@pinecall/skills` — do not edit by hand; edit the
40
+ docs and re-run `node build.mjs`.*
@@ -0,0 +1,149 @@
1
+ ---
2
+ title: "DataChannel Protocol"
3
+ description: "The raw WebRTC DataChannel protocol — every server event and every client command."
4
+ ---
5
+
6
+ # DataChannel Protocol
7
+
8
+ `VoiceSession` opens an ordered DataChannel named `"events"` over WebRTC. The protocol is plain JSON in both directions. This page documents every message the server sends and every command the client can send.
9
+
10
+ > Most apps don't need to use the protocol directly — the state machine handles it. Use this when you need to read events the state doesn't expose (tool calls, audio metrics) or when you're sending custom client commands.
11
+
12
+ ## Accessing raw events
13
+
14
+ Subscribe to the `"event"` listener — every server message is forwarded as-is:
15
+
16
+ ```typescript
17
+ session.addEventListener("event", (e) => {
18
+ const raw = e.detail; // the parsed JSON from the server
19
+ console.log(raw.event, raw);
20
+ });
21
+ ```
22
+
23
+ ## Server → Client events
24
+
25
+ ### Speech detection (STT)
26
+
27
+ | Event | Fields | Description |
28
+ |---|---|---|
29
+ | `speech.started` | — | User started physically speaking (VAD detected voice) |
30
+ | `speech.ended` | — | User stopped speaking (VAD silence) |
31
+ | `user.speaking` | `text` | STT partial/interim result — text may change |
32
+ | `user.message` | `text` | STT final result — text is locked, turn is over |
33
+
34
+ ### Turn detection
35
+
36
+ | Event | Fields | Description |
37
+ |---|---|---|
38
+ | `turn.pause` | — | Brief silence detected — user might still be talking |
39
+ | `turn.end` | — | Silence confirmed — user's turn is over, LLM starts |
40
+ | `turn.resumed` | — | User started speaking again during the pause |
41
+
42
+ ### Bot speech (TTS)
43
+
44
+ | Event | Fields | Description |
45
+ |---|---|---|
46
+ | `bot.speaking` | `message_id`, `text` | TTS generation started. `text` has the full intended response. The widget intentionally starts empty and builds word-by-word. |
47
+ | `bot.word` | `message_id`, `word`, `word_index` | A single word was spoken by TTS. Arrives in real-time as audio plays. |
48
+ | `bot.finished` | `message_id`, `text` | TTS completed normally. `text` is the polished final response. |
49
+ | `bot.interrupted` | `message_id` | User barged in — TTS was cut short. |
50
+
51
+ ### Audio metrics
52
+
53
+ When enabled via session config (`analysis.send_audio_metrics`):
54
+
55
+ | Event | Fields | Description |
56
+ |---|---|---|
57
+ | `audio.metrics` | `source`, `energy_db`, `rms`, `peak`, `is_speech`, `vad_prob` | Server-side audio analysis. `source` is `"user"` or `"bot"`. Sent every ~100ms. |
58
+
59
+ Use it to build live waveform meters, energy bars, or VAD visualizations.
60
+
61
+ ### LLM / tool events
62
+
63
+ These events are **not** processed by the state machine but are forwarded through the `"event"` listener. They come from the Pinecall server's LLM handler:
64
+
65
+ | Event | Fields | Description |
66
+ |---|---|---|
67
+ | `llm.thinking` | — | LLM started generating a response |
68
+ | `llm.toolCall` | `tool_calls[]`, `msg_id`, `call_id` | LLM requested tool/function calls. Each item has `id`, `name`, `arguments` (JSON string). |
69
+ | `llm.tool_result` | `call_id`, `msg_id`, `results[]` | Tool execution results sent back to LLM. Each item has `tool_call_id`, `result`. |
70
+ | `llm.response` | `text`, `finish_reason` | LLM finished generating (text may be empty for tool-only turns) |
71
+ | `llm.error` | `error` | LLM error occurred |
72
+
73
+ ### Session limits
74
+
75
+ | Event | Fields | Description |
76
+ |---|---|---|
77
+ | `session.idleWarning` | `remaining_seconds` | User hasn't spoken — call will timeout in `remaining_seconds`. Drives the `idleWarning` state field. |
78
+ | `session.timeout` | `reason` | Session timed out (`"idle_timeout"` or `"max_duration"`). The client auto-disconnects. |
79
+
80
+ ## Client → Server commands
81
+
82
+ The client sends these through the DataChannel:
83
+
84
+ | Message | Format | Description |
85
+ |---|---|---|
86
+ | Ping | `"ping"` (string) | Keepalive, sent every 1s by the SDK |
87
+ | Mute | `{ "action": "mute" }` | Stop processing user audio server-side |
88
+ | Unmute | `{ "action": "unmute" }` | Resume processing user audio |
89
+ | Configure | `{ "action": "configure", ...config }` | Hot-swap voice, STT, language, or turn detection mid-call |
90
+ | Inject Text | `{ "action": "inject_text", "text": "..." }` | Send text as if the user spoke it (for tool UI interactions) |
91
+ | Set Context | `{ "action": "set_context", "key": "...", "value": "..." }` | Inject/update keyed context in the LLM prompt |
92
+
93
+ Most of these have helper methods on `VoiceSession` (`toggleMute`, `configure`). The lower-level commands (`inject_text`, `set_context`) are used by `@pinecall/web` to power the [Tools API](/web/widget/tools-api) and dynamic context injection.
94
+
95
+ ## Worked examples
96
+
97
+ ### Monitoring tool calls
98
+
99
+ ```typescript
100
+ session.addEventListener("event", (e) => {
101
+ const { event, tool_calls, results } = e.detail;
102
+
103
+ if (event === "llm.toolCall" && tool_calls) {
104
+ for (const tc of tool_calls) {
105
+ console.log(`Agent calling ${tc.name}(${tc.arguments})`);
106
+ }
107
+ }
108
+ if (event === "llm.tool_result") {
109
+ console.log("Tool results:", results);
110
+ }
111
+ });
112
+ ```
113
+
114
+ ### Custom audio meter from `audio.metrics`
115
+
116
+ ```typescript
117
+ const meter = document.getElementById("meter");
118
+
119
+ session.addEventListener("event", (e) => {
120
+ if (e.detail.event === "audio.metrics" && e.detail.source === "user") {
121
+ meter.style.width = `${e.detail.rms * 100}%`;
122
+ }
123
+ });
124
+ ```
125
+
126
+ ### Injecting text from a button click
127
+
128
+ If you have UI components that the user can click to "say" something:
129
+
130
+ ```typescript
131
+ // User clicks "Yes, that's right" instead of saying it
132
+ document.getElementById("yes-btn").onclick = () => {
133
+ session.send(JSON.stringify({ action: "inject_text", text: "Yes, that's right" }));
134
+ };
135
+ ```
136
+
137
+ The `@pinecall/web` exposes this as the `sendText()` helper — see [Tools API](/web/widget/tools-api).
138
+
139
+ ## WebRTC connection flow
140
+
141
+ For completeness, here's what happens when you call `connect()`:
142
+
143
+ ![WebRTC connection sequence](/assets/diagrams/webrtc-connection-sequence.png)
144
+
145
+ ## What's next
146
+
147
+ - [`VoiceSession`](/web/core/voice-session) — the high-level API
148
+ - [State and phases](/web/core/state-and-phases) — how raw events map to state
149
+ - [Tools API](/web/widget/tools-api) — building UI that responds to `llm.toolCall`