supafone-labs 0.4.10 → 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
@@ -468,6 +468,7 @@ and `const { Supafone } = require("supafone-labs")` both work, with full types.
468
468
  | `labs.agents.create/createInbound/createOutbound/list/get` | `/api/v1/labs/agents*` on the Supafone API |
469
469
  | `labs.agents.createInboundWithNumber/createOutboundWithNumber` | Agent creation plus Supafone-managed number buy/assign |
470
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 |
471
472
  | `labs.telephony.get/configure/useSupafoneManaged` | `/api/v1/labs/telephony` |
472
473
  | `labs.presets.list()` · `labs.tools.list()` · `labs.voices.list()` | Supafone hosted-agent discovery |
473
474
  | `whisper(transcript, opts?)` | convenience over the oracle |
@@ -483,6 +484,12 @@ and `const { Supafone } = require("supafone-labs")` both work, with full types.
483
484
 
484
485
  Get a key (5 free minutes, no card): <https://labs.supafone.ai/get-key.html>
485
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
+
486
493
  The runtime, all provider adapters, and the offline (bring-your-own-keys) mode
487
494
  are open source and MIT-licensed — the Python package `pip install supafone-labs`
488
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
  /**
@@ -1459,9 +1505,11 @@ declare class CampaignsNamespace {
1459
1505
  declare class LabsNamespace {
1460
1506
  private sm;
1461
1507
  readonly agents: LabsAgentsNamespace;
1508
+ readonly billing: LabsBillingNamespace;
1462
1509
  readonly presets: LabsPresetsNamespace;
1463
1510
  readonly tools: LabsToolsNamespace;
1464
1511
  readonly voices: LabsVoicesNamespace;
1512
+ readonly runtime: LabsRuntimeNamespace;
1465
1513
  readonly phoneNumbers: LabsPhoneNumbersNamespace;
1466
1514
  readonly telephony: LabsTelephonyNamespace;
1467
1515
  readonly calls: LabsCallsNamespace;
@@ -1471,11 +1519,57 @@ declare class LabsNamespace {
1471
1519
  /** Discover the Supafone convenience layer over Ultravox. */
1472
1520
  capabilities(): Promise<LabsCapabilitiesResponse>;
