@pinecall/web 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.
@@ -0,0 +1,144 @@
1
+ type SessionStatus = "idle" | "connecting" | "connected" | "error";
2
+ type CallPhase = "idle" | "listening" | "speaking" | "pause" | "thinking";
3
+ interface TranscriptMessage {
4
+ id: number;
5
+ role: "user" | "bot" | "system";
6
+ text: string;
7
+ /** User: STT partial (still typing) */
8
+ isInterim?: boolean;
9
+ /** Bot: TTS currently playing */
10
+ speaking?: boolean;
11
+ /** Bot: user interrupted the agent */
12
+ interrupted?: boolean;
13
+ /** Bot: server-assigned message ID for word-by-word tracking */
14
+ messageId?: string;
15
+ /** System: tool call ID (for updating with result) */
16
+ toolCallId?: string;
17
+ }
18
+ /**
19
+ * A tool call event received from the server-side LLM via DataChannel.
20
+ *
21
+ * The server sends `llm.tool_call` with a batch of tool calls.
22
+ * Each call has an `id`, `name`, and JSON `arguments`.
23
+ */
24
+ interface ToolCallEvent {
25
+ /** Correlation ID — echoed in `llm.tool_result`. */
26
+ msgId: string;
27
+ /** Individual tool calls in this batch. */
28
+ calls: {
29
+ id: string;
30
+ name: string;
31
+ arguments: Record<string, unknown>;
32
+ }[];
33
+ }
34
+ /**
35
+ * A tool result event relayed from the server via DataChannel.
36
+ *
37
+ * Sent after the Node.js SDK agent executes a tool and returns the result.
38
+ */
39
+ interface ToolResultEvent {
40
+ /** Tool call ID (matches `ToolCallEvent.calls[].id`). */
41
+ toolCallId: string;
42
+ /** Tool function name. */
43
+ name: string;
44
+ /** The result value (any JSON). */
45
+ result: unknown;
46
+ }
47
+ /**
48
+ * A pending tool UI entry tracked in session state.
49
+ *
50
+ * Created when `llm.tool_call` arrives for a tracked tool name.
51
+ * Updated with `result` when `llm.tool_result` arrives.
52
+ * Removed via `dismissTool()` after the user interacts.
53
+ */
54
+ interface ToolUI {
55
+ /** Tool call ID. */
56
+ toolCallId: string;
57
+ /** Tool function name. */
58
+ name: string;
59
+ /** Parsed arguments from the LLM. */
60
+ arguments: Record<string, unknown>;
61
+ /** Result from server execution (`undefined` while pending). */
62
+ result?: unknown;
63
+ /** Timestamp when the tool call was received. */
64
+ timestamp: number;
65
+ }
66
+ interface VoiceSessionState {
67
+ status: SessionStatus;
68
+ error: string | null;
69
+ isMuted: boolean;
70
+ phase: CallPhase;
71
+ userSpeaking: boolean;
72
+ agentSpeaking: boolean;
73
+ duration: number;
74
+ messages: TranscriptMessage[];
75
+ /** Active tool UI entries (only for tracked tools). */
76
+ toolCalls: ToolUI[];
77
+ /**
78
+ * Idle warning state. Non-null when `session.idle_warning` fires,
79
+ * meaning the user hasn't spoken and the call will timeout soon.
80
+ * Contains the remaining seconds until timeout.
81
+ * Resets to `null` when the user speaks.
82
+ */
83
+ idleWarning: number | null;
84
+ }
85
+ interface VoiceSessionOptions {
86
+ /** Agent ID to connect to */
87
+ agent: string;
88
+ /**
89
+ * Pinecall API base URL for token exchange.
90
+ * Default: `"https://voice.pinecall.io"`.
91
+ *
92
+ * The SDK fetches a WebRTC token from `GET {server}/webrtc/token?agent_id=X`.
93
+ * The response includes the actual voice server URL for ICE + SDP negotiation.
94
+ */
95
+ server?: string;
96
+ /**
97
+ * Initial session config overrides sent in the WebRTC offer body.
98
+ * Supported keys: `voice`, `stt`, `language`, `turnDetection`, `greeting`.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * { voice: "alloy", stt: "deepgram", language: "es", greeting: "¡Hola!" }
103
+ * ```
104
+ */
105
+ config?: Record<string, unknown>;
106
+ /** Call metadata sent in the WebRTC offer body. */
107
+ metadata?: Record<string, unknown>;
108
+ /**
109
+ * Tool names to track for UI rendering.
110
+ *
111
+ * When listed, VoiceSession tracks `llm.tool_call` → `llm.tool_result`
112
+ * for these tools and exposes them in `state.toolCalls`.
113
+ * Tools NOT listed are ignored (handled silently by the server-side agent).
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * trackedTools: ["getAvailableSlots", "showLocationMap"]
118
+ * ```
119
+ */
120
+ trackedTools?: string[];
121
+ /**
122
+ * Custom token provider — call your backend to generate tokens instead
123
+ * of hitting /webrtc/token directly. Keeps API keys server-side.
124
+ *
125
+ * When set, VoiceSession calls this function instead of
126
+ * `GET /webrtc/token?agent_id=X`. It should return the token response
127
+ * from your authenticated backend endpoint.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * tokenProvider: async () => {
132
+ * const res = await fetch("/api/token?channel=webrtc", { credentials: "include" });
133
+ * return res.json(); // { token, server, expires_in }
134
+ * }
135
+ * ```
136
+ */
137
+ tokenProvider?: () => Promise<{
138
+ token: string;
139
+ server: string;
140
+ expires_in?: number;
141
+ }>;
142
+ }
143
+
144
+ export type { CallPhase as C, SessionStatus as S, ToolCallEvent as T, VoiceSessionOptions as V, VoiceSessionState as a, ToolResultEvent as b, ToolUI as c, TranscriptMessage as d };
@@ -0,0 +1,144 @@
1
+ type SessionStatus = "idle" | "connecting" | "connected" | "error";
2
+ type CallPhase = "idle" | "listening" | "speaking" | "pause" | "thinking";
3
+ interface TranscriptMessage {
4
+ id: number;
5
+ role: "user" | "bot" | "system";
6
+ text: string;
7
+ /** User: STT partial (still typing) */
8
+ isInterim?: boolean;
9
+ /** Bot: TTS currently playing */
10
+ speaking?: boolean;
11
+ /** Bot: user interrupted the agent */
12
+ interrupted?: boolean;
13
+ /** Bot: server-assigned message ID for word-by-word tracking */
14
+ messageId?: string;
15
+ /** System: tool call ID (for updating with result) */
16
+ toolCallId?: string;
17
+ }
18
+ /**
19
+ * A tool call event received from the server-side LLM via DataChannel.
20
+ *
21
+ * The server sends `llm.tool_call` with a batch of tool calls.
22
+ * Each call has an `id`, `name`, and JSON `arguments`.
23
+ */
24
+ interface ToolCallEvent {
25
+ /** Correlation ID — echoed in `llm.tool_result`. */
26
+ msgId: string;
27
+ /** Individual tool calls in this batch. */
28
+ calls: {
29
+ id: string;
30
+ name: string;
31
+ arguments: Record<string, unknown>;
32
+ }[];
33
+ }
34
+ /**
35
+ * A tool result event relayed from the server via DataChannel.
36
+ *
37
+ * Sent after the Node.js SDK agent executes a tool and returns the result.
38
+ */
39
+ interface ToolResultEvent {
40
+ /** Tool call ID (matches `ToolCallEvent.calls[].id`). */
41
+ toolCallId: string;
42
+ /** Tool function name. */
43
+ name: string;
44
+ /** The result value (any JSON). */
45
+ result: unknown;
46
+ }
47
+ /**
48
+ * A pending tool UI entry tracked in session state.
49
+ *
50
+ * Created when `llm.tool_call` arrives for a tracked tool name.
51
+ * Updated with `result` when `llm.tool_result` arrives.
52
+ * Removed via `dismissTool()` after the user interacts.
53
+ */
54
+ interface ToolUI {
55
+ /** Tool call ID. */
56
+ toolCallId: string;
57
+ /** Tool function name. */
58
+ name: string;
59
+ /** Parsed arguments from the LLM. */
60
+ arguments: Record<string, unknown>;
61
+ /** Result from server execution (`undefined` while pending). */
62
+ result?: unknown;
63
+ /** Timestamp when the tool call was received. */
64
+ timestamp: number;
65
+ }
66
+ interface VoiceSessionState {
67
+ status: SessionStatus;
68
+ error: string | null;
69
+ isMuted: boolean;
70
+ phase: CallPhase;
71
+ userSpeaking: boolean;
72
+ agentSpeaking: boolean;
73
+ duration: number;
74
+ messages: TranscriptMessage[];
75
+ /** Active tool UI entries (only for tracked tools). */
76
+ toolCalls: ToolUI[];
77
+ /**
78
+ * Idle warning state. Non-null when `session.idle_warning` fires,
79
+ * meaning the user hasn't spoken and the call will timeout soon.
80
+ * Contains the remaining seconds until timeout.
81
+ * Resets to `null` when the user speaks.
82
+ */
83
+ idleWarning: number | null;
84
+ }
85
+ interface VoiceSessionOptions {
86
+ /** Agent ID to connect to */
87
+ agent: string;
88
+ /**
89
+ * Pinecall API base URL for token exchange.
90
+ * Default: `"https://voice.pinecall.io"`.
91
+ *
92
+ * The SDK fetches a WebRTC token from `GET {server}/webrtc/token?agent_id=X`.
93
+ * The response includes the actual voice server URL for ICE + SDP negotiation.
94
+ */
95
+ server?: string;
96
+ /**
97
+ * Initial session config overrides sent in the WebRTC offer body.
98
+ * Supported keys: `voice`, `stt`, `language`, `turnDetection`, `greeting`.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * { voice: "alloy", stt: "deepgram", language: "es", greeting: "¡Hola!" }
103
+ * ```
104
+ */
105
+ config?: Record<string, unknown>;
106
+ /** Call metadata sent in the WebRTC offer body. */
107
+ metadata?: Record<string, unknown>;
108
+ /**
109
+ * Tool names to track for UI rendering.
110
+ *
111
+ * When listed, VoiceSession tracks `llm.tool_call` → `llm.tool_result`
112
+ * for these tools and exposes them in `state.toolCalls`.
113
+ * Tools NOT listed are ignored (handled silently by the server-side agent).
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * trackedTools: ["getAvailableSlots", "showLocationMap"]
118
+ * ```
119
+ */
120
+ trackedTools?: string[];
121
+ /**
122
+ * Custom token provider — call your backend to generate tokens instead
123
+ * of hitting /webrtc/token directly. Keeps API keys server-side.
124
+ *
125
+ * When set, VoiceSession calls this function instead of
126
+ * `GET /webrtc/token?agent_id=X`. It should return the token response
127
+ * from your authenticated backend endpoint.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * tokenProvider: async () => {
132
+ * const res = await fetch("/api/token?channel=webrtc", { credentials: "include" });
133
+ * return res.json(); // { token, server, expires_in }
134
+ * }
135
+ * ```
136
+ */
137
+ tokenProvider?: () => Promise<{
138
+ token: string;
139
+ server: string;
140
+ expires_in?: number;
141
+ }>;
142
+ }
143
+
144
+ export type { CallPhase as C, SessionStatus as S, ToolCallEvent as T, VoiceSessionOptions as V, VoiceSessionState as a, ToolResultEvent as b, ToolUI as c, TranscriptMessage as d };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @pinecall/web/chat — Type definitions.
3
+ */
4
+ /** Chat session connection status. */
5
+ type ChatStatus = "idle" | "connecting" | "connected" | "error" | "destroyed";
6
+ /** A single message in the conversation. */
7
+ interface ChatMessage {
8
+ id: number;
9
+ role: "user" | "bot";
10
+ text: string;
11
+ /** Server-assigned message ID (for bot messages). */
12
+ messageId?: string;
13
+ /** True while the bot is still streaming this message. */
14
+ isStreaming?: boolean;
15
+ }
16
+ /** Full state of a ChatSession (immutable snapshots). */
17
+ interface ChatSessionState {
18
+ status: ChatStatus;
19
+ error: string | null;
20
+ /** All messages in the conversation. */
21
+ messages: ChatMessage[];
22
+ /** True while the bot is streaming a response. */
23
+ typing: boolean;
24
+ /** Partial text of the current bot response being streamed. */
25
+ streamingText: string;
26
+ /** Session ID assigned by the server. */
27
+ sessionId: string | null;
28
+ }
29
+ /** Options for creating a ChatSession. */
30
+ interface ChatSessionOptions {
31
+ /** Agent ID to connect to (e.g. "florencia", "dev-berna-florencia"). */
32
+ agent: string;
33
+ /**
34
+ * Voice server URL.
35
+ * The SDK fetches a chat token from `GET {server}/chat/token?agent_id=X`.
36
+ * Then opens a WebSocket to `{server}/chat/ws?token=cht_xxx`.
37
+ *
38
+ * @default "https://voice.pinecall.io"
39
+ */
40
+ server?: string;
41
+ /**
42
+ * Custom token provider — call your backend to generate tokens instead
43
+ * of hitting /chat/token directly. Keeps API keys server-side.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * tokenProvider: async () => {
48
+ * const res = await fetch("/api/token?channel=chat");
49
+ * return res.json(); // { token, server }
50
+ * }
51
+ * ```
52
+ */
53
+ tokenProvider?: () => Promise<{
54
+ token: string;
55
+ server: string;
56
+ }>;
57
+ }
58
+ /** Events emitted by ChatSession via EventTarget. */
59
+ type ChatEventType = "status" | "message" | "error" | "change";
60
+
61
+ export type { ChatSessionOptions as C, ChatSessionState as a, ChatEventType as b, ChatMessage as c, ChatStatus as d };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @pinecall/web/chat — Type definitions.
3
+ */
4
+ /** Chat session connection status. */
5
+ type ChatStatus = "idle" | "connecting" | "connected" | "error" | "destroyed";
6
+ /** A single message in the conversation. */
7
+ interface ChatMessage {
8
+ id: number;
9
+ role: "user" | "bot";
10
+ text: string;
11
+ /** Server-assigned message ID (for bot messages). */
12
+ messageId?: string;
13
+ /** True while the bot is still streaming this message. */
14
+ isStreaming?: boolean;
15
+ }
16
+ /** Full state of a ChatSession (immutable snapshots). */
17
+ interface ChatSessionState {
18
+ status: ChatStatus;
19
+ error: string | null;
20
+ /** All messages in the conversation. */
21
+ messages: ChatMessage[];
22
+ /** True while the bot is streaming a response. */
23
+ typing: boolean;
24
+ /** Partial text of the current bot response being streamed. */
25
+ streamingText: string;
26
+ /** Session ID assigned by the server. */
27
+ sessionId: string | null;
28
+ }
29
+ /** Options for creating a ChatSession. */
30
+ interface ChatSessionOptions {
31
+ /** Agent ID to connect to (e.g. "florencia", "dev-berna-florencia"). */
32
+ agent: string;
33
+ /**
34
+ * Voice server URL.
35
+ * The SDK fetches a chat token from `GET {server}/chat/token?agent_id=X`.
36
+ * Then opens a WebSocket to `{server}/chat/ws?token=cht_xxx`.
37
+ *
38
+ * @default "https://voice.pinecall.io"
39
+ */
40
+ server?: string;
41
+ /**
42
+ * Custom token provider — call your backend to generate tokens instead
43
+ * of hitting /chat/token directly. Keeps API keys server-side.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * tokenProvider: async () => {
48
+ * const res = await fetch("/api/token?channel=chat");
49
+ * return res.json(); // { token, server }
50
+ * }
51
+ * ```
52
+ */
53
+ tokenProvider?: () => Promise<{
54
+ token: string;
55
+ server: string;
56
+ }>;
57
+ }
58
+ /** Events emitted by ChatSession via EventTarget. */
59
+ type ChatEventType = "status" | "message" | "error" | "change";
60
+
61
+ export type { ChatSessionOptions as C, ChatSessionState as a, ChatEventType as b, ChatMessage as c, ChatStatus as d };
package/package.json ADDED
@@ -0,0 +1,113 @@
1
+ {
2
+ "name": "@pinecall/web",
3
+ "version": "0.1.0",
4
+ "description": "Pinecall web client — WebRTC voice, text chat, and React widgets for Pinecall agents",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "./core": {
22
+ "import": {
23
+ "types": "./dist/core/index.d.ts",
24
+ "default": "./dist/core/index.js"
25
+ },
26
+ "require": {
27
+ "types": "./dist/core/index.d.cts",
28
+ "default": "./dist/core/index.cjs"
29
+ }
30
+ },
31
+ "./chat": {
32
+ "import": {
33
+ "types": "./dist/chat/index.d.ts",
34
+ "default": "./dist/chat/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/chat/index.d.cts",
38
+ "default": "./dist/chat/index.cjs"
39
+ }
40
+ },
41
+ "./chat/react": {
42
+ "import": {
43
+ "types": "./dist/chat/react.d.ts",
44
+ "default": "./dist/chat/react.js"
45
+ },
46
+ "require": {
47
+ "types": "./dist/chat/react.d.cts",
48
+ "default": "./dist/chat/react.cjs"
49
+ }
50
+ },
51
+ "./package.json": "./package.json"
52
+ },
53
+ "files": [
54
+ "dist",
55
+ "README.md"
56
+ ],
57
+ "sideEffects": false,
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "typecheck": "tsc --noEmit",
62
+ "release": "pnpm build && npm publish"
63
+ },
64
+ "dependencies": {
65
+ "marked": "^18.0.0"
66
+ },
67
+ "peerDependencies": {
68
+ "react": ">=18.0.0",
69
+ "react-dom": ">=18.0.0"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "react": {
73
+ "optional": true
74
+ },
75
+ "react-dom": {
76
+ "optional": true
77
+ }
78
+ },
79
+ "devDependencies": {
80
+ "@types/react": "^18.3.12",
81
+ "@types/react-dom": "^18.3.1",
82
+ "react": "^18.3.1",
83
+ "react-dom": "^18.3.1",
84
+ "tsup": "^8.3.5",
85
+ "typescript": "^5.6.3"
86
+ },
87
+ "publishConfig": {
88
+ "access": "public"
89
+ },
90
+ "keywords": [
91
+ "webrtc",
92
+ "voice",
93
+ "chat",
94
+ "ai",
95
+ "agent",
96
+ "react",
97
+ "widget",
98
+ "pinecall"
99
+ ],
100
+ "repository": {
101
+ "type": "git",
102
+ "url": "https://github.com/pinecall/web"
103
+ },
104
+ "packageManager": "pnpm@10.2.0",
105
+ "engines": {
106
+ "node": ">=18"
107
+ },
108
+ "pnpm": {
109
+ "onlyBuiltDependencies": [
110
+ "esbuild"
111
+ ]
112
+ }
113
+ }