experimental-ash 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Minimal Twilio REST API wrapper used by the Twilio channel.
3
+ *
4
+ * Requests use Twilio's normal `application/x-www-form-urlencoded`
5
+ * body encoding and HTTP Basic auth. No Twilio SDK dependency is
6
+ * required or exposed through Ash public APIs.
7
+ */
8
+ import { type TwilioAuthToken } from "#public/channels/twilio/verify.js";
9
+ /** Twilio Account SID, materialized directly or from an async secret provider. */
10
+ export type TwilioAccountSid = string | (() => string | Promise<string>);
11
+ /** Fetch implementation override used by tests or non-standard runtimes. */
12
+ export type TwilioFetch = typeof fetch;
13
+ /** Credentials required for Twilio REST API calls and webhook verification. */
14
+ export interface TwilioCredentials {
15
+ readonly accountSid?: TwilioAccountSid;
16
+ readonly authToken?: TwilioAuthToken;
17
+ }
18
+ /** Shared Twilio REST API options. */
19
+ export interface TwilioApiOptions {
20
+ readonly credentials?: TwilioCredentials;
21
+ readonly apiBaseUrl?: string;
22
+ readonly fetch?: TwilioFetch;
23
+ }
24
+ /** Raw Twilio REST API response body. */
25
+ export interface TwilioApiResponse {
26
+ readonly status: number;
27
+ readonly ok: boolean;
28
+ readonly body: unknown;
29
+ }
30
+ /** Parameters for creating an outbound Twilio message. */
31
+ export interface TwilioSendMessageInput extends TwilioApiOptions {
32
+ readonly to: string;
33
+ readonly body: string;
34
+ readonly from?: string;
35
+ readonly messagingServiceSid?: string;
36
+ readonly statusCallbackUrl?: string;
37
+ }
38
+ /** Parameters for updating a live Twilio call with new TwiML. */
39
+ export interface TwilioUpdateCallInput extends TwilioApiOptions {
40
+ readonly callSid: string;
41
+ readonly twiml: string;
42
+ }
43
+ /** Resolves a Twilio Account SID, falling back to `TWILIO_ACCOUNT_SID`. */
44
+ export declare function resolveTwilioAccountSid(accountSid?: TwilioAccountSid): Promise<string>;
45
+ /**
46
+ * Calls Twilio's REST API with Basic auth and form-encoded body fields.
47
+ *
48
+ * `path` is relative to `https://api.twilio.com` by default and may be
49
+ * pointed elsewhere through `apiBaseUrl` for tests or proxies.
50
+ */
51
+ export declare function callTwilioApi(input: {
52
+ readonly credentials?: TwilioCredentials;
53
+ readonly apiBaseUrl?: string;
54
+ readonly fetch?: TwilioFetch;
55
+ readonly path: string;
56
+ readonly body: Readonly<Record<string, string | number | boolean | undefined | null>>;
57
+ }): Promise<TwilioApiResponse>;
58
+ /** Sends an outbound SMS/MMS-style message via Twilio's Messages resource. */
59
+ export declare function sendTwilioMessage(input: TwilioSendMessageInput): Promise<TwilioApiResponse>;
60
+ /** Updates a live Twilio call by posting replacement TwiML to the Calls resource. */
61
+ export declare function updateTwilioCall(input: TwilioUpdateCallInput): Promise<TwilioApiResponse>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Minimal Twilio REST API wrapper used by the Twilio channel.
3
+ *
4
+ * Requests use Twilio's normal `application/x-www-form-urlencoded`
5
+ * body encoding and HTTP Basic auth. No Twilio SDK dependency is
6
+ * required or exposed through Ash public APIs.
7
+ */
8
+ import { resolveTwilioAuthToken } from "#public/channels/twilio/verify.js";
9
+ /** Resolves a Twilio Account SID, falling back to `TWILIO_ACCOUNT_SID`. */
10
+ export async function resolveTwilioAccountSid(accountSid) {
11
+ const source = accountSid ?? process.env.TWILIO_ACCOUNT_SID;
12
+ if (!source)
13
+ throw new Error("TWILIO_ACCOUNT_SID is required.");
14
+ return typeof source === "function" ? await source() : source;
15
+ }
16
+ /**
17
+ * Calls Twilio's REST API with Basic auth and form-encoded body fields.
18
+ *
19
+ * `path` is relative to `https://api.twilio.com` by default and may be
20
+ * pointed elsewhere through `apiBaseUrl` for tests or proxies.
21
+ */
22
+ export async function callTwilioApi(input) {
23
+ const accountSid = await resolveTwilioAccountSid(input.credentials?.accountSid);
24
+ const authToken = await resolveTwilioAuthToken(input.credentials?.authToken);
25
+ const apiFetch = input.fetch ?? fetch;
26
+ const url = `${input.apiBaseUrl ?? "https://api.twilio.com"}${input.path}`;
27
+ const body = encodeForm(input.body);
28
+ const response = await apiFetch(url, {
29
+ method: "POST",
30
+ headers: {
31
+ authorization: `Basic ${Buffer.from(`${accountSid}:${authToken}`).toString("base64")}`,
32
+ "content-type": "application/x-www-form-urlencoded;charset=UTF-8",
33
+ },
34
+ body,
35
+ });
36
+ return {
37
+ status: response.status,
38
+ ok: response.ok,
39
+ body: await parseResponseBody(response),
40
+ };
41
+ }
42
+ /** Sends an outbound SMS/MMS-style message via Twilio's Messages resource. */
43
+ export async function sendTwilioMessage(input) {
44
+ if (!input.from && !input.messagingServiceSid) {
45
+ throw new Error("twilioChannel: sending a message requires from or messagingServiceSid.");
46
+ }
47
+ const accountSid = await resolveTwilioAccountSid(input.credentials?.accountSid);
48
+ return callTwilioApi({
49
+ apiBaseUrl: input.apiBaseUrl,
50
+ credentials: input.credentials,
51
+ fetch: input.fetch,
52
+ path: `/2010-04-01/Accounts/${encodeURIComponent(accountSid)}/Messages.json`,
53
+ body: {
54
+ Body: input.body,
55
+ From: input.from,
56
+ MessagingServiceSid: input.messagingServiceSid,
57
+ StatusCallback: input.statusCallbackUrl,
58
+ To: input.to,
59
+ },
60
+ });
61
+ }
62
+ /** Updates a live Twilio call by posting replacement TwiML to the Calls resource. */
63
+ export async function updateTwilioCall(input) {
64
+ const accountSid = await resolveTwilioAccountSid(input.credentials?.accountSid);
65
+ return callTwilioApi({
66
+ apiBaseUrl: input.apiBaseUrl,
67
+ credentials: input.credentials,
68
+ fetch: input.fetch,
69
+ path: `/2010-04-01/Accounts/${encodeURIComponent(accountSid)}/Calls/${encodeURIComponent(input.callSid)}.json`,
70
+ body: { Twiml: input.twiml },
71
+ });
72
+ }
73
+ function encodeForm(body) {
74
+ const params = new URLSearchParams();
75
+ for (const [key, value] of Object.entries(body)) {
76
+ if (value === undefined || value === null)
77
+ continue;
78
+ params.set(key, String(value));
79
+ }
80
+ return params;
81
+ }
82
+ async function parseResponseBody(response) {
83
+ const text = await response.text();
84
+ if (!text)
85
+ return null;
86
+ try {
87
+ return JSON.parse(text);
88
+ }
89
+ catch {
90
+ return text;
91
+ }
92
+ }
@@ -0,0 +1,17 @@
1
+ import type { SessionAuthContext } from "#channel/types.js";
2
+ import type { TwilioTextMessage, TwilioVoiceCall, TwilioVoiceTranscription } from "#public/channels/twilio/inbound.js";
3
+ import type { TwilioChannelEvents, TwilioContext, TwilioInboundResult, TwilioVoiceResult } from "#public/channels/twilio/twilioChannel.js";
4
+ /** Default phone-number auth projection for Twilio webhook actors. */
5
+ export declare function defaultTwilioAuth(input: {
6
+ readonly from: string;
7
+ readonly to?: string;
8
+ readonly channel: "text" | "voice";
9
+ }): SessionAuthContext;
10
+ /** Default inbound text hook: dispatch with Twilio phone-number auth. */
11
+ export declare function defaultOnText(_ctx: TwilioContext, message: TwilioTextMessage): TwilioInboundResult;
12
+ /** Default inbound voice hook: accept the call with configured voice defaults. */
13
+ export declare function defaultOnVoice(_ctx: TwilioContext, _call: TwilioVoiceCall): TwilioVoiceResult;
14
+ /** Default inbound voice hook: dispatch with Twilio phone-number auth. */
15
+ export declare function defaultOnVoiceTranscription(_ctx: TwilioContext, transcription: TwilioVoiceTranscription): TwilioInboundResult;
16
+ /** Built-in Twilio event handlers for text delivery and terminal errors. */
17
+ export declare const defaultEvents: TwilioChannelEvents;
@@ -0,0 +1,69 @@
1
+ import { extractErrorId, formatErrorHint } from "#internal/logging.js";
2
+ /** Default phone-number auth projection for Twilio webhook actors. */
3
+ export function defaultTwilioAuth(input) {
4
+ const attributes = {
5
+ channel: input.channel,
6
+ from: input.from,
7
+ };
8
+ if (input.to !== undefined)
9
+ attributes.to = input.to;
10
+ return {
11
+ attributes,
12
+ authenticator: "twilio-webhook",
13
+ issuer: "twilio",
14
+ principalId: `twilio:${input.from}`,
15
+ principalType: "user",
16
+ };
17
+ }
18
+ /** Default inbound text hook: dispatch with Twilio phone-number auth. */
19
+ export function defaultOnText(_ctx, message) {
20
+ return {
21
+ auth: defaultTwilioAuth({
22
+ channel: "text",
23
+ from: message.from,
24
+ to: message.to,
25
+ }),
26
+ };
27
+ }
28
+ /** Default inbound voice hook: accept the call with configured voice defaults. */
29
+ export function defaultOnVoice(_ctx, _call) {
30
+ return {};
31
+ }
32
+ /** Default inbound voice hook: dispatch with Twilio phone-number auth. */
33
+ export function defaultOnVoiceTranscription(_ctx, transcription) {
34
+ return {
35
+ auth: defaultTwilioAuth({
36
+ channel: "voice",
37
+ from: transcription.from,
38
+ to: transcription.to,
39
+ }),
40
+ };
41
+ }
42
+ /** Built-in Twilio event handlers for text delivery and terminal errors. */
43
+ export const defaultEvents = {
44
+ async "message.completed"(event, ctx) {
45
+ if (event.finishReason === "tool-calls" || !event.message)
46
+ return;
47
+ await ctx.twilio.sendMessage(event.message);
48
+ },
49
+ async "turn.failed"(event, ctx) {
50
+ const hint = formatErrorHint(event);
51
+ const errorId = extractErrorId(event.details);
52
+ await ctx.twilio.sendMessage([
53
+ `I hit an error while handling your request${hint}.`,
54
+ "",
55
+ "Please try again, rephrase, or reach out if it keeps failing.",
56
+ ...(errorId ? ["", `Error id: ${errorId}`] : []),
57
+ ].join("\n"));
58
+ },
59
+ async "session.failed"(event, ctx) {
60
+ const hint = formatErrorHint(event);
61
+ const errorId = extractErrorId(event.details);
62
+ await ctx.twilio.sendMessage([
63
+ `This session could not recover from an error${hint}.`,
64
+ "",
65
+ "Start a new message to continue.",
66
+ ...(errorId ? ["", `Error id: ${errorId}`] : []),
67
+ ].join("\n"));
68
+ },
69
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Twilio inbound webhook parsing and prompt shaping.
3
+ *
4
+ * The channel owns these small data shapes instead of exposing raw
5
+ * Twilio webhook payloads as the public API surface.
6
+ */
7
+ import type { UserContent } from "ai";
8
+ /** Channel-owned representation of one inbound Twilio text message. */
9
+ export interface TwilioTextMessage {
10
+ readonly from: string;
11
+ readonly to: string | undefined;
12
+ readonly body: string;
13
+ readonly messageSid: string | undefined;
14
+ readonly accountSid: string | undefined;
15
+ readonly raw: URLSearchParams;
16
+ }
17
+ /** Channel-owned representation of one inbound Twilio voice call. */
18
+ export interface TwilioVoiceCall {
19
+ readonly from: string;
20
+ readonly to: string | undefined;
21
+ readonly callSid: string | undefined;
22
+ readonly accountSid: string | undefined;
23
+ readonly raw: URLSearchParams;
24
+ }
25
+ /** Channel-owned representation of one inbound Twilio voice transcription. */
26
+ export interface TwilioVoiceTranscription {
27
+ readonly from: string;
28
+ readonly to: string | undefined;
29
+ readonly callSid: string | undefined;
30
+ readonly text: string;
31
+ readonly confidence: number | undefined;
32
+ readonly transcriptionSid: string | undefined;
33
+ readonly raw: URLSearchParams;
34
+ }
35
+ /** Inbound identity and response guidance rendered into the model-visible `<twilio_context>` block. */
36
+ export interface TwilioInboundContext {
37
+ readonly from: string;
38
+ readonly to?: string;
39
+ readonly messageSid?: string;
40
+ readonly callSid?: string;
41
+ readonly channel: "text" | "voice";
42
+ }
43
+ /** Parses Twilio's incoming-message webhook fields. */
44
+ export declare function parseTwilioTextMessage(params: URLSearchParams): TwilioTextMessage | null;
45
+ /** Parses Twilio's incoming-call webhook fields. */
46
+ export declare function parseTwilioVoiceCall(params: URLSearchParams): TwilioVoiceCall | null;
47
+ /**
48
+ * Parses Twilio voice transcription fields.
49
+ *
50
+ * Supports `<Gather input="speech">` (`SpeechResult`), recording
51
+ * transcription callbacks (`TranscriptionText`), and real-time
52
+ * transcription callbacks (`TranscriptionData` JSON). Real-time partial
53
+ * results are ignored until Twilio marks them final.
54
+ */
55
+ export declare function parseTwilioVoiceTranscription(params: URLSearchParams): TwilioVoiceTranscription | null;
56
+ /** Renders a deterministic `<twilio_context>` block for the model. */
57
+ export declare function formatTwilioContextBlock(context: TwilioInboundContext): string;
58
+ /** Prepends a `<twilio_context>` block to the inbound turn message. */
59
+ export declare function prependTwilioContext(message: string | UserContent, context: TwilioInboundContext): string | UserContent;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Twilio inbound webhook parsing and prompt shaping.
3
+ *
4
+ * The channel owns these small data shapes instead of exposing raw
5
+ * Twilio webhook payloads as the public API surface.
6
+ */
7
+ const TWILIO_SMS_RESPONSE_INSTRUCTIONS = "Reply for SMS in plain text. Keep the response concise and avoid Markdown formatting, " +
8
+ "tables, headings, code fences, and long lists. Ask at most one short follow-up question " +
9
+ "when more information is needed.";
10
+ /** Parses Twilio's incoming-message webhook fields. */
11
+ export function parseTwilioTextMessage(params) {
12
+ const from = requiredParam(params, "From");
13
+ const body = requiredParam(params, "Body");
14
+ if (!from || !body)
15
+ return null;
16
+ return {
17
+ accountSid: optionalParam(params, "AccountSid"),
18
+ body,
19
+ from,
20
+ messageSid: optionalParam(params, "MessageSid") ?? optionalParam(params, "SmsMessageSid"),
21
+ raw: params,
22
+ to: optionalParam(params, "To"),
23
+ };
24
+ }
25
+ /** Parses Twilio's incoming-call webhook fields. */
26
+ export function parseTwilioVoiceCall(params) {
27
+ const from = requiredParam(params, "From") ?? requiredParam(params, "Caller");
28
+ if (!from)
29
+ return null;
30
+ return {
31
+ accountSid: optionalParam(params, "AccountSid"),
32
+ callSid: optionalParam(params, "CallSid"),
33
+ from,
34
+ raw: params,
35
+ to: optionalParam(params, "To") ?? optionalParam(params, "Called"),
36
+ };
37
+ }
38
+ /**
39
+ * Parses Twilio voice transcription fields.
40
+ *
41
+ * Supports `<Gather input="speech">` (`SpeechResult`), recording
42
+ * transcription callbacks (`TranscriptionText`), and real-time
43
+ * transcription callbacks (`TranscriptionData` JSON). Real-time partial
44
+ * results are ignored until Twilio marks them final.
45
+ */
46
+ export function parseTwilioVoiceTranscription(params) {
47
+ const from = requiredParam(params, "From") ?? requiredParam(params, "Caller");
48
+ if (!from)
49
+ return null;
50
+ const parsedData = parseTranscriptionData(optionalParam(params, "TranscriptionData"));
51
+ const final = optionalParam(params, "Final");
52
+ if (final === "false")
53
+ return null;
54
+ const text = optionalParam(params, "SpeechResult") ??
55
+ optionalParam(params, "TranscriptionText") ??
56
+ parsedData?.transcript ??
57
+ "";
58
+ if (!text.trim())
59
+ return null;
60
+ return {
61
+ callSid: optionalParam(params, "CallSid"),
62
+ confidence: parseConfidence(optionalParam(params, "Confidence") ?? parsedData?.confidence),
63
+ from,
64
+ raw: params,
65
+ text,
66
+ to: optionalParam(params, "To") ?? optionalParam(params, "Called"),
67
+ transcriptionSid: optionalParam(params, "TranscriptionSid"),
68
+ };
69
+ }
70
+ /** Renders a deterministic `<twilio_context>` block for the model. */
71
+ export function formatTwilioContextBlock(context) {
72
+ const lines = [
73
+ "<twilio_context>",
74
+ `channel: ${context.channel}`,
75
+ "response_medium: sms",
76
+ `response_instructions: ${TWILIO_SMS_RESPONSE_INSTRUCTIONS}`,
77
+ `from: ${context.from}`,
78
+ ...(context.to ? [`to: ${context.to}`] : []),
79
+ ...(context.messageSid ? [`message_sid: ${context.messageSid}`] : []),
80
+ ...(context.callSid ? [`call_sid: ${context.callSid}`] : []),
81
+ "</twilio_context>",
82
+ ];
83
+ return lines.join("\n");
84
+ }
85
+ /** Prepends a `<twilio_context>` block to the inbound turn message. */
86
+ export function prependTwilioContext(message, context) {
87
+ const block = formatTwilioContextBlock(context);
88
+ if (typeof message === "string") {
89
+ return message.length > 0 ? `${block}\n\n${message}` : block;
90
+ }
91
+ const contextPart = { type: "text", text: block };
92
+ return [contextPart, ...message];
93
+ }
94
+ function requiredParam(params, name) {
95
+ const value = params.get(name);
96
+ return value && value.trim().length > 0 ? value : null;
97
+ }
98
+ function optionalParam(params, name) {
99
+ const value = params.get(name);
100
+ return value === null || value.length === 0 ? undefined : value;
101
+ }
102
+ function parseTranscriptionData(value) {
103
+ if (!value)
104
+ return null;
105
+ try {
106
+ const parsed = JSON.parse(value);
107
+ return {
108
+ confidence: typeof parsed.confidence === "number" || typeof parsed.confidence === "string"
109
+ ? String(parsed.confidence)
110
+ : undefined,
111
+ transcript: typeof parsed.transcript === "string" ? parsed.transcript : undefined,
112
+ };
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ function parseConfidence(value) {
119
+ if (value === undefined)
120
+ return undefined;
121
+ const parsed = Number(value);
122
+ return Number.isFinite(parsed) ? parsed : undefined;
123
+ }
@@ -0,0 +1,5 @@
1
+ export { twilioChannel, type TwilioAllowFrom, type TwilioChannel, type TwilioChannelConfig, type TwilioChannelCredentials, type TwilioChannelEvents, type TwilioChannelState, type TwilioContext, type TwilioEventContext, type TwilioHandle, type TwilioInboundResult, type TwilioInboundResultOrPromise, type TwilioMessagingConfig, type TwilioReceiveArgs, type TwilioSendMessageOptions, type TwilioVoiceConfig, type TwilioVoiceResult, type TwilioVoiceResultOrPromise, } from "#public/channels/twilio/twilioChannel.js";
2
+ export { callTwilioApi, resolveTwilioAccountSid, sendTwilioMessage, updateTwilioCall, type TwilioAccountSid, type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials, type TwilioFetch, type TwilioSendMessageInput, type TwilioUpdateCallInput, } from "#public/channels/twilio/api.js";
3
+ export type { TwilioInboundContext, TwilioTextMessage, TwilioVoiceCall, TwilioVoiceTranscription, } from "#public/channels/twilio/inbound.js";
4
+ export { emptyTwilioResponse, escapeXml, gatherSpeechTwilioResponse, sayTwilioResponse, twimlResponse, type TwilioGatherTwimlOptions, } from "#public/channels/twilio/twiml.js";
5
+ export { buildTwilioSignatureBase, resolveTwilioAuthToken, signTwilioRequest, verifyTwilioRequest, type TwilioAuthToken, type TwilioVerifiedRequest, type TwilioVerifyOptions, type TwilioWebhookUrl, } from "#public/channels/twilio/verify.js";
@@ -0,0 +1,4 @@
1
+ export { twilioChannel, } from "#public/channels/twilio/twilioChannel.js";
2
+ export { callTwilioApi, resolveTwilioAccountSid, sendTwilioMessage, updateTwilioCall, } from "#public/channels/twilio/api.js";
3
+ export { emptyTwilioResponse, escapeXml, gatherSpeechTwilioResponse, sayTwilioResponse, twimlResponse, } from "#public/channels/twilio/twiml.js";
4
+ export { buildTwilioSignatureBase, resolveTwilioAuthToken, signTwilioRequest, verifyTwilioRequest, } from "#public/channels/twilio/verify.js";
@@ -0,0 +1,179 @@
1
+ import type { SessionAuthContext } from "#channel/types.js";
2
+ import type { HandleMessageStreamEvent } from "#protocol/message.js";
3
+ import { type TwilioApiOptions, type TwilioApiResponse, type TwilioCredentials } from "#public/channels/twilio/api.js";
4
+ import { type TwilioTextMessage, type TwilioVoiceCall, type TwilioVoiceTranscription } from "#public/channels/twilio/inbound.js";
5
+ import { type TwilioAuthToken, type TwilioWebhookUrl } from "#public/channels/twilio/verify.js";
6
+ import { type Channel } from "#public/definitions/defineChannel.js";
7
+ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
8
+ type: T;
9
+ }> extends {
10
+ data: infer D;
11
+ } ? D : undefined;
12
+ /** Pre-dispatch Twilio context passed to inbound text and voice hooks. */
13
+ export interface TwilioContext {
14
+ readonly twilio: TwilioHandle;
15
+ }
16
+ /** Event-handler Twilio context, including mutable per-phone channel state. */
17
+ export interface TwilioEventContext extends TwilioContext {
18
+ state: TwilioChannelState;
19
+ }
20
+ /** JSON-serializable state for the phone-number conversation. */
21
+ export interface TwilioChannelState {
22
+ /** Caller / sender phone number. */
23
+ from: string | null;
24
+ /** Twilio number or sender that received the latest session-starting webhook. */
25
+ to: string | null;
26
+ /** Most recent inbound SMS SID when this session was started by text. */
27
+ lastMessageSid?: string | null;
28
+ /** Most recent inbound Call SID when this session was started by voice. */
29
+ lastCallSid?: string | null;
30
+ }
31
+ /** Twilio channel credentials. `authToken` also verifies inbound webhook signatures. */
32
+ export interface TwilioChannelCredentials extends TwilioCredentials {
33
+ readonly authToken?: TwilioAuthToken;
34
+ }
35
+ /** Arguments accepted by `receive(twilio, args)` for proactive phone-number sessions. */
36
+ export interface TwilioReceiveArgs {
37
+ readonly phoneNumber: string;
38
+ /** Twilio sender included in the phone-pair continuation token. */
39
+ readonly from?: string;
40
+ }
41
+ /** Result of an inbound Twilio text or transcription hook. Return `null` to drop the webhook. */
42
+ export type TwilioInboundResult = {
43
+ auth: SessionAuthContext | null;
44
+ } | null;
45
+ /** Sync or async {@link TwilioInboundResult}. */
46
+ export type TwilioInboundResultOrPromise = TwilioInboundResult | Promise<TwilioInboundResult>;
47
+ /** Phone-number allow list for inbound Twilio webhook triggers. `"*"` allows every sender. */
48
+ export type TwilioAllowFrom = string | readonly string[] | (() => string | readonly string[] | Promise<string | readonly string[]>);
49
+ /**
50
+ * Result of an inbound Twilio voice hook. Return `null` to reject the call.
51
+ * Any result other than `null` accepts the call and can override the answering TwiML.
52
+ */
53
+ export interface TwilioVoiceResult {
54
+ /** Prompt spoken before Twilio starts speech recognition. */
55
+ readonly prompt?: string;
56
+ /** BCP 47 language used for speech recognition and the nested `<Say>` prompt. */
57
+ readonly language?: string;
58
+ /** Twilio `<Say voice>` used for the prompt, e.g. `Polly.Joanna-Neural`. */
59
+ readonly voice?: string;
60
+ /** Twilio `<Gather speechModel>` used for speech recognition. */
61
+ readonly speechModel?: string;
62
+ /** Twilio `<Gather timeout>` in seconds. */
63
+ readonly timeoutSeconds?: number;
64
+ /** Twilio `<Gather speechTimeout>`, such as `"auto"` or a second count string. */
65
+ readonly speechTimeout?: string;
66
+ /** Twilio `<Gather hints>` for expected words or phrases. */
67
+ readonly hints?: string | readonly string[];
68
+ /** Twilio `<Gather profanityFilter>` toggle. */
69
+ readonly profanityFilter?: boolean;
70
+ }
71
+ /** Sync or async {@link TwilioVoiceResult}. */
72
+ export type TwilioVoiceResultOrPromise = TwilioVoiceResult | null | undefined | Promise<TwilioVoiceResult | null | undefined>;
73
+ type TwilioEventHandler<T extends HandleMessageStreamEvent["type"]> = (data: EventData<T>, ctx: TwilioEventContext) => void | Promise<void>;
74
+ /** Event handlers supported by `twilioChannel({ events })`. */
75
+ export interface TwilioChannelEvents {
76
+ readonly "turn.started"?: TwilioEventHandler<"turn.started">;
77
+ readonly "actions.requested"?: TwilioEventHandler<"actions.requested">;
78
+ readonly "action.result"?: TwilioEventHandler<"action.result">;
79
+ readonly "message.completed"?: TwilioEventHandler<"message.completed">;
80
+ readonly "message.appended"?: TwilioEventHandler<"message.appended">;
81
+ readonly "input.requested"?: TwilioEventHandler<"input.requested">;
82
+ readonly "turn.failed"?: TwilioEventHandler<"turn.failed">;
83
+ readonly "turn.completed"?: TwilioEventHandler<"turn.completed">;
84
+ readonly "session.failed"?: TwilioEventHandler<"session.failed">;
85
+ readonly "session.completed"?: TwilioEventHandler<"session.completed">;
86
+ readonly "session.waiting"?: TwilioEventHandler<"session.waiting">;
87
+ readonly "connection.authorization_required"?: TwilioEventHandler<"connection.authorization_required">;
88
+ readonly "connection.authorization_pending"?: TwilioEventHandler<"connection.authorization_pending">;
89
+ readonly "connection.authorization_completed"?: TwilioEventHandler<"connection.authorization_completed">;
90
+ }
91
+ /** SMS/Messaging defaults for Twilio outbound replies. */
92
+ export interface TwilioMessagingConfig {
93
+ /** Sender phone number. Defaults to the inbound `To` number when available. */
94
+ readonly from?: string;
95
+ /** Messaging Service SID. Used instead of `from` when supplied. */
96
+ readonly messagingServiceSid?: string;
97
+ /** Optional Twilio status callback URL for outbound messages. */
98
+ readonly statusCallbackUrl?: string;
99
+ }
100
+ /** Voice webhook defaults for accepting calls and gathering speech. */
101
+ export interface TwilioVoiceConfig {
102
+ /** Prompt spoken when a caller reaches the voice route. */
103
+ readonly prompt?: string;
104
+ /** Twilio `<Say voice>` used for the prompt, e.g. `Polly.Joanna-Neural`. */
105
+ readonly voice?: string;
106
+ /** Spoken acknowledgement after a transcription webhook is accepted. */
107
+ readonly acknowledgement?: string;
108
+ /** BCP 47 language used for speech recognition and the nested `<Say>` prompt. */
109
+ readonly language?: string;
110
+ /** Twilio `<Gather speechModel>` used for speech recognition. */
111
+ readonly speechModel?: string;
112
+ /** Twilio `<Gather timeout>` in seconds. */
113
+ readonly timeoutSeconds?: number;
114
+ /** Twilio `<Gather speechTimeout>`, such as `"auto"` or a second count string. */
115
+ readonly speechTimeout?: string;
116
+ /** Twilio `<Gather hints>` for expected words or phrases. */
117
+ readonly hints?: string | readonly string[];
118
+ /** Twilio `<Gather profanityFilter>` toggle. */
119
+ readonly profanityFilter?: boolean;
120
+ }
121
+ /** Configuration for {@link twilioChannel}. */
122
+ export interface TwilioChannelConfig {
123
+ readonly credentials?: TwilioChannelCredentials;
124
+ /**
125
+ * Base route for Twilio webhooks. Defaults to `/ash/v1/twilio` and
126
+ * mounts `/messages`, `/voice`, and `/voice/transcription` below it.
127
+ */
128
+ readonly route?: string;
129
+ /**
130
+ * Public URL Twilio used for signing. Set this when proxies or local
131
+ * tunnels make `request.url` differ from the configured webhook URL.
132
+ */
133
+ readonly webhookUrl?: TwilioWebhookUrl;
134
+ /** Public base URL used to render absolute voice `<Gather action>` URLs. */
135
+ readonly publicBaseUrl?: string | ((request: Request) => string | Promise<string>);
136
+ /**
137
+ * Exact caller/sender numbers allowed to reach inbound hooks, or `"*"` to
138
+ * allow every verified Twilio sender. Resolvers run on each inbound webhook.
139
+ */
140
+ readonly allowFrom: TwilioAllowFrom;
141
+ readonly messaging?: TwilioMessagingConfig;
142
+ readonly voice?: TwilioVoiceConfig;
143
+ readonly api?: Omit<TwilioApiOptions, "credentials">;
144
+ /** Inbound text hook. Defaults to phone-number auth and dispatch. */
145
+ onText?(ctx: TwilioContext, message: TwilioTextMessage): TwilioInboundResultOrPromise;
146
+ /** Inbound voice hook. Return `null` to reject the call before gathering speech. */
147
+ onVoice?(ctx: TwilioContext, call: TwilioVoiceCall): TwilioVoiceResultOrPromise;
148
+ /** Inbound voice transcription hook. Defaults to phone-number auth and dispatch. */
149
+ onVoiceTranscription?(ctx: TwilioContext, transcription: TwilioVoiceTranscription): TwilioInboundResultOrPromise;
150
+ readonly events?: TwilioChannelEvents;
151
+ }
152
+ /** Low-level Twilio handle exposed to hooks and event handlers. */
153
+ export interface TwilioHandle {
154
+ /** Caller / sender phone number bound to this conversation. */
155
+ readonly from: string;
156
+ /** Twilio receiver / sender number for replies, when known. */
157
+ readonly to: string | undefined;
158
+ /** Most recent call SID, when the session started from a voice transcription. */
159
+ readonly callSid: string | undefined;
160
+ /** Raw Twilio REST API escape hatch. */
161
+ request(path: string, body: Readonly<Record<string, string | number | boolean | undefined | null>>): Promise<TwilioApiResponse>;
162
+ /** Sends a text message to this conversation's phone number by default. */
163
+ sendMessage(message: string, options?: TwilioSendMessageOptions): Promise<TwilioApiResponse>;
164
+ /** Updates a live call with replacement TwiML. */
165
+ updateCall(callSid: string, twiml: string): Promise<TwilioApiResponse>;
166
+ }
167
+ /** Per-call overrides for {@link TwilioHandle.sendMessage}. */
168
+ export interface TwilioSendMessageOptions {
169
+ readonly to?: string;
170
+ readonly from?: string;
171
+ readonly messagingServiceSid?: string;
172
+ readonly statusCallbackUrl?: string;
173
+ }
174
+ /** Concrete return type of {@link twilioChannel}. */
175
+ export interface TwilioChannel extends Channel<TwilioChannelState> {
176
+ }
177
+ /** Twilio channel factory for SMS and speech-transcribed inbound calls. */
178
+ export declare function twilioChannel(config: TwilioChannelConfig): TwilioChannel;
179
+ export {};