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 +21 -0
- package/README.md +259 -0
- package/dist/cjs/index.d.ts +736 -0
- package/dist/cjs/index.js +761 -0
- package/dist/cjs/package.json +1 -0
- package/dist/index.d.ts +737 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +757 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
- package/src/index.ts +1370 -0
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
|