supafone-labs 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam Savage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # @supafonesupafone-labs
2
+
3
+ **The TypeScript client for the Supafone agent framework.**
4
+
5
+ Use this package to create hosted Supafone agents from code: inbound
6
+ receptionists, outbound sales agents, web agents, Supafone-managed phone
7
+ numbers, built-in stages, tools, recordings, transcripts, widgets, and Supafone
8
+ Pro watcher. It also includes the [Supafone Labs cloud](https:/supafone-labs.supafone.ai)
9
+ sidecar oracle, hosted TTS/STT, live multilingual transcription, the builder,
10
+ and the adversarial QA suite.
11
+
12
+ Dependency-free. Works in Node 18+ and the browser (native `fetch` / `WebSocket`).
13
+
14
+ ```bash
15
+ npm i @supafonesupafone-labs
16
+ ```
17
+
18
+ ## Keys
19
+
20
+ The SDK talks to two related APIs:
21
+
22
+ | Environment variable | Key shape | Used for |
23
+ | --- | --- | --- |
24
+ | `SUPAFONE_LABS_API_KEY` | `sl_live_...` | Labs cloud oracle, hosted TTS/STT, logs, usage, QA, optimizer |
25
+ | `SUPAFONE_API_KEY` | `sf_live_...` | Hosted Supafone agents on `/api/v1supafone-labs/*` |
26
+
27
+ If you only use hosted-agent methods, `SUPAFONE_API_KEY` is enough. If you use
28
+ both products from one SDK instance, pass `SUPAFONE_API_KEY` as
29
+ `supafoneApiKey`.
30
+
31
+ ## Quick start
32
+
33
+ ```ts
34
+ import { Supafone } from "@supafonesupafone-labs";
35
+
36
+ const supafone = new Supafone({ apiKey: process.env.SUPAFONE_LABS_API_KEY! });
37
+
38
+ // The one-liner: transcript in, a silent coaching directive out.
39
+ const directive = await supafone.whisper(
40
+ "Agent: How can I help?\nCaller: I was rear-ended — what do you charge?",
41
+ { guardrails: "Never quote fees. Acknowledge injury first." },
42
+ );
43
+ // -> "She may be hurt — acknowledge that first, and do not state any fee."
44
+ ```
45
+
46
+ Feed `directive` back into your agent however your platform injects context
47
+ (Ultravox `inject_message`, Vapi `assistant_override`, ElevenLabs
48
+ `contextual_update`, an OpenAI Realtime `session.update`, …). When it's empty,
49
+ the agent is doing fine — say nothing.
50
+
51
+ ## Spawn a hosted Supafone agent
52
+
53
+ Use the same package to create finished Supafone agents from code. This hits the
54
+ Supafone API (`/api/v1supafone-labs/*`), not a raw Ultravox endpoint: Supafone keeps the
55
+ multistage state machine, managed voice accounts, tools, recordings,
56
+ transcripts, account sync, and Supafone Pro watcher attached.
57
+
58
+ The default path is fully Supafone-managed. Developers do **not** need a Twilio
59
+ account, Ultravox account, or voice-provider account to buy a number and launch
60
+ an agent.
61
+
62
+ ```ts
63
+ import { Supafone } from "@supafonesupafone-labs";
64
+
65
+ const supafone = new Supafone({
66
+ apiKey: process.env.SUPAFONE_LABS_API_KEY || process.env.SUPAFONE_API_KEY!,
67
+ supafoneApiKey: process.env.SUPAFONE_API_KEY!,
68
+ // Defaults to https://api.supafone.ai. Override for staging/local tests.
69
+ // supafoneApiBaseUrl: "http://localhost:8000",
70
+ });
71
+
72
+ const capabilities = await supafone.labs.capabilities();
73
+ console.log(capabilities.default_agent_contract);
74
+
75
+ // Inbound: receptionist/intake agent + Supafone-managed phone number.
76
+ const inbound = await supafone.labs.agents.createInboundWithNumber({
77
+ agentKey: "medivoice-intake",
78
+ name: "MediVoice intake",
79
+ assistantName: "Maya",
80
+ businessName: "MediVoice",
81
+ websiteUrl: "https://medivoice.org",
82
+ number: { search: { areaCode: "787" } },
83
+ voice: {
84
+ provider: "cartesia",
85
+ voiceId: "Jacqueline",
86
+ },
87
+ labs: {
88
+ enabled: true,
89
+ model: "gemma",
90
+ },
91
+ tools: {
92
+ callRouting: true,
93
+ scheduling: true,
94
+ sms: true,
95
+ email: true,
96
+ firmKnowledge: true,
97
+ voicemail: true,
98
+ emergencyEscalation: true,
99
+ },
100
+ ultravox: {
101
+ vadSettings: {
102
+ turnEndpointDelay: "0.384s",
103
+ minimumTurnDuration: "0s",
104
+ frameActivationThreshold: 0.1,
105
+ minimumInterruptionDuration: "0.25s",
106
+ },
107
+ firstSpeakerSettings: {
108
+ agent: { uninterruptible: false },
109
+ },
110
+ },
111
+ });
112
+
113
+ console.log(inbound.agent.agent_key);
114
+ console.log(inbound.number?.number.phone_number);
115
+ console.log(inbound.widget?.snippet);
116
+
117
+ // Outbound: sales/speed-to-lead agent + Supafone-managed caller ID.
118
+ const outbound = await supafone.labs.agents.createOutboundWithNumber({
119
+ agentKey: "medivoice-sales",
120
+ name: "MediVoice sales team",
121
+ assistantName: "Maya",
122
+ businessName: "MediVoice",
123
+ websiteUrl: "https://medivoice.org",
124
+ number: { search: { areaCode: "787" } },
125
+ labs: { enabled: true, model: "gemma" },
126
+ });
127
+
128
+ console.log(outbound.number?.assignment);
129
+ ```
130
+
131
+ If you already own telephony, keep Supafone's agent framework and configure
132
+ BYOK as the advanced path:
133
+
134
+ ```ts
135
+ await supafone.labs.telephony.configure({
136
+ mode: "byok",
137
+ provider: "twilio",
138
+ credentials: {
139
+ accountSid: process.env.TWILIO_ACCOUNT_SID!,
140
+ authToken: process.env.TWILIO_AUTH_TOKEN!,
141
+ fromNumber: "+14155550123",
142
+ },
143
+ });
144
+ ```
145
+
146
+ ## Verify the hosted-agent path
147
+
148
+ Use the production smoke example before handing a key to a customer or testing a
149
+ new Supafone API environment:
150
+
151
+ ```bash
152
+ cd supafone-labs
153
+ SUPAFONE_API_KEY=sf_live_... \
154
+ SUPAFONE_API_BASE_URL=https://api.supafone.ai \
155
+ npx tsx examples/smoke-hosted-agent.ts
156
+ ```
157
+
158
+ The script discovers capabilities, presets, and voices; creates a web intake
159
+ agent; fetches it back; verifies `provider_accounts.mode` is
160
+ `supafone_managed`; verifies no developer provider keys are required; and prints
161
+ the returned widget snippet.
162
+
163
+ ## Hosted voices
164
+
165
+ ```ts
166
+ const wav = await supafone.tts("You're all set — talk soon!", "supafone-labs-calm-en");
167
+ // wav: Uint8Array
168
+
169
+ const text = await supafone.stt(audioBytes, { language: "en" });
170
+ ```
171
+
172
+ ## Live multilingual transcription
173
+
174
+ Stream PCM in, language-tagged results out (Deepgram nova-3 `multi`, code-switching):
175
+
176
+ ```ts
177
+ const live = supafone.liveTranscribe({
178
+ language: "multi",
179
+ onResult: (r) => console.log(`[${r.languages.join(",")}]`, r.transcript, r.isFinal ? "(final)" : ""),
180
+ });
181
+ live.feed(pcmFrame); // per audio frame
182
+ live.close();
183
+ ```
184
+
185
+ On Node < 22 (no global `WebSocket`), pass one in: `liveTranscribe({ WebSocketImpl: WebSocket })` from the [`ws`](https://npmjs.com/package/ws) package.
186
+
187
+ ## Telemetry & reads (work with your API key)
188
+
189
+ ```ts
190
+ await supafone.reportNudge({ text: "Do not quote fees", confidence: 0.9, injected: true });
191
+ await supafone.reportCall({ session_id: "call-1", score: 0.92, outcome: "clean", turns: 6 });
192
+
193
+ await supafone.balance(); // { minutes_remaining, plan, … }
194
+ await supafone.usage(); // today's usage vs caps
195
+ await supafone.logs(); // auditable whisper/billing log
196
+ await supafone.nudges(); // structured whisper feed
197
+ await supafone.metrics(7); // injection rate, latency, by-dimension
198
+ ```
199
+
200
+ ## Agent builder & QA (session-scoped — call `login()` first)
201
+
202
+ The builder and `qa.run` are account features, so authenticate with a console
203
+ login before using them (everything else works with just the API key):
204
+
205
+ ```ts
206
+ await supafone.login(process.env.SM_EMAIL!, process.env.SM_PASSWORD!);
207
+ console.log(supafone.isLoggedIn); // true
208
+
209
+ // A supervised builder turn: whisper + guided reply.
210
+ const turn = await supafone.builder.chat("call-1", [
211
+ { role: "agent", text: "Hi, how can I help?" },
212
+ { role: "caller", text: "You said I was booked but nobody called me back." },
213
+ ]);
214
+ await supafone.builder.saveConfig({ agent_prompt: "…", agent_label: "intake" });
215
+
216
+ // Adversarial QA — every scenario runs A/B (supervised vs not); the delta is the lift.
217
+ const qa = await supafone.qa.run({ turns: 2 });
218
+ console.log(`+${Math.round(qa.summary.avg_lift * 100)} avg lift`);
219
+
220
+ // Improve the standing directive from graded calls (OPRO-style).
221
+ const better = await supafone.optimizer.improve("builder");
222
+ const reports = await supafone.optimizer.reports("builder");
223
+ ```
224
+
225
+ All errors throw `SupafoneLabsError` (with `.status` and `.body`); catch it to
226
+ inspect gateway responses.
227
+
228
+ ## Module format
229
+
230
+ Ships **both ESM and CommonJS**. `import { Supafone } from "@supafonesupafone-labs"`
231
+ and `const { Supafone } = require("@supafonesupafone-labs")` both work, with full types.
232
+
233
+ ## API
234
+
235
+ | Method | Endpoint |
236
+ | --- | --- |
237
+ | `labs.capabilities()` | `GET /api/v1supafone-labs/capabilities` on the Supafone API |
238
+ | `labs.agents.create/createInbound/createOutbound/list/get` | `/api/v1supafone-labs/agents*` on the Supafone API |
239
+ | `labs.agents.createInboundWithNumber/createOutboundWithNumber` | Agent creation plus Supafone-managed number buy/assign |
240
+ | `labs.phoneNumbers.search/buy/assign/list/buyAndAssign` | `/api/v1supafone-labs/phone-numbers*` |
241
+ | `labs.telephony.get/configure/useSupafoneManaged` | `/api/v1supafone-labs/telephony` |
242
+ | `labs.presets.list()` · `labs.tools.list()` · `labs.voices.list()` | Supafone hosted-agent discovery |
243
+ | `whisper(transcript, opts?)` | convenience over the oracle |
244
+ | `oracle({ messages, model?, ... })` | `POST /v1/oracle/complete` |
245
+ | `tts(text, voice?)` | `POST /v1/tts` |
246
+ | `stt(audio, opts?)` | `POST /v1/stt` |
247
+ | `liveTranscribe(opts?)` | `WS /v1/stt/live` |
248
+ | `balance()` · `models()` · `voices()` · `usage()` | reads |
249
+ | `builder.chat/finish/config` | `/v1/builder/*` |
250
+ | `qa.run/history` | `/v1/qa/*` |
251
+ | `optimizer.improve/standing` | `/v1/optimizer/*` |
252
+
253
+ Get a key (5 free minutes, no card): <https:/supafone-labs.supafone.ai/get-key.html>
254
+
255
+ The runtime, all provider adapters, and the offline (bring-your-own-keys) mode
256
+ are open source and MIT-licensed — the Python package `pip install supafone-labs`
257
+ is the full harness. This client is the thin cloud SDK.
258
+
259
+ MIT © Sam Savage