supafone-labs 0.4.9 → 0.4.12

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
@@ -432,6 +432,12 @@ await sf.campaigns.launch(campaign.id); // real calls
432
432
 
433
433
  const live = await sf.campaigns.live(campaign.id); // in-flight calls + listen links
434
434
  await sf.callFromAgent({ agentId: agents[0].id, toNumber: "+15551234567" }); // ring a phone right now
435
+
436
+ // Browser voice: creates WebRTC credentials without dialing a phone number.
437
+ const webCall = await sf.startWebRtcCall({ agentId: agents[0].id });
438
+ if (!webCall.browser_session.available) throw new Error("Browser voice unavailable");
439
+ // Pass webCall.browser_session.join_url to the adapter named by
440
+ // webCall.browser_session.transport (currently the Ultravox browser client).
435
441
  ```
436
442
 
437
443
  `campaigns.live()` returns a portal link (`app.supafone.ai/app/developer`) and
@@ -462,6 +468,7 @@ and `const { Supafone } = require("supafone-labs")` both work, with full types.
462
468
  | `labs.agents.create/createInbound/createOutbound/list/get` | `/api/v1/labs/agents*` on the Supafone API |
463
469
  | `labs.agents.createInboundWithNumber/createOutboundWithNumber` | Agent creation plus Supafone-managed number buy/assign |
464
470
  | `labs.phoneNumbers.search/buy/assign/list/buyAndAssign` | `/api/v1/labs/phone-numbers*` |
471
+ | `labs.billing.checkout/status/portal` | Stripe-hosted Checkout, payment polling, and Customer Portal |
465
472
  | `labs.telephony.get/configure/useSupafoneManaged` | `/api/v1/labs/telephony` |
466
473
  | `labs.presets.list()` · `labs.tools.list()` · `labs.voices.list()` | Supafone hosted-agent discovery |
467
474
  | `whisper(transcript, opts?)` | convenience over the oracle |
@@ -477,6 +484,12 @@ and `const { Supafone } = require("supafone-labs")` both work, with full types.
477
484
 
478
485
  Get a key (5 free minutes, no card): <https://labs.supafone.ai/get-key.html>
479
486
 
487
+ For `dedicated` or `premium`, `labs.phoneNumbers.buy()` first returns a public
488
+ `checkout_url`. Open it, poll `labs.billing.status()`, then call `buy()` again
489
+ with `billingCheckoutSessionId`. The server verifies and consumes the paid
490
+ entitlement before carrier provisioning; Stripe secrets and card data never
491
+ enter the SDK.
492
+
480
493
  The runtime, all provider adapters, and the offline (bring-your-own-keys) mode
481
494
  are open source and MIT-licensed — the Python package `pip install supafone-labs`
482
495
  is the full harness. This client is the thin cloud SDK.
@@ -131,13 +131,29 @@ export interface LabsCallStage {
131
131
  key?: string;
132
132
  id?: string;
133
133
  name: string;
134
+ /** Plain-English job description used by the hosted call-plan generator. */
135
+ description?: string;
134
136
  goal?: string;
135
137
  instructions?: string;
136
138
  exitCriteria?: string[];
137
139
  exit_criteria?: string[];
138
140
  tools?: string[];
141
+ temperature?: number;
142
+ nextStages?: string[];
143
+ next_stages?: string[];
139
144
  metadata?: Record<string, unknown>;
140
145
  }
146
+ export type LabsStageGeneration = "oracle" | "template" | "off";
147
+ export interface LabsCallPlan {
148
+ version: "supafone_call_plan_v1" | string;
149
+ summary: string;
150
+ base_system_prompt: string;
151
+ call_stages: LabsCallStage[];
152
+ generated_by: "supafone_hosted_haiku" | "deterministic_template" | "deterministic_fallback" | "developer" | string;
153
+ model: string;
154
+ fallback: boolean;
155
+ warnings?: string[];
156
+ }
141
157
  export interface LabsVoiceSelection {
142
158
  provider?: string;
143
159
  voiceId?: string;
@@ -454,6 +470,8 @@ export interface CreateLabsAgentRequest {
454
470
  agentStyle?: LabsAgentStyle;
455
471
  agent_style?: LabsAgentStyle;
456
472
  name: string;
473
+ /** Plain-language job description used by the hosted call-plan generator. */
474
+ description?: string;
457
475
  assistantName?: string;
458
476
  assistant_name?: string;
459
477
  businessName?: string;
@@ -473,12 +491,18 @@ export interface CreateLabsAgentRequest {
473
491
  preset_key?: string;
474
492
  runtimeMode?: LabsRuntimeMode;
475
493
  runtime_mode?: LabsRuntimeMode;
476
- /** Default true. When true, the SDK creates sensible call stages from prompt metadata. */
477
- callStages?: boolean | LabsCallStage[];
478
- call_stages?: boolean | LabsCallStage[];
494
+ /** Defaults to Supafone's hosted planner; model credentials remain server-side. */
495
+ callStages?: boolean | LabsStageGeneration | LabsCallStage[];
496
+ call_stages?: boolean | LabsStageGeneration | LabsCallStage[];
479
497
  stages?: LabsCallStage[];
480
498
  autoCallStages?: boolean;
481
499
  auto_call_stages?: boolean;
500
+ stageGeneration?: LabsStageGeneration;
501
+ stage_generation?: LabsStageGeneration;
502
+ stageCount?: number;
503
+ stage_count?: number;
504
+ stageDetail?: "compact" | "standard" | "detailed";
505
+ stage_detail?: "compact" | "standard" | "detailed";
482
506
  goal?: string;
483
507
  greeting?: string;
484
508
  systemPrompt?: string;
@@ -544,6 +568,10 @@ export interface LabsToolListResponse {
544
568
  }
545
569
  export interface LabsVoiceListOptions {
546
570
  provider?: string;
571
+ search?: string;
572
+ language?: string;
573
+ cursor?: number;
574
+ limit?: number;
547
575
  }
548
576
  export interface LabsVoiceListResponse {
549
577
  voices: Array<Record<string, unknown>>;
@@ -552,6 +580,14 @@ export interface LabsVoiceListResponse {
552
580
  provider_accounts?: Record<string, unknown>;
553
581
  errors?: Record<string, string>;
554
582
  }
583
+ export interface LabsRuntimeResponse {
584
+ account_id: string;
585
+ provider: "ultravox" | string;
586
+ managed: boolean;
587
+ byok_connected: boolean;
588
+ base_url?: string;
589
+ updated_at?: string;
590
+ }
555
591
  export interface LabsCallListOptions {
556
592
  agencyId?: string;
557
593
  agentKey?: string;
@@ -669,6 +705,8 @@ export interface LabsPhoneNumberProvisionRequest {
669
705
  numberPool?: string;
670
706
  number_pool?: string;
671
707
  premium?: boolean;
708
+ billingCheckoutSessionId?: string;
709
+ billing_checkout_session_id?: string;
672
710
  style?: LabsAgentStyle;
673
711
  agentStyle?: LabsAgentStyle;
674
712
  agent_style?: LabsAgentStyle;
@@ -721,7 +759,7 @@ export interface CreateLabsAgentWithNumberRequest extends CreateLabsAgentRequest
721
759
  number?: LabsPhoneNumberBuyAndAssignRequest;
722
760
  }
723
761
  export interface CreateLabsAgentWithNumberResponse extends CreateLabsAgentResponse {
724
- number?: LabsPhoneNumberProvisionResponse;
762
+ number?: LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse;
725
763
  }
726
764
  export interface LabsAgentResponse {
727
765
  id?: string;
@@ -745,6 +783,7 @@ export interface CreateLabsAgentResponse {
745
783
  snippet?: string;
746
784
  [extra: string]: unknown;
747
785
  };
786
+ call_plan?: LabsCallPlan;
748
787
  [extra: string]: unknown;
749
788
  }
750
789
  export interface ListLabsAgentsResponse {
@@ -1029,6 +1068,13 @@ export declare class SupafoneLabs {
1029
1068
  readonly optimizer: OptimizerNamespace;
1030
1069
  readonly campaigns: CampaignsNamespace;
1031
1070
  constructor(opts: SupafoneLabsOptions);
1071
+ /** Build a complete hosted plan with the same Supafone key used to create agents. */
1072
+ generateCallStages(input: CreateLabsAgentRequest & {
1073
+ description?: string;
1074
+ }): Promise<LabsCallPlan>;
1075
+ generate_call_stages(input: CreateLabsAgentRequest & {
1076
+ description?: string;
1077
+ }): Promise<LabsCallPlan>;
1032
1078
  /** True once login() (or a passed sessionToken) is in effect. */
1033
1079
  get isLoggedIn(): boolean;
1034
1080
  /**
@@ -1069,6 +1115,18 @@ export declare class SupafoneLabs {
1069
1115
  agentId: string;
1070
1116
  toNumber: string;
1071
1117
  }): Promise<PlaceCallResult>;
1118
+ /**
1119
+ * Start a browser WebRTC session for an owned voice agent. No phone number or
1120
+ * PSTN leg is created. Join browser_session.join_url with the provider's
1121
+ * browser client (currently Ultravox).
1122
+ */
1123
+ startWebRtcCall(opts: {
1124
+ agentId: string;
1125
+ }): Promise<WebRtcCallResult>;
1126
+ /** Alias for startWebRtcCall(), named for browser-oriented applications. */
1127
+ startBrowserCall(opts: {
1128
+ agentId: string;
1129
+ }): Promise<WebRtcCallResult>;
1072
1130
  /** The account's voice agents — pick an agent id for campaigns/calls. */
1073
1131
  listVoiceAgents(): Promise<{
1074
1132
  agents: Record<string, unknown>[];
@@ -1190,6 +1248,33 @@ export interface PlaceCallResult {
1190
1248
  call_sid?: string | null;
1191
1249
  provider?: string;
1192
1250
  }
1251
+ export interface BrowserVoiceSession {
1252
+ version: string;
1253
+ available: boolean;
1254
+ provider: string;
1255
+ transport: string;
1256
+ join_url: string | null;
1257
+ features: {
1258
+ microphone: boolean;
1259
+ speaker_audio: boolean;
1260
+ live_transcripts: boolean;
1261
+ };
1262
+ }
1263
+ export interface WebRtcCallResult {
1264
+ success: boolean;
1265
+ simulated?: boolean;
1266
+ call_id?: string | null;
1267
+ call_record_id?: string | null;
1268
+ join_url?: string | null;
1269
+ max_duration_seconds?: number;
1270
+ free_calls_remaining?: number;
1271
+ browser_session: BrowserVoiceSession;
1272
+ test_capabilities?: {
1273
+ browser_voice: boolean;
1274
+ universal_phone_grade: boolean;
1275
+ phone_grade_url: string;
1276
+ };
1277
+ }
1193
1278
  export interface CampaignRecipientInput {
1194
1279
  name?: string;
1195
1280
  phone?: string;
@@ -1420,9 +1505,11 @@ declare class CampaignsNamespace {
1420
1505
  declare class LabsNamespace {
1421
1506
  private sm;
1422
1507
  readonly agents: LabsAgentsNamespace;
1508
+ readonly billing: LabsBillingNamespace;
1423
1509
  readonly presets: LabsPresetsNamespace;
1424
1510
  readonly tools: LabsToolsNamespace;
1425
1511
  readonly voices: LabsVoicesNamespace;
1512
+ readonly runtime: LabsRuntimeNamespace;
1426
1513
  readonly phoneNumbers: LabsPhoneNumbersNamespace;
1427
1514
  readonly telephony: LabsTelephonyNamespace;
1428
1515
  readonly calls: LabsCallsNamespace;
@@ -1432,11 +1519,57 @@ declare class LabsNamespace {
1432
1519
  /** Discover the Supafone convenience layer over Ultravox. */
1433
1520
  capabilities(): Promise<LabsCapabilitiesResponse>;
1434
1521
  }
1522
+ export type LabsBillingCheckoutKind = "plan" | "credits" | "number_addon";
1523
+ export interface LabsBillingCheckoutInput {
1524
+ kind?: LabsBillingCheckoutKind;
1525
+ planKey?: "developer" | "growth" | "scale" | string;
1526
+ plan_key?: string;
1527
+ numberStrategy?: "dedicated" | "premium" | string;
1528
+ number_strategy?: string;
1529
+ phoneNumber?: string;
1530
+ phone_number?: string;
1531
+ quantity?: number;
1532
+ successUrl?: string;
1533
+ success_url?: string;
1534
+ cancelUrl?: string;
1535
+ cancel_url?: string;
1536
+ }
1537
+ export interface LabsBillingCheckoutResponse {
1538
+ status: "requires_payment" | "pending" | "paid" | string;
1539
+ checkout_session_id: string;
1540
+ checkout_url?: string;
1541
+ kind: LabsBillingCheckoutKind | string;
1542
+ plan_key?: string | null;
1543
+ number_strategy?: string | null;
1544
+ phone_number?: string | null;
1545
+ stripe_subscription_id?: string | null;
1546
+ ready_to_provision?: boolean;
1547
+ next_step?: string;
1548
+ }
1549
+ declare class LabsBillingNamespace {
1550
+ private sm;
1551
+ constructor(sm: SupafoneLabs);
1552
+ /** Start hosted Stripe Checkout. MCP callers should render checkout_url as a link. */
1553
+ checkout(input?: LabsBillingCheckoutInput): Promise<LabsBillingCheckoutResponse>;
1554
+ status(checkoutSessionId: string): Promise<LabsBillingCheckoutResponse>;
1555
+ portal(): Promise<{
1556
+ url: string;
1557
+ }>;
1558
+ createCheckout(input?: LabsBillingCheckoutInput): Promise<LabsBillingCheckoutResponse>;
1559
+ getCheckout(checkoutSessionId: string): Promise<LabsBillingCheckoutResponse>;
1560
+ }
1435
1561
  declare class LabsAgentsNamespace {
1436
1562
  private sm;
1437
1563
  constructor(sm: SupafoneLabs);
1438
1564
  /** Spawn a durable hosted Supafone agent backed by Ultravox and Supafone-managed providers. */
1439
1565
  create(input: CreateLabsAgentRequest): Promise<CreateLabsAgentResponse>;
1566
+ /** Preview the exact validated call-plan contract the Supafone runtime executes. */
1567
+ plan(input: CreateLabsAgentRequest & {
1568
+ description?: string;
1569
+ }): Promise<LabsCallPlan>;
1570
+ generateCallStages(input: CreateLabsAgentRequest & {
1571
+ description?: string;
1572
+ }): Promise<LabsCallPlan>;
1440
1573
  /** Default the agent onto the client's Voice Watcher setting (live supervision
1441
1574
  * + QA + scoring) unless the caller set it explicitly; mirror into
1442
1575
  * labs.voice_watcher when a labs block exists. Never overwrites a caller value. */
@@ -1480,6 +1613,25 @@ declare class LabsVoicesNamespace {
1480
1613
  /** Supafone-managed Cartesia voices. Other TTS engines remain explicit BYOK choices. */
1481
1614
  list(opts?: LabsVoiceListOptions): Promise<LabsVoiceListResponse>;
1482
1615
  }
1616
+ declare class LabsRuntimeNamespace {
1617
+ private sm;
1618
+ constructor(sm: SupafoneLabs);
1619
+ get(opts?: {
1620
+ agencyId?: string;
1621
+ agency_id?: string;
1622
+ }): Promise<LabsRuntimeResponse>;
1623
+ configure(input: {
1624
+ agencyId?: string;
1625
+ agency_id?: string;
1626
+ provider?: "ultravox" | string;
1627
+ credentials?: {
1628
+ apiKey?: string;
1629
+ api_key?: string;
1630
+ baseUrl?: string;
1631
+ base_url?: string;
1632
+ };
1633
+ }): Promise<LabsRuntimeResponse>;
1634
+ }
1483
1635
  declare class LabsCallsNamespace {
1484
1636
  private sm;
1485
1637
  constructor(sm: SupafoneLabs);
@@ -1515,8 +1667,11 @@ declare class LabsPhoneNumbersNamespace {
1515
1667
  list(opts?: LabsPhoneNumberListOptions): Promise<LabsPhoneNumberListResponse>;
1516
1668
  /** Search Supafone-managed inventory. This uses Supafone's master telephony account. */
1517
1669
  search(opts?: LabsPhoneNumberSearchOptions): Promise<LabsPhoneNumberSearchResponse>;
1518
- /** Buy a Supafone-managed number. Developers do not need a Twilio account. */
1519
- buy(input: LabsPhoneNumberProvisionRequest): Promise<LabsPhoneNumberProvisionResponse>;
1670
+ /**
1671
+ * Buy a managed number. Paid strategies return a hosted Checkout link first;
1672
+ * call again with billingCheckoutSessionId after Checkout reports paid.
1673
+ */
1674
+ buy(input: LabsPhoneNumberProvisionRequest): Promise<LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse>;
1520
1675
  /** Attach an existing Supafone number to an inbound or outbound agent. */
1521
1676
  assign(numberId: string, input?: LabsPhoneNumberAssignRequest): Promise<LabsPhoneNumberAssignResponse>;
1522
1677
  /** Unassign a number from an agent but keep it reserved on the account. */
@@ -1531,7 +1686,7 @@ declare class LabsPhoneNumbersNamespace {
1531
1686
  * Search if needed, buy the first matching Supafone-managed number, and assign
1532
1687
  * it to the supplied agent. This is the zero-Twilio-account happy path.
1533
1688
  */
1534
- buyAndAssign(input: LabsPhoneNumberBuyAndAssignRequest): Promise<LabsPhoneNumberProvisionResponse>;
1689
+ buyAndAssign(input: LabsPhoneNumberBuyAndAssignRequest): Promise<LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse>;
1535
1690
  }
1536
1691
  declare class LabsTelephonyNamespace {
1537
1692
  private sm;
package/dist/cjs/index.js CHANGED
@@ -94,6 +94,13 @@ class SupafoneLabs {
94
94
  this.optimizer = new OptimizerNamespace(this);
95
95
  this.campaigns = new CampaignsNamespace(this);
96
96
  }
97
+ /** Build a complete hosted plan with the same Supafone key used to create agents. */
98
+ generateCallStages(input) {
99
+ return this.labs.agents.plan(input);
100
+ }
101
+ generate_call_stages(input) {
102
+ return this.generateCallStages(input);
103
+ }
97
104
  /** True once login() (or a passed sessionToken) is in effect. */
98
105
  get isLoggedIn() {
99
106
  return !!this.sessionToken;
@@ -268,6 +275,21 @@ class SupafoneLabs {
268
275
  async placeCall(opts) {
269
276
  return this.callFromAgent(opts);
270
277
  }
278
+ /**
279
+ * Start a browser WebRTC session for an owned voice agent. No phone number or
280
+ * PSTN leg is created. Join browser_session.join_url with the provider's
281
+ * browser client (currently Ultravox).
282
+ */
283
+ async startWebRtcCall(opts) {
284
+ if (!opts?.agentId) {
285
+ throw new SupafoneLabsError("agentId is required (see listVoiceAgents())");
286
+ }
287
+ return this.requestAccountApi("POST", `/api/v1/agents/${encodeURIComponent(opts.agentId)}/test-call`);
288
+ }
289
+ /** Alias for startWebRtcCall(), named for browser-oriented applications. */
290
+ async startBrowserCall(opts) {
291
+ return this.startWebRtcCall(opts);
292
+ }
271
293
  /** The account's voice agents — pick an agent id for campaigns/calls. */
272
294
  async listVoiceAgents() {
273
295
  return this.requestAccountApi("GET", "/api/v1/agents");
@@ -758,9 +780,11 @@ class CampaignsNamespace {
758
780
  class LabsNamespace {
759
781
  sm;
760
782
  agents;
783
+ billing;
761
784
  presets;
762
785
  tools;
763
786
  voices;
787
+ runtime;
764
788
  phoneNumbers;
765
789
  telephony;
766
790
  calls;
@@ -769,9 +793,11 @@ class LabsNamespace {
769
793
  constructor(sm) {
770
794
  this.sm = sm;
771
795
  this.agents = new LabsAgentsNamespace(sm);
796
+ this.billing = new LabsBillingNamespace(sm);
772
797
  this.presets = new LabsPresetsNamespace(sm);
773
798
  this.tools = new LabsToolsNamespace(sm);
774
799
  this.voices = new LabsVoicesNamespace(sm);
800
+ this.runtime = new LabsRuntimeNamespace(sm);
775
801
  this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
776
802
  this.telephony = new LabsTelephonyNamespace(sm);
777
803
  this.calls = new LabsCallsNamespace(sm);
@@ -783,6 +809,38 @@ class LabsNamespace {
783
809
  return this.sm.requestSupafoneApi("GET", "/api/v1/labs/capabilities");
784
810
  }
785
811
  }
812
+ class LabsBillingNamespace {
813
+ sm;
814
+ constructor(sm) {
815
+ this.sm = sm;
816
+ }
817
+ /** Start hosted Stripe Checkout. MCP callers should render checkout_url as a link. */
818
+ checkout(input = {}) {
819
+ return this.sm.request("POST", "/v1/billing/checkout", compact({
820
+ kind: input.kind ?? "plan",
821
+ plan_key: input.plan_key ?? input.planKey,
822
+ number_strategy: input.number_strategy ?? input.numberStrategy,
823
+ phone_number: input.phone_number ?? input.phoneNumber,
824
+ quantity: input.quantity,
825
+ success_url: input.success_url ?? input.successUrl,
826
+ cancel_url: input.cancel_url ?? input.cancelUrl,
827
+ }));
828
+ }
829
+ status(checkoutSessionId) {
830
+ if (!checkoutSessionId?.trim())
831
+ throw new SupafoneLabsError("checkoutSessionId is required");
832
+ return this.sm.request("GET", `/v1/billing/checkout/${encodeURIComponent(checkoutSessionId)}`);
833
+ }
834
+ portal() {
835
+ return this.sm.request("POST", "/v1/billing/portal", {});
836
+ }
837
+ createCheckout(input = {}) {
838
+ return this.checkout(input);
839
+ }
840
+ getCheckout(checkoutSessionId) {
841
+ return this.status(checkoutSessionId);
842
+ }
843
+ }
786
844
  class LabsAgentsNamespace {
787
845
  sm;
788
846
  constructor(sm) {
@@ -792,6 +850,13 @@ class LabsAgentsNamespace {
792
850
  create(input) {
793
851
  return this.sm.requestSupafoneApi("POST", "/api/v1/labs/agents", labsAgentPayload(this.withVoiceWatcher(input)));
794
852
  }
853
+ /** Preview the exact validated call-plan contract the Supafone runtime executes. */
854
+ plan(input) {
855
+ return this.sm.requestSupafoneApi("POST", "/api/v1/labs/agent-plans", stagePlanPayload(input));
856
+ }
857
+ generateCallStages(input) {
858
+ return this.plan(input);
859
+ }
795
860
  /** Default the agent onto the client's Voice Watcher setting (live supervision
796
861
  * + QA + scoring) unless the caller set it explicitly; mirror into
797
862
  * labs.voice_watcher when a labs block exists. Never overwrites a caller value. */
@@ -929,10 +994,43 @@ class LabsVoicesNamespace {
929
994
  const q = new URLSearchParams();
930
995
  if (opts.provider)
931
996
  q.set("provider", opts.provider);
997
+ if (opts.search)
998
+ q.set("search", opts.search);
999
+ if (opts.language)
1000
+ q.set("language", opts.language);
1001
+ if (opts.cursor !== undefined)
1002
+ q.set("cursor", String(opts.cursor));
1003
+ if (opts.limit !== undefined)
1004
+ q.set("limit", String(opts.limit));
932
1005
  const suffix = q.toString() ? `?${q}` : "";
933
1006
  return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
934
1007
  }
935
1008
  }
1009
+ class LabsRuntimeNamespace {
1010
+ sm;
1011
+ constructor(sm) {
1012
+ this.sm = sm;
1013
+ }
1014
+ get(opts = {}) {
1015
+ const q = new URLSearchParams();
1016
+ const agencyId = opts.agency_id ?? opts.agencyId;
1017
+ if (agencyId)
1018
+ q.set("agency_id", agencyId);
1019
+ const suffix = q.toString() ? `?${q}` : "";
1020
+ return this.sm.requestSupafoneApi("GET", `/api/v1/labs/runtime${suffix}`);
1021
+ }
1022
+ configure(input) {
1023
+ const credentials = input.credentials ?? {};
1024
+ return this.sm.requestSupafoneApi("PUT", "/api/v1/labs/runtime", compact({
1025
+ agency_id: input.agency_id ?? input.agencyId,
1026
+ provider: input.provider ?? "ultravox",
1027
+ credentials: compact({
1028
+ api_key: credentials.api_key ?? credentials.apiKey,
1029
+ base_url: credentials.base_url ?? credentials.baseUrl,
1030
+ }),
1031
+ }));
1032
+ }
1033
+ }
936
1034
  class LabsCallsNamespace {
937
1035
  sm;
938
1036
  constructor(sm) {
@@ -1021,10 +1119,27 @@ class LabsPhoneNumbersNamespace {
1021
1119
  search(opts = {}) {
1022
1120
  return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers/search", phoneNumberSearchPayload(opts));
1023
1121
  }
1024
- /** Buy a Supafone-managed number. Developers do not need a Twilio account. */
1122
+ /**
1123
+ * Buy a managed number. Paid strategies return a hosted Checkout link first;
1124
+ * call again with billingCheckoutSessionId after Checkout reports paid.
1125
+ */
1025
1126
  buy(input) {
1127
+ const strategy = input.number_strategy ?? input.numberStrategy ?? (input.premium ? "premium" : "default_pool");
1128
+ const checkoutSessionId = input.billing_checkout_session_id ?? input.billingCheckoutSessionId;
1129
+ if ((strategy === "dedicated" || strategy === "premium") && !checkoutSessionId) {
1130
+ const phoneNumber = input.phone_number ?? input.phoneNumber;
1131
+ if (!phoneNumber) {
1132
+ return Promise.reject(new SupafoneLabsError("phoneNumber is required before starting number Checkout"));
1133
+ }
1134
+ return this.sm.labs.billing.checkout({
1135
+ kind: "number_addon",
1136
+ numberStrategy: strategy,
1137
+ phoneNumber,
1138
+ });
1139
+ }
1026
1140
  return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers", phoneNumberProvisionPayload({
1027
1141
  ...input,
1142
+ numberStrategy: strategy,
1028
1143
  telephony: input.telephony ?? { mode: "supafone_managed", provider: "supafone" },
1029
1144
  }));
1030
1145
  }
@@ -1247,6 +1362,7 @@ function labsAgentPayload(input) {
1247
1362
  agent_type: input.agent_type ?? input.agentType,
1248
1363
  style: input.agent_style ?? input.agentStyle ?? input.style,
1249
1364
  name: input.name,
1365
+ description: input.description,
1250
1366
  assistant_name: input.assistant_name ?? input.assistantName,
1251
1367
  business_name: input.business_name ?? input.businessName,
1252
1368
  industry: input.industry,
@@ -1259,6 +1375,9 @@ function labsAgentPayload(input) {
1259
1375
  preset_key: input.preset_key ?? input.presetKey,
1260
1376
  runtime_mode: input.runtime_mode ?? input.runtimeMode,
1261
1377
  call_stages: callStagesPayload(input),
1378
+ stage_generation: input.stage_generation ?? input.stageGeneration,
1379
+ stage_count: input.stage_count ?? input.stageCount,
1380
+ stage_detail: input.stage_detail ?? input.stageDetail,
1262
1381
  goal: input.goal,
1263
1382
  greeting: input.greeting,
1264
1383
  system_prompt: input.system_prompt ?? input.systemPrompt,
@@ -1360,6 +1479,7 @@ function phoneNumberProvisionPayload(input) {
1360
1479
  number_strategy: input.number_strategy ?? input.numberStrategy,
1361
1480
  number_pool: input.number_pool ?? input.numberPool,
1362
1481
  premium: input.premium,
1482
+ billing_checkout_session_id: input.billing_checkout_session_id ?? input.billingCheckoutSessionId,
1363
1483
  style: input.agent_style ?? input.agentStyle ?? input.style,
1364
1484
  direction: input.direction,
1365
1485
  telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
@@ -1397,8 +1517,31 @@ function callStagesPayload(input) {
1397
1517
  if (Array.isArray(explicit))
1398
1518
  return explicit.map(callStagePayload);
1399
1519
  if (explicit === false || auto === false)
1400
- return undefined;
1401
- return generateCallStages(input).map(callStagePayload);
1520
+ return false;
1521
+ if (explicit === "oracle" || explicit === "template" || explicit === "off")
1522
+ return explicit;
1523
+ // Omitted means the private Supafone API generates and compiles the plan.
1524
+ return undefined;
1525
+ }
1526
+ function stagePlanPayload(input) {
1527
+ return compact({
1528
+ agency_id: input.agency_id ?? input.agencyId,
1529
+ description: input.description,
1530
+ name: input.name,
1531
+ assistant_name: input.assistant_name ?? input.assistantName,
1532
+ business_name: input.business_name ?? input.businessName,
1533
+ industry: input.industry,
1534
+ direction: input.direction ?? input.agent_style ?? input.agentStyle ?? input.style,
1535
+ goal: input.goal,
1536
+ system_prompt: input.system_prompt ?? input.systemPrompt,
1537
+ tools: input.tools ? toolsPayload(input.tools) : undefined,
1538
+ call_stages: Array.isArray(input.call_stages ?? input.callStages ?? input.stages)
1539
+ ? (input.call_stages ?? input.callStages ?? input.stages)
1540
+ : undefined,
1541
+ stage_generation: input.stage_generation ?? input.stageGeneration,
1542
+ stage_count: input.stage_count ?? input.stageCount,
1543
+ stage_detail: input.stage_detail ?? input.stageDetail,
1544
+ });
1402
1545
  }
1403
1546
  function callStagePayload(input) {
1404
1547
  return compact({
@@ -1408,10 +1551,14 @@ function callStagePayload(input) {
1408
1551
  instructions: input.instructions,
1409
1552
  exit_criteria: input.exit_criteria ?? input.exitCriteria,
1410
1553
  tools: input.tools,
1554
+ temperature: input.temperature,
1555
+ next_stages: input.next_stages ?? input.nextStages,
1411
1556
  metadata: input.metadata,
1412
1557
  });
1413
1558
  }
1414
1559
  function generateCallStages(input) {
1560
+ // Offline compatibility template. New code should use
1561
+ // client.generateCallStages(...) for the hosted, executable plan.
1415
1562
  const direction = String(input.direction ?? input.agent_style ?? input.agentStyle ?? input.style ?? "inbound").toLowerCase();
1416
1563
  const haystack = [
1417
1564
  input.name,