@finalai/final-agent-provider 0.8.0 → 0.10.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.
Files changed (53) hide show
  1. package/HOSTING.md +3 -3
  2. package/README.md +7 -91
  3. package/dist/modules/channel-factories.d.mts +5 -6
  4. package/dist/modules/channel-factories.mjs +7 -11
  5. package/dist/modules/channel-factories.mjs.map +3 -3
  6. package/dist/modules/channels/inbound.mjs +2 -2
  7. package/dist/modules/channels/inbound.mjs.map +2 -2
  8. package/dist/modules/channels/index.d.mts +0 -7
  9. package/dist/modules/channels/index.mjs +0 -7
  10. package/dist/modules/channels/index.mjs.map +2 -2
  11. package/dist/modules/channels/prompt.d.mts +2 -2
  12. package/dist/modules/channels/prompt.mjs +3 -3
  13. package/dist/modules/channels/prompt.mjs.map +2 -2
  14. package/dist/modules/channels/routing.d.mts +1 -1
  15. package/dist/modules/channels/routing.mjs +1 -1
  16. package/dist/modules/channels/routing.mjs.map +1 -1
  17. package/package.json +4 -40
  18. package/dist/modules/channels/channel-utils.d.mts +0 -132
  19. package/dist/modules/channels/channel-utils.mjs +0 -445
  20. package/dist/modules/channels/channel-utils.mjs.map +0 -7
  21. package/dist/modules/channels/discord-dispatch.d.mts +0 -17
  22. package/dist/modules/channels/discord-dispatch.mjs +0 -139
  23. package/dist/modules/channels/discord-dispatch.mjs.map +0 -7
  24. package/dist/modules/channels/discord-gateway.d.mts +0 -19
  25. package/dist/modules/channels/discord-gateway.mjs +0 -233
  26. package/dist/modules/channels/discord-gateway.mjs.map +0 -7
  27. package/dist/modules/channels/discord.d.mts +0 -9
  28. package/dist/modules/channels/discord.mjs +0 -904
  29. package/dist/modules/channels/discord.mjs.map +0 -7
  30. package/dist/modules/channels/github.d.mts +0 -5
  31. package/dist/modules/channels/github.mjs +0 -720
  32. package/dist/modules/channels/github.mjs.map +0 -7
  33. package/dist/modules/channels/linear.d.mts +0 -4
  34. package/dist/modules/channels/linear.mjs +0 -621
  35. package/dist/modules/channels/linear.mjs.map +0 -7
  36. package/dist/modules/channels/phone.d.mts +0 -8
  37. package/dist/modules/channels/phone.mjs +0 -382
  38. package/dist/modules/channels/phone.mjs.map +0 -7
  39. package/dist/modules/channels/reply-receipts.d.mts +0 -31
  40. package/dist/modules/channels/reply-receipts.mjs +0 -63
  41. package/dist/modules/channels/reply-receipts.mjs.map +0 -7
  42. package/dist/modules/channels/slack.d.mts +0 -7
  43. package/dist/modules/channels/slack.mjs +0 -1134
  44. package/dist/modules/channels/slack.mjs.map +0 -7
  45. package/dist/modules/channels/voice-recording.d.mts +0 -7
  46. package/dist/modules/channels/voice-recording.mjs +0 -49
  47. package/dist/modules/channels/voice-recording.mjs.map +0 -7
  48. package/dist/modules/channels/voice.d.mts +0 -33
  49. package/dist/modules/channels/voice.mjs +0 -851
  50. package/dist/modules/channels/voice.mjs.map +0 -7
  51. package/dist/modules/channels/webhook.d.mts +0 -5
  52. package/dist/modules/channels/webhook.mjs +0 -76
  53. package/dist/modules/channels/webhook.mjs.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/channels/voice.ts"],