1473
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
+ }
1474
1561
  declare class LabsAgentsNamespace {
1475
1562
  private sm;
1476
1563
  constructor(sm: SupafoneLabs);
1477
1564
  /** Spawn a durable hosted Supafone agent backed by Ultravox and Supafone-managed providers. */
1478
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>;
1479
1573
  /** Default the agent onto the client's Voice Watcher setting (live supervision
1480
1574
  * + QA + scoring) unless the caller set it explicitly; mirror into
1481
1575
  * labs.voice_watcher when a labs block exists. Never overwrites a caller value. */
@@ -1519,6 +1613,25 @@ declare class LabsVoicesNamespace {
1519
1613
  /** Supafone-managed Cartesia voices. Other TTS engines remain explicit BYOK choices. */
1520
1614
  list(opts?: LabsVoiceListOptions): Promise<LabsVoiceListResponse>;
1521
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
+ }
1522
1635
  declare class LabsCallsNamespace {
1523
1636
  private sm;
1524
1637
  constructor(sm: SupafoneLabs);
@@ -1554,8 +1667,11 @@ declare class LabsPhoneNumbersNamespace {
1554
1667
  list(opts?: LabsPhoneNumberListOptions): Promise<LabsPhoneNumberListResponse>;
1555
1668
  /** Search Supafone-managed inventory. This uses Supafone's master telephony account. */
1556
1669
  search(opts?: LabsPhoneNumberSearchOptions): Promise<LabsPhoneNumberSearchResponse>;
1557
- /** Buy a Supafone-managed number. Developers do not need a Twilio account. */
1558
- 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>;
1559
1675
  /** Attach an existing Supafone number to an inbound or outbound agent. */
1560
1676
  assign(numberId: string, input?: LabsPhoneNumberAssignRequest): Promise<LabsPhoneNumberAssignResponse>;
1561
1677
  /** Unassign a number from an agent but keep it reserved on the account. */
@@ -1570,7 +1686,7 @@ declare class LabsPhoneNumbersNamespace {
1570
1686
  * Search if needed, buy the first matching Supafone-managed number, and assign
1571
1687
  * it to the supplied agent. This is the zero-Twilio-account happy path.
1572
1688
  */
1573
- buyAndAssign(input: LabsPhoneNumberBuyAndAssignRequest): Promise<LabsPhoneNumberProvisionResponse>;
1689
+ buyAndAssign(input: LabsPhoneNumberBuyAndAssignRequest): Promise<LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse>;
1574
1690
  }
1575
1691
  declare class LabsTelephonyNamespace {
1576
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;
@@ -773,9 +780,11 @@ class CampaignsNamespace {
773
780
  class LabsNamespace {
774
781
  sm;
775
782
  agents;
783
+ billing;
776
784
  presets;
777
785
  tools;
778
786
  voices;
787
+ runtime;
779
788
  phoneNumbers;
780
789
  telephony;
781
790
  calls;
@@ -784,9 +793,11 @@ class LabsNamespace {
784
793
  constructor(sm) {
785
794
  this.sm = sm;
786
795
  this.agents = new LabsAgentsNamespace(sm);
796
+ this.billing = new LabsBillingNamespace(sm);
787
797
  this.presets = new LabsPresetsNamespace(sm);
788
798
  this.tools = new LabsToolsNamespace(sm);
789
799
  this.voices = new LabsVoicesNamespace(sm);
800
+ this.runtime = new LabsRuntimeNamespace(sm);
790
801
  this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
791
802
  this.telephony = new LabsTelephonyNamespace(sm);
792
803
  this.calls = new LabsCallsNamespace(sm);
@@ -798,6 +809,38 @@ class LabsNamespace {
798
809
  return this.sm.requestSupafoneApi("GET", "/api/v1/labs/capabilities");
799
810
  }
800
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
+ }
801
844
  class LabsAgentsNamespace {
802
845
  sm;
803
846
  constructor(sm) {
@@ -807,6 +850,13 @@ class LabsAgentsNamespace {
807
850
  create(input) {
808
851
  return this.sm.requestSupafoneApi("POST", "/api/v1/labs/agents", labsAgentPayload(this.withVoiceWatcher(input)));
809
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
+ }
810
860
  /** Default the agent onto the client's Voice Watcher setting (live supervision
811
861
  * + QA + scoring) unless the caller set it explicitly; mirror into
812
862
  * labs.voice_watcher when a labs block exists. Never overwrites a caller value. */
@@ -944,10 +994,43 @@ class LabsVoicesNamespace {
944
994
  const q = new URLSearchParams();
945
995
  if (opts.provider)
946
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));
947
1005
  const suffix = q.toString() ? `?${q}` : "";
948
1006
  return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
949
1007
  }
950
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
+ }
951
1034
  class LabsCallsNamespace {
952
1035
  sm;
953
1036
  constructor(sm) {
@@ -1036,10 +1119,27 @@ class LabsPhoneNumbersNamespace {
1036
1119
  search(opts = {}) {
1037
1120
  return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers/search", phoneNumberSearchPayload(opts));
1038
1121
  }
1039
- /** 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
+ */
1040
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
+ }
1041
1140
  return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers", phoneNumberProvisionPayload({
1042
1141
  ...input,
1142
+ numberStrategy: strategy,
1043
1143
  telephony: input.telephony ?? { mode: "supafone_managed", provider: "supafone" },
1044
1144
  }));
1045
1145
  }
@@ -1262,6 +1362,7 @@ function labsAgentPayload(input) {
1262
1362
  agent_type: input.agent_type ?? input.agentType,
1263
1363
  style: input.agent_style ?? input.agentStyle ?? input.style,
1264
1364
  name: input.name,
1365
+ description: input.description,
1265
1366
  assistant_name: input.assistant_name ?? input.assistantName,
1266
1367
  business_name: input.business_name ?? input.businessName,
1267
1368
  industry: input.industry,
@@ -1274,6 +1375,9 @@ function labsAgentPayload(input) {
1274
1375
  preset_key: input.preset_key ?? input.presetKey,
1275
1376
  runtime_mode: input.runtime_mode ?? input.runtimeMode,
1276
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,
1277
1381
  goal: input.goal,
1278
1382
  greeting: input.greeting,
1279
1383
  system_prompt: input.system_prompt ?? input.systemPrompt,
@@ -1375,6 +1479,7 @@ function phoneNumberProvisionPayload(input) {
1375
1479
  number_strategy: input.number_strategy ?? input.numberStrategy,
1376
1480
  number_pool: input.number_pool ?? input.numberPool,
1377
1481
  premium: input.premium,
1482
+ billing_checkout_session_id: input.billing_checkout_session_id ?? input.billingCheckoutSessionId,
1378
1483
  style: input.agent_style ?? input.agentStyle ?? input.style,
1379
1484
  direction: input.direction,
1380
1485
  telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
@@ -1412,8 +1517,31 @@ function callStagesPayload(input) {
1412
1517
  if (Array.isArray(explicit))
1413
1518
  return explicit.map(callStagePayload);
1414
1519
  if (explicit === false || auto === false)
1415
- return undefined;
1416
- 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
+ });
1417
1545
  }
1418
1546
  function callStagePayload(input) {
1419
1547
  return compact({
@@ -1423,10 +1551,14 @@ function callStagePayload(input) {
1423
1551
  instructions: input.instructions,
1424
1552
  exit_criteria: input.exit_criteria ?? input.exitCriteria,
1425
1553
  tools: input.tools,
1554
+ temperature: input.temperature,
1555
+ next_stages: input.next_stages ?? input.nextStages,
1426
1556
  metadata: input.metadata,
1427
1557
  });
1428
1558
  }
1429
1559
  function generateCallStages(input) {
1560
+ // Offline compatibility template. New code should use
1561
+ // client.generateCallStages(...) for the hosted, executable plan.
1430
1562
  const direction = String(input.direction ?? input.agent_style ?? input.agentStyle ?? input.style ?? "inbound").toLowerCase();
1431
1563
  const haystack = [
1432
1564
  input.name,