@voicelayer/sdk 0.6.0 → 0.6.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/dist/brain/index.d.ts +2 -2
- package/dist/brain/index.js +155 -162
- package/dist/index.d.ts +707 -257
- package/dist/index.js +1251 -454
- package/dist/runtime/text-session.d.ts +1 -1
- package/dist/runtime/text-session.js +256 -176
- package/dist/{text-session-CoyEcdmg.d.ts → text-session-B-T33y5P.d.ts} +116 -110
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { b1 as EmailResolver, b2 as FlowResolver, b3 as GraphResolvers, b4 as GraphTraceEvent, b5 as KnowledgeResolver, b6 as LiveTextConversation, b7 as LiveTurnResult, b8 as PlaybookLibraryEntry, b9 as PlaybookResolver, ba as RegistryToolsResolver, bb as RunLiveTextOptions, ag as RunTextSessionOptions, ah as RunTextTranscriptOptions, ay as TextSession, az as TextTranscriptResult, n as ToolExecutor, bc as dedupeConversationEmails, bd as denyPrivateNetwork, be as privateNetworkOptInHonoured, bf as runLiveTextConversation, aZ as runTextSession, a_ as runTextTranscript, bg as runWithOpenAIScope, bh as toPlaybookResolution } from '../text-session-
|
|
1
|
+
export { b1 as EmailResolver, b2 as FlowResolver, b3 as GraphResolvers, b4 as GraphTraceEvent, b5 as KnowledgeResolver, b6 as LiveTextConversation, b7 as LiveTurnResult, b8 as PlaybookLibraryEntry, b9 as PlaybookResolver, ba as RegistryToolsResolver, bb as RunLiveTextOptions, ag as RunTextSessionOptions, ah as RunTextTranscriptOptions, ay as TextSession, az as TextTranscriptResult, n as ToolExecutor, bc as dedupeConversationEmails, bd as denyPrivateNetwork, be as privateNetworkOptInHonoured, bf as runLiveTextConversation, aZ as runTextSession, a_ as runTextTranscript, bg as runWithOpenAIScope, bh as toPlaybookResolution } from '../text-session-B-T33y5P.js';
|
|
2
2
|
import '@livekit/agents';
|
|
3
3
|
import 'zod';
|
|
4
4
|
import '../types-KqrAfY85.js';
|
|
@@ -693,7 +693,7 @@ var init_consultation = __esm({
|
|
|
693
693
|
});
|
|
694
694
|
}
|
|
695
695
|
});
|
|
696
|
-
var CallEventKind, OP_CALL_EVENT_KIND, OpCallEventKind, commandId, SayCommand, HangupCommand, DtmfCommand, InstructCommand, InjectContextCommand;
|
|
696
|
+
var CallEventKind, OP_CALL_EVENT_KIND, OpCallEventKind, LatencyMs, commandId, SayCommand, HangupCommand, DtmfCommand, InstructCommand, InjectContextCommand;
|
|
697
697
|
var init_call_events = __esm({
|
|
698
698
|
"../contracts/src/call-events.ts"() {
|
|
699
699
|
init_consultation();
|
|
@@ -719,7 +719,9 @@ var init_call_events = __esm({
|
|
|
719
719
|
"dtmf.sent",
|
|
720
720
|
// A host's runtime intervention (burn-down G-6): call_say / call_send_guidance / call_inject_context / call_instruct,
|
|
721
721
|
// with masked args and the API key — written by the API only, never through the worker's /events endpoint.
|
|
722
|
-
"mcp.interaction"
|
|
722
|
+
"mcp.interaction",
|
|
723
|
+
// One per caller turn the agent answered (burn-down G-44): where that turn's wait went — see TurnLatencyPayload.
|
|
724
|
+
"turn.latency"
|
|
723
725
|
]);
|
|
724
726
|
OP_CALL_EVENT_KIND = /^(tool|handoff|lookup|record|notify|engine)\.[a-z0-9_]{1,48}(\.[a-z0-9_]{1,48})?$/;
|
|
725
727
|
OpCallEventKind = z.string().regex(OP_CALL_EVENT_KIND);
|
|
@@ -746,6 +748,17 @@ var init_call_events = __esm({
|
|
|
746
748
|
code: z.number().int().min(0).max(15),
|
|
747
749
|
participantId: z.string().optional()
|
|
748
750
|
});
|
|
751
|
+
LatencyMs = z.number().int().min(0).max(6e5).nullable();
|
|
752
|
+
z.object({
|
|
753
|
+
turn: z.number().int().min(1),
|
|
754
|
+
atMs: z.number().int().min(0),
|
|
755
|
+
endpointingMs: LatencyMs,
|
|
756
|
+
sttMs: LatencyMs,
|
|
757
|
+
llmMs: LatencyMs,
|
|
758
|
+
ttsMs: LatencyMs,
|
|
759
|
+
endToEndMs: LatencyMs,
|
|
760
|
+
realtime: z.boolean().optional()
|
|
761
|
+
});
|
|
749
762
|
z.object({
|
|
750
763
|
totalUsd: z.string(),
|
|
751
764
|
breakdown: z.object({
|
|
@@ -862,9 +875,65 @@ var init_text_turns = __esm({
|
|
|
862
875
|
]);
|
|
863
876
|
}
|
|
864
877
|
});
|
|
878
|
+
var PLATFORM_MAX_CALL_MINUTES, Message, CallerRateLimit, CallLimits, CallerChannel, CallDurationLimits;
|
|
879
|
+
var init_call_limits = __esm({
|
|
880
|
+
"../contracts/src/call-limits.ts"() {
|
|
881
|
+
PLATFORM_MAX_CALL_MINUTES = 240;
|
|
882
|
+
Message = z.string().trim().min(1).max(300);
|
|
883
|
+
CallerRateLimit = z.object({
|
|
884
|
+
enabled: z.boolean().optional(),
|
|
885
|
+
perTenMinutes: z.number().int().min(1).max(100).optional(),
|
|
886
|
+
perDay: z.number().int().min(1).max(1e3).optional(),
|
|
887
|
+
withheldPerTenMinutes: z.number().int().min(1).max(100).optional(),
|
|
888
|
+
withheldPerDay: z.number().int().min(1).max(1e3).optional()
|
|
889
|
+
});
|
|
890
|
+
CallLimits = z.object({
|
|
891
|
+
/** The agent's own ceiling in minutes; clamped to the workspace's plan ceiling. Absent ⇒ DEFAULT_MAX_CALL_MINUTES. */
|
|
892
|
+
maxDurationMinutes: z.number().int().min(1).max(PLATFORM_MAX_CALL_MINUTES).optional(),
|
|
893
|
+
/** Seconds before the limit the wrap-up line is spoken; 0 ⇒ no warning. */
|
|
894
|
+
wrapUpWarningSeconds: z.number().int().min(0).max(300).optional(),
|
|
895
|
+
wrapUpMessage: Message.optional(),
|
|
896
|
+
goodbyeMessage: Message.optional(),
|
|
897
|
+
callerRateLimit: CallerRateLimit.optional(),
|
|
898
|
+
rateLimitedMessage: Message.optional()
|
|
899
|
+
});
|
|
900
|
+
CallerChannel = z.enum(["phone", "web"]);
|
|
901
|
+
z.object({
|
|
902
|
+
roomName: z.string().min(1).max(256),
|
|
903
|
+
/** From the dispatch metadata: lets the API admit (gate) a room whose call row doesn't exist yet. */
|
|
904
|
+
agentId: z.string().max(128).optional(),
|
|
905
|
+
phoneNumberId: z.string().max(128).optional(),
|
|
906
|
+
caller: z.object({
|
|
907
|
+
channel: CallerChannel,
|
|
908
|
+
/** The caller's number as the carrier presented it; null / absent / not E.164 ⇒ the withheld bucket. */
|
|
909
|
+
number: z.string().max(64).nullable().optional()
|
|
910
|
+
})
|
|
911
|
+
});
|
|
912
|
+
CallDurationLimits = z.object({
|
|
913
|
+
/** The agent's own ceiling (already clamped to the plan), or null when it set none. */
|
|
914
|
+
agentMaxSeconds: z.number().int().min(1).nullable(),
|
|
915
|
+
/** The plan ceiling — the platform bound no call outlives. */
|
|
916
|
+
ceilingSeconds: z.number().int().min(1),
|
|
917
|
+
wrapUpWarningSeconds: z.number().int().min(0),
|
|
918
|
+
wrapUpMessage: z.string(),
|
|
919
|
+
goodbyeMessage: z.string()
|
|
920
|
+
});
|
|
921
|
+
z.discriminatedUnion("admitted", [
|
|
922
|
+
z.object({ admitted: z.literal(true), limits: CallDurationLimits }),
|
|
923
|
+
z.object({
|
|
924
|
+
admitted: z.literal(false),
|
|
925
|
+
/** 'rate_limited:caller' | 'inbound_refused:<code>' — already recorded as the call's end reason. */
|
|
926
|
+
endReason: z.string(),
|
|
927
|
+
/** The fixed line to speak before hanging up. */
|
|
928
|
+
message: z.string()
|
|
929
|
+
})
|
|
930
|
+
]);
|
|
931
|
+
}
|
|
932
|
+
});
|
|
865
933
|
var VoiceConfig, ModelConfig, SttConfig, RealtimeConfig, PipelineMode, ToolBinding, ProcessFieldType, ProcessFieldSpec, ProcessCompleteWhen, ProcessBackendAckSpec, ProcessSpec, TriggerConditionSpec, TriggerActionSpec, TriggerSpec, RequiredInfoField, AgentConfig;
|
|
866
934
|
var init_agent_config = __esm({
|
|
867
935
|
"../contracts/src/agent-config.ts"() {
|
|
936
|
+
init_call_limits();
|
|
868
937
|
init_consultation();
|
|
869
938
|
VoiceConfig = z.object({
|
|
870
939
|
// Finite, vetted list — platform-level enum.
|
|
@@ -1028,6 +1097,9 @@ var init_agent_config = __esm({
|
|
|
1028
1097
|
// LLM slot through the connector instead of a first-party model. Null/absent →
|
|
1029
1098
|
// first-party pipeline (unchanged default). See docs/specs/voice-brain-connector.
|
|
1030
1099
|
brainConnectorId: z.string().uuid().nullable().optional(),
|
|
1100
|
+
// Per-agent call bounds: caller rate limit, max duration and their spoken lines (burn-down G-41, G-43). Absent ⇒
|
|
1101
|
+
// every default (call-limits.ts) — the rate limit is ON by default.
|
|
1102
|
+
callLimits: CallLimits.optional(),
|
|
1031
1103
|
// Provenance: 'sdk' (hand-coded defineAgent worker), 'flow' (canvas deploy),
|
|
1032
1104
|
// 'playbook' (agent-mode deploy), or 'connector' (brain-connector agent).
|
|
1033
1105
|
// DERIVED from the linked agents row's deploy tags (flowId / playbookId /
|
|
@@ -1341,6 +1413,7 @@ var init_agent_versions = __esm({
|
|
|
1341
1413
|
"../contracts/src/agent-versions.ts"() {
|
|
1342
1414
|
init_agent_config();
|
|
1343
1415
|
init_agents();
|
|
1416
|
+
init_call_limits();
|
|
1344
1417
|
init_consultation();
|
|
1345
1418
|
z.object({
|
|
1346
1419
|
systemPrompt: z.string().max(64e3),
|
|
@@ -1355,6 +1428,8 @@ var init_agent_versions = __esm({
|
|
|
1355
1428
|
consultation: ConsultationPolicy,
|
|
1356
1429
|
tags: z.array(z.string().max(64)).max(32).nullable(),
|
|
1357
1430
|
brainConnectorId: z.string().uuid().nullable(),
|
|
1431
|
+
// Versions published before call limits existed carry none: read as null (every default).
|
|
1432
|
+
callLimits: CallLimits.nullable().default(null),
|
|
1358
1433
|
flowVersion: z.number().int().nullable(),
|
|
1359
1434
|
processSchema: ProcessSchemaDTO.nullable()
|
|
1360
1435
|
});
|
|
@@ -1390,6 +1465,7 @@ var init_agent_versions = __esm({
|
|
|
1390
1465
|
"language",
|
|
1391
1466
|
"tools",
|
|
1392
1467
|
"brainConnectorId",
|
|
1468
|
+
"callLimits",
|
|
1393
1469
|
"consultation",
|
|
1394
1470
|
"tags",
|
|
1395
1471
|
"flowVersion",
|
|
@@ -1531,6 +1607,9 @@ var init_model_catalog = __esm({
|
|
|
1531
1607
|
CostEstimateBilledPerMinute = z.object({
|
|
1532
1608
|
platformFee: z.number().nonnegative(),
|
|
1533
1609
|
providerPassthrough: z.number().nonnegative(),
|
|
1610
|
+
// The flat own-key fee per minute (burn-down B-26); 0 when every provider runs on the platform's keys. Optional
|
|
1611
|
+
// only for responses from an API older than the fee.
|
|
1612
|
+
ownKeyFee: z.number().nonnegative().optional(),
|
|
1534
1613
|
telephony: z.number().nonnegative(),
|
|
1535
1614
|
allIn: z.number().nonnegative()
|
|
1536
1615
|
});
|
|
@@ -1760,9 +1839,46 @@ var init_call_review = __esm({
|
|
|
1760
1839
|
});
|
|
1761
1840
|
}
|
|
1762
1841
|
});
|
|
1842
|
+
var CAPTURED_FIELDS_MAX, CAPTURED_STRING_MAX_CHARS, CAPTURED_ARRAY_MAX_ITEMS, CapturedFieldValue, CapturedField, StructuredOutputs;
|
|
1843
|
+
var init_call_outcome = __esm({
|
|
1844
|
+
"../contracts/src/call-outcome.ts"() {
|
|
1845
|
+
init_agent_config();
|
|
1846
|
+
CAPTURED_FIELDS_MAX = 200;
|
|
1847
|
+
CAPTURED_STRING_MAX_CHARS = 8e3;
|
|
1848
|
+
CAPTURED_ARRAY_MAX_ITEMS = 100;
|
|
1849
|
+
CapturedFieldValue = z.union([
|
|
1850
|
+
z.string().max(CAPTURED_STRING_MAX_CHARS),
|
|
1851
|
+
z.number().finite(),
|
|
1852
|
+
z.boolean(),
|
|
1853
|
+
z.array(z.string().max(CAPTURED_STRING_MAX_CHARS)).max(CAPTURED_ARRAY_MAX_ITEMS),
|
|
1854
|
+
z.null()
|
|
1855
|
+
]);
|
|
1856
|
+
CapturedField = z.object({
|
|
1857
|
+
name: z.string().min(1).max(64),
|
|
1858
|
+
type: ProcessFieldType,
|
|
1859
|
+
required: z.boolean(),
|
|
1860
|
+
// false ⇒ the call never captured it; `value` is then null
|
|
1861
|
+
filled: z.boolean(),
|
|
1862
|
+
value: CapturedFieldValue
|
|
1863
|
+
});
|
|
1864
|
+
StructuredOutputs = z.record(CapturedFieldValue);
|
|
1865
|
+
z.object({
|
|
1866
|
+
// Every declared field, in declaration order; empty when the agent declares none.
|
|
1867
|
+
fields: z.array(CapturedField).max(CAPTURED_FIELDS_MAX)
|
|
1868
|
+
}).superRefine((body, ctx) => {
|
|
1869
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1870
|
+
for (const f of body.fields) {
|
|
1871
|
+
if (seen.has(f.name)) ctx.addIssue({ code: z.ZodIssueCode.custom, message: `duplicate field ${f.name}` });
|
|
1872
|
+
seen.add(f.name);
|
|
1873
|
+
if (!f.filled && f.value !== null) ctx.addIssue({ code: z.ZodIssueCode.custom, message: `${f.name}: an unfilled field has no value` });
|
|
1874
|
+
}
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1763
1878
|
var TranscriptTurn, ConsultationAudit, ToolCallAudit, McpInteractionAudit;
|
|
1764
1879
|
var init_call_result = __esm({
|
|
1765
1880
|
"../contracts/src/call-result.ts"() {
|
|
1881
|
+
init_call_outcome();
|
|
1766
1882
|
TranscriptTurn = z.object({
|
|
1767
1883
|
speaker: z.enum(["caller", "agent"]),
|
|
1768
1884
|
text: z.string(),
|
|
@@ -1811,9 +1927,13 @@ var init_call_result = __esm({
|
|
|
1811
1927
|
startedAt: z.string().datetime(),
|
|
1812
1928
|
endedAt: z.string().datetime(),
|
|
1813
1929
|
durationMs: z.number().int().min(0),
|
|
1930
|
+
// Final segments only, in call order.
|
|
1814
1931
|
transcript: z.array(TranscriptTurn),
|
|
1815
|
-
//
|
|
1816
|
-
|
|
1932
|
+
// The filled fields of the agent's process / flow, keyed by name (burn-down G-20). Absent when the agent declares no
|
|
1933
|
+
// fields, or its worker never reported them.
|
|
1934
|
+
structuredOutputs: StructuredOutputs.optional(),
|
|
1935
|
+
// Every declared field with what the call captured and whether it was filled — the same list `call.ended` carries.
|
|
1936
|
+
fields: z.array(CapturedField).optional(),
|
|
1817
1937
|
recordingUrl: z.string().url().optional(),
|
|
1818
1938
|
cost: z.object({
|
|
1819
1939
|
totalUsd: z.string(),
|
|
@@ -1825,9 +1945,11 @@ var init_call_result = __esm({
|
|
|
1825
1945
|
});
|
|
1826
1946
|
}
|
|
1827
1947
|
});
|
|
1828
|
-
var WEBHOOK_EVENT_TYPES, CallWebhookDirection, CallWebhookOutcome, CallWebhookAttributes, Party;
|
|
1948
|
+
var WEBHOOK_EVENT_TYPES, CallWebhookDirection, CallWebhookOutcome, CallWebhookAttributes, Party, CALL_ENDED_PAYLOAD_VERSION, CallEndedTranscript;
|
|
1829
1949
|
var init_webhook_events = __esm({
|
|
1830
1950
|
"../contracts/src/webhook-events.ts"() {
|
|
1951
|
+
init_call_result();
|
|
1952
|
+
init_call_outcome();
|
|
1831
1953
|
WEBHOOK_EVENT_TYPES = ["call.started", "call.ended", "recording.ready"];
|
|
1832
1954
|
z.enum(WEBHOOK_EVENT_TYPES);
|
|
1833
1955
|
CallWebhookDirection = z.enum(["inbound", "outbound", "web"]);
|
|
@@ -1845,7 +1967,18 @@ var init_webhook_events = __esm({
|
|
|
1845
1967
|
// set, it is the same value `call.ended` carries; `call.ended` always has one.
|
|
1846
1968
|
direction: CallWebhookDirection.nullable()
|
|
1847
1969
|
});
|
|
1970
|
+
CALL_ENDED_PAYLOAD_VERSION = 2;
|
|
1971
|
+
CallEndedTranscript = z.object({
|
|
1972
|
+
// Final segments only (what was said, never an interim STT guess), in call order. Empty when `truncated`.
|
|
1973
|
+
turns: z.array(TranscriptTurn),
|
|
1974
|
+
// How many final turns the call has — the length of `turns` unless truncated.
|
|
1975
|
+
turnCount: z.number().int().min(0),
|
|
1976
|
+
truncated: z.boolean(),
|
|
1977
|
+
// Where the whole transcript is: GET /v1/calls/:callId/result (its `transcript`). Always set.
|
|
1978
|
+
fetchPath: z.string().regex(/^\/v1\/calls\/[0-9a-f-]{36}\/result$/)
|
|
1979
|
+
});
|
|
1848
1980
|
z.object({
|
|
1981
|
+
version: z.literal(CALL_ENDED_PAYLOAD_VERSION),
|
|
1849
1982
|
callId: z.string().uuid(),
|
|
1850
1983
|
durationMs: z.number().int().min(0),
|
|
1851
1984
|
endedAt: z.string().datetime(),
|
|
@@ -1856,7 +1989,20 @@ var init_webhook_events = __esm({
|
|
|
1856
1989
|
toE164: Party,
|
|
1857
1990
|
direction: CallWebhookDirection,
|
|
1858
1991
|
outcome: CallWebhookOutcome,
|
|
1859
|
-
attributes: CallWebhookAttributes
|
|
1992
|
+
attributes: CallWebhookAttributes,
|
|
1993
|
+
// Every field the agent declared (its process, or a flow's slots), in declaration order, with what the call captured
|
|
1994
|
+
// and whether it was filled. Empty when the agent declares none.
|
|
1995
|
+
fields: z.array(CapturedField),
|
|
1996
|
+
// The filled fields as one object keyed by name (as GET /v1/calls/:id/result returns them). Absent when the agent
|
|
1997
|
+
// declares no fields.
|
|
1998
|
+
structuredOutputs: StructuredOutputs.optional(),
|
|
1999
|
+
transcript: CallEndedTranscript,
|
|
2000
|
+
// true: the agent's worker confirmed its last transcript segment and captured field had landed before this payload
|
|
2001
|
+
// was built. false: built without that word (a worker that crashed or runs an SDK before 0.6.2, or one that didn't
|
|
2002
|
+
// answer within the hold) — the transcript and fields are what had arrived; GET /v1/calls/:id/result has any later.
|
|
2003
|
+
finalized: z.boolean(),
|
|
2004
|
+
// true: the payload would have passed CALL_ENDED_PAYLOAD_MAX_BYTES, so values were left out (see the cap) — fetch them.
|
|
2005
|
+
valuesOmitted: z.boolean()
|
|
1860
2006
|
});
|
|
1861
2007
|
z.object({
|
|
1862
2008
|
callId: z.string().uuid(),
|
|
@@ -2432,166 +2578,6 @@ var init_telephony = __esm({
|
|
|
2432
2578
|
});
|
|
2433
2579
|
}
|
|
2434
2580
|
});
|
|
2435
|
-
var SHORTENER_HOSTS, PLACEHOLDER_HOSTS, PublicUrl, A2pBusinessType, A2pBusinessInfo, A2pOptInType, A2pUseCase, A2pMessagingProfile, A2pState;
|
|
2436
|
-
var init_a2p = __esm({
|
|
2437
|
-
"../contracts/src/a2p.ts"() {
|
|
2438
|
-
SHORTENER_HOSTS = [
|
|
2439
|
-
"bit.ly",
|
|
2440
|
-
"tinyurl.com",
|
|
2441
|
-
"t.co",
|
|
2442
|
-
"goo.gl",
|
|
2443
|
-
"ow.ly",
|
|
2444
|
-
"is.gd",
|
|
2445
|
-
"buff.ly",
|
|
2446
|
-
"rebrand.ly",
|
|
2447
|
-
"cutt.ly",
|
|
2448
|
-
"shorturl.at",
|
|
2449
|
-
"tiny.cc"
|
|
2450
|
-
];
|
|
2451
|
-
PLACEHOLDER_HOSTS = ["acme.com", "example.com", "example.org", "test.com", "localhost"];
|
|
2452
|
-
PublicUrl = z.string().url().refine((v) => v.startsWith("https://"), "must be https").refine((v) => {
|
|
2453
|
-
try {
|
|
2454
|
-
const host = new URL(v).hostname.toLowerCase().replace(/^www\./, "");
|
|
2455
|
-
return !SHORTENER_HOSTS.includes(host);
|
|
2456
|
-
} catch {
|
|
2457
|
-
return false;
|
|
2458
|
-
}
|
|
2459
|
-
}, "public URL shorteners are rejected by carriers \u2014 use your own domain").refine((v) => {
|
|
2460
|
-
try {
|
|
2461
|
-
const host = new URL(v).hostname.toLowerCase().replace(/^www\./, "");
|
|
2462
|
-
return !PLACEHOLDER_HOSTS.some((p) => host === p || host.endsWith(`.${p}`));
|
|
2463
|
-
} catch {
|
|
2464
|
-
return false;
|
|
2465
|
-
}
|
|
2466
|
-
}, "placeholder domain \u2014 reviewers will follow this link and reject the campaign");
|
|
2467
|
-
A2pBusinessType = z.enum([
|
|
2468
|
-
"Sole Proprietorship",
|
|
2469
|
-
"Partnership",
|
|
2470
|
-
"Corporation",
|
|
2471
|
-
"Co-operative",
|
|
2472
|
-
"Limited Liability Corporation",
|
|
2473
|
-
"Non-profit Corporation"
|
|
2474
|
-
]);
|
|
2475
|
-
A2pBusinessInfo = z.object({
|
|
2476
|
-
legalName: z.string().trim().min(2).max(200),
|
|
2477
|
-
/** EIN (US) or equivalent registration number. */
|
|
2478
|
-
registrationNumber: z.string().trim().min(4).max(50),
|
|
2479
|
-
businessType: A2pBusinessType,
|
|
2480
|
-
/** Publicly reachable production site — not staging, not a 404. */
|
|
2481
|
-
website: PublicUrl,
|
|
2482
|
-
industry: z.string().trim().min(2).max(60),
|
|
2483
|
-
address: z.object({
|
|
2484
|
-
street: z.string().trim().min(2).max(200),
|
|
2485
|
-
city: z.string().trim().min(1).max(100),
|
|
2486
|
-
region: z.string().trim().min(1).max(100),
|
|
2487
|
-
postalCode: z.string().trim().min(2).max(20),
|
|
2488
|
-
isoCountry: z.string().trim().length(2)
|
|
2489
|
-
}),
|
|
2490
|
-
authorizedRep: z.object({
|
|
2491
|
-
firstName: z.string().trim().min(1).max(100),
|
|
2492
|
-
lastName: z.string().trim().min(1).max(100),
|
|
2493
|
-
email: z.string().trim().email(),
|
|
2494
|
-
phone: z.string().trim().regex(/^\+[1-9]\d{6,14}$/, "must be E.164"),
|
|
2495
|
-
jobTitle: z.string().trim().min(2).max(100)
|
|
2496
|
-
})
|
|
2497
|
-
});
|
|
2498
|
-
A2pOptInType = z.enum(["WEB_FORM", "PAPER_FORM", "VERBAL", "VIA_TEXT", "MOBILE_QR_CODE"]);
|
|
2499
|
-
A2pUseCase = z.enum([
|
|
2500
|
-
"MIXED",
|
|
2501
|
-
"CUSTOMER_CARE",
|
|
2502
|
-
"MARKETING",
|
|
2503
|
-
"ACCOUNT_NOTIFICATION",
|
|
2504
|
-
"2FA",
|
|
2505
|
-
"DELIVERY_NOTIFICATION",
|
|
2506
|
-
"HIGHER_EDUCATION",
|
|
2507
|
-
"POLLING_VOTING",
|
|
2508
|
-
"PUBLIC_SERVICE_ANNOUNCEMENT",
|
|
2509
|
-
"LOW_VOLUME"
|
|
2510
|
-
]);
|
|
2511
|
-
A2pMessagingProfile = z.object({
|
|
2512
|
-
useCase: A2pUseCase,
|
|
2513
|
-
/** Specific, not generic. "We send texts" gets rejected; describe the actual
|
|
2514
|
-
* messages and when they are sent. */
|
|
2515
|
-
description: z.string().trim().min(40).max(4096),
|
|
2516
|
-
/**
|
|
2517
|
-
* The single most-rejected field. Must describe HOW people opt in, state the
|
|
2518
|
-
* message frequency, include the "message and data rates may apply"
|
|
2519
|
-
* disclosure, and link to publicly reachable evidence. Twilio's API bounds it
|
|
2520
|
-
* to 40–2049 characters.
|
|
2521
|
-
*/
|
|
2522
|
-
messageFlow: z.string().trim().min(40).max(2049),
|
|
2523
|
-
optInType: A2pOptInType,
|
|
2524
|
-
/** Publicly accessible screenshots/pages showing the opt-in. Reviewers open
|
|
2525
|
-
* these; anything behind a login fails. */
|
|
2526
|
-
optInEvidenceUrls: z.array(PublicUrl).min(1).max(5),
|
|
2527
|
-
/** Real messages the customer will send. Must reflect the declared use case
|
|
2528
|
-
* and carry opt-out language. */
|
|
2529
|
-
messageSamples: z.array(z.string().trim().min(10).max(1024)).min(2).max(5),
|
|
2530
|
-
privacyPolicyUrl: PublicUrl,
|
|
2531
|
-
termsAndConditionsUrl: PublicUrl,
|
|
2532
|
-
hasEmbeddedLinks: z.boolean().default(false),
|
|
2533
|
-
hasEmbeddedPhone: z.boolean().default(false)
|
|
2534
|
-
});
|
|
2535
|
-
z.object({
|
|
2536
|
-
business: A2pBusinessInfo,
|
|
2537
|
-
messaging: A2pMessagingProfile,
|
|
2538
|
-
/** Register against Twilio's mock endpoints — exercises the full pipeline
|
|
2539
|
-
* with no fees and no real carrier submission. Used in CI and staging. */
|
|
2540
|
-
mock: z.boolean().default(false)
|
|
2541
|
-
}).superRefine((v, ctx) => {
|
|
2542
|
-
const flow = v.messaging.messageFlow.toLowerCase();
|
|
2543
|
-
if (!/(msg|message)\s*(&|and)\s*data rates/.test(flow)) {
|
|
2544
|
-
ctx.addIssue({
|
|
2545
|
-
code: z.ZodIssueCode.custom,
|
|
2546
|
-
path: ["messaging", "messageFlow"],
|
|
2547
|
-
message: 'must include a "Message and data rates may apply" disclosure \u2014 carriers reject without it'
|
|
2548
|
-
});
|
|
2549
|
-
}
|
|
2550
|
-
if (!/\d/.test(flow) || !/(msg|message|text)/.test(flow)) {
|
|
2551
|
-
ctx.addIssue({
|
|
2552
|
-
code: z.ZodIssueCode.custom,
|
|
2553
|
-
path: ["messaging", "messageFlow"],
|
|
2554
|
-
message: 'must state message frequency, e.g. "Up to 4 msgs/month"'
|
|
2555
|
-
});
|
|
2556
|
-
}
|
|
2557
|
-
const hasOptOut = v.messaging.messageSamples.some((s) => /stop/i.test(s));
|
|
2558
|
-
if (!hasOptOut) {
|
|
2559
|
-
ctx.addIssue({
|
|
2560
|
-
code: z.ZodIssueCode.custom,
|
|
2561
|
-
path: ["messaging", "messageSamples"],
|
|
2562
|
-
message: 'at least one sample must include opt-out language (e.g. "Reply STOP to opt out")'
|
|
2563
|
-
});
|
|
2564
|
-
}
|
|
2565
|
-
});
|
|
2566
|
-
A2pState = z.enum([
|
|
2567
|
-
"none",
|
|
2568
|
-
"profile_pending",
|
|
2569
|
-
"profile_approved",
|
|
2570
|
-
"profile_failed",
|
|
2571
|
-
"brand_pending",
|
|
2572
|
-
"brand_approved",
|
|
2573
|
-
"brand_failed",
|
|
2574
|
-
"campaign_pending",
|
|
2575
|
-
"messaging_ready",
|
|
2576
|
-
"campaign_failed"
|
|
2577
|
-
]);
|
|
2578
|
-
z.object({
|
|
2579
|
-
state: A2pState,
|
|
2580
|
-
customerProfileSid: z.string().nullable(),
|
|
2581
|
-
trustProductSid: z.string().nullable(),
|
|
2582
|
-
brandSid: z.string().nullable(),
|
|
2583
|
-
messagingServiceSid: z.string().nullable(),
|
|
2584
|
-
campaignSid: z.string().nullable(),
|
|
2585
|
-
mock: z.boolean(),
|
|
2586
|
-
/** Carrier/Twilio rejection details, surfaced verbatim so the customer can
|
|
2587
|
-
* fix the specific field rather than guess. */
|
|
2588
|
-
failures: z.array(z.object({ code: z.number().nullable(), field: z.string().nullable(), message: z.string() })).default([]),
|
|
2589
|
-
/** Plain-language next step for the dashboard. */
|
|
2590
|
-
nextAction: z.string().nullable(),
|
|
2591
|
-
updatedAt: z.string().nullable()
|
|
2592
|
-
});
|
|
2593
|
-
}
|
|
2594
|
-
});
|
|
2595
2581
|
var ConnectorMode, ConnectorStatus, ConnectorNormalize;
|
|
2596
2582
|
var init_connector = __esm({
|
|
2597
2583
|
"../contracts/src/connector.ts"() {
|
|
@@ -2706,7 +2692,7 @@ var init_connector_stream = __esm({
|
|
|
2706
2692
|
]);
|
|
2707
2693
|
}
|
|
2708
2694
|
});
|
|
2709
|
-
var FLOW_BOOT_FAILURES;
|
|
2695
|
+
var FLOW_BOOT_FAILURES, AGENT_END_REASONS;
|
|
2710
2696
|
var init_call_end_reason = __esm({
|
|
2711
2697
|
"../contracts/src/call-end-reason.ts"() {
|
|
2712
2698
|
FLOW_BOOT_FAILURES = [
|
|
@@ -2717,9 +2703,11 @@ var init_call_end_reason = __esm({
|
|
|
2717
2703
|
"worker_identity_refused",
|
|
2718
2704
|
"provider_unavailable"
|
|
2719
2705
|
];
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2706
|
+
AGENT_END_REASONS = [
|
|
2707
|
+
"max_duration",
|
|
2708
|
+
...FLOW_BOOT_FAILURES.map((cause) => `flow_boot:${cause}`)
|
|
2709
|
+
];
|
|
2710
|
+
z.enum(AGENT_END_REASONS);
|
|
2723
2711
|
}
|
|
2724
2712
|
});
|
|
2725
2713
|
var EnvironmentSpec;
|
|
@@ -3915,6 +3903,23 @@ var init_wallet = __esm({
|
|
|
3915
3903
|
});
|
|
3916
3904
|
}
|
|
3917
3905
|
});
|
|
3906
|
+
var PipelineSlotKey;
|
|
3907
|
+
var init_pipeline_keys = __esm({
|
|
3908
|
+
"../contracts/src/pipeline-keys.ts"() {
|
|
3909
|
+
PipelineSlotKey = z.object({
|
|
3910
|
+
/** The provider as the slot reports it (LiveKit's label: `openai`, `api.openai.com`, `Deepgram`, …). */
|
|
3911
|
+
provider: z.string().min(1).max(200),
|
|
3912
|
+
/** true = the workspace's own key built it; false = the platform's. */
|
|
3913
|
+
byok: z.boolean()
|
|
3914
|
+
});
|
|
3915
|
+
z.object({
|
|
3916
|
+
llm: PipelineSlotKey.optional(),
|
|
3917
|
+
stt: PipelineSlotKey.optional(),
|
|
3918
|
+
tts: PipelineSlotKey.optional(),
|
|
3919
|
+
realtime: PipelineSlotKey.optional()
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3922
|
+
});
|
|
3918
3923
|
|
|
3919
3924
|
// ../contracts/src/index.ts
|
|
3920
3925
|
var init_src = __esm({
|
|
@@ -3944,6 +3949,7 @@ var init_src = __esm({
|
|
|
3944
3949
|
init_tests();
|
|
3945
3950
|
init_call_review();
|
|
3946
3951
|
init_call_result();
|
|
3952
|
+
init_call_outcome();
|
|
3947
3953
|
init_webhook_events();
|
|
3948
3954
|
init_numbers();
|
|
3949
3955
|
init_project_limits();
|
|
@@ -3959,12 +3965,12 @@ var init_src = __esm({
|
|
|
3959
3965
|
init_connection();
|
|
3960
3966
|
init_secret_headers();
|
|
3961
3967
|
init_telephony();
|
|
3962
|
-
init_a2p();
|
|
3963
3968
|
init_connector();
|
|
3964
3969
|
init_connector_stream();
|
|
3965
3970
|
init_flow_compile();
|
|
3966
3971
|
init_flow_compile_check();
|
|
3967
3972
|
init_call_end_reason();
|
|
3973
|
+
init_call_limits();
|
|
3968
3974
|
init_flow_enrichment();
|
|
3969
3975
|
init_environments();
|
|
3970
3976
|
init_variables();
|
|
@@ -3980,6 +3986,7 @@ var init_src = __esm({
|
|
|
3980
3986
|
init_wallet();
|
|
3981
3987
|
init_network_address();
|
|
3982
3988
|
init_dispatch_metadata();
|
|
3989
|
+
init_pipeline_keys();
|
|
3983
3990
|
}
|
|
3984
3991
|
});
|
|
3985
3992
|
|
|
@@ -4159,9 +4166,78 @@ var init_dist = __esm({
|
|
|
4159
4166
|
init_chat_params();
|
|
4160
4167
|
}
|
|
4161
4168
|
});
|
|
4169
|
+
function requestedModel(body) {
|
|
4170
|
+
const model2 = body?.model;
|
|
4171
|
+
return typeof model2 === "string" && model2 ? model2 : void 0;
|
|
4172
|
+
}
|
|
4173
|
+
function safely(fn) {
|
|
4174
|
+
try {
|
|
4175
|
+
fn();
|
|
4176
|
+
} catch (err) {
|
|
4177
|
+
console.warn("[voicelayer] model meter listener failed", { err: err instanceof Error ? err.message : String(err) });
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
function metered(target, create, byok) {
|
|
4181
|
+
return async (...args) => {
|
|
4182
|
+
const listener = meterScope.getStore();
|
|
4183
|
+
const startedAt = Date.now();
|
|
4184
|
+
const model2 = requestedModel(args[0]);
|
|
4185
|
+
if (listener) safely(() => listener.started());
|
|
4186
|
+
let res;
|
|
4187
|
+
try {
|
|
4188
|
+
res = await Reflect.apply(create, target, args);
|
|
4189
|
+
return res;
|
|
4190
|
+
} finally {
|
|
4191
|
+
if (listener) {
|
|
4192
|
+
const usage = res?.usage;
|
|
4193
|
+
const record = {
|
|
4194
|
+
provider: "openai",
|
|
4195
|
+
model: model2 ?? res?.model ?? "unknown",
|
|
4196
|
+
...usage ? { usage: { inputTokens: usage.prompt_tokens ?? 0, outputTokens: usage.completion_tokens ?? 0 } } : {},
|
|
4197
|
+
...byok !== void 0 ? { byok } : {},
|
|
4198
|
+
startedAt,
|
|
4199
|
+
endedAt: Date.now()
|
|
4200
|
+
};
|
|
4201
|
+
safely(() => listener.finished(record));
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4205
|
+
}
|
|
4206
|
+
function meteredClient(client, byok) {
|
|
4207
|
+
const hit = wrapped.get(client);
|
|
4208
|
+
if (hit) return hit;
|
|
4209
|
+
const chat = client.chat;
|
|
4210
|
+
const embeddings = client.embeddings;
|
|
4211
|
+
const completionsCreate = chat ? metered(chat.completions, chat.completions.create, byok) : void 0;
|
|
4212
|
+
const embeddingsCreate = embeddings ? metered(embeddings, embeddings.create, byok) : void 0;
|
|
4213
|
+
const completions = chat ? new Proxy(chat.completions, { get: (t, p, r) => p === "create" ? completionsCreate : Reflect.get(t, p, r) }) : void 0;
|
|
4214
|
+
const chatProxy = chat ? new Proxy(chat, { get: (t, p, r) => p === "completions" ? completions : Reflect.get(t, p, r) }) : void 0;
|
|
4215
|
+
const embeddingsProxy = embeddings ? new Proxy(embeddings, { get: (t, p, r) => p === "create" ? embeddingsCreate : Reflect.get(t, p, r) }) : void 0;
|
|
4216
|
+
const proxy = new Proxy(client, {
|
|
4217
|
+
get(target, prop, receiver) {
|
|
4218
|
+
if (prop === "chat" && chatProxy) return chatProxy;
|
|
4219
|
+
if (prop === "embeddings" && embeddingsProxy) return embeddingsProxy;
|
|
4220
|
+
return Reflect.get(target, prop, receiver);
|
|
4221
|
+
}
|
|
4222
|
+
});
|
|
4223
|
+
wrapped.set(client, proxy);
|
|
4224
|
+
wrapped.set(proxy, proxy);
|
|
4225
|
+
return proxy;
|
|
4226
|
+
}
|
|
4227
|
+
var meterScope, wrapped;
|
|
4228
|
+
var init_model_meter = __esm({
|
|
4229
|
+
"src/runtime/model-meter.ts"() {
|
|
4230
|
+
meterScope = new AsyncLocalStorage();
|
|
4231
|
+
wrapped = /* @__PURE__ */ new WeakMap();
|
|
4232
|
+
}
|
|
4233
|
+
});
|
|
4162
4234
|
function runWithOpenAIScope(client, fn) {
|
|
4163
4235
|
return scope.run(client, fn);
|
|
4164
4236
|
}
|
|
4237
|
+
function helperKeyIsCustomers() {
|
|
4238
|
+
if (credential) return true;
|
|
4239
|
+
return callCredentialSource !== "worker-token";
|
|
4240
|
+
}
|
|
4165
4241
|
function defaultOpenAIOptions() {
|
|
4166
4242
|
if (!credential) return {};
|
|
4167
4243
|
return {
|
|
@@ -4169,27 +4245,31 @@ function defaultOpenAIOptions() {
|
|
|
4169
4245
|
...credential.baseURL ? { baseURL: credential.baseURL } : {}
|
|
4170
4246
|
};
|
|
4171
4247
|
}
|
|
4172
|
-
function internalChatClient(
|
|
4248
|
+
function internalChatClient() {
|
|
4173
4249
|
const cred = defaultOpenAIOptions();
|
|
4174
4250
|
return createChatClient({
|
|
4175
4251
|
...cred.apiKey ? { apiKey: cred.apiKey } : {},
|
|
4176
|
-
...cred.baseURL ? { baseURL: cred.baseURL } : {}
|
|
4177
|
-
...{}
|
|
4252
|
+
...cred.baseURL ? { baseURL: cred.baseURL } : {}
|
|
4178
4253
|
});
|
|
4179
4254
|
}
|
|
4180
4255
|
function defaultOpenAI() {
|
|
4181
|
-
|
|
4256
|
+
const scoped = scope.getStore();
|
|
4257
|
+
if (scoped) return meteredClient(scoped);
|
|
4258
|
+
if (!cached) cached = meteredClient(internalChatClient(), helperKeyIsCustomers());
|
|
4259
|
+
return cached;
|
|
4182
4260
|
}
|
|
4183
4261
|
function hasOpenAiKey() {
|
|
4184
4262
|
return scope.getStore() !== void 0 || credential !== null || Boolean(process.env["OPENAI_API_KEY"]);
|
|
4185
4263
|
}
|
|
4186
|
-
var credential, cached, scope;
|
|
4264
|
+
var credential, cached, scope, callCredentialSource;
|
|
4187
4265
|
var init_openai_default = __esm({
|
|
4188
4266
|
"src/runtime/openai-default.ts"() {
|
|
4189
4267
|
init_dist();
|
|
4268
|
+
init_model_meter();
|
|
4190
4269
|
credential = null;
|
|
4191
4270
|
cached = null;
|
|
4192
4271
|
scope = new AsyncLocalStorage();
|
|
4272
|
+
callCredentialSource = null;
|
|
4193
4273
|
}
|
|
4194
4274
|
});
|
|
4195
4275
|
var init_ssrf = __esm({
|