@speechos/core 0.2.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 @@
1
+ {"version":3,"file":"index.js","names":["DEFAULT_HOST: string","defaultConfig: Required<SpeechOSConfig>","userConfig: SpeechOSConfig","validPositions: Array<SpeechOSConfig[\"position\"]>","currentConfig: Required<SpeechOSConfig>","config: SpeechOSConfig","userId: string","event: K","callback: EventCallback<K>","payload: SpeechOSEventMap[K]","event?: keyof SpeechOSEventMap","event: keyof SpeechOSEventMap","events: SpeechOSEventEmitter","initialState: SpeechOSState","initialState","partial: Partial<SpeechOSState>","callback: StateChangeCallback","element: HTMLElement | null","action: SpeechOSState[\"activeAction\"]","recordingState: SpeechOSState[\"recordingState\"]","isConnected: boolean","isMicEnabled: boolean","message: string","state: StateManager","initial?: Partial<SpeechOSState>","ms: number","errorMessage: string","errorCode: string","errorSource: ErrorSource","value: T","error: Error","config","data: Uint8Array","participant?: RemoteParticipant","_participant?: RemoteParticipant","message: object","originalText: string","livekit: LiveKitManager","text: string","action: TranscriptAction","originalText?: string","entry: TranscriptEntry","id: string","transcriptStore: {\n getTranscripts: typeof getTranscripts;\n saveTranscript: typeof saveTranscript;\n clearTranscripts: typeof clearTranscripts;\n deleteTranscript: typeof deleteTranscript;\n}","config: SpeechOSConfig","currentConfig","originalText: string","speechOS: SpeechOSCore"],"sources":["../src/config.ts","../src/events.ts","../src/state.ts","../src/livekit.ts","../src/transcript-store.ts","../src/speechos.ts","../src/index.ts"],"sourcesContent":["/**\n * Configuration management for SpeechOS SDK\n */\n\nimport type { SpeechOSConfig } from \"./types.js\";\n\n/**\n * Default host - can be overridden by SPEECHOS_HOST env var at build time\n */\nexport const DEFAULT_HOST: string =\n (typeof process !== \"undefined\" && process.env?.SPEECHOS_HOST) ||\n \"https://app.speechos.ai\";\n\n/**\n * Default configuration values\n */\nexport const defaultConfig: Required<SpeechOSConfig> = {\n apiKey: \"\",\n userId: \"\",\n host: DEFAULT_HOST,\n position: \"bottom-center\",\n zIndex: 999999,\n debug: false,\n};\n\n/**\n * Validates and merges user config with defaults\n * @param userConfig - User-provided configuration\n * @returns Validated and merged configuration\n */\nexport function validateConfig(\n userConfig: SpeechOSConfig = {}\n): Required<SpeechOSConfig> {\n // Validate apiKey is provided\n if (!userConfig.apiKey) {\n throw new Error(\n \"SpeechOS requires an apiKey. Get one from your team dashboard at /a/<team-slug>/.\"\n );\n }\n\n const config = { ...defaultConfig, ...userConfig };\n\n // Validate position\n const validPositions: Array<SpeechOSConfig[\"position\"]> = [\n \"bottom-center\",\n \"bottom-right\",\n \"bottom-left\",\n ];\n\n if (!validPositions.includes(config.position)) {\n console.warn(\n `Invalid position \"${config.position}\". Using default \"bottom-center\".`\n );\n config.position = \"bottom-center\";\n }\n\n // Validate zIndex\n if (typeof config.zIndex !== \"number\" || config.zIndex < 0) {\n console.warn(\n `Invalid zIndex \"${config.zIndex}\". Using default ${defaultConfig.zIndex}.`\n );\n config.zIndex = defaultConfig.zIndex;\n }\n\n return config;\n}\n\n/**\n * Current active configuration (singleton)\n */\nlet currentConfig: Required<SpeechOSConfig> = defaultConfig;\n\n/**\n * Get the current configuration\n */\nexport function getConfig(): Required<SpeechOSConfig> {\n return { ...currentConfig };\n}\n\n/**\n * Set the current configuration\n * @param config - Configuration to set\n */\nexport function setConfig(config: SpeechOSConfig): void {\n currentConfig = validateConfig(config);\n}\n\n/**\n * Reset configuration to defaults\n */\nexport function resetConfig(): void {\n currentConfig = { ...defaultConfig };\n}\n\n/**\n * Update the userId in the current configuration\n * @param userId - The user identifier to set\n */\nexport function updateUserId(userId: string): void {\n currentConfig = { ...currentConfig, userId };\n}\n","/**\n * Typed event system for SpeechOS SDK\n * Provides a type-safe event emitter for cross-component communication\n */\n\nimport type { SpeechOSEventMap, UnsubscribeFn } from \"./types.js\";\n\ntype EventCallback<K extends keyof SpeechOSEventMap> = (\n payload: SpeechOSEventMap[K]\n) => void;\n\n/**\n * Type-safe event emitter for SpeechOS events\n */\nexport class SpeechOSEventEmitter {\n private listeners: Map<keyof SpeechOSEventMap, Set<EventCallback<any>>> =\n new Map();\n\n /**\n * Subscribe to an event\n * @param event - Event name to listen to\n * @param callback - Function to call when event is emitted\n * @returns Unsubscribe function\n */\n on<K extends keyof SpeechOSEventMap>(\n event: K,\n callback: EventCallback<K>\n ): UnsubscribeFn {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n\n this.listeners.get(event)!.add(callback);\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.listeners.delete(event);\n }\n }\n };\n }\n\n /**\n * Subscribe to an event once (automatically unsubscribes after first call)\n * @param event - Event name to listen to\n * @param callback - Function to call when event is emitted\n * @returns Unsubscribe function\n */\n once<K extends keyof SpeechOSEventMap>(\n event: K,\n callback: EventCallback<K>\n ): UnsubscribeFn {\n const unsubscribe = this.on(event, (payload) => {\n unsubscribe();\n callback(payload);\n });\n return unsubscribe;\n }\n\n /**\n * Emit an event to all subscribers\n * @param event - Event name to emit\n * @param payload - Event payload data\n */\n emit<K extends keyof SpeechOSEventMap>(\n event: K,\n payload: SpeechOSEventMap[K]\n ): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.forEach((callback) => {\n try {\n callback(payload);\n } catch (error) {\n console.error(\n `Error in event listener for \"${String(event)}\":`,\n error\n );\n }\n });\n }\n }\n\n /**\n * Remove all listeners for a specific event or all events\n * @param event - Optional event name to clear listeners for\n */\n clear(event?: keyof SpeechOSEventMap): void {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n\n /**\n * Get the number of listeners for an event\n * @param event - Event name\n * @returns Number of listeners\n */\n listenerCount(event: keyof SpeechOSEventMap): number {\n return this.listeners.get(event)?.size ?? 0;\n }\n}\n\n// Export singleton instance\nexport const events: SpeechOSEventEmitter = new SpeechOSEventEmitter();\n","/**\n * Reactive state management for SpeechOS SDK\n * Provides a centralized state store with subscriptions (similar to Zustand pattern)\n */\n\nimport type {\n SpeechOSState,\n StateChangeCallback,\n UnsubscribeFn,\n} from \"./types.js\";\nimport { events } from \"./events.js\";\n\n/**\n * Initial state\n */\nconst initialState: SpeechOSState = {\n isVisible: false,\n isExpanded: false,\n isConnected: false,\n isMicEnabled: false,\n activeAction: null,\n focusedElement: null,\n recordingState: \"idle\",\n errorMessage: null,\n};\n\n/**\n * State manager class\n */\nclass StateManager {\n private state: SpeechOSState;\n private subscribers: Set<StateChangeCallback> = new Set();\n\n constructor(initialState: SpeechOSState) {\n this.state = { ...initialState };\n }\n\n /**\n * Get the current state (returns a copy to prevent mutations)\n */\n getState(): SpeechOSState {\n return { ...this.state };\n }\n\n /**\n * Update state with partial values\n * @param partial - Partial state to merge with current state\n */\n setState(partial: Partial<SpeechOSState>): void {\n const prevState = { ...this.state };\n this.state = { ...this.state, ...partial };\n\n // Notify subscribers\n this.subscribers.forEach((callback) => {\n try {\n callback(this.state, prevState);\n } catch (error) {\n console.error(\"Error in state change callback:\", error);\n }\n });\n\n // Emit state change event\n events.emit(\"state:change\", { state: this.state });\n }\n\n /**\n * Subscribe to state changes\n * @param callback - Function to call when state changes\n * @returns Unsubscribe function\n */\n subscribe(callback: StateChangeCallback): UnsubscribeFn {\n this.subscribers.add(callback);\n\n // Return unsubscribe function\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n /**\n * Reset state to initial values\n */\n reset(): void {\n this.setState(initialState);\n }\n\n /**\n * Show the widget\n */\n show(): void {\n this.setState({ isVisible: true });\n events.emit(\"widget:show\", undefined);\n }\n\n /**\n * Hide the widget and reset expanded state\n */\n hide(): void {\n this.setState({\n isVisible: false,\n isExpanded: false,\n activeAction: null,\n });\n events.emit(\"widget:hide\", undefined);\n }\n\n /**\n * Toggle the action bubbles expansion\n */\n toggleExpanded(): void {\n this.setState({ isExpanded: !this.state.isExpanded });\n }\n\n /**\n * Set the focused form element\n * @param element - The form element that has focus\n */\n setFocusedElement(element: HTMLElement | null): void {\n this.setState({ focusedElement: element });\n }\n\n /**\n * Set the active action\n * @param action - The action to set as active\n */\n setActiveAction(action: SpeechOSState[\"activeAction\"]): void {\n this.setState({ activeAction: action });\n }\n\n /**\n * Set the recording state\n * @param recordingState - The recording state to set\n */\n setRecordingState(recordingState: SpeechOSState[\"recordingState\"]): void {\n this.setState({ recordingState });\n }\n\n /**\n * Set the connection state\n * @param isConnected - Whether connected to LiveKit\n */\n setConnected(isConnected: boolean): void {\n this.setState({ isConnected });\n }\n\n /**\n * Set the microphone enabled state\n * @param isMicEnabled - Whether microphone is enabled\n */\n setMicEnabled(isMicEnabled: boolean): void {\n this.setState({ isMicEnabled });\n }\n\n /**\n * Start recording flow (connecting → recording)\n */\n startRecording(): void {\n this.setState({\n recordingState: \"connecting\",\n isExpanded: false, // Collapse bubbles when recording starts\n });\n }\n\n /**\n * Stop recording and start processing\n */\n stopRecording(): void {\n this.setState({ recordingState: \"processing\", isMicEnabled: false });\n }\n\n /**\n * Complete the recording flow and return to idle\n */\n completeRecording(): void {\n this.setState({\n recordingState: \"idle\",\n activeAction: null,\n isConnected: false,\n isMicEnabled: false,\n });\n }\n\n /**\n * Cancel recording and return to idle\n */\n cancelRecording(): void {\n this.setState({\n recordingState: \"idle\",\n activeAction: null,\n errorMessage: null,\n isConnected: false,\n isMicEnabled: false,\n });\n }\n\n /**\n * Set error state with a message\n * @param message - Error message to display\n */\n setError(message: string): void {\n this.setState({\n recordingState: \"error\",\n errorMessage: message,\n });\n }\n\n /**\n * Clear error state and return to idle\n */\n clearError(): void {\n this.setState({\n recordingState: \"idle\",\n errorMessage: null,\n });\n }\n}\n\n// Export singleton instance\nexport const state: StateManager = new StateManager(initialState);\n\n/**\n * Create a new state manager instance (useful for testing)\n */\nexport function createStateManager(\n initial?: Partial<SpeechOSState>\n): StateManager {\n return new StateManager({ ...initialState, ...initial });\n}\n","/**\n * LiveKit integration for SpeechOS SDK\n * Handles room connections, audio streaming, and transcription requests\n */\n\nimport {\n Room,\n RoomEvent,\n Track,\n createLocalAudioTrack,\n type LocalAudioTrack,\n type RemoteParticipant,\n} from \"livekit-client\";\nimport type {\n LiveKitTokenResponse,\n ServerErrorMessage,\n ErrorSource,\n} from \"./types.js\";\nimport { getConfig } from \"./config.js\";\nimport { events } from \"./events.js\";\nimport { state } from \"./state.js\";\n\n// Protocol constants (matching backend TranscriptionManager)\nconst MESSAGE_TYPE_REQUEST_TRANSCRIPT = \"request_transcript\";\nconst MESSAGE_TYPE_TRANSCRIPT = \"transcript\";\nconst MESSAGE_TYPE_EDIT_TEXT = \"edit_text\";\nconst MESSAGE_TYPE_EDITED_TEXT = \"edited_text\";\nconst MESSAGE_TYPE_ERROR = \"error\";\nconst TOPIC_SPEECHOS = \"speechos\";\n\n/**\n * A deferred promise with timeout support.\n * Encapsulates resolve/reject/timeout in a single object for cleaner async handling.\n */\nexport class Deferred<T> {\n readonly promise: Promise<T>;\n private _resolve!: (value: T) => void;\n private _reject!: (error: Error) => void;\n private _timeoutId: ReturnType<typeof setTimeout> | null = null;\n private _settled = false;\n\n constructor() {\n this.promise = new Promise<T>((resolve, reject) => {\n this._resolve = resolve;\n this._reject = reject;\n });\n }\n\n /**\n * Set a timeout that will reject the promise with the given error\n */\n setTimeout(\n ms: number,\n errorMessage: string,\n errorCode: string,\n errorSource: ErrorSource\n ): void {\n this._timeoutId = setTimeout(() => {\n if (!this._settled) {\n console.error(`[SpeechOS] Error: ${errorMessage} (${errorCode})`);\n events.emit(\"error\", {\n code: errorCode,\n message: errorMessage,\n source: errorSource,\n });\n this.reject(new Error(errorMessage));\n }\n }, ms);\n }\n\n resolve(value: T): void {\n if (!this._settled) {\n this._settled = true;\n this.clearTimeout();\n this._resolve(value);\n }\n }\n\n reject(error: Error): void {\n if (!this._settled) {\n this._settled = true;\n this.clearTimeout();\n this._reject(error);\n }\n }\n\n private clearTimeout(): void {\n if (this._timeoutId !== null) {\n clearTimeout(this._timeoutId);\n this._timeoutId = null;\n }\n }\n\n get isSettled(): boolean {\n return this._settled;\n }\n}\n\n/**\n * LiveKit connection manager\n */\nclass LiveKitManager {\n private room: Room | null = null;\n private tokenData: LiveKitTokenResponse | null = null;\n private micTrack: LocalAudioTrack | null = null;\n\n // Pending async operations using Deferred pattern\n private pendingTranscript: Deferred<string> | null = null;\n private pendingEditText: Deferred<string> | null = null;\n private pendingTrackSubscribed: Deferred<void> | null = null;\n\n // Pre-warming state (token only, mic permission requested on Dictate)\n private preWarmPromise: Promise<void> | null = null;\n\n // Track original text for edit operations (to include in events)\n private editOriginalText: string | null = null;\n\n /**\n * Pre-warm resources for faster connection\n * Call this when user shows intent (e.g., expands widget)\n * Only fetches token - mic permission is requested when user clicks Dictate\n */\n async preWarm(): Promise<void> {\n // Don't pre-warm if already have token, in progress, or connected\n if (\n this.tokenData ||\n this.preWarmPromise ||\n this.room?.state === \"connected\"\n ) {\n const config = getConfig();\n if (config.debug) {\n console.log(\"[SpeechOS] Pre-warm skipped - token already available\");\n }\n return;\n }\n\n const config = getConfig();\n if (config.debug) {\n console.log(\"[SpeechOS] Pre-warming: fetching token...\");\n }\n\n this.preWarmPromise = (async () => {\n try {\n await this.fetchToken();\n\n if (config.debug) {\n console.log(\"[SpeechOS] Pre-warm complete - token ready\");\n }\n } catch (error) {\n if (config.debug) {\n console.warn(\"[SpeechOS] Pre-warm failed:\", error);\n }\n // Reset so we can try again\n this.preWarmPromise = null;\n }\n })();\n\n await this.preWarmPromise;\n }\n\n /**\n * Fetch a LiveKit token from the backend\n */\n async fetchToken(): Promise<LiveKitTokenResponse> {\n const config = getConfig();\n const url = `${config.host}/livekit/api/token/`;\n\n if (config.debug) {\n console.log(\"[SpeechOS] Fetching LiveKit token from:\", url);\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(config.apiKey ? { Authorization: `Api-Key ${config.apiKey}` } : {}),\n },\n body: JSON.stringify({\n user_id: config.userId || null,\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch LiveKit token: ${response.status} ${response.statusText}`\n );\n }\n\n const data = (await response.json()) as LiveKitTokenResponse;\n this.tokenData = data;\n\n if (config.debug) {\n console.log(\"[SpeechOS] LiveKit token received:\", {\n room: data.room,\n identity: data.identity,\n ws_url: data.ws_url,\n });\n }\n\n return data;\n }\n\n /**\n * Connect to a LiveKit room (fresh connection each time)\n */\n async connect(): Promise<Room> {\n const config = getConfig();\n\n // Use existing token if pre-warmed, otherwise fetch new one\n if (!this.tokenData) {\n await this.fetchToken();\n } else if (config.debug) {\n console.log(\"[SpeechOS] Using pre-fetched token\");\n }\n\n if (!this.tokenData) {\n throw new Error(\"No token available for LiveKit connection\");\n }\n\n // Create a new room instance\n this.room = new Room({\n adaptiveStream: true,\n dynacast: true,\n });\n\n // Set up event listeners\n this.setupRoomEvents();\n\n if (config.debug) {\n console.log(\n \"[SpeechOS] Connecting to LiveKit room:\",\n this.tokenData.room\n );\n }\n\n // Connect to the room\n await this.room.connect(this.tokenData.ws_url, this.tokenData.token);\n\n // Update state\n state.setConnected(true);\n\n if (config.debug) {\n console.log(\"[SpeechOS] Connected to LiveKit room:\", this.room.name);\n }\n\n return this.room;\n }\n\n /**\n * Wait until the agent is ready to receive audio\n * Resolves when LocalTrackSubscribed event is received\n */\n async waitUntilReady(): Promise<void> {\n if (!this.room || this.room.state !== \"connected\") {\n throw new Error(\"Not connected to room\");\n }\n\n // If already set up (from startVoiceSession), just return the existing promise\n if (this.pendingTrackSubscribed) {\n return this.pendingTrackSubscribed.promise;\n }\n\n // Create a new deferred\n this.pendingTrackSubscribed = new Deferred<void>();\n this.pendingTrackSubscribed.setTimeout(\n 15000,\n \"Connection timed out - agent not available\",\n \"connection_timeout\",\n \"connection\"\n );\n\n return this.pendingTrackSubscribed.promise;\n }\n\n /**\n * Set up LiveKit room event listeners\n */\n private setupRoomEvents(): void {\n if (!this.room) return;\n\n const config = getConfig();\n\n this.room.on(RoomEvent.Connected, () => {\n if (config.debug) {\n console.log(\"[SpeechOS] Room connected\");\n }\n state.setConnected(true);\n });\n\n this.room.on(RoomEvent.Disconnected, (reason) => {\n if (config.debug) {\n console.log(\"[SpeechOS] Room disconnected:\", reason);\n }\n state.setConnected(false);\n state.setMicEnabled(false);\n });\n\n this.room.on(RoomEvent.ParticipantConnected, (participant) => {\n if (config.debug) {\n console.log(\"[SpeechOS] Participant connected:\", participant.identity);\n }\n });\n\n // Fired when a remote participant subscribes to our local track\n // This confirms the agent is ready to receive our audio\n this.room.on(RoomEvent.LocalTrackSubscribed, (publication) => {\n if (config.debug) {\n console.log(\n \"[SpeechOS] LocalTrackSubscribed event fired:\",\n publication.trackSid\n );\n }\n\n // Resolve the promise when our track is subscribed to\n if (this.pendingTrackSubscribed) {\n this.pendingTrackSubscribed.resolve();\n this.pendingTrackSubscribed = null;\n }\n });\n\n // Also log all room events for debugging\n this.room.on(RoomEvent.LocalTrackPublished, (publication) => {\n if (config.debug) {\n console.log(\n \"[SpeechOS] LocalTrackPublished:\",\n publication.trackSid,\n publication.source\n );\n }\n });\n\n // Handle incoming data messages (transcriptions, acks, etc.)\n this.room.on(\n RoomEvent.DataReceived,\n (data: Uint8Array, participant?: RemoteParticipant) => {\n this.handleDataMessage(data, participant);\n }\n );\n }\n\n /**\n * Handle incoming data messages from the agent\n */\n private handleDataMessage(\n data: Uint8Array,\n _participant?: RemoteParticipant\n ): void {\n const config = getConfig();\n\n try {\n const message = JSON.parse(new TextDecoder().decode(data));\n\n if (config.debug) {\n console.log(\"[SpeechOS] Data received:\", message);\n }\n\n if (message.type === MESSAGE_TYPE_TRANSCRIPT) {\n // Transcript received from agent\n const transcript = message.transcript || \"\";\n\n if (config.debug) {\n console.log(\"[SpeechOS] Transcript received:\", transcript);\n }\n\n // Emit transcription:complete event\n events.emit(\"transcription:complete\", { text: transcript });\n\n // Resolve the pending transcript promise\n if (this.pendingTranscript) {\n this.pendingTranscript.resolve(transcript);\n this.pendingTranscript = null;\n }\n } else if (message.type === MESSAGE_TYPE_EDITED_TEXT) {\n // Edited text received from agent\n const editedText = message.text || \"\";\n\n if (config.debug) {\n console.log(\"[SpeechOS] Edited text received:\", editedText);\n }\n\n // Emit edit:complete event\n events.emit(\"edit:complete\", {\n text: editedText,\n originalText: this.editOriginalText || \"\",\n });\n\n // Resolve the pending edit text promise\n if (this.pendingEditText) {\n this.pendingEditText.resolve(editedText);\n this.pendingEditText = null;\n }\n\n // Clear stored original text\n this.editOriginalText = null;\n } else if (message.type === MESSAGE_TYPE_ERROR) {\n // Server error received from agent\n const serverError = message as ServerErrorMessage;\n const errorCode = serverError.code || \"server_error\";\n const errorMessage = serverError.message || \"A server error occurred\";\n\n console.error(`[SpeechOS] Error: ${errorMessage} (${errorCode})`);\n\n if (config.debug && serverError.details) {\n console.error(\"[SpeechOS] Error details:\", serverError.details);\n }\n\n // Emit error event for UI to handle\n events.emit(\"error\", {\n code: errorCode,\n message: errorMessage,\n source: \"server\",\n });\n\n // Reject any pending operations\n const error = new Error(errorMessage);\n if (this.pendingTranscript) {\n this.pendingTranscript.reject(error);\n this.pendingTranscript = null;\n }\n if (this.pendingEditText) {\n this.pendingEditText.reject(error);\n this.pendingEditText = null;\n }\n }\n } catch (error) {\n console.error(\"[SpeechOS] Failed to parse data message:\", error);\n }\n }\n\n /**\n * Publish microphone audio track\n */\n async enableMicrophone(): Promise<void> {\n if (!this.room || this.room.state !== \"connected\") {\n throw new Error(\"Not connected to room\");\n }\n\n const config = getConfig();\n\n if (!this.micTrack) {\n if (config.debug) {\n console.log(\"[SpeechOS] Creating microphone track...\");\n }\n this.micTrack = await createLocalAudioTrack({\n echoCancellation: true,\n noiseSuppression: true,\n });\n }\n\n // Publish the track if not already published\n const existingPub = this.room.localParticipant.getTrackPublication(\n Track.Source.Microphone\n );\n if (!existingPub) {\n await this.room.localParticipant.publishTrack(this.micTrack, {\n source: Track.Source.Microphone,\n });\n\n // Update state\n state.setMicEnabled(true);\n\n if (config.debug) {\n console.log(\"[SpeechOS] Microphone track published\");\n }\n }\n }\n\n /**\n * Disable microphone audio track\n */\n async disableMicrophone(): Promise<void> {\n const config = getConfig();\n\n if (this.micTrack) {\n if (config.debug) {\n console.log(\"[SpeechOS] Disabling microphone track...\");\n }\n\n // Unpublish from room if connected\n if (this.room?.state === \"connected\") {\n try {\n await this.room.localParticipant.unpublishTrack(this.micTrack);\n if (config.debug) {\n console.log(\"[SpeechOS] Microphone track unpublished\");\n }\n } catch (error) {\n console.warn(\"[SpeechOS] Error unpublishing track:\", error);\n }\n }\n\n // Stop the track to release the microphone\n this.micTrack.stop();\n\n // Detach from any elements (per LiveKit best practices)\n this.micTrack.detach();\n\n this.micTrack = null;\n\n // Update state\n state.setMicEnabled(false);\n\n if (config.debug) {\n console.log(\"[SpeechOS] Microphone track stopped and detached\");\n }\n }\n }\n\n /**\n * Send a data message to the room\n */\n async sendDataMessage(message: object): Promise<void> {\n if (!this.room || this.room.state !== \"connected\") {\n throw new Error(\"Not connected to room\");\n }\n\n const data = new TextEncoder().encode(JSON.stringify(message));\n await this.room.localParticipant.publishData(data, {\n reliable: true,\n topic: TOPIC_SPEECHOS,\n });\n }\n\n /**\n * Start a voice session\n * Connects to room, enables microphone, and waits for agent to subscribe to our track\n */\n async startVoiceSession(): Promise<void> {\n const config = getConfig();\n if (config.debug) {\n console.log(\"[SpeechOS] Starting voice session...\");\n }\n\n // Wait for any pre-warm in progress\n if (this.preWarmPromise) {\n if (config.debug) {\n console.log(\"[SpeechOS] Waiting for pre-warm to complete...\");\n }\n await this.preWarmPromise;\n }\n\n // Use cached token if available (from init), otherwise fetch fresh\n // Token is cleared after each disconnect, so subsequent calls fetch fresh\n if (this.tokenData) {\n if (config.debug) {\n console.log(\"[SpeechOS] Using cached token from init\");\n }\n } else {\n if (config.debug) {\n console.log(\"[SpeechOS] Fetching fresh token for session...\");\n }\n await this.fetchToken();\n }\n\n // Set up the deferred BEFORE connecting so we don't miss the event\n this.pendingTrackSubscribed = new Deferred<void>();\n this.pendingTrackSubscribed.setTimeout(\n 15000,\n \"Connection timed out - agent not available\",\n \"connection_timeout\",\n \"connection\"\n );\n\n // Connect to room using the fresh token\n await this.connect();\n\n // Enable microphone (uses pre-warmed track if available)\n await this.enableMicrophone();\n\n if (config.debug) {\n console.log(\n \"[SpeechOS] Microphone published, waiting for LocalTrackSubscribed event...\"\n );\n }\n\n // Wait for agent to subscribe to our track\n await this.pendingTrackSubscribed.promise;\n this.pendingTrackSubscribed = null;\n\n if (config.debug) {\n console.log(\"[SpeechOS] Voice session ready - agent subscribed to audio\");\n }\n }\n\n /**\n * Stop the voice session and request the transcript\n * Returns a promise that resolves with the transcript text\n * @throws Error if timeout occurs waiting for transcript\n */\n async stopVoiceSession(): Promise<string> {\n const config = getConfig();\n\n if (config.debug) {\n console.log(\n \"[SpeechOS] Stopping voice session, requesting transcript...\"\n );\n }\n\n // Disable microphone\n await this.disableMicrophone();\n\n if (config.debug) {\n console.log(\"[SpeechOS] Requesting transcript from agent...\");\n }\n\n // Create deferred for transcript (resolved when transcript message is received)\n this.pendingTranscript = new Deferred<string>();\n this.pendingTranscript.setTimeout(\n 10000,\n \"Transcription timed out. Please try again.\",\n \"transcription_timeout\",\n \"timeout\"\n );\n\n // Request the transcript from the agent\n await this.sendDataMessage({\n type: MESSAGE_TYPE_REQUEST_TRANSCRIPT,\n });\n\n const result = await this.pendingTranscript.promise;\n this.pendingTranscript = null;\n return result;\n }\n\n /**\n * Alias for stopVoiceSession - granular API naming\n */\n async stopAndGetTranscript(): Promise<string> {\n return this.stopVoiceSession();\n }\n\n /**\n * Request text editing using the transcript as instructions\n * Sends the original text to the backend, which applies the spoken instructions\n * Returns a promise that resolves with the edited text\n * @throws Error if timeout occurs waiting for edited text\n */\n async requestEditText(originalText: string): Promise<string> {\n const config = getConfig();\n\n if (config.debug) {\n console.log(\"[SpeechOS] Requesting text edit...\");\n }\n\n // Store original text for the event\n this.editOriginalText = originalText;\n\n // Disable microphone first\n await this.disableMicrophone();\n\n if (config.debug) {\n console.log(\"[SpeechOS] Sending edit_text request to agent...\");\n }\n\n // Create deferred for edited text (resolved when edited_text message is received)\n this.pendingEditText = new Deferred<string>();\n this.pendingEditText.setTimeout(\n 15000,\n \"Edit request timed out. Please try again.\",\n \"edit_timeout\",\n \"timeout\"\n );\n\n // Send the edit request to the agent\n await this.sendDataMessage({\n type: MESSAGE_TYPE_EDIT_TEXT,\n text: originalText,\n });\n\n const result = await this.pendingEditText.promise;\n this.pendingEditText = null;\n return result;\n }\n\n /**\n * Alias for requestEditText - granular API naming\n */\n async stopAndEdit(originalText: string): Promise<string> {\n return this.requestEditText(originalText);\n }\n\n /**\n * Disconnect from the current room\n * Clears the token so a fresh one is fetched for the next session\n */\n async disconnect(): Promise<void> {\n const config = getConfig();\n\n if (config.debug) {\n console.log(\"[SpeechOS] Disconnecting from room...\");\n }\n\n // Disable and cleanup microphone track\n await this.disableMicrophone();\n\n if (this.room) {\n // Remove all event listeners before disconnecting\n this.room.removeAllListeners();\n\n // Disconnect from the room\n await this.room.disconnect();\n this.room = null;\n\n // Update state\n state.setConnected(false);\n\n if (config.debug) {\n console.log(\"[SpeechOS] Room disconnected and cleaned up\");\n }\n }\n\n // Reject any pending operations (so callers don't hang)\n if (this.pendingTranscript) {\n this.pendingTranscript.reject(new Error(\"Disconnected\"));\n this.pendingTranscript = null;\n }\n if (this.pendingEditText) {\n this.pendingEditText.reject(new Error(\"Disconnected\"));\n this.pendingEditText = null;\n }\n if (this.pendingTrackSubscribed) {\n this.pendingTrackSubscribed.reject(new Error(\"Disconnected\"));\n this.pendingTrackSubscribed = null;\n }\n\n // Clear all session state including token\n // (Fresh token will be fetched for next session)\n this.tokenData = null;\n this.preWarmPromise = null;\n this.editOriginalText = null;\n\n if (config.debug) {\n console.log(\"[SpeechOS] Session state cleared\");\n }\n }\n\n /**\n * Get the current room instance\n */\n getRoom(): Room | null {\n return this.room;\n }\n\n /**\n * Get the current token data\n */\n getTokenData(): LiveKitTokenResponse | null {\n return this.tokenData;\n }\n\n /**\n * Check if connected to a room\n */\n isConnected(): boolean {\n return this.room?.state === \"connected\";\n }\n\n /**\n * Check if microphone is enabled\n */\n isMicrophoneEnabled(): boolean {\n return this.micTrack !== null;\n }\n\n /**\n * Clear the cached token\n * Used when user identity changes to ensure next session gets a fresh token\n */\n clearToken(): void {\n const config = getConfig();\n if (config.debug) {\n console.log(\"[SpeechOS] Clearing cached token\");\n }\n this.tokenData = null;\n this.preWarmPromise = null;\n }\n}\n\n// Export singleton instance\nexport const livekit: LiveKitManager = new LiveKitManager();\n","/**\n * Transcript history store\n * Persists transcripts to localStorage for viewing in the settings modal\n */\n\nexport type TranscriptAction = \"dictate\" | \"edit\";\n\nexport interface TranscriptEntry {\n id: string;\n text: string;\n timestamp: number;\n action: TranscriptAction;\n /** Original text before edit (only for edit actions) */\n originalText?: string;\n}\n\nconst STORAGE_KEY = \"speechos_transcripts\";\nconst MAX_ENTRIES = 50;\n\n/**\n * Generate a unique ID for transcript entries\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\n/**\n * Get all transcripts from localStorage\n */\nexport function getTranscripts(): TranscriptEntry[] {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (!stored) return [];\n const entries = JSON.parse(stored) as TranscriptEntry[];\n // Return newest first\n return entries.sort((a, b) => b.timestamp - a.timestamp);\n } catch {\n return [];\n }\n}\n\n/**\n * Save a new transcript entry\n */\nexport function saveTranscript(\n text: string,\n action: TranscriptAction,\n originalText?: string\n): TranscriptEntry {\n const entry: TranscriptEntry = {\n id: generateId(),\n text,\n timestamp: Date.now(),\n action,\n ...(originalText && { originalText }),\n };\n\n const entries = getTranscripts();\n entries.unshift(entry);\n\n // Prune to max entries\n const pruned = entries.slice(0, MAX_ENTRIES);\n\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(pruned));\n } catch {\n // localStorage full or unavailable - silently fail\n }\n\n return entry;\n}\n\n/**\n * Clear all transcript history\n */\nexport function clearTranscripts(): void {\n try {\n localStorage.removeItem(STORAGE_KEY);\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Delete a single transcript by ID\n */\nexport function deleteTranscript(id: string): void {\n const entries = getTranscripts().filter((e) => e.id !== id);\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(entries));\n } catch {\n // Silently fail\n }\n}\n\nexport const transcriptStore: {\n getTranscripts: typeof getTranscripts;\n saveTranscript: typeof saveTranscript;\n clearTranscripts: typeof clearTranscripts;\n deleteTranscript: typeof deleteTranscript;\n} = {\n getTranscripts,\n saveTranscript,\n clearTranscripts,\n deleteTranscript,\n};\n","/**\n * SpeechOS Core SDK\n *\n * Provides both low-level and high-level APIs for voice interaction.\n * This is the main entry point for headless usage of SpeechOS.\n */\n\nimport type { SpeechOSConfig } from \"./types.js\";\nimport { setConfig, getConfig, resetConfig } from \"./config.js\";\nimport { livekit } from \"./livekit.js\";\nimport { state } from \"./state.js\";\nimport { events } from \"./events.js\";\nimport { transcriptStore } from \"./transcript-store.js\";\n\n/**\n * SpeechOS Core SDK\n *\n * Provides two API layers:\n * 1. Low-level API: Granular control over LiveKit connection lifecycle\n * 2. High-level API: One-shot methods for common voice tasks\n */\nclass SpeechOSCore {\n private initialized = false;\n\n /**\n * Initialize the SDK with configuration\n * @param config - Configuration options including apiKey\n */\n init(config: SpeechOSConfig): void {\n setConfig(config);\n this.initialized = true;\n\n const currentConfig = getConfig();\n if (currentConfig.debug) {\n console.log(\"[SpeechOS] Initialized with config:\", {\n host: currentConfig.host,\n position: currentConfig.position,\n debug: currentConfig.debug,\n });\n }\n }\n\n /**\n * Check if the SDK is initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n\n // ============================================\n // Low-level API (granular control)\n // ============================================\n\n /**\n * Connect to LiveKit (fetches token, establishes connection)\n * Call this before other low-level methods\n */\n async connect(): Promise<void> {\n this.ensureInitialized();\n await livekit.connect();\n }\n\n /**\n * Wait until the agent is ready to receive audio\n * Resolves when the agent subscribes to our audio track\n */\n async waitUntilReady(): Promise<void> {\n return livekit.waitUntilReady();\n }\n\n /**\n * Enable microphone (user is now being recorded)\n */\n async enableMicrophone(): Promise<void> {\n await livekit.enableMicrophone();\n state.setRecordingState(\"recording\");\n }\n\n /**\n * Stop recording and get the transcript\n * @returns The transcribed text\n */\n async stopAndGetTranscript(): Promise<string> {\n state.setRecordingState(\"processing\");\n try {\n const transcript = await livekit.stopAndGetTranscript();\n\n // Save to transcript store\n transcriptStore.saveTranscript(transcript, \"dictate\");\n\n state.completeRecording();\n return transcript;\n } catch (error) {\n state.setError(\n error instanceof Error ? error.message : \"Transcription failed\"\n );\n throw error;\n }\n }\n\n /**\n * Stop recording and get edited text\n * @param originalText - The original text to edit based on voice instructions\n * @returns The edited text\n */\n async stopAndEdit(originalText: string): Promise<string> {\n state.setRecordingState(\"processing\");\n try {\n const editedText = await livekit.stopAndEdit(originalText);\n\n // Save to transcript store\n transcriptStore.saveTranscript(editedText, \"edit\", originalText);\n\n state.completeRecording();\n return editedText;\n } catch (error) {\n state.setError(\n error instanceof Error ? error.message : \"Edit request failed\"\n );\n throw error;\n }\n }\n\n /**\n * Disconnect from LiveKit\n */\n async disconnect(): Promise<void> {\n await livekit.disconnect();\n state.completeRecording();\n }\n\n // ============================================\n // High-level API (convenience methods)\n // ============================================\n\n /**\n * One-shot dictation: connect, wait for agent, record, and get transcript\n * Automatically handles the full voice session lifecycle\n *\n * @returns The transcribed text\n */\n async dictate(): Promise<string> {\n this.ensureInitialized();\n\n state.setActiveAction(\"dictate\");\n state.startRecording();\n\n try {\n // Start voice session (connect + wait + mic)\n await livekit.startVoiceSession();\n state.setRecordingState(\"recording\");\n\n // User is now being recorded...\n // The caller should call stopDictation() when done\n // Or they can just await this if they want to handle it themselves\n\n // For this high-level API, we return immediately after setup\n // The UI should handle when to stop\n return new Promise<string>((resolve, reject) => {\n // Store resolvers for stopDictation to use\n this._dictateResolve = resolve;\n this._dictateReject = reject;\n });\n } catch (error) {\n state.setError(\n error instanceof Error ? error.message : \"Failed to start dictation\"\n );\n await this.cleanup();\n throw error;\n }\n }\n\n private _dictateResolve?: (transcript: string) => void;\n private _dictateReject?: (error: Error) => void;\n\n /**\n * Stop dictation and get the transcript\n * Call this after dictate() when user stops speaking\n */\n async stopDictation(): Promise<string> {\n state.setRecordingState(\"processing\");\n\n try {\n const transcript = await livekit.stopVoiceSession();\n\n // Save to transcript store\n transcriptStore.saveTranscript(transcript, \"dictate\");\n\n state.completeRecording();\n\n // Resolve the dictate promise if it exists\n if (this._dictateResolve) {\n this._dictateResolve(transcript);\n this._dictateResolve = undefined;\n this._dictateReject = undefined;\n }\n\n return transcript;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"Transcription failed\");\n state.setError(err.message);\n\n // Reject the dictate promise if it exists\n if (this._dictateReject) {\n this._dictateReject(err);\n this._dictateResolve = undefined;\n this._dictateReject = undefined;\n }\n\n throw err;\n } finally {\n await this.cleanup();\n }\n }\n\n /**\n * One-shot edit: connect, wait for agent, record voice instructions, apply to text\n * Automatically handles the full voice session lifecycle\n *\n * @param originalText - The text to edit\n * @returns The edited text\n */\n async edit(originalText: string): Promise<string> {\n this.ensureInitialized();\n\n state.setActiveAction(\"edit\");\n state.startRecording();\n this._editOriginalText = originalText;\n\n try {\n // Start voice session (connect + wait + mic)\n await livekit.startVoiceSession();\n state.setRecordingState(\"recording\");\n\n // Return a promise that will be resolved when stopEdit is called\n return new Promise<string>((resolve, reject) => {\n this._editResolve = resolve;\n this._editReject = reject;\n });\n } catch (error) {\n state.setError(\n error instanceof Error ? error.message : \"Failed to start edit\"\n );\n await this.cleanup();\n throw error;\n }\n }\n\n private _editOriginalText?: string;\n private _editResolve?: (editedText: string) => void;\n private _editReject?: (error: Error) => void;\n\n /**\n * Stop edit recording and get the edited text\n * Call this after edit() when user stops speaking\n */\n async stopEdit(): Promise<string> {\n state.setRecordingState(\"processing\");\n\n try {\n const originalText = this._editOriginalText || \"\";\n const editedText = await livekit.requestEditText(originalText);\n\n // Save to transcript store\n transcriptStore.saveTranscript(editedText, \"edit\", originalText);\n\n state.completeRecording();\n\n // Resolve the edit promise if it exists\n if (this._editResolve) {\n this._editResolve(editedText);\n this._editResolve = undefined;\n this._editReject = undefined;\n }\n\n return editedText;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"Edit request failed\");\n state.setError(err.message);\n\n // Reject the edit promise if it exists\n if (this._editReject) {\n this._editReject(err);\n this._editResolve = undefined;\n this._editReject = undefined;\n }\n\n throw err;\n } finally {\n this._editOriginalText = undefined;\n await this.cleanup();\n }\n }\n\n /**\n * Cancel the current operation\n */\n async cancel(): Promise<void> {\n const err = new Error(\"Operation cancelled\");\n\n if (this._dictateReject) {\n this._dictateReject(err);\n this._dictateResolve = undefined;\n this._dictateReject = undefined;\n }\n\n if (this._editReject) {\n this._editReject(err);\n this._editResolve = undefined;\n this._editReject = undefined;\n }\n\n this._editOriginalText = undefined;\n\n await this.cleanup();\n state.cancelRecording();\n }\n\n // ============================================\n // State and Events access\n // ============================================\n\n /**\n * Access the state manager for subscribing to state changes\n */\n get state(): typeof state {\n return state;\n }\n\n /**\n * Access the event emitter for listening to events\n */\n get events(): typeof events {\n return events;\n }\n\n /**\n * Get the current config\n */\n getConfig(): SpeechOSConfig {\n return getConfig();\n }\n\n // ============================================\n // Private helpers\n // ============================================\n\n private ensureInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n \"SpeechOS not initialized. Call speechOS.init({ apiKey: ... }) first.\"\n );\n }\n }\n\n private async cleanup(): Promise<void> {\n try {\n await livekit.disconnect();\n } catch (error) {\n // Ignore disconnect errors during cleanup\n const config = getConfig();\n if (config.debug) {\n console.warn(\"[SpeechOS] Cleanup disconnect error:\", error);\n }\n }\n }\n\n /**\n * Reset the SDK (useful for testing)\n */\n reset(): void {\n this.initialized = false;\n this._dictateResolve = undefined;\n this._dictateReject = undefined;\n this._editResolve = undefined;\n this._editReject = undefined;\n this._editOriginalText = undefined;\n resetConfig();\n state.reset();\n events.clear();\n }\n}\n\n// Export singleton instance\nexport const speechOS: SpeechOSCore = new SpeechOSCore();\n","/**\n * @speechos/core\n *\n * Headless core SDK for SpeechOS - state management, events, and LiveKit integration.\n * No DOM dependencies - can be used in any JavaScript environment.\n */\n\n// Main SDK class\nexport { speechOS } from \"./speechos.js\";\n\n// Core modules\nexport { events, SpeechOSEventEmitter } from \"./events.js\";\nexport { state, createStateManager } from \"./state.js\";\nexport {\n getConfig,\n setConfig,\n resetConfig,\n updateUserId,\n validateConfig,\n defaultConfig,\n DEFAULT_HOST,\n} from \"./config.js\";\nexport { livekit, Deferred } from \"./livekit.js\";\nexport { transcriptStore } from \"./transcript-store.js\";\nexport type { TranscriptEntry, TranscriptAction } from \"./transcript-store.js\";\n\n// Types\nexport type {\n SpeechOSConfig,\n SpeechOSState,\n SpeechOSAction,\n SpeechOSEventMap,\n StateChangeCallback,\n UnsubscribeFn,\n RecordingState,\n LiveKitTokenResponse,\n ServerErrorMessage,\n ErrorSource,\n} from \"./types.js\";\n\n// Version\nexport const VERSION = \"0.1.0\";\n"],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * LiveKit integration for SpeechOS SDK
3
+ * Handles room connections, audio streaming, and transcription requests
4
+ */
5
+ import { Room } from "livekit-client";
6
+ import type { LiveKitTokenResponse, ErrorSource } from "./types.js";
7
+ /**
8
+ * A deferred promise with timeout support.
9
+ * Encapsulates resolve/reject/timeout in a single object for cleaner async handling.
10
+ */
11
+ export declare class Deferred<T> {
12
+ readonly promise: Promise<T>;
13
+ private _resolve;
14
+ private _reject;
15
+ private _timeoutId;
16
+ private _settled;
17
+ constructor();
18
+ /**
19
+ * Set a timeout that will reject the promise with the given error
20
+ */
21
+ setTimeout(ms: number, errorMessage: string, errorCode: string, errorSource: ErrorSource): void;
22
+ resolve(value: T): void;
23
+ reject(error: Error): void;
24
+ private clearTimeout;
25
+ get isSettled(): boolean;
26
+ }
27
+ /**
28
+ * LiveKit connection manager
29
+ */
30
+ declare class LiveKitManager {
31
+ private room;
32
+ private tokenData;
33
+ private micTrack;
34
+ private pendingTranscript;
35
+ private pendingEditText;
36
+ private pendingTrackSubscribed;
37
+ private preWarmPromise;
38
+ private editOriginalText;
39
+ /**
40
+ * Pre-warm resources for faster connection
41
+ * Call this when user shows intent (e.g., expands widget)
42
+ * Only fetches token - mic permission is requested when user clicks Dictate
43
+ */
44
+ preWarm(): Promise<void>;
45
+ /**
46
+ * Fetch a LiveKit token from the backend
47
+ */
48
+ fetchToken(): Promise<LiveKitTokenResponse>;
49
+ /**
50
+ * Connect to a LiveKit room (fresh connection each time)
51
+ */
52
+ connect(): Promise<Room>;
53
+ /**
54
+ * Wait until the agent is ready to receive audio
55
+ * Resolves when LocalTrackSubscribed event is received
56
+ */
57
+ waitUntilReady(): Promise<void>;
58
+ /**
59
+ * Set up LiveKit room event listeners
60
+ */
61
+ private setupRoomEvents;
62
+ /**
63
+ * Handle incoming data messages from the agent
64
+ */
65
+ private handleDataMessage;
66
+ /**
67
+ * Publish microphone audio track
68
+ */
69
+ enableMicrophone(): Promise<void>;
70
+ /**
71
+ * Disable microphone audio track
72
+ */
73
+ disableMicrophone(): Promise<void>;
74
+ /**
75
+ * Send a data message to the room
76
+ */
77
+ sendDataMessage(message: object): Promise<void>;
78
+ /**
79
+ * Start a voice session
80
+ * Connects to room, enables microphone, and waits for agent to subscribe to our track
81
+ */
82
+ startVoiceSession(): Promise<void>;
83
+ /**
84
+ * Stop the voice session and request the transcript
85
+ * Returns a promise that resolves with the transcript text
86
+ * @throws Error if timeout occurs waiting for transcript
87
+ */
88
+ stopVoiceSession(): Promise<string>;
89
+ /**
90
+ * Alias for stopVoiceSession - granular API naming
91
+ */
92
+ stopAndGetTranscript(): Promise<string>;
93
+ /**
94
+ * Request text editing using the transcript as instructions
95
+ * Sends the original text to the backend, which applies the spoken instructions
96
+ * Returns a promise that resolves with the edited text
97
+ * @throws Error if timeout occurs waiting for edited text
98
+ */
99
+ requestEditText(originalText: string): Promise<string>;
100
+ /**
101
+ * Alias for requestEditText - granular API naming
102
+ */
103
+ stopAndEdit(originalText: string): Promise<string>;
104
+ /**
105
+ * Disconnect from the current room
106
+ * Clears the token so a fresh one is fetched for the next session
107
+ */
108
+ disconnect(): Promise<void>;
109
+ /**
110
+ * Get the current room instance
111
+ */
112
+ getRoom(): Room | null;
113
+ /**
114
+ * Get the current token data
115
+ */
116
+ getTokenData(): LiveKitTokenResponse | null;
117
+ /**
118
+ * Check if connected to a room
119
+ */
120
+ isConnected(): boolean;
121
+ /**
122
+ * Check if microphone is enabled
123
+ */
124
+ isMicrophoneEnabled(): boolean;
125
+ /**
126
+ * Clear the cached token
127
+ * Used when user identity changes to ensure next session gets a fresh token
128
+ */
129
+ clearToken(): void;
130
+ }
131
+ export declare const livekit: LiveKitManager;
132
+ export {};
@@ -0,0 +1,132 @@
1
+ /**
2
+ * LiveKit integration for SpeechOS SDK
3
+ * Handles room connections, audio streaming, and transcription requests
4
+ */
5
+ import { Room } from "livekit-client";
6
+ import type { LiveKitTokenResponse, ErrorSource } from "./types.js";
7
+ /**
8
+ * A deferred promise with timeout support.
9
+ * Encapsulates resolve/reject/timeout in a single object for cleaner async handling.
10
+ */
11
+ export declare class Deferred<T> {
12
+ readonly promise: Promise<T>;
13
+ private _resolve;
14
+ private _reject;
15
+ private _timeoutId;
16
+ private _settled;
17
+ constructor();
18
+ /**
19
+ * Set a timeout that will reject the promise with the given error
20
+ */
21
+ setTimeout(ms: number, errorMessage: string, errorCode: string, errorSource: ErrorSource): void;
22
+ resolve(value: T): void;
23
+ reject(error: Error): void;
24
+ private clearTimeout;
25
+ get isSettled(): boolean;
26
+ }
27
+ /**
28
+ * LiveKit connection manager
29
+ */
30
+ declare class LiveKitManager {
31
+ private room;
32
+ private tokenData;
33
+ private micTrack;
34
+ private pendingTranscript;
35
+ private pendingEditText;
36
+ private pendingTrackSubscribed;
37
+ private preWarmPromise;
38
+ private editOriginalText;
39
+ /**
40
+ * Pre-warm resources for faster connection
41
+ * Call this when user shows intent (e.g., expands widget)
42
+ * Only fetches token - mic permission is requested when user clicks Dictate
43
+ */
44
+ preWarm(): Promise<void>;
45
+ /**
46
+ * Fetch a LiveKit token from the backend
47
+ */
48
+ fetchToken(): Promise<LiveKitTokenResponse>;
49
+ /**
50
+ * Connect to a LiveKit room (fresh connection each time)
51
+ */
52
+ connect(): Promise<Room>;
53
+ /**
54
+ * Wait until the agent is ready to receive audio
55
+ * Resolves when LocalTrackSubscribed event is received
56
+ */
57
+ waitUntilReady(): Promise<void>;
58
+ /**
59
+ * Set up LiveKit room event listeners
60
+ */
61
+ private setupRoomEvents;
62
+ /**
63
+ * Handle incoming data messages from the agent
64
+ */
65
+ private handleDataMessage;
66
+ /**
67
+ * Publish microphone audio track
68
+ */
69
+ enableMicrophone(): Promise<void>;
70
+ /**
71
+ * Disable microphone audio track
72
+ */
73
+ disableMicrophone(): Promise<void>;
74
+ /**
75
+ * Send a data message to the room
76
+ */
77
+ sendDataMessage(message: object): Promise<void>;
78
+ /**
79
+ * Start a voice session
80
+ * Connects to room, enables microphone, and waits for agent to subscribe to our track
81
+ */
82
+ startVoiceSession(): Promise<void>;
83
+ /**
84
+ * Stop the voice session and request the transcript
85
+ * Returns a promise that resolves with the transcript text
86
+ * @throws Error if timeout occurs waiting for transcript
87
+ */
88
+ stopVoiceSession(): Promise<string>;
89
+ /**
90
+ * Alias for stopVoiceSession - granular API naming
91
+ */
92
+ stopAndGetTranscript(): Promise<string>;
93
+ /**
94
+ * Request text editing using the transcript as instructions
95
+ * Sends the original text to the backend, which applies the spoken instructions
96
+ * Returns a promise that resolves with the edited text
97
+ * @throws Error if timeout occurs waiting for edited text
98
+ */
99
+ requestEditText(originalText: string): Promise<string>;
100
+ /**
101
+ * Alias for requestEditText - granular API naming
102
+ */
103
+ stopAndEdit(originalText: string): Promise<string>;
104
+ /**
105
+ * Disconnect from the current room
106
+ * Clears the token so a fresh one is fetched for the next session
107
+ */
108
+ disconnect(): Promise<void>;
109
+ /**
110
+ * Get the current room instance
111
+ */
112
+ getRoom(): Room | null;
113
+ /**
114
+ * Get the current token data
115
+ */
116
+ getTokenData(): LiveKitTokenResponse | null;
117
+ /**
118
+ * Check if connected to a room
119
+ */
120
+ isConnected(): boolean;
121
+ /**
122
+ * Check if microphone is enabled
123
+ */
124
+ isMicrophoneEnabled(): boolean;
125
+ /**
126
+ * Clear the cached token
127
+ * Used when user identity changes to ensure next session gets a fresh token
128
+ */
129
+ clearToken(): void;
130
+ }
131
+ export declare const livekit: LiveKitManager;
132
+ export {};
@@ -0,0 +1,111 @@
1
+ /**
2
+ * SpeechOS Core SDK
3
+ *
4
+ * Provides both low-level and high-level APIs for voice interaction.
5
+ * This is the main entry point for headless usage of SpeechOS.
6
+ */
7
+ import type { SpeechOSConfig } from "./types.js";
8
+ import { state } from "./state.js";
9
+ import { events } from "./events.js";
10
+ /**
11
+ * SpeechOS Core SDK
12
+ *
13
+ * Provides two API layers:
14
+ * 1. Low-level API: Granular control over LiveKit connection lifecycle
15
+ * 2. High-level API: One-shot methods for common voice tasks
16
+ */
17
+ declare class SpeechOSCore {
18
+ private initialized;
19
+ /**
20
+ * Initialize the SDK with configuration
21
+ * @param config - Configuration options including apiKey
22
+ */
23
+ init(config: SpeechOSConfig): void;
24
+ /**
25
+ * Check if the SDK is initialized
26
+ */
27
+ isInitialized(): boolean;
28
+ /**
29
+ * Connect to LiveKit (fetches token, establishes connection)
30
+ * Call this before other low-level methods
31
+ */
32
+ connect(): Promise<void>;
33
+ /**
34
+ * Wait until the agent is ready to receive audio
35
+ * Resolves when the agent subscribes to our audio track
36
+ */
37
+ waitUntilReady(): Promise<void>;
38
+ /**
39
+ * Enable microphone (user is now being recorded)
40
+ */
41
+ enableMicrophone(): Promise<void>;
42
+ /**
43
+ * Stop recording and get the transcript
44
+ * @returns The transcribed text
45
+ */
46
+ stopAndGetTranscript(): Promise<string>;
47
+ /**
48
+ * Stop recording and get edited text
49
+ * @param originalText - The original text to edit based on voice instructions
50
+ * @returns The edited text
51
+ */
52
+ stopAndEdit(originalText: string): Promise<string>;
53
+ /**
54
+ * Disconnect from LiveKit
55
+ */
56
+ disconnect(): Promise<void>;
57
+ /**
58
+ * One-shot dictation: connect, wait for agent, record, and get transcript
59
+ * Automatically handles the full voice session lifecycle
60
+ *
61
+ * @returns The transcribed text
62
+ */
63
+ dictate(): Promise<string>;
64
+ private _dictateResolve?;
65
+ private _dictateReject?;
66
+ /**
67
+ * Stop dictation and get the transcript
68
+ * Call this after dictate() when user stops speaking
69
+ */
70
+ stopDictation(): Promise<string>;
71
+ /**
72
+ * One-shot edit: connect, wait for agent, record voice instructions, apply to text
73
+ * Automatically handles the full voice session lifecycle
74
+ *
75
+ * @param originalText - The text to edit
76
+ * @returns The edited text
77
+ */
78
+ edit(originalText: string): Promise<string>;
79
+ private _editOriginalText?;
80
+ private _editResolve?;
81
+ private _editReject?;
82
+ /**
83
+ * Stop edit recording and get the edited text
84
+ * Call this after edit() when user stops speaking
85
+ */
86
+ stopEdit(): Promise<string>;
87
+ /**
88
+ * Cancel the current operation
89
+ */
90
+ cancel(): Promise<void>;
91
+ /**
92
+ * Access the state manager for subscribing to state changes
93
+ */
94
+ get state(): typeof state;
95
+ /**
96
+ * Access the event emitter for listening to events
97
+ */
98
+ get events(): typeof events;
99
+ /**
100
+ * Get the current config
101
+ */
102
+ getConfig(): SpeechOSConfig;
103
+ private ensureInitialized;
104
+ private cleanup;
105
+ /**
106
+ * Reset the SDK (useful for testing)
107
+ */
108
+ reset(): void;
109
+ }
110
+ export declare const speechOS: SpeechOSCore;
111
+ export {};
@@ -0,0 +1,111 @@
1
+ /**
2
+ * SpeechOS Core SDK
3
+ *
4
+ * Provides both low-level and high-level APIs for voice interaction.
5
+ * This is the main entry point for headless usage of SpeechOS.
6
+ */
7
+ import type { SpeechOSConfig } from "./types.js";
8
+ import { state } from "./state.js";
9
+ import { events } from "./events.js";
10
+ /**
11
+ * SpeechOS Core SDK
12
+ *
13
+ * Provides two API layers:
14
+ * 1. Low-level API: Granular control over LiveKit connection lifecycle
15
+ * 2. High-level API: One-shot methods for common voice tasks
16
+ */
17
+ declare class SpeechOSCore {
18
+ private initialized;
19
+ /**
20
+ * Initialize the SDK with configuration
21
+ * @param config - Configuration options including apiKey
22
+ */
23
+ init(config: SpeechOSConfig): void;
24
+ /**
25
+ * Check if the SDK is initialized
26
+ */
27
+ isInitialized(): boolean;
28
+ /**
29
+ * Connect to LiveKit (fetches token, establishes connection)
30
+ * Call this before other low-level methods
31
+ */
32
+ connect(): Promise<void>;
33
+ /**
34
+ * Wait until the agent is ready to receive audio
35
+ * Resolves when the agent subscribes to our audio track
36
+ */
37
+ waitUntilReady(): Promise<void>;
38
+ /**
39
+ * Enable microphone (user is now being recorded)
40
+ */
41
+ enableMicrophone(): Promise<void>;
42
+ /**
43
+ * Stop recording and get the transcript
44
+ * @returns The transcribed text
45
+ */
46
+ stopAndGetTranscript(): Promise<string>;
47
+ /**
48
+ * Stop recording and get edited text
49
+ * @param originalText - The original text to edit based on voice instructions
50
+ * @returns The edited text
51
+ */
52
+ stopAndEdit(originalText: string): Promise<string>;
53
+ /**
54
+ * Disconnect from LiveKit
55
+ */
56
+ disconnect(): Promise<void>;
57
+ /**
58
+ * One-shot dictation: connect, wait for agent, record, and get transcript
59
+ * Automatically handles the full voice session lifecycle
60
+ *
61
+ * @returns The transcribed text
62
+ */
63
+ dictate(): Promise<string>;
64
+ private _dictateResolve?;
65
+ private _dictateReject?;
66
+ /**
67
+ * Stop dictation and get the transcript
68
+ * Call this after dictate() when user stops speaking
69
+ */
70
+ stopDictation(): Promise<string>;
71
+ /**
72
+ * One-shot edit: connect, wait for agent, record voice instructions, apply to text
73
+ * Automatically handles the full voice session lifecycle
74
+ *
75
+ * @param originalText - The text to edit
76
+ * @returns The edited text
77
+ */
78
+ edit(originalText: string): Promise<string>;
79
+ private _editOriginalText?;
80
+ private _editResolve?;
81
+ private _editReject?;
82
+ /**
83
+ * Stop edit recording and get the edited text
84
+ * Call this after edit() when user stops speaking
85
+ */
86
+ stopEdit(): Promise<string>;
87
+ /**
88
+ * Cancel the current operation
89
+ */
90
+ cancel(): Promise<void>;
91
+ /**
92
+ * Access the state manager for subscribing to state changes
93
+ */
94
+ get state(): typeof state;
95
+ /**
96
+ * Access the event emitter for listening to events
97
+ */
98
+ get events(): typeof events;
99
+ /**
100
+ * Get the current config
101
+ */
102
+ getConfig(): SpeechOSConfig;
103
+ private ensureInitialized;
104
+ private cleanup;
105
+ /**
106
+ * Reset the SDK (useful for testing)
107
+ */
108
+ reset(): void;
109
+ }
110
+ export declare const speechOS: SpeechOSCore;
111
+ export {};