supafone-labs 0.6.3 → 0.7.2

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 CHANGED
@@ -28,7 +28,49 @@ If you only use hosted-agent methods, `SUPAFONE_API_KEY` is enough. If you use
28
28
  both products from one SDK instance, pass `SUPAFONE_API_KEY` as
29
29
  `supafoneApiKey`.
30
30
 
31
- ## Quick start
31
+ ## Quick start: S2S with Supervisor
32
+
33
+ **Hosted requirements:** the selected speaking provider and Supervisor need
34
+ configured credentials. Check hosted readiness before a call. See
35
+ [SDK installation](https://labs.supafone.ai/docs/sdk-installation/).
36
+
37
+ ```ts
38
+ import { Supafone, SupafoneS2S } from "supafone-labs";
39
+
40
+ const client = new Supafone({ apiKey: process.env.SUPAFONE_API_KEY! });
41
+ const engine = new SupafoneS2S(client, { provider: "openai" });
42
+ const agent = await engine.create({
43
+ name: "Front desk",
44
+ supervisor: true,
45
+ });
46
+ ```
47
+
48
+ Set `provider`, `model` and `voice` on the constructor; set `supervisor` on
49
+ **`create`**. This uses hosted Agent Factory coaching without a separate
50
+ Supervisor client or standalone adapter. A new `Supafone` client already
51
+ defaults to enabled supervision. To explicitly choose platform-managed
52
+ reasoning, pass `supervisor: { enabled: true, mode: "managed" }` instead.
53
+ Runtime support and configured server credentials determine readiness.
54
+
55
+ For an existing agent:
56
+
57
+ ```ts
58
+ const agentKey = agent.agent.agent_key!;
59
+ await client.labs.agents.update(agentKey, {
60
+ supervisor: { enabled: true, mode: "managed" },
61
+ });
62
+ // To disable coaching:
63
+ await client.labs.agents.update(agentKey, { supervisor: false });
64
+ ```
65
+
66
+ Use `{ supervisor: true }` to enable the saved profile without requesting a
67
+ change to managed mode. Updates affect the saved agent; they are not mid-call
68
+ controls. Switching providers with `apply` preserves the saved Supervisor
69
+ configuration and changes only the speaking selection.
70
+
71
+ ## Standalone supervision
72
+
73
+ For a voice stack you already operate, request and deliver guidance yourself:
32
74
 
33
75
  ```ts
34
76
  import { Supafone } from "supafone-labs";
@@ -52,58 +94,50 @@ silent directive, and the exact primitive for each, is in
52
94
 
53
95
  ## Supported frameworks
54
96
 
55
- Silent injection feeds the live agent hidden guidance it acts on but never
56
- speaks. Two mechanisms cover every supported framework:
57
-
58
- - **Mode A — native silent event:** speech-to-speech models take a vendor event
59
- that adds context without triggering speech.
60
- - **Mode B — own the LLM:** for STT→LLM→TTS pipelines, Supafone plugs in as the
61
- LLM and splices a `system`/`developer` message into the prompt.
62
-
63
- **Possible — 10 frameworks**, each with a real injection door:
64
-
65
- | Framework | Mode | Exact primitive |
66
- | --- | :--: | --- |
67
- | Ultravox | A | `send_data_message` (`urgency:"later"`) — **live/proven today** |
68
- | OpenAI Realtime | A | `conversation.item.create` (role `system`, no `response.create`) |
69
- | Grok (xAI) | A | OpenAI-Realtime-compatible item inject |
70
- | Gemini Live | A | `clientContent` (`turnComplete:false`, role `user`) |
71
- | ElevenLabs | A | `contextual_update` |
72
- | Inworld | A | OpenAI-Realtime-compatible item inject |
73
- | Vapi | A+B | `add-message` (`triggerResponseEnabled:false`) or custom-LLM splice |
74
- | Retell | B | `system` message into the custom-LLM turn |
75
- | Deepgram | A+B | `UpdatePrompt`, or own the `think` LLM |
76
- | LiveKit | B | inject into `chat_ctx` in-process |
77
-
78
- **Impossible — Bland:** its live-call API is stop/listen/transfer only, with no
79
- mid-call inject channel and no custom-LLM. Observe and score it, but you cannot
80
- whisper to it live — a permanent vendor limitation, not a Supafone gap.
81
- **Cartesia** (a TTS voice) and **Pipecat** (a DIY framework you own end to end)
82
- are not conversational agents, so there is nothing to inject into.
83
-
84
- Injection is *possible* for all 10, but managed delivery is wired end-to-end
85
- **only for the legacy managed Supervisor delivery path**; the other nine are
86
- supported via their native primitive with managed delivery rolling out / BYO.
87
- Native realtime Agent Factory delivery is a separate first-class path. A live test against any vendor
88
- needs that vendor's key — free/trial tiers exist for all except OpenAI Realtime
89
- (paid, no free tier). Full matrix:
90
- [gitbook/framework-support.md](../gitbook/framework-support.md). *(The npm
91
- package-page copy updates on the next release.)*
97
+ Supervisor delivers approved guidance through the control surface exposed by
98
+ the selected adapter. The standalone SDK and hosted Agent Factory have separate
99
+ capability contracts:
100
+
101
+ - **Native control adapters:** Supafone/Ultravox, OpenAI Realtime, Grok,
102
+ Vapi, ElevenLabs Agents, Deepgram Voice Agent and Inworld Realtime.
103
+ For example, Ultravox uses a deferred data message; Grok uses
104
+ `response.create.instructions`, with `response.created` followed by
105
+ `response.done` as protocol acknowledgement.
106
+ - **Developer-owned conversation context:** Retell custom LLM, LiveKit Agents
107
+ and Pipecat apply guidance inside the loop the integrator controls.
108
+ - **Observation-only by default:** Gemini Developer Live and Bland emit no
109
+ hidden instruction through the default standalone adapter. Ordinary Gemini
110
+ `clientContent` is conversation history, not privileged system control.
111
+ Cartesia Line needs an explicit custom-event hook in the target agent.
112
+
113
+ Hosted Agent Factory supports coaching across Ultravox, OpenAI, Gemini, Grok
114
+ and Hydra through its managed call paths. Native calls use `check_guidance`
115
+ tool results; Hydra's live observations are model-reported because it supplies
116
+ no native transcript stream. This does not change the standalone adapter's
117
+ capabilities or prove that a model followed the guidance.
118
+
119
+ See the [framework matrix](../gitbook/framework-support.md) and
120
+ [provider contracts](../docs/providers.md) for delivery and acceptance details.
121
+ Live validation requires configured provider access; local contract tests do
122
+ not certify a production call.
92
123
 
93
124
  ## One S2S interface, five providers
94
125
 
95
- Version 0.6.3 exports `SupafoneS2S`, the shared parent of `UltravoxS2S`,
126
+ Since version 0.6.3, the SDK exports `SupafoneS2S`, the shared parent of `UltravoxS2S`,
96
127
  `OpenAIS2S`, `GeminiS2S`, `GrokS2S`, and `HydraS2S`. These classes use
97
128
  Supafone's hosted Agent Factory. Supafone handles the audio adapter, tools,
98
129
  stages, and supported phone transport behind one API.
99
130
 
100
131
  ```ts
101
- import { Supafone, HydraS2S, OpenAIS2S, UltravoxS2S } from "supafone-labs";
132
+ import { Supafone, SupafoneS2S, OpenAIS2S, UltravoxS2S } from "supafone-labs";
102
133
 
103
134
  const client = new Supafone({ apiKey: process.env.SUPAFONE_API_KEY! });
104
- const hydra = new HydraS2S(client, { model: "hydra-v1.1", voice: "maya" });
135
+ const hydra = new SupafoneS2S(client, {
136
+ provider: "smallest", model: "hydra-v1.1", voice: "maya",
137
+ });
105
138
  const created = await hydra.create({
106
139
  name: "Intake", goal: "Collect the caller's details and book a consultation",
140
+ supervisor: { enabled: true, mode: "managed" },
107
141
  telephony: { mode: "supafone_managed", provider: "supafone" },
108
142
  });
109
143
  const agentKey = created.agent.agent_key!;
@@ -115,7 +149,8 @@ await new OpenAIS2S(client, { model: "gpt-realtime-2.1", voice: "marin" }).apply
115
149
  await new UltravoxS2S(client).apply(agentKey);
116
150
  ```
117
151
 
118
- `apply` saves a selection for subsequent calls. `testCall` previews the saved
152
+ `apply` saves only the speaking selection for subsequent calls and preserves
153
+ the saved Supervisor profile. `testCall` previews the saved
119
154
  agent and does not implicitly switch providers. The same constructors and
120
155
  `create`, `apply`, and `test_call` methods are available in Python.
121
156
 
@@ -130,7 +165,8 @@ agent and does not implicitly switch providers. The same constructors and
130
165
  | Smallest AI | `hydra-v1.0` | `sterling` |
131
166
 
132
167
  The existing `realtime: { provider, model, voice }` API remains supported.
133
- Native models use the fixed intake → booking → confirmation flow. The phone
168
+ All five speaking families use the shared generated/custom stage plan, Manager
169
+ and specialist consultations. The phone
134
170
  paths are Supafone-managed numbers, BYO Twilio, Telnyx, Plivo, and SIP through
135
171
  LiveKit. Carrier provisioning and credentials still determine call readiness.
136
172
 
@@ -140,12 +176,43 @@ provider key when Supafone has one configured. Missing keys produce an
140
176
  unavailable preview instead of a live provider call.
141
177
 
142
178
  Hydra supports English audio and tools, without transcript events or mutable
143
- mid-session persona/voice. Supafone supplies its fixed stages at session start
179
+ mid-session persona/voice. Supafone supplies the frozen call plan at session start
144
180
  and advances the stage using tool results. Hydra 1.1 uses 16 kHz input / 24 kHz
145
181
  output; 1.0 uses 16 kHz / 48 kHz and a different voice roster. Native sessions
146
- do not claim Ultravox's recording, Supervisor, transfer, or agent-team parity.
182
+ support opt-in recording, public widgets, configured carrier controls and
183
+ allowed-model handoff. A handoff opens a replacement session; a carrier transfer
184
+ acknowledgement does not mean a human answered. Hydra has no live transcript
185
+ stream; optional post-call transcription needs a recording and server Deepgram
186
+ credentials. Ultravox retains compatible external TTS and its profile router.
187
+ All five hosted speaking families support optional Supervisor coaching. Native
188
+ models receive guidance through `check_guidance`; Hydra supplies explicitly
189
+ labeled model-reported context rather than transcripts.
147
190
  See the [unified S2S guide](https://labs.supafone.ai/docs/unified-s2s/) and
148
191
  [capability matrix](https://labs.supafone.ai/docs/realtime-agent-factory/).
192
+ See [Shared runtime, Manager and teams](https://labs.supafone.ai/docs/shared-agent-runtime/)
193
+ for `manager`, `agentTeam`, structured stage requirements, `recording` and
194
+ `runtimeRouting`. Both SDKs preserve explicit empty stage-tool and edge arrays.
195
+
196
+ ## Choose speaking keys
197
+
198
+ All five speaking providers use a saved account key when one exists, otherwise
199
+ Supafone's configured platform key. Configure the account before live calls:
200
+
201
+ ```ts
202
+ await client.labs.runtime.configure({
203
+ provider: "openai", mode: "byok", credentials: { apiKey: process.env.OPENAI_API_KEY! },
204
+ });
205
+ const status = await client.labs.runtime.get({ provider: "openai" });
206
+ // Account-wide reset for this provider's future calls; omit credentials.
207
+ await client.labs.runtime.configure({ provider: "openai", mode: "supafone_managed" });
208
+ ```
209
+
210
+ Use `ultravox`, `openai`, `google`, `xai` or `smallest`. Runtime changes require
211
+ account-admin permission and do not change Supervisor's reasoning profile.
212
+ Explicit reset and provider readiness are **0.7.2** features; reset requires the
213
+ matching hosted backend. [All provider keys and readiness](https://labs.supafone.ai/docs/byok-providers/)
214
+ covers environment variables, CLI commands and masked readiness fields.
215
+
149
216
  ## Spawn a hosted Supafone agent
150
217
 
151
218
  Use the same package to create finished Supafone agents from code. This hits the
@@ -229,7 +296,7 @@ import { Supafone } from "supafone-labs";
229
296
  const supafone = new Supafone({
230
297
  apiKey: process.env.SUPAFONE_LABS_API_KEY || process.env.SUPAFONE_API_KEY!,
231
298
  supafoneApiKey: process.env.SUPAFONE_API_KEY!,
232
- // Supafone Supervisor is on by default for live guidance, QA, and scoring.
299
+ // Requests Supervisor by default; its model credentials must also be configured.
233
300
  // Set false only when you intentionally need an unsupervised agent.
234
301
  supervisor: true,
235
302
  // Defaults to https://api.supafone.ai. Override for staging/local tests.
@@ -257,11 +324,7 @@ const inbound = await supafone.labs.agents.createInboundWithNumber({
257
324
  },
258
325
  recording: {
259
326
  enabled: true,
260
- recordAudio: true,
261
- consentRequired: true,
262
- announcement: "This call may be recorded for quality and training.",
263
- retentionDays: 30,
264
- redactPii: true,
327
+ transcribe: true,
265
328
  },
266
329
  transcription: {
267
330
  enabled: true,
@@ -384,37 +447,40 @@ agent; fetches it back; verifies `runtime.telephony.mode` is `supafone_managed`
384
447
  and the runtime is managed (`runtime.managed === true`, no developer Ultravox key
385
448
  required); and prints the returned widget snippet.
386
449
 
387
- ## Hosted voices
450
+ ## Hosted voices: Ultravox + custom TTS
388
451
 
389
- Select a current provider voice from plain-language intent:
452
+ The managed Ultravox option remains part of the shared S2S offering. Its
453
+ conversation runtime and compatible TTS voice are separate choices:
390
454
 
391
455
  ```ts
392
- const matches = await supafone.labs.voices.recommend({
393
- description: "calm Spanish customer-support voice",
394
- language: "es-MX",
395
- configuredOnly: true,
396
- });
456
+ import { UltravoxS2S } from "supafone-labs";
397
457
 
398
- const voice = matches.matches[0].voice;
399
- await supafone.labs.agents.createInbound({
458
+ const catalog = await supafone.labs.voices.list({ provider: "cartesia" });
459
+ console.log(catalog.voices);
460
+
461
+ await new UltravoxS2S(supafone).create({
400
462
  name: "Spanish support",
401
- voice: supafone.labs.voices.selection(voice),
463
+ voice: { provider: "cartesia", voiceId: process.env.CARTESIA_VOICE_ID! },
402
464
  });
403
465
  ```
404
466
 
405
- The hosted catalog refreshes Ultravox, Cartesia, ElevenLabs, and Inworld and
406
- normalizes names, languages, gender, accent, voice type, model limits, and the
407
- Ultravox-compatible language intersection. Full reference:
408
- [Dynamic Voice Catalog and Selection](../gitbook/voice-catalog-and-selection.md).
409
-
410
- Labs Cloud also exposes direct hosted TTS and STT:
411
-
412
- ```ts
413
- const wav = await supafone.tts("You're all set — talk soon!", "supafone-labs-calm-en");
414
- // wav: Uint8Array
415
-
416
- const text = await supafone.stt(audioBytes, { language: "en" });
417
- ```
467
+ Use a real, configured catalog voice. Compatible Ultravox, Cartesia, ElevenLabs,
468
+ and Inworld voices can supply the managed agent's speech. Native OpenAI,
469
+ Gemini, Grok, and Hydra use their own model voices; external TTS is not a
470
+ universal override. Deepgram is preview-only in the current hosted Ultravox
471
+ integration. Custom TTS backends are for developer-owned synthesis unless a
472
+ hosted runtime bridge explicitly supports them.
473
+
474
+ The published SDK 0.7.0 exposes `labs.voices.list`, `listAll`, `capabilities`,
475
+ `recommend`, `preview`, and `selection`. Use `listAll` for automatic catalog
476
+ pagination, then pass `selection(voice)` into managed agent creation. These
477
+ helpers query the hosted voice catalog; they do not enable external TTS on
478
+ native OpenAI, Gemini, Grok, or Hydra sessions.
479
+ See [voice-output choices](https://labs.supafone.ai/docs/voice-output-modes/) and
480
+ [the voice catalog](https://labs.supafone.ai/docs/voice-catalog-and-selection/).
481
+
482
+ Labs Cloud's standalone `tts` and `stt` methods remain separate synthesis and
483
+ transcription tools; using them does not change a saved agent's speaking model.
418
484
 
419
485
  ## Live multilingual transcription
420
486
 
@@ -199,8 +199,74 @@ export interface LabsCallStage {
199
199
  temperature?: number;
200
200
  nextStages?: string[];
201
201
  next_stages?: string[];
202
+ /** Server-enforced exit gates, independent of conversational exit criteria. */
203
+ requirements?: LabsStageRequirements;
204
+ role?: string;
205
+ specialistId?: string;
206
+ specialist_id?: string;
202
207
  metadata?: Record<string, unknown>;
203
208
  }
209
+ export interface LabsStageRequirements {
210
+ requiredFields?: string[];
211
+ required_fields?: string[];
212
+ successfulTools?: string[];
213
+ successful_tools?: string[];
214
+ }
215
+ export interface LabsManagerConfig {
216
+ enabled?: boolean;
217
+ /** Reuse the existing encrypted Supervisor profile, or use managed reasoning. */
218
+ reasoning?: "managed" | "supervisor";
219
+ model?: "supafone-manager" | "supafone-manager-pro";
220
+ maxTasks?: number;
221
+ max_tasks?: number;
222
+ maxParallel?: number;
223
+ max_parallel?: number;
224
+ timeoutSeconds?: number;
225
+ timeout_seconds?: number;
226
+ }
227
+ export interface LabsSpecialist {
228
+ id: string;
229
+ role?: string;
230
+ label?: string;
231
+ description?: string;
232
+ instructions?: string;
233
+ enabled?: boolean;
234
+ stageKeys?: string[];
235
+ stage_keys?: string[];
236
+ tools?: string[];
237
+ }
238
+ export interface LabsAgentTeam {
239
+ enabled?: boolean;
240
+ routingMode?: "auto" | "sequential";
241
+ routing_mode?: "auto" | "sequential";
242
+ fallbackMemberId?: string;
243
+ fallback_member_id?: string;
244
+ members?: LabsSpecialist[];
245
+ routingRules?: Array<{
246
+ intent: string;
247
+ member_id: string;
248
+ priority?: string;
249
+ action?: string;
250
+ }>;
251
+ routing_rules?: Array<{
252
+ intent: string;
253
+ member_id: string;
254
+ priority?: string;
255
+ action?: string;
256
+ }>;
257
+ }
258
+ export interface LabsRuntimeRoutingConfig {
259
+ enabled?: boolean;
260
+ allowedModels?: LabsRealtimeSelection[];
261
+ allowed_models?: LabsRealtimeSelection[];
262
+ /** Server-supported range is 1–3 handoffs per call. */
263
+ maxHandoffs?: number;
264
+ max_handoffs?: number;
265
+ recoverOnDisconnect?: boolean;
266
+ recover_on_disconnect?: boolean;
267
+ allowedLanguages?: string[];
268
+ allowed_languages?: string[];
269
+ }
204
270
  export type LabsStageGeneration = "managed" | "template" | "off" | "oracle";
205
271
  export interface LabsCallPlan {
206
272
  version: "supafone_call_plan_v1" | string;
@@ -646,6 +712,8 @@ export interface LabsEmailConfig {
646
712
  }
647
713
  export interface LabsRecordingConfig {
648
714
  enabled?: boolean;
715
+ /** Request post-call transcription of the recording where configured. */
716
+ transcribe?: boolean;
649
717
  recordAudio?: boolean;
650
718
  record_audio?: boolean;
651
719
  consentRequired?: boolean;
@@ -885,6 +953,17 @@ export interface CreateLabsAgentRequest {
885
953
  presetKey?: string;
886
954
  preset_key?: string;
887
955
  realtime?: LabsRealtimeSelection;
956
+ manager?: boolean | LabsManagerConfig;
957
+ agentTeam?: LabsAgentTeam;
958
+ agent_team?: LabsAgentTeam;
959
+ runtimeRouting?: LabsRuntimeRoutingConfig;
960
+ runtime_routing?: LabsRuntimeRoutingConfig;
961
+ captureFields?: string[];
962
+ capture_fields?: string[];
963
+ timezone?: string;
964
+ businessTimezone?: string;
965
+ timeAwareness?: boolean;
966
+ time_awareness?: boolean;
888
967
  runtimeMode?: LabsRuntimeMode;
889
968
  runtime_mode?: LabsRuntimeMode;
890
969
  /** Defaults to Supafone's hosted planner; model credentials remain server-side. */
@@ -927,7 +1006,7 @@ export interface CreateLabsAgentRequest {
927
1006
  customSip?: LabsCustomSipConfig;
928
1007
  custom_sip?: LabsCustomSipConfig;
929
1008
  sip?: LabsCustomSipConfig;
930
- recording?: LabsRecordingConfig;
1009
+ recording?: boolean | LabsRecordingConfig;
931
1010
  transcription?: LabsTranscriptionConfig;
932
1011
  artifacts?: LabsArtifactsConfig;
933
1012
  compliance?: Record<string, unknown>;
@@ -1125,11 +1204,28 @@ export interface LabsVoicePreview {
1125
1204
  export interface LabsRuntimeResponse {
1126
1205
  account_id: string;
1127
1206
  provider: "ultravox" | string;
1128
- managed: boolean;
1129
- byok_connected: boolean;
1207
+ /** Optional for compatibility with earlier hosted API deployments. */
1208
+ managed?: boolean;
1209
+ byok_connected?: boolean;
1210
+ configured?: boolean;
1211
+ connected?: boolean;
1212
+ source?: "account" | "platform" | "none" | "invalid";
1130
1213
  base_url?: string;
1131
1214
  updated_at?: string;
1132
1215
  }
1216
+ export interface LabsRuntimeConfig {
1217
+ agencyId?: string;
1218
+ agency_id?: string;
1219
+ provider?: string;
1220
+ /** Account-wide: managed removes this provider's saved key for future calls. */
1221
+ mode?: "supafone_managed" | "byok";
1222
+ credentials?: {
1223
+ apiKey?: string;
1224
+ api_key?: string;
1225
+ baseUrl?: string;
1226
+ base_url?: string;
1227
+ };
1228
+ }
1133
1229
  export interface LabsCallListOptions {
1134
1230
  agencyId?: string;
1135
1231
  agency_id?: string;
@@ -2334,18 +2430,10 @@ declare class LabsRuntimeNamespace {
2334
2430
  get(opts?: {
2335
2431
  agencyId?: string;
2336
2432
  agency_id?: string;
2433
+ provider?: string;
2337
2434
  }): Promise<LabsRuntimeResponse>;
2338
- configure(input: {
2339
- agencyId?: string;
2340
- agency_id?: string;
2341
- provider?: "ultravox" | string;
2342
- credentials?: {
2343
- apiKey?: string;
2344
- api_key?: string;
2345
- baseUrl?: string;
2346
- base_url?: string;
2347
- };
2348
- }): Promise<LabsRuntimeResponse>;
2435
+ /** Set an account speaking key, or restore the selected provider's platform default. */
2436
+ configure(input: LabsRuntimeConfig): Promise<LabsRuntimeResponse>;
2349
2437
  }
2350
2438
  declare class LabsCallsNamespace {
2351
2439
  private sm;
package/dist/cjs/index.js CHANGED
@@ -1513,15 +1513,28 @@ class LabsRuntimeNamespace {
1513
1513
  const agencyId = opts.agency_id ?? opts.agencyId;
1514
1514
  if (agencyId)
1515
1515
  q.set("agency_id", agencyId);
1516
+ if (opts.provider)
1517
+ q.set("provider", opts.provider);
1516
1518
  const suffix = q.toString() ? `?${q}` : "";
1517
1519
  return this.sm.requestSupafoneApi("GET", `/api/v1/labs/runtime${suffix}`);
1518
1520
  }
1521
+ /** Set an account speaking key, or restore the selected provider's platform default. */
1519
1522
  configure(input) {
1523
+ if (input.mode !== undefined && !["supafone_managed", "byok"].includes(input.mode)) {
1524
+ throw new Error("runtime mode must be supafone_managed or byok");
1525
+ }
1520
1526
  const credentials = input.credentials ?? {};
1527
+ if (typeof credentials !== "object" || Array.isArray(credentials)) {
1528
+ throw new Error("runtime credentials must be an object");
1529
+ }
1530
+ if (input.mode === "supafone_managed" && Object.values(credentials).some(value => typeof value === "string" ? Boolean(value.trim()) : Boolean(value))) {
1531
+ throw new Error("managed runtime mode cannot include credentials");
1532
+ }
1521
1533
  return this.sm.requestSupafoneApi("PUT", "/api/v1/labs/runtime", compact({
1522
1534
  agency_id: input.agency_id ?? input.agencyId,
1523
1535
  provider: input.provider ?? "ultravox",
1524
- credentials: compact({
1536
+ mode: input.mode,
1537
+ credentials: input.mode === "supafone_managed" ? undefined : compact({
1525
1538
  api_key: credentials.api_key ?? credentials.apiKey,
1526
1539
  base_url: credentials.base_url ?? credentials.baseUrl,
1527
1540
  }),
@@ -2015,6 +2028,40 @@ function outboundCallModeRequiredCapabilities(payload) {
2015
2028
  required.push("observability");
2016
2029
  return required;
2017
2030
  }
2031
+ function managerPayload(input) {
2032
+ if (typeof input !== "object" || input === null)
2033
+ return input;
2034
+ return compact({
2035
+ enabled: input.enabled, reasoning: input.reasoning, model: input.model,
2036
+ max_tasks: input.max_tasks ?? input.maxTasks,
2037
+ max_parallel: input.max_parallel ?? input.maxParallel,
2038
+ timeout_seconds: input.timeout_seconds ?? input.timeoutSeconds,
2039
+ });
2040
+ }
2041
+ function agentTeamPayload(input) {
2042
+ if (!input)
2043
+ return undefined;
2044
+ return compact({
2045
+ enabled: input.enabled, routing_mode: input.routing_mode ?? input.routingMode,
2046
+ fallback_member_id: input.fallback_member_id ?? input.fallbackMemberId,
2047
+ routing_rules: input.routing_rules ?? input.routingRules,
2048
+ members: input.members?.map((member) => compact({
2049
+ id: member.id, role: member.role, label: member.label, description: member.description,
2050
+ instructions: member.instructions, enabled: member.enabled, tools: member.tools,
2051
+ stage_keys: member.stage_keys ?? member.stageKeys,
2052
+ })),
2053
+ });
2054
+ }
2055
+ function runtimeRoutingPayload(input) {
2056
+ if (!input)
2057
+ return undefined;
2058
+ return compact({
2059
+ enabled: input.enabled, allowed_models: input.allowed_models ?? input.allowedModels,
2060
+ max_handoffs: input.max_handoffs ?? input.maxHandoffs,
2061
+ recover_on_disconnect: input.recover_on_disconnect ?? input.recoverOnDisconnect,
2062
+ allowed_languages: input.allowed_languages ?? input.allowedLanguages,
2063
+ });
2064
+ }
2018
2065
  function labsAgentMetadataPayload(input) {
2019
2066
  const metadata = { ...(input.metadata ?? {}) };
2020
2067
  const mode = input.outboundCallMode ?? input.outbound_call_mode;
@@ -2056,6 +2103,12 @@ function labsAgentPayload(input) {
2056
2103
  preset_key: input.preset_key ?? input.presetKey,
2057
2104
  runtime_mode: input.runtime_mode ?? input.runtimeMode,
2058
2105
  realtime: input.realtime,
2106
+ manager: managerPayload(input.manager),
2107
+ agent_team: agentTeamPayload(input.agent_team ?? input.agentTeam),
2108
+ runtime_routing: runtimeRoutingPayload(input.runtime_routing ?? input.runtimeRouting),
2109
+ capture_fields: input.capture_fields ?? input.captureFields,
2110
+ timezone: input.timezone ?? input.businessTimezone,
2111
+ time_awareness: input.time_awareness ?? input.timeAwareness,
2059
2112
  call_stages: callStagesPayload(input),
2060
2113
  stage_generation: input.stage_generation ?? input.stageGeneration,
2061
2114
  stage_count: input.stage_count ?? input.stageCount,
@@ -2073,7 +2126,7 @@ function labsAgentPayload(input) {
2073
2126
  byok: input.byok ? byokPayload(input.byok) : undefined,
2074
2127
  telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
2075
2128
  custom_sip: customSipPayload(input.custom_sip ?? input.customSip ?? input.sip),
2076
- recording: input.recording ? recordingPayload(input.recording) : undefined,
2129
+ recording: input.recording !== undefined ? recordingPayload(input.recording) : undefined,
2077
2130
  transcription: input.transcription ? transcriptionPayload(input.transcription) : undefined,
2078
2131
  artifacts: input.artifacts ? artifactsPayload(input.artifacts) : undefined,
2079
2132
  compliance: input.compliance,
@@ -2217,9 +2270,6 @@ function callStagesPayload(input) {
2217
2270
  return explicit;
2218
2271
  if (explicit === "oracle")
2219
2272
  return "managed";
2220
- // The S2S backend owns its validated intake → booking → confirmation flow.
2221
- if (input.realtime)
2222
- return undefined;
2223
2273
  // Omitted means the private Supafone API generates and compiles the plan.
2224
2274
  return undefined;
2225
2275
  }
@@ -2236,8 +2286,7 @@ function stagePlanPayload(input) {
2236
2286
  system_prompt: input.system_prompt ?? input.systemPrompt,
2237
2287
  tools: input.tools ? toolsPayload(input.tools) : undefined,
2238
2288
  call_stages: Array.isArray(input.call_stages ?? input.callStages ?? input.stages)
2239
- ? (input.call_stages ?? input.callStages ?? input.stages)
2240
- : undefined,
2289
+ ? callStagesPayload(input) : undefined,
2241
2290
  stage_generation: input.stage_generation ?? input.stageGeneration,
2242
2291
  stage_count: input.stage_count ?? input.stageCount,
2243
2292
  stage_detail: input.stage_detail ?? input.stageDetail,
@@ -2253,6 +2302,12 @@ function callStagePayload(input) {
2253
2302
  tools: input.tools,
2254
2303
  temperature: input.temperature,
2255
2304
  next_stages: input.next_stages ?? input.nextStages,
2305
+ requirements: input.requirements ? compact({
2306
+ required_fields: input.requirements.required_fields ?? input.requirements.requiredFields,
2307
+ successful_tools: input.requirements.successful_tools ?? input.requirements.successfulTools,
2308
+ }) : undefined,
2309
+ role: input.role,
2310
+ specialist_id: input.specialist_id ?? input.specialistId,
2256
2311
  metadata: input.metadata,
2257
2312
  });
2258
2313
  }
@@ -2471,8 +2526,11 @@ function emailPayload(input) {
2471
2526
  });
2472
2527
  }
2473
2528
  function recordingPayload(input) {
2529
+ if (typeof input === "boolean")
2530
+ return input;
2474
2531
  return compact({
2475
2532
  enabled: input.enabled,
2533
+ transcribe: input.transcribe,
2476
2534
  record_audio: input.record_audio ?? input.recordAudio,
2477
2535
  consent_required: input.consent_required ?? input.consentRequired,
2478
2536
  announcement: input.announcement,