4
- "sourcesContent": ["import {\n defineChannel,\n packageRef,\n principalRefSchema,\n type ChannelReceiveContextIface,\n type ChannelSocketIface,\n type JsonObject,\n type JsonValue,\n type PlaceRef,\n type PrincipalRef,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { bytesToBase64 } from '@finalai/agent-provider/sandbox/bytes';\nimport { recordingRawLimit, recordingWav } from \"./voice-recording.mjs\";\nimport {\n abortable,\n channelBaseUrl,\n confirmOAuth,\n createQueue,\n decisionChoices,\n decisionSummary,\n decisionAnswer,\n textOf,\n turnStatus,\n type DecisionChoiceIface,\n parseJson,\n personFor,\n responseJson,\n startOAuth,\n takeOAuth,\n} from \"./channel-utils.mjs\";\n\nconst configSchema = z.object({\n baseUrl: z.string().url().optional(),\n oauthAuthorizeUrl: z.string().url().optional(),\n oauthTokenUrl: z.string().url().optional(),\n oauthUserInfoUrl: z.string().url().optional(),\n oauthClientId: z.string().url().optional(),\n oauthResource: z.string().url().optional(),\n voice: z.string().optional(),\n});\nexport type VoiceConfig = z.infer<typeof configSchema>;\nconst userInfoSchema = z\n .object({ sub: z.string(), organizationId: z.string(), displayName: z.string() })\n .transform((value) => ({\n id: value.sub,\n tenant: value.organizationId,\n displayName: value.displayName,\n }));\nconst startSchema = z.object({\n type: z.literal('start'),\n encoding: z.literal('linear16'),\n sampleRate: z.literal(16000),\n principal: principalRefSchema,\n recording: z.boolean().default(false),\n});\nexport interface VoiceConnectionOptionsIface<Config> {\n ctx: ChannelReceiveContextIface<Config>;\n socket: ChannelSocketIface;\n principal: PrincipalRef;\n place: PlaceRef;\n encoding: string;\n sampleRate: number;\n voice?: string;\n recording?: boolean;\n}\n// A single channel owns transcription, posting, observation and synthesis on every transport.\nexport const runVoiceConnection = async <Config>(\n options: VoiceConnectionOptionsIface<Config>,\n): Promise<void> => {\n const { ctx, socket } = options;\n const failed = new AbortController();\n const signal = AbortSignal.any([socket.signal, failed.signal]);\n const send = (data: string | Uint8Array): void => {\n if (!signal.aborted) socket.send(data);\n };\n const connectionKey = crypto.randomUUID();\n const trace = (\n stage: string,\n detail: { utterance?: string; turnId?: string; at?: number } = {},\n ) => {\n const metadata = { connectionId: connectionKey, at: Date.now(), ...detail };\n send(JSON.stringify({ type: 'trace', stage, ...metadata }));\n ctx.log(`voice.${stage}`, metadata);\n };\n let activeSpeech: AbortController | null = null;\n let activeRefusal: AbortController | null = null;\n let refusalPlayback: Promise<void> | null = null;\n let playbackGeneration = 0;\n const speakRefusal = async (message: string, refusalSignal: AbortSignal = signal) => {\n const preceding = refusalPlayback;\n const finished = Promise.withResolvers<void>();\n refusalPlayback = finished.promise;\n const speech = new AbortController();\n activeRefusal?.abort();\n activeRefusal = speech;\n activeSpeech?.abort();\n const playbackVersion = ++playbackGeneration;\n send(JSON.stringify({ type: 'speech.start' }));\n const outputSignal = AbortSignal.any([refusalSignal, speech.signal]);\n try {\n await preceding;\n const text = async function* () {\n yield message;\n };\n for await (const frame of abortable(\n ctx.media.synthesize(text(), {\n voice: options.voice,\n encoding: options.encoding,\n sampleRate: options.sampleRate,\n signal: outputSignal,\n }),\n outputSignal,\n )) {\n if (playbackVersion !== playbackGeneration || outputSignal.aborted) break;\n send(frame);\n }\n } finally {\n speech.abort();\n if (activeRefusal === speech) activeRefusal = null;\n finished.resolve();\n if (refusalPlayback === finished.promise) refusalPlayback = null;\n }\n };\n const person = await personFor(ctx, options.principal);\n const route = await ctx.places.resolve(options.place);\n if (\n typeof person !== 'string' &&\n (person.refused !== 'unlinked' ||\n route.kind !== 'connected' ||\n route.policy.audience !== 'anyone')\n ) {\n send(JSON.stringify({ type: 'linked', linkUrl: person.linkUrl, message: person.message }));\n await speakRefusal(person.message);\n socket.close(1008, 'Account link required');\n return;\n }\n const from = typeof person === 'string' ? person : undefined;\n const conversation = await (async () => {\n try {\n return route.kind === 'connected'\n ? route.conversation\n : route.defaults == null || from == null\n ? null\n : await ctx.places.connect(options.place, { by: from, create: route.defaults.create });\n } catch {\n return null;\n }\n })();\n if (conversation == null) {\n const message = 'This call could not connect to a chat. Check your access in Final.';\n send(JSON.stringify({ type: 'notice', message }));\n await speakRefusal(message, AbortSignal.any([signal, AbortSignal.timeout(10_000)]));\n send(JSON.stringify({ type: 'speech.end', final: true }));\n socket.close(1000);\n return;\n }\n const recordingEnabled = options.recording !== false;\n const recordingLimit = recordingEnabled ? recordingRawLimit(options.encoding) : null;\n if (recordingEnabled)\n send(\n JSON.stringify({\n type: 'recording.notice',\n message: 'Your transcript and recording are saved to this chat.',\n }),\n );\n const audio = createQueue<Uint8Array>();\n const transcriptionStopped = new AbortController();\n const transcriptionSignal = AbortSignal.any([signal, transcriptionStopped.signal]);\n let terminalRefusal = false;\n let microphoneInputClosed = false;\n const closeAudio = () => {\n microphoneInputClosed = true;\n audio.close();\n };\n const microphoneEnded = new AbortController();\n const microphoneLifecycle: { onEnd: (() => void) | null; stopObservation: (() => void) | null } =\n { onEnd: null, stopObservation: null };\n const utterances = createQueue<{ text: string; key: string; finalAt: number }>();\n let queuedAudioBytes = 0;\n const transcriptionAudio = async function* (): AsyncGenerator<Uint8Array> {\n for await (const frame of audio.stream) {\n queuedAudioBytes -= frame.byteLength;\n yield frame;\n }\n };\n let recording: Uint8Array[] = [];\n let recordingSize = 0;\n let segment = 0;\n let generation = 0;\n let activeTurn = false;\n const turnLifecycle: { accepting: { interrupted: boolean } | null; activeId: string | null } = {\n accepting: null,\n activeId: null,\n };\n let audioStarted = false;\n let userSpeaking = false;\n let utteranceNumber = 0;\n let decision: { id: string; choices: DecisionChoiceIface[] } | null = null;\n const pendingDecisions = new Map<string, { id: string; choices: DecisionChoiceIface[] }>();\n const clearDecisions = () => {\n pendingDecisions.clear();\n decision = null;\n };\n const fail = (cause: unknown): void => {\n if (signal.aborted) return;\n ctx.log('voice connection failed', {\n error: cause instanceof Error ? cause.message : 'stream failed',\n });\n send(\n JSON.stringify({ type: 'error', message: 'The voice connection failed. Please reconnect.' }),\n );\n socket.close(1011, 'Voice connection failed');\n failed.abort();\n closeAudio();\n utterances.close();\n };\n interface RecordingSegmentIface {\n chunks: Uint8Array[];\n bytes: number;\n index: number;\n encoded: string | null;\n }\n let upload: {\n segment: RecordingSegmentIface;\n result: Promise<{ ok: true } | { error: unknown }>;\n } | null = null;\n const attachRecording = async (\n part: RecordingSegmentIface,\n deadline?: AbortSignal,\n ): Promise<void> => {\n deadline?.throwIfAborted();\n if (part.encoded == null) {\n part.encoded = bytesToBase64(\n recordingWav({\n chunks: part.chunks,\n bytes: part.bytes,\n encoding: options.encoding,\n sampleRate: options.sampleRate,\n }),\n );\n // Retries reuse the same payload without retaining its raw copy.\n part.chunks = [];\n }\n const result = await ctx.conversation(conversation).attach({\n from,\n label: `Voice recording, part ${part.index}`,\n origin: { place: options.place },\n idempotencyKey: `${options.place.id}:${connectionKey}:recording:${part.index}`,\n content: [{ type: 'audio', data: part.encoded, mimeType: 'audio/wav' }],\n });\n if ('refused' in result) throw new Error(result.message);\n };\n const beginRecordingUpload = (deadline?: AbortSignal): void => {\n if (recordingSize === 0) return;\n const part: RecordingSegmentIface = {\n chunks: recording,\n bytes: recordingSize,\n index: ++segment,\n encoded: null,\n };\n recording = [];\n recordingSize = 0;\n upload = {\n segment: part,\n result: attachRecording(part, deadline).then(\n () => ({ ok: true }),\n (error: unknown) => ({ error }),\n ),\n };\n };\n const settleRecordingUpload = async (retry: boolean, deadline?: AbortSignal): Promise<void> => {\n const pending = upload;\n if (pending == null) return;\n const stop = deadline ?? signal;\n const interrupted = Promise.withResolvers<never>();\n const abortUpload = () =>\n interrupted.reject(\n stop.reason instanceof Error ? stop.reason : new Error('Recording upload interrupted.'),\n );\n stop.addEventListener('abort', abortUpload, { once: true });\n if (stop.aborted) abortUpload();\n let result: { ok: true } | { error: unknown };\n try {\n result = await Promise.race([pending.result, interrupted.promise]);\n if ('error' in result) {\n if (!retry) throw result.error;\n await Promise.race([attachRecording(pending.segment, deadline), interrupted.promise]);\n }\n } finally {\n stop.removeEventListener('abort', abortUpload);\n }\n if (upload === pending) upload = null;\n };\n const rotateRecording = async (): Promise<void> => {\n // At most one segment uploads while the next accumulates; only a second full segment waits.\n await settleRecordingUpload(true);\n signal.throwIfAborted();\n beginRecordingUpload();\n };\n const finishRecording = async (): Promise<void> => {\n const deadline = new AbortController();\n const expired = Promise.withResolvers<never>();\n const timer = setTimeout(() => {\n const cause = new Error('Recording cleanup timed out.');\n deadline.abort(cause);\n expired.reject(cause);\n }, 10_000);\n const finish = async () => {\n let failure: Error | null = null;\n try {\n await settleRecordingUpload(true, deadline.signal);\n } catch (cause) {\n failure = cause instanceof Error ? cause : new Error('Recording attachment failed.');\n upload = null;\n }\n if (recordingSize > 0 && !deadline.signal.aborted) {\n try {\n beginRecordingUpload(deadline.signal);\n await settleRecordingUpload(true, deadline.signal);\n } catch (cause) {\n failure ??= cause instanceof Error ? cause : new Error('Recording attachment failed.');\n }\n }\n if (failure != null) throw failure;\n };\n try {\n await Promise.race([finish(), expired.promise]);\n } finally {\n clearTimeout(timer);\n deadline.abort();\n }\n };\n const reportRefusal = async (\n message: string,\n options: { recoverable?: boolean } = {},\n ): Promise<void> => {\n if (terminalRefusal) return;\n const recoverable =\n options.recoverable !== false &&\n (from == null ||\n (await ctx.people.identify(from).then(\n (principal) => principal != null,\n () => false,\n )));\n if (terminalRefusal) return;\n if (recoverable) {\n send(JSON.stringify({ type: 'error', message, recoverable: true }));\n await speakRefusal(message);\n return;\n }\n terminalRefusal = true;\n closeAudio();\n transcriptionStopped.abort();\n utterances.close();\n activeSpeech?.abort();\n microphoneLifecycle.stopObservation?.();\n send(JSON.stringify({ type: 'notice', message }));\n await speakRefusal(message, AbortSignal.any([signal, AbortSignal.timeout(10_000)]));\n };\n const requestCancellation = async (turnId: string): Promise<void> => {\n try {\n const result = await ctx.turns.cancel({ conversation, by: from, turnId });\n if ('refused' in result) await reportRefusal(result.message);\n else if (!result.accepted)\n await reportRefusal(\n 'No running turn could be interrupted from this connection. Check Final if work is still running.',\n );\n } catch {\n await reportRefusal('The turn could not be interrupted. Try stopping it in Final.');\n }\n };\n const cancel = async (withdrawDecision = false) => {\n playbackGeneration += 1;\n activeSpeech?.abort();\n send(JSON.stringify({ type: 'speech.start' }));\n if (turnLifecycle.accepting != null) {\n turnLifecycle.accepting.interrupted = true;\n generation += 1;\n return;\n }\n if (activeTurn && (decision == null || withdrawDecision)) {\n clearDecisions();\n generation += 1;\n microphoneLifecycle.stopObservation?.();\n const target = turnLifecycle.activeId;\n if (target != null) await requestCancellation(target);\n }\n };\n const answerDecision = async (text: string): Promise<boolean> => {\n if (decision == null) return false;\n const normalized = text\n .trim()\n .toLowerCase()\n .replace(/[.!?]$/g, '');\n const isDecisionAnswer = ['yes', 'no', 'approve', 'deny'].includes(normalized);\n if (pendingDecisions.size > 1) {\n if (\n isDecisionAnswer ||\n [...pendingDecisions.values()].some(\n (pending) => decisionAnswer(pending.choices, normalized) != null,\n )\n ) {\n await speakRefusal('Multiple actions need approval. Answer them in Final.');\n return true;\n }\n return false;\n }\n\n if (from == null) {\n if (!isDecisionAnswer) return false;\n await speakRefusal('This action needs approval in Final.');\n return true;\n }\n const choice = decisionAnswer(decision.choices, normalized);\n if (choice == null) {\n if (!['yes', 'no', 'approve', 'deny'].includes(normalized)) return false;\n await speakRefusal('Choose the exact option in Final.');\n return true;\n }\n const decisionId = decision.id;\n try {\n const result = await ctx.decisions.resolve({\n decisionId,\n outcome: choice.outcome,\n by: from,\n });\n if ('refused' in result) await reportRefusal(result.message);\n else {\n pendingDecisions.delete(decisionId);\n decision = pendingDecisions.values().next().value ?? null;\n if (!result.resolved) await reportRefusal('This decision is no longer waiting in Final.');\n }\n } catch {\n await reportRefusal(\n 'The approval could not be answered. Please try again or answer in Final.',\n );\n }\n return true;\n };\n const speak = async (\n text: AsyncIterable<string>,\n version: number,\n correlation: { utterance: string; turnId: string },\n ) => {\n let firstAudio = true;\n for await (const sentence of abortable(text, signal)) {\n while (refusalPlayback != null) await refusalPlayback;\n if (version !== generation || signal.aborted) break;\n const playbackVersion = playbackGeneration;\n const single = async function* () {\n yield sentence;\n };\n const speech = new AbortController();\n activeSpeech = speech;\n const speechSignal = AbortSignal.any([signal, speech.signal]);\n try {\n for await (const frame of abortable(\n ctx.media.synthesize(single(), {\n voice: options.voice,\n encoding: options.encoding,\n sampleRate: options.sampleRate,\n signal: speechSignal,\n }),\n speechSignal,\n )) {\n if (version !== generation || playbackVersion !== playbackGeneration) break;\n if (firstAudio) {\n firstAudio = false;\n trace('first.audio.out', correlation);\n }\n send(frame);\n }\n } catch (cause) {\n if (!speechSignal.aborted) throw cause;\n } finally {\n speech.abort();\n if (activeSpeech === speech) activeSpeech = null;\n }\n }\n };\n const work = (async () => {\n for await (const utterance of abortable(utterances.stream, signal)) {\n if (terminalRefusal) break;\n const version = ++generation;\n const { text, key, finalAt } = utterance;\n const origin = {\n place: options.place,\n ref: {\n externalKey: key,\n connectionId: connectionKey,\n utterance: key,\n utteranceFinalAt: finalAt,\n },\n };\n const accepting = { interrupted: false };\n turnLifecycle.accepting = accepting;\n const posted =\n from == null\n ? await ctx.conversation(conversation).input({\n content: [{ type: 'text', text }],\n origin,\n idempotencyKey: key,\n waitForAcceptance: true,\n principal: options.principal,\n label: `From ${options.principal.displayName ?? options.principal.id} in ${options.place.title ?? options.place.kind}`,\n })\n : await ctx.conversation(conversation).post({\n from,\n content: [{ type: 'text', text }],\n origin,\n idempotencyKey: key,\n waitForAcceptance: true,\n });\n if (turnLifecycle.accepting === accepting) turnLifecycle.accepting = null;\n if (signal.aborted || terminalRefusal) return;\n if (accepting.interrupted || version !== generation) {\n if (!('refused' in posted) && posted.turnId != null) {\n trace('post.accepted', { utterance: key, turnId: posted.turnId });\n await requestCancellation(posted.turnId);\n }\n continue;\n }\n if ('refused' in posted) {\n await reportRefusal(posted.message, { recoverable: false });\n continue;\n }\n if (posted.turnId == null) {\n const message = \"I couldn't start a voice reply for this request. Check the chat in Final.\";\n await reportRefusal(message);\n continue;\n }\n trace('post.accepted', { utterance: key, turnId: posted.turnId });\n activeTurn = true;\n turnLifecycle.activeId = posted.turnId;\n const sentences = createQueue<string>();\n const output = speak(sentences.stream, version, {\n utterance: key,\n turnId: posted.turnId,\n }).catch(fail);\n const observation = new AbortController();\n const observationSignal = AbortSignal.any([signal, observation.signal]);\n const stopObservation = () => observation.abort();\n microphoneLifecycle.stopObservation = stopObservation;\n const microphoneEnd = () => {\n if (decision == null) return;\n sentences.push('Continue this approval in Final.');\n observation.abort();\n };\n microphoneLifecycle.onEnd = microphoneEnd;\n let hasReply = false;\n let buffer = '';\n let first = true;\n const streamed = new Set<string>();\n const completed = new Set<string>();\n try {\n for await (const event of abortable(\n ctx.observe(conversation, { turnId: posted.turnId, signal: observationSignal }),\n observationSignal,\n )) {\n if (version !== generation) break;\n if (event.type === 'message.delta') {\n if (first) {\n first = false;\n trace('first.delta', { utterance: key, turnId: posted.turnId });\n }\n if (event.data.delta.type !== 'text' || typeof event.data.delta.text !== 'string')\n continue;\n if (event.data.delta.text.length > 0) streamed.add(event.data.messageId);\n if (event.data.delta.text.trim().length > 0) hasReply = true;\n buffer += event.data.delta.text;\n let boundary: RegExpExecArray | null;\n while ((boundary = /[!?](?:\\s|$)|(?<!\\d)\\.(?:\\s|$)|\\.\\s/.exec(buffer)) != null) {\n const end = boundary.index + boundary[0].length;\n sentences.push(buffer.slice(0, end));\n buffer = buffer.slice(end);\n }\n } else if (event.type === 'message.completed') {\n const id = event.data.message.id;\n if (!completed.has(id)) {\n if (!streamed.has(id)) buffer += textOf(event.data.message.content);\n if (buffer.trim().length > 0) {\n hasReply = true;\n sentences.push(buffer);\n }\n buffer = '';\n completed.add(id);\n }\n } else if (event.type === 'decision.created') {\n const choices =\n from == null || microphoneEnded.signal.aborted\n ? []\n : decisionChoices(event.data.request);\n decision = { id: event.data.decisionId, choices };\n pendingDecisions.set(decision.id, decision);\n sentences.push(\n `${decisionSummary(event.data.request)} ${\n pendingDecisions.size > 1\n ? 'Multiple actions need approval. Answer them in Final.'\n : from == null\n ? 'This action needs approval in Final.'\n : choices.length === 0\n ? \"I've sent that to you in Final.\"\n : choices.filter((choice) => choice.answer === 'approve').length === 1 &&\n choices.filter((choice) => choice.answer === 'deny').length <= 1\n ? `Your options are ${choices.map((choice) => choice.label).join(' or ')}. Do you approve this action? Say yes or no.`\n : 'Choose an option in Final.'\n }`,\n );\n if (microphoneEnded.signal.aborted) break;\n } else if (event.type === 'decision.resolved') {\n pendingDecisions.delete(event.data.decisionId);\n decision = pendingDecisions.values().next().value ?? null;\n } else if (event.type === 'turn.completed') {\n turnLifecycle.activeId = null;\n for (const message of event.data.messages ?? []) {\n if (!streamed.has(message.id) && !completed.has(message.id)) {\n const text = textOf(message.content);\n if (text.trim().length > 0) {\n hasReply = true;\n sentences.push(text);\n }\n completed.add(message.id);\n }\n }\n if (streamed.size === 0 && completed.size === 0 && event.data.content != null) {\n const text = textOf(event.data.content);\n if (text.trim().length > 0) {\n hasReply = true;\n sentences.push(text);\n }\n }\n if (buffer.trim().length > 0) {\n sentences.push(buffer);\n buffer = '';\n }\n const status = turnStatus(event.data);\n if (status != null) sentences.push(status);\n else if (!hasReply)\n sentences.push('The turn finished without a spoken reply. Open Final for details.');\n clearDecisions();\n break;\n }\n }\n if (version === generation && buffer.trim().length > 0) sentences.push(buffer);\n } finally {\n clearDecisions();\n observation.abort();\n if (microphoneLifecycle.stopObservation === stopObservation)\n microphoneLifecycle.stopObservation = null;\n if (microphoneLifecycle.onEnd === microphoneEnd) microphoneLifecycle.onEnd = null;\n sentences.close();\n await output;\n activeTurn = false;\n if (turnLifecycle.activeId === posted.turnId) turnLifecycle.activeId = null;\n send(JSON.stringify({ type: 'speech.end', final: false }));\n }\n }\n })().catch(fail);\n const transcription = (async () => {\n try {\n for await (const transcript of abortable(\n ctx.media.transcribe(transcriptionAudio(), {\n encoding: options.encoding,\n sampleRate: options.sampleRate,\n endpointing: true,\n signal: transcriptionSignal,\n }),\n transcriptionSignal,\n )) {\n send(JSON.stringify({ type: 'transcript', ...transcript }));\n if (!transcript.final && transcript.text.trim().length > 0 && !userSpeaking) {\n userSpeaking = true;\n await cancel();\n }\n if (transcript.final && transcript.text.trim().length === 0) userSpeaking = false;\n if (transcript.final && transcript.text.trim().length > 0) {\n const key = `${options.place.id}:${connectionKey}:${++utteranceNumber}`;\n const finalAt = Date.now();\n trace('utterance.final', { utterance: key, at: finalAt });\n if (!userSpeaking && (activeTurn || turnLifecycle.accepting != null) && decision == null)\n await cancel();\n userSpeaking = false;\n if (!(await answerDecision(transcript.text)) && !terminalRefusal) {\n if (decision != null) await cancel(true);\n utterances.push({ text: transcript.text, key, finalAt });\n }\n }\n }\n } finally {\n utterances.close();\n if (!microphoneInputClosed && !signal.aborted)\n fail(new Error('The transcription stream ended unexpectedly.'));\n microphoneEnded.abort();\n microphoneLifecycle.onEnd?.();\n }\n })().catch(fail);\n const aborted = Promise.withResolvers<void>();\n const abort = () => {\n aborted.resolve();\n closeAudio();\n utterances.close();\n generation += 1;\n };\n signal.addEventListener('abort', abort, { once: true });\n if (signal.aborted) abort();\n let cleanupFailure: Error | null = null;\n try {\n for await (const frame of abortable(\n socket.messages,\n AbortSignal.any([signal, microphoneEnded.signal]),\n )) {\n if (terminalRefusal) break;\n if (frame instanceof Uint8Array) {\n if (queuedAudioBytes + frame.byteLength > 2 * 1024 * 1024) {\n fail(new Error('Microphone audio exceeded the pending transcription limit.'));\n break;\n }\n if (recordingLimit != null) {\n for (let offset = 0; offset < frame.byteLength; ) {\n const length = Math.min(frame.byteLength - offset, recordingLimit - recordingSize);\n recording.push(frame.slice(offset, offset + length));\n recordingSize += length;\n offset += length;\n if (recordingSize === recordingLimit) await rotateRecording();\n }\n }\n if (!audioStarted) {\n audioStarted = true;\n trace('audio.in');\n }\n queuedAudioBytes += frame.byteLength;\n audio.push(frame);\n continue;\n }\n const control = z.object({ type: z.string() }).safeParse(parseJson(frame));\n if (control.success && control.data.type === 'cancel') await cancel(true);\n if (control.success && control.data.type === 'stop') {\n closeAudio();\n break;\n }\n }\n closeAudio();\n await Promise.race([transcription, aborted.promise]);\n await Promise.race([work, aborted.promise]);\n } catch (cause) {\n fail(cause);\n } finally {\n signal.removeEventListener('abort', abort);\n abort();\n if (signal.aborted) {\n await Promise.race([Promise.all([transcription, work]), aborted.promise]);\n } else await Promise.all([transcription, work]);\n try {\n await finishRecording();\n } catch (cause) {\n if (signal.aborted)\n cleanupFailure = cause instanceof Error ? cause : new Error('Recording attachment failed.');\n else fail(cause);\n }\n if (!signal.aborted) {\n send(JSON.stringify({ type: 'speech.end', final: true }));\n socket.close(1000);\n }\n }\n if (cleanupFailure != null) throw cleanupFailure;\n};\n\nconst oauthClient = async (\n ctx: ChannelReceiveContextIface<VoiceConfig>,\n config: VoiceConfig,\n): Promise<string> => {\n if (config.oauthClientId != null) return config.oauthClientId;\n const saved = z\n .string()\n .url()\n .safeParse(await ctx.store.get('voice-oauth-client'));\n if (saved.success) return saved.data;\n const endpoint = new URL(\n '/oauth2/register',\n config.oauthAuthorizeUrl ?? 'https://api.final.ai/oauth2/authorize',\n );\n const result = z.object({ client_id: z.string().url() }).parse(\n await responseJson(\n await ctx.credentials.fetch(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n client_name: 'Final Voice Channel',\n redirect_uris: [`${channelBaseUrl(config)}/b/${ctx.binding.id}/oauth/callback`],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n }),\n }),\n ),\n );\n await ctx.store.set('voice-oauth-client', result.client_id);\n return result.client_id;\n};\nexport const voiceChannel = (options: JsonObject = {}) => {\n const config = configSchema.parse(options);\n return defineChannel<VoiceConfig, JsonValue, JsonValue>({\n name: 'voice',\n config,\n installed: packageRef('@finalai/final-agent-provider/channels/voice'),\n credentials: { app: null },\n identity: {\n kind: 'redirect',\n start: async (args, ctx) =>\n startOAuth(\n {\n ...args,\n authorizeUrl: config.oauthAuthorizeUrl ?? 'https://api.final.ai/oauth2/authorize',\n clientId: await oauthClient(ctx, config),\n redirectUri: `${channelBaseUrl(config)}/b/${ctx.binding.id}/oauth/callback`,\n scope: 'offline_access',\n pkce: true,\n extra: { resource: config.oauthResource ?? 'https://api.final.ai/v1/connectors/mcp' },\n },\n ctx,\n ),\n },\n places: {\n kinds: ['live'],\n defaults: () => ({ create: 'direct-with-agent', replies: 'origin', audience: 'members' }),\n },\n receive: async () => ({ ignored: true, reason: 'voice-uses-stream' }),\n routes: [\n {\n method: 'GET',\n path: '/oauth/callback',\n handler: async (ctx) => {\n const saved = await takeOAuth(ctx.request, ctx);\n if (saved == null)\n return new Response('Invalid or expired link request', { status: 400 });\n const token = z.object({ access_token: z.string() }).parse(\n await responseJson(\n await ctx.credentials.fetch(\n config.oauthTokenUrl ?? 'https://api.final.ai/oauth2/token',\n {\n method: 'POST',\n body: new URLSearchParams({\n client_id: await oauthClient(ctx, config),\n resource: config.oauthResource ?? 'https://api.final.ai/v1/connectors/mcp',\n grant_type: 'authorization_code',\n code: saved.code,\n redirect_uri: saved.redirectUri,\n ...(saved.verifier == null ? {} : { code_verifier: saved.verifier }),\n }),\n },\n ),\n ),\n );\n const principal = userInfoSchema.parse(\n await responseJson(\n await ctx.credentials.fetch(\n config.oauthUserInfoUrl ?? 'https://api.final.ai/oauth2/userinfo',\n {\n headers: { authorization: `Bearer ${token.access_token}` },\n },\n ),\n ),\n );\n return confirmOAuth(\n { ticket: saved.ticket, expected: saved.principal, actual: principal },\n ctx,\n );\n },\n },\n {\n method: 'WS',\n path: '/stream',\n handler: async (ctx) => {\n const token =\n ctx.request.headers.get('authorization')?.replace(/^Bearer /, '') ??\n new URL(ctx.request.url).searchParams.get('access_token');\n if (token == null && ctx.authenticatedPrincipal == null)\n return new Response('A Final OAuth token is required.', { status: 401 });\n let authenticated = ctx.authenticatedPrincipal;\n if (authenticated == null && token != null) {\n try {\n authenticated = userInfoSchema.parse(\n await responseJson(\n await ctx.credentials.fetch(\n config.oauthUserInfoUrl ?? 'https://api.final.ai/oauth2/userinfo',\n {\n headers: { authorization: `Bearer ${token}` },\n },\n ),\n ),\n );\n } catch {\n return new Response('Invalid Final identity', { status: 401 });\n }\n }\n const principal = principalRefSchema.safeParse(authenticated);\n if (!principal.success) return new Response('Invalid Final identity', { status: 401 });\n ctx.socket.accept();\n const iterator = ctx.socket.messages[Symbol.asyncIterator]();\n const initial = await iterator.next();\n const start = startSchema.safeParse(\n typeof initial.value === 'string' ? parseJson(initial.value) : null,\n );\n if (\n !start.success ||\n start.data.principal.id !== principal.data.id ||\n start.data.principal.tenant !== principal.data.tenant\n ) {\n ctx.socket.close(1008, 'The speaker does not match the authenticated account.');\n return;\n }\n const remaining: AsyncIterable<string | Uint8Array> = {\n [Symbol.asyncIterator]: () => iterator,\n };\n await runVoiceConnection({\n ctx,\n socket: {\n ...ctx.socket,\n signal: ctx.socket.signal,\n messages: remaining,\n accept: (args) => ctx.socket.accept(args),\n send: (data) => ctx.socket.send(data),\n close: (code, reason) => ctx.socket.close(code, reason),\n },\n principal: principal.data,\n place: {\n kind: 'live',\n id: crypto.randomUUID(),\n ...(principal.data.tenant == null ? {} : { tenant: principal.data.tenant }),\n parent: {\n kind: 'person',\n id: principal.data.id,\n ...(principal.data.tenant == null ? {} : { tenant: principal.data.tenant }),\n },\n },\n encoding: start.data.encoding,\n sampleRate: start.data.sampleRate,\n voice: config.voice,\n recording: start.data.recording,\n });\n },\n },\n ],\n });\n};\n"],
5
- "mappings": "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",
6
- "names": ["options", "text"]
7
- }
@@ -1,5 +0,0 @@
1
- import { type JsonObject, type JsonValue } from '@finalai/agent-provider';
2
- export declare const webhookChannel: (options?: JsonObject) => import("@finalai/agent-provider").ChannelDefinitionIface<{
3
- secret: string;
4
- signatureHeader: string;
5
- }, JsonValue, JsonValue>;
@@ -1,76 +0,0 @@
1
- import {
2
- defineChannel,
3
- jsonObjectSchema,
4
- jsonValueSchema,
5
- packageRef,
6
- placeRefSchema
7
- } from "@finalai/agent-provider";
8
- import * as z from "zod";
9
- import { contentBlocksOf } from "./content.mjs";
10
- import { parseJson, receipt, verifyHmac } from "./channel-utils.mjs";
11
- const configSchema = z.object({
12
- secret: z.string().min(1).default("webhook-signing-secret"),
13
- signatureHeader: z.string().default("x-final-signature")
14
- });
15
- const inputSchema = z.object({
16
- place: placeRefSchema,
17
- content: z.array(jsonObjectSchema).min(1),
18
- label: z.string().min(1),
19
- idempotencyKey: z.string().min(1),
20
- context: z.array(z.string()).optional()
21
- });
22
- const webhookChannel = (options = {}) => {
23
- const config = configSchema.parse(options);
24
- return defineChannel({
25
- name: "webhook",
26
- config,
27
- installed: packageRef("@finalai/final-agent-provider/channels/webhook"),
28
- credentials: { app: null, secrets: [config.secret] },
29
- identity: { kind: "none" },
30
- places: { kinds: ["webhook"] },
31
- routes: [
32
- {
33
- method: "POST",
34
- path: "/webhook",
35
- handler: async (ctx) => {
36
- const body = await ctx.request.text();
37
- const secret = await ctx.credentials.secret(config.secret);
38
- const signature = ctx.request.headers.get(config.signatureHeader);
39
- if (secret == null || signature == null || !await verifyHmac({ secret, body, signature: signature.replace(/^sha256=/, "") }))
40
- return new Response(null, { status: 401 });
41
- const parsed = inputSchema.safeParse(parseJson(body));
42
- if (!parsed.success) return new Response("Invalid webhook input", { status: 400 });
43
- const result = await ctx.receive(jsonValueSchema.parse(parsed.data));
44
- return Response.json(result, {
45
- status: "accepted" in result && !result.accepted ? 403 : 202
46
- });
47
- }
48
- }
49
- ],
50
- receive: async (input, ctx) => {
51
- const parsed = inputSchema.safeParse(input);
52
- if (!parsed.success) return { ignored: true, reason: "invalid-input" };
53
- const route = await ctx.places.resolve(parsed.data.place);
54
- if (route.kind !== "connected")
55
- return {
56
- accepted: false,
57
- refusal: {
58
- refused: "unconnected",
59
- connectUrl: route.connectUrl,
60
- message: "Connect this webhook to a chat in Final."
61
- }
62
- };
63
- return receipt(
64
- await ctx.conversation(route.conversation).input({
65
- ...parsed.data,
66
- content: contentBlocksOf(parsed.data.content),
67
- origin: { place: parsed.data.place }
68
- })
69
- );
70
- }
71
- });
72
- };
73
- export {
74
- webhookChannel
75
- };
76
- //# sourceMappingURL=webhook.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/channels/webhook.ts"],
4
- "sourcesContent": ["import {\n defineChannel,\n jsonObjectSchema,\n jsonValueSchema,\n packageRef,\n placeRefSchema,\n type JsonObject,\n type JsonValue,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlocksOf } from \"./content.mjs\";\nimport { parseJson, receipt, verifyHmac } from \"./channel-utils.mjs\";\n\nconst configSchema = z.object({\n secret: z.string().min(1).default('webhook-signing-secret'),\n signatureHeader: z.string().default('x-final-signature'),\n});\nconst inputSchema = z.object({\n place: placeRefSchema,\n content: z.array(jsonObjectSchema).min(1),\n label: z.string().min(1),\n idempotencyKey: z.string().min(1),\n context: z.array(z.string()).optional(),\n});\nexport const webhookChannel = (options: JsonObject = {}) => {\n const config = configSchema.parse(options);\n return defineChannel({\n name: 'webhook',\n config,\n installed: packageRef('@finalai/final-agent-provider/channels/webhook'),\n credentials: { app: null, secrets: [config.secret] },\n identity: { kind: 'none' },\n places: { kinds: ['webhook'] },\n routes: [\n {\n method: 'POST',\n path: '/webhook',\n handler: async (ctx) => {\n const body = await ctx.request.text();\n const secret = await ctx.credentials.secret(config.secret);\n const signature = ctx.request.headers.get(config.signatureHeader);\n if (\n secret == null ||\n signature == null ||\n !(await verifyHmac({ secret, body, signature: signature.replace(/^sha256=/, '') }))\n )\n return new Response(null, { status: 401 });\n const parsed = inputSchema.safeParse(parseJson(body));\n if (!parsed.success) return new Response('Invalid webhook input', { status: 400 });\n const result = await ctx.receive(jsonValueSchema.parse(parsed.data));\n return Response.json(result, {\n status: 'accepted' in result && !result.accepted ? 403 : 202,\n });\n },\n },\n ],\n receive: async (input: JsonValue, ctx) => {\n const parsed = inputSchema.safeParse(input);\n if (!parsed.success) return { ignored: true, reason: 'invalid-input' };\n const route = await ctx.places.resolve(parsed.data.place);\n if (route.kind !== 'connected')\n return {\n accepted: false,\n refusal: {\n refused: 'unconnected',\n connectUrl: route.connectUrl,\n message: 'Connect this webhook to a chat in Final.',\n },\n };\n return receipt(\n await ctx.conversation(route.conversation).input({\n ...parsed.data,\n content: contentBlocksOf(parsed.data.content),\n origin: { place: parsed.data.place },\n }),\n );\n },\n });\n};\n"],
5
- "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,YAAY,OAAO;AACnB,SAAS,uBAAuB;AAChC,SAAS,WAAW,SAAS,kBAAkB;AAE/C,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,wBAAwB;AAAA,EAC1D,iBAAiB,EAAE,OAAO,EAAE,QAAQ,mBAAmB;AACzD,CAAC;AACD,MAAM,cAAc,EAAE,OAAO;AAAA,EAC3B,OAAO;AAAA,EACP,SAAS,EAAE,MAAM,gBAAgB,EAAE,IAAI,CAAC;AAAA,EACxC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAChC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AACxC,CAAC;AACM,MAAM,iBAAiB,CAAC,UAAsB,CAAC,MAAM;AAC1D,QAAM,SAAS,aAAa,MAAM,OAAO;AACzC,SAAO,cAAc;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,IACA,WAAW,WAAW,gDAAgD;AAAA,IACtE,aAAa,EAAE,KAAK,MAAM,SAAS,CAAC,OAAO,MAAM,EAAE;AAAA,IACnD,UAAU,EAAE,MAAM,OAAO;AAAA,IACzB,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE;AAAA,IAC7B,QAAQ;AAAA,MACN;AAAA,QACE,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS,OAAO,QAAQ;AACtB,gBAAM,OAAO,MAAM,IAAI,QAAQ,KAAK;AACpC,gBAAM,SAAS,MAAM,IAAI,YAAY,OAAO,OAAO,MAAM;AACzD,gBAAM,YAAY,IAAI,QAAQ,QAAQ,IAAI,OAAO,eAAe;AAChE,cACE,UAAU,QACV,aAAa,QACb,CAAE,MAAM,WAAW,EAAE,QAAQ,MAAM,WAAW,UAAU,QAAQ,YAAY,EAAE,EAAE,CAAC;AAEjF,mBAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAC3C,gBAAM,SAAS,YAAY,UAAU,UAAU,IAAI,CAAC;AACpD,cAAI,CAAC,OAAO,QAAS,QAAO,IAAI,SAAS,yBAAyB,EAAE,QAAQ,IAAI,CAAC;AACjF,gBAAM,SAAS,MAAM,IAAI,QAAQ,gBAAgB,MAAM,OAAO,IAAI,CAAC;AACnE,iBAAO,SAAS,KAAK,QAAQ;AAAA,YAC3B,QAAQ,cAAc,UAAU,CAAC,OAAO,WAAW,MAAM;AAAA,UAC3D,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS,OAAO,OAAkB,QAAQ;AACxC,YAAM,SAAS,YAAY,UAAU,KAAK;AAC1C,UAAI,CAAC,OAAO,QAAS,QAAO,EAAE,SAAS,MAAM,QAAQ,gBAAgB;AACrE,YAAM,QAAQ,MAAM,IAAI,OAAO,QAAQ,OAAO,KAAK,KAAK;AACxD,UAAI,MAAM,SAAS;AACjB,eAAO;AAAA,UACL,UAAU;AAAA,UACV,SAAS;AAAA,YACP,SAAS;AAAA,YACT,YAAY,MAAM;AAAA,YAClB,SAAS;AAAA,UACX;AAAA,QACF;AACF,aAAO;AAAA,QACL,MAAM,IAAI,aAAa,MAAM,YAAY,EAAE,MAAM;AAAA,UAC/C,GAAG,OAAO;AAAA,UACV,SAAS,gBAAgB,OAAO,KAAK,OAAO;AAAA,UAC5C,QAAQ,EAAE,OAAO,OAAO,KAAK,MAAM;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AACH;",
6
- "names": []
7
- }