@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.
- package/README.md +65 -0
- package/build.mjs +204 -0
- package/package.json +29 -0
- package/skills/pinecall-concepts/SKILL.md +41 -0
- package/skills/pinecall-concepts/references/concepts/agents-and-channels.md +155 -0
- package/skills/pinecall-concepts/references/concepts/deployment-topologies.md +120 -0
- package/skills/pinecall-concepts/references/concepts/hot-reload.md +119 -0
- package/skills/pinecall-concepts/references/concepts/philosophy.md +100 -0
- package/skills/pinecall-concepts/references/concepts/server-vs-client-llm.md +119 -0
- package/skills/pinecall-examples/SKILL.md +59 -0
- package/skills/pinecall-examples/references/examples/browser-widget.md +206 -0
- package/skills/pinecall-examples/references/examples/chat-bot.md +184 -0
- package/skills/pinecall-examples/references/examples/headless-agent.md +121 -0
- package/skills/pinecall-examples/references/examples/index.md +183 -0
- package/skills/pinecall-examples/references/examples/multi-channel-bot.md +173 -0
- package/skills/pinecall-examples/references/examples/outbound-dispatch.md +109 -0
- package/skills/pinecall-examples/references/examples/turn-detection.md +150 -0
- package/skills/pinecall-guides/SKILL.md +68 -0
- package/skills/pinecall-guides/references/guides/call-ringing.md +149 -0
- package/skills/pinecall-guides/references/guides/conversation-history.md +377 -0
- package/skills/pinecall-guides/references/guides/dev-mode.md +130 -0
- package/skills/pinecall-guides/references/guides/events.md +677 -0
- package/skills/pinecall-guides/references/guides/human-takeover.md +184 -0
- package/skills/pinecall-guides/references/guides/inbound-voice.md +201 -0
- package/skills/pinecall-guides/references/guides/knowledge-bases.md +166 -0
- package/skills/pinecall-guides/references/guides/live-listening.md +199 -0
- package/skills/pinecall-guides/references/guides/multi-tenant.md +158 -0
- package/skills/pinecall-guides/references/guides/outbound-calls.md +279 -0
- package/skills/pinecall-guides/references/guides/sse-streaming.md +207 -0
- package/skills/pinecall-guides/references/guides/testing-agents.md +272 -0
- package/skills/pinecall-guides/references/guides/tools-and-functions.md +254 -0
- package/skills/pinecall-guides/references/guides/webrtc-browser.md +200 -0
- package/skills/pinecall-guides/references/guides/whatsapp.md +370 -0
- package/skills/pinecall-guides/references/guides/ws-streaming.md +235 -0
- package/skills/pinecall-quickstart/SKILL.md +54 -0
- package/skills/pinecall-quickstart/references/index.md +123 -0
- package/skills/pinecall-quickstart/references/quickstart.md +185 -0
- package/skills/pinecall-reference/SKILL.md +43 -0
- package/skills/pinecall-reference/references/reference/cli.md +578 -0
- package/skills/pinecall-reference/references/reference/events.md +366 -0
- package/skills/pinecall-reference/references/reference/llm-providers.md +263 -0
- package/skills/pinecall-reference/references/reference/rest-api.md +122 -0
- package/skills/pinecall-reference/references/reference/session-limits.md +119 -0
- package/skills/pinecall-reference/references/reference/stt-providers.md +174 -0
- package/skills/pinecall-reference/references/reference/tts-providers.md +149 -0
- package/skills/pinecall-sdk-api/SKILL.md +56 -0
- package/skills/pinecall-sdk-api/references/api/agent.md +328 -0
- package/skills/pinecall-sdk-api/references/api/call.md +324 -0
- package/skills/pinecall-sdk-api/references/api/pinecall.md +186 -0
- package/skills/pinecall-sdk-api/references/api/reply-stream.md +148 -0
- package/skills/pinecall-security/SKILL.md +37 -0
- package/skills/pinecall-security/references/security.md +138 -0
- package/skills/pinecall-web-chat/SKILL.md +38 -0
- package/skills/pinecall-web-chat/references/web/chat/chat-session.md +178 -0
- package/skills/pinecall-web-chat/references/web/chat/overview.md +98 -0
- package/skills/pinecall-web-components/SKILL.md +37 -0
- package/skills/pinecall-web-components/references/web/components/overview.md +128 -0
- package/skills/pinecall-web-voice/SKILL.md +40 -0
- package/skills/pinecall-web-voice/references/web/core/datachannel-protocol.md +149 -0
- package/skills/pinecall-web-voice/references/web/core/overview.md +70 -0
- package/skills/pinecall-web-voice/references/web/core/state-and-phases.md +153 -0
- package/skills/pinecall-web-voice/references/web/core/voice-session.md +279 -0
- package/skills/pinecall-web-widget/SKILL.md +41 -0
- package/skills/pinecall-web-widget/references/web/widget/overview.md +67 -0
- package/skills/pinecall-web-widget/references/web/widget/props.md +291 -0
- package/skills/pinecall-web-widget/references/web/widget/theming.md +131 -0
- package/skills/pinecall-web-widget/references/web/widget/tools-api.md +381 -0
- 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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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`
|