@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,291 @@
1
+ ---
2
+ title: "Props"
3
+ description: "Every prop the VoiceWidget accepts — including token security, tools, theming, and multi-language."
4
+ ---
5
+
6
+ # Props
7
+
8
+ Full reference for `<VoiceWidget />`.
9
+
10
+ ## All props
11
+
12
+ | Prop | Type | Default | Description |
13
+ |---|---|---|---|
14
+ | `agent` | `string` | **required** | Agent ID to connect to |
15
+ | `server` | `string` | `"https://voice.pinecall.io"` | Pinecall API base URL (override for self-hosted) |
16
+ | `name` | `string` | `"Agent"` | Display name shown in status label |
17
+ | `label` | `string` | `"Talk to {name}"` | Tooltip shown on hover when idle |
18
+ | `preset` | `VoiceWidgetPreset` | `"dark"` | Theme preset (`dark`, `midnight`, `aurora`, `sunset`, `light`) |
19
+ | `theme` | `Partial<VoiceWidgetTheme>` | — | Custom theme overrides, merged on top of `preset` |
20
+ | `config` | `Record<string, unknown>` | — | Session config overrides (voice, STT, language) |
21
+ | `metadata` | `Record<string, unknown>` | — | Metadata passed to the agent (available as `call.metadata`) |
22
+ | `languages` | `Record<string, LanguagePreset>` | — | Multi-language presets (see below) |
23
+ | `defaultLanguage` | `string` | first key | Initial language selection |
24
+ | `onLanguageChange` | `(lang, preset) => void` | — | Called when the user picks a language |
25
+ | `tokenProvider` | `() => Promise<{token, server}>` | — | Custom token provider for WebRTC (keeps API keys server-side) |
26
+ | `trackedTools` | `string[]` | — | Tool names to track in widget state for UI rendering |
27
+ | `tools` | `Record<string, ToolRenderer>` | — | Map of tool names → render functions for inline tool UI |
28
+ | `onStatusChange` | `(status) => void` | — | Called when connection status changes |
29
+ | `className` | `string` | — | Extra CSS class on the root wrapper |
30
+
31
+ ## `tokenProvider` — token security
32
+
33
+ Browser connections require **short-lived tokens**. Your backend generates them using `@pinecall/sdk`, and the widget fetches them via the `tokenProvider` callback. This keeps your API key server-side.
34
+
35
+ ### Backend setup
36
+
37
+ ```typescript
38
+ // server.js
39
+ import express from "express";
40
+ import { Pinecall } from "@pinecall/sdk";
41
+
42
+ const app = express();
43
+ const pc = new Pinecall();
44
+
45
+ const florencia = pc.agent("florencia", {
46
+ voice: "elevenlabs/sarah",
47
+ language: "es",
48
+ stt: "deepgram/flux-en",
49
+ llm: "openai/gpt-5-chat-latest",
50
+ prompt: "...",
51
+ greeting: "¡Hola!",
52
+ });
53
+
54
+ // Token endpoint — add your own auth in production
55
+ app.get("/api/token", authMiddleware, async (req, res) => {
56
+ const token = await florencia.createToken("webrtc");
57
+ res.json(token);
58
+ });
59
+
60
+ app.listen(3000);
61
+ ```
62
+
63
+ ### Two ways to generate tokens
64
+
65
+ | Method | When to use |
66
+ |---|---|
67
+ | `agent.createToken(channel)` | You have the `Agent` instance in the same process |
68
+ | `pc.createToken(channel, agentId)` | The agent runs in a separate process; you only have the `Pinecall` client |
69
+
70
+ ```typescript
71
+ // Option A: from the agent instance
72
+ const token = await florencia.createToken("webrtc");
73
+
74
+ // Option B: from the Pinecall client (agent in another process)
75
+ const token = await pc.createToken("webrtc", "florencia");
76
+ ```
77
+
78
+ Both return the same shape:
79
+
80
+ ```json
81
+ { "token": "tok_...", "server": "wss://voice.pinecall.io", "expires_in": 60 }
82
+ ```
83
+
84
+ ### Frontend
85
+
86
+ ```tsx
87
+ <VoiceWidget
88
+ agent="florencia"
89
+ tokenProvider={async () => {
90
+ const res = await fetch("/api/token?channel=webrtc", {
91
+ credentials: "include", // send your session cookie
92
+ });
93
+ if (!res.ok) throw new Error(`Token failed: ${res.status}`);
94
+ return res.json();
95
+ }}
96
+ />
97
+ ```
98
+
99
+ ### `allowedOrigins` + `tokenProvider` — recommended combo
100
+
101
+ Use **both** for the best experience:
102
+
103
+ - `allowedOrigins` lets the widget auto-fetch tokens during **local development** (where your backend might not be running)
104
+ - `tokenProvider` provides **production security** — tokens go through your backend with your auth
105
+
106
+ ```typescript
107
+ // Backend — agent config
108
+ const florencia = pc.agent("florencia", {
109
+ // Dev fallback — widget can auto-fetch tokens from matching origins
110
+ allowedOrigins: ["https://mysite.com", "http://localhost:*"],
111
+ // ...
112
+ });
113
+ ```
114
+
115
+ ```tsx
116
+ // Frontend — tokenProvider for production
117
+ <VoiceWidget
118
+ agent="florencia"
119
+ tokenProvider={async () => {
120
+ const res = await fetch("/api/token");
121
+ if (!res.ok) throw new Error(`Token failed: ${res.status}`);
122
+ return res.json();
123
+ }}
124
+ />
125
+ ```
126
+
127
+ When `tokenProvider` is set, the widget uses it. When it's not set (or fails), `@pinecall/web/core` falls back to fetching directly from the server using `allowedOrigins`.
128
+
129
+ > **Security note:** `allowedOrigins` alone is origin-header based — real browsers can't spoof it, but scripts/curl can. Always pair it with `tokenProvider` in production.
130
+
131
+ ### How the token is used
132
+
133
+ The token is consumed **once** during the WebRTC handshake. Here's the full flow:
134
+
135
+ ```
136
+ 1. Widget calls tokenProvider()
137
+ → returns { token: "tok_...", server: "wss://voice.pinecall.io" }
138
+
139
+ 2. Widget fetches ICE config
140
+ → GET {server}/webrtc/ice-servers → STUN/TURN servers
141
+
142
+ 3. Browser requests mic access
143
+ → navigator.mediaDevices.getUserMedia()
144
+
145
+ 4. Widget creates a WebRTC offer
146
+ → new RTCPeerConnection → addTrack(mic) → createOffer()
147
+
148
+ 5. Widget sends the offer + token to the server
149
+ → POST {server}/webrtc/offer
150
+ { sdp: "...", type: "offer", token: "tok_...", config, metadata }
151
+
152
+ └── token goes here, consumed on use
153
+
154
+ 6. Server validates the token, creates a session, returns the SDP answer
155
+ → { sdp: "...", type: "answer" }
156
+
157
+ 7. WebRTC connection established — audio flows peer-to-peer
158
+ → token is discarded, all communication is via PeerConnection
159
+ ```
160
+
161
+ After step 5, the token is gone. It can't be reused, replayed, or shared. The WebRTC connection is secured by the PeerConnection itself.
162
+
163
+ ### Token properties
164
+
165
+ | Property | Value | Effect |
166
+ |---|---|---|
167
+ | Single-use | Consumed on first connection | Can't be reused |
168
+ | Short-lived | 60 second TTL | Expires quickly |
169
+ | Scoped | Locked to agent + org | Can't be used elsewhere |
170
+
171
+ > **Never** store API keys in frontend code. See [Security](/security) for the full token model.
172
+
173
+ ## `config` — session overrides
174
+
175
+ Pass session-level overrides to the agent:
176
+
177
+ ```tsx
178
+ <VoiceWidget
179
+ agent="mara"
180
+ config={{
181
+ voice: "elevenlabs/sarah",
182
+ stt: "deepgram/flux-en",
183
+ language: "es",
184
+ }}
185
+ />
186
+ ```
187
+
188
+ ## `metadata` — server-side context
189
+
190
+ Whatever you pass shows up as `call.metadata` in your agent:
191
+
192
+ ```tsx
193
+ <VoiceWidget
194
+ agent="mara"
195
+ metadata={{
196
+ userId: currentUser.id,
197
+ plan: currentUser.plan,
198
+ }}
199
+ />
200
+ ```
201
+
202
+ On the server:
203
+
204
+ ```typescript
205
+ agent.on("call.started", (call) => {
206
+ console.log("Call from user", call.metadata.userId);
207
+ });
208
+ ```
209
+
210
+ ## `languages` — multi-language selector
211
+
212
+ Enables a language pill bar that appears on hover and stays visible during calls.
213
+
214
+ ```tsx
215
+ <VoiceWidget
216
+ agent="mara"
217
+ languages={{
218
+ en: {
219
+ label: "English",
220
+ flag: "🇬🇧",
221
+ voice: "elevenlabs/sarah",
222
+ stt: "deepgram/flux-en",
223
+ language: "en",
224
+ },
225
+ es: {
226
+ label: "Español",
227
+ flag: "🇪🇸",
228
+ voice: "elevenlabs/george",
229
+ stt: "deepgram/flux-en",
230
+ language: "es",
231
+ },
232
+ }}
233
+ defaultLanguage="en"
234
+ />
235
+ ```
236
+
237
+ ### `LanguagePreset` shape
238
+
239
+ | Field | Type | Description |
240
+ |---|---|---|
241
+ | `label` | `string` | Display name (e.g. `"Español"`) |
242
+ | `flag` | `string` | Flag emoji (e.g. `"🇪🇸"`) |
243
+ | `voice` | `string` | Voice ID in `provider:id` format |
244
+ | `stt` | `string \| object` | STT shortcut (`"deepgram/flux-en"`) or full config |
245
+ | `language` | `string` | Language code for STT (`"es"`, `"en"`, etc.) |
246
+
247
+ ### Behavior
248
+
249
+ - **Pre-call**: Pill bar appears on hover. Selecting a language updates the session config.
250
+ - **Mid-call**: Pills stay visible. Selecting a language sends a `configure` message via DataChannel — voice, STT, and language hot-swap without disconnecting.
251
+
252
+ ## `tools` — inline tool renderers
253
+
254
+ Map tool names to React render functions. When a server-side tool completes, the result renders inline:
255
+
256
+ ```tsx
257
+ <VoiceWidget
258
+ agent="booking-demo"
259
+ tools={{
260
+ getAvailableSlots: (result, { respond, dismiss }) => (
261
+ <div className="slots">
262
+ {result.slots.map((slot: string) => (
263
+ <button key={slot} onClick={() => { respond(`I'd like ${slot}`); dismiss(); }}>
264
+ {slot}
265
+ </button>
266
+ ))}
267
+ </div>
268
+ ),
269
+ }}
270
+ />
271
+ ```
272
+
273
+ See [Tools API](/web/widget/tools-api) for the full pattern.
274
+
275
+ ## `onStatusChange` — observability
276
+
277
+ ```tsx
278
+ <VoiceWidget
279
+ agent="mara"
280
+ onStatusChange={(status) => {
281
+ if (status === "connected") analytics.track("call_started");
282
+ if (status === "idle") analytics.track("call_ended");
283
+ }}
284
+ />
285
+ ```
286
+
287
+ ## What's next
288
+
289
+ - [Theming](/web/widget/theming) — all CSS variables and preset values
290
+ - [Tools API](/web/widget/tools-api) — interactive UI from tool calls
291
+ - [`useVoiceSession` hook](/web/widget/use-voice-session-hook) — bypass the orb, build custom UI
@@ -0,0 +1,131 @@
1
+ ---
2
+ title: "Theming"
3
+ description: "Theme presets, CSS variables, and full customization of the orb and transcript UI."
4
+ ---
5
+
6
+ # Theming
7
+
8
+ Every visual aspect of the widget is controlled by CSS custom properties. You can pick a built-in preset, override individual values, or skip the props entirely and override variables with CSS.
9
+
10
+ ## Five built-in presets
11
+
12
+ | Preset | Orb | Rings | Panels | Best for |
13
+ |---|---|---|---|---|
14
+ | `"dark"` | Pearl white | Warm red | Dark purple glass | Dark-themed sites (default) |
15
+ | `"midnight"` | Deep sapphire | Ice blue | Navy glass | Corporate / professional |
16
+ | `"aurora"` | Emerald / teal | Green | Forest dark | Nature / wellness brands |
17
+ | `"sunset"` | Warm coral | Golden amber | Warm dark | Hospitality / warm brands |
18
+ | `"light"` | Clean white | Soft blue | White glass | Light-themed sites |
19
+
20
+ ```tsx
21
+ <VoiceWidget preset="midnight" agent="mara" />
22
+ <VoiceWidget preset="aurora" agent="mara" />
23
+ <VoiceWidget preset="sunset" agent="mara" />
24
+ <VoiceWidget preset="light" agent="mara" />
25
+ <VoiceWidget preset="dark" agent="mara" /> // default
26
+ ```
27
+
28
+ Access preset values programmatically:
29
+
30
+ ```tsx
31
+ import { PRESETS } from "@pinecall/web";
32
+ console.log(PRESETS.midnight); // full theme object
33
+ ```
34
+
35
+ ## Custom themes via the `theme` prop
36
+
37
+ Pass overrides — they merge on top of the chosen `preset`:
38
+
39
+ ```tsx
40
+ <VoiceWidget
41
+ agent="mara"
42
+ preset="midnight"
43
+ theme={{
44
+ orbFrom: "200, 150, 255",
45
+ orbMid: "140, 80, 220",
46
+ orbTo: "80, 30, 160",
47
+ colorAccent: "124, 58, 237",
48
+ ringColor: "216, 65, 44",
49
+ panelBg: "rgba(16, 14, 20, .92)",
50
+ bubbleUserColor: "#e0d4f7",
51
+ }}
52
+ />
53
+ ```
54
+
55
+ ## All theme variables
56
+
57
+ | Field | CSS variable | Type | Default (dark) | Controls |
58
+ |---|---|---|---|---|
59
+ | `orbFrom` | `--vw-orb-from` | RGB triplet | `255, 255, 255` | Idle orb gradient center |
60
+ | `orbMid` | `--vw-orb-mid` | RGB triplet | `240, 238, 231` | Idle orb gradient midtone |
61
+ | `orbTo` | `--vw-orb-to` | RGB triplet | `184, 181, 168` | Idle orb gradient edge |
62
+ | `colorConnecting` | `--vw-color-connecting` | RGB triplet | `245, 158, 11` | Connecting state orb |
63
+ | `colorActive` | `--vw-color-active` | RGB triplet | `76, 175, 80` | Connected / listening orb |
64
+ | `colorUserSpeaking` | `--vw-color-user-speaking` | RGB triplet | `52, 211, 153` | User speaking orb |
65
+ | `colorSpeaking` | `--vw-color-speaking` | RGB triplet | `248, 113, 113` | Agent speaking orb |
66
+ | `colorThinking` | `--vw-color-thinking` | RGB triplet | `139, 92, 246` | Thinking / processing orb |
67
+ | `colorWarning` | `--vw-color-warning` | RGB triplet | `255, 160, 0` | Idle warning blink |
68
+ | `colorAccent` | `--vw-color-accent` | RGB triplet | `124, 58, 237` | User bubble accent |
69
+ | `ringColor` | `--vw-ring-color` | RGB triplet | `216, 65, 44` | Idle ring glow |
70
+ | `panelBg` | `--vw-panel-bg` | CSS color | `rgba(16,14,20,.92)` | Transcript panel bg |
71
+ | `panelBorder` | `--vw-panel-border` | CSS color | `rgba(255,255,255,.08)` | Transcript panel border |
72
+ | `bubbleBotBg` | `--vw-bubble-bot-bg` | CSS color | `rgba(18,16,22,.9)` | Bot bubble background |
73
+ | `bubbleBotColor` | `--vw-bubble-bot-color` | CSS color | `#e8e4f0` | Bot bubble text |
74
+ | `bubbleUserColor` | `--vw-bubble-user-color` | CSS color | `#e0d4f7` | User bubble text |
75
+ | `labelBg` | `--vw-label-bg` | CSS color | `#181818` | Label tooltip background |
76
+ | `labelColor` | `--vw-label-color` | CSS color | `#fff` | Label tooltip text |
77
+
78
+ > **RGB triplet vs CSS color.** Variables that need alpha variants are stored as `"R, G, B"` strings (the widget uses `rgba(var(--vw-color-x), 0.3)` etc.). Plain CSS color values are used for backgrounds and text where alpha is baked into the value.
79
+
80
+ ## CSS-only override (no JS)
81
+
82
+ Skip the `theme` prop entirely — override the CSS variables directly:
83
+
84
+ ```css
85
+ .vw-wrap {
86
+ --vw-orb-from: 200, 150, 255;
87
+ --vw-ring-color: 100, 80, 200;
88
+ --vw-panel-bg: rgba(20, 10, 40, .95);
89
+ }
90
+ ```
91
+
92
+ This is handy when you want the theme to follow a parent context (e.g. dark/light mode toggle in your app's own CSS).
93
+
94
+ ## Orb visual states
95
+
96
+ The orb gets a different look per phase:
97
+
98
+ | State | Visual | CSS class | When |
99
+ |---|---|---|---|
100
+ | Idle | Pearl gradient, breathing rings | (default) | Not connected |
101
+ | Connecting | Amber pulse | `.connecting` | Establishing WebRTC |
102
+ | Active | Soft green glow | `.active` | Connected, listening |
103
+ | User speaking | Emerald glow | `.user-speaking` | User is talking |
104
+ | Agent speaking | Rose pulse | `.speaking` | Bot TTS playing |
105
+ | Thinking | Violet pulse | `.thinking` | Waiting for LLM response |
106
+ | **Idle warning** | **Orange blink** | `.idle-warning` | User silent — call will timeout soon |
107
+
108
+ The idle warning state is driven by the server's `session.idleWarning` event and clears when the user speaks or the call ends.
109
+
110
+ ## Building on top of presets
111
+
112
+ Start from a preset and modify selectively:
113
+
114
+ ```tsx
115
+ import { PRESETS } from "@pinecall/web";
116
+ import type { VoiceWidgetTheme } from "@pinecall/web";
117
+
118
+ const brandTheme: Partial<VoiceWidgetTheme> = {
119
+ ...PRESETS.midnight,
120
+ colorAccent: "255, 87, 34", // brand orange
121
+ ringColor: "255, 87, 34",
122
+ bubbleUserColor: "#ffccbc",
123
+ };
124
+
125
+ <VoiceWidget agent="mara" preset="midnight" theme={brandTheme} />;
126
+ ```
127
+
128
+ ## What's next
129
+
130
+ - [Props reference](/web/widget/props) — everything else the widget accepts
131
+ - [`useVoiceSession` hook](/web/widget/use-voice-session-hook) — for fully custom UIs that bypass the orb entirely