diff --git a/README.md b/README.md index fa212f1..d19580e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # EventBuzz Concierge -Independent Node.js service for the EventBuzz Virtual Concierge chatbot. Uses Amazon Bedrock for Call 1 (intent classification). Connects to the same MongoDB as the main EventBuzz backend. +Independent Node.js service for the EventBuzz Virtual Concierge chatbot. Uses Amazon Bedrock **tool calling** (router + synthesis). Connects to the same MongoDB as the main EventBuzz backend. ## Setup @@ -30,26 +30,31 @@ First time on the server: create `/var/www/concierge/.env` (copy from `.env.exam ## Embed demo 1. Start the server. -2. Open [http://localhost:3010/embed/demo](http://localhost:3010/embed/demo). -3. Set `botId` in the page script to a real `ConciergeBot` document `_id` from MongoDB. +2. Open [http://localhost:3010/embed/demo](http://localhost:3010/embed/demo) for the floating widget, or [http://localhost:3010/embed/avatar](http://localhost:3010/embed/avatar) for the SIMLI avatar + chat page. +3. Set `botId` in the page script to a real `ConciergeBot` document `_id` from MongoDB. The avatar page also needs `SIMLI_API_KEY` and `SIMLI_FACE_ID` in `.env`, plus LiveKit voice. ## API | Method | Path | Purpose | |--------|------|---------| | GET | `/api/public/concierge/{botId}/config` | Widget greeting / config | -| POST | `/api/public/concierge/{botId}/chat` | Send message (Call 1 classification) | +| POST | `/api/public/concierge/{botId}/chat` | Send a chat turn (tool-calling agent) | | GET | `/api/public/concierge/{botId}/session/{sessionRef}` | Resume session | +| POST | `/api/public/concierge/{botId}/voice/token` | LiveKit visitor token | +| POST | `/api/public/concierge/{botId}/simli/token` | SIMLI session token (API key stays on the server) | | GET | `/webhook/whatsapp` | Meta webhook verification | | POST | `/webhook/whatsapp` | Inbound WhatsApp (reception + follow-up) | -## Pipeline +## Agent -1. **Call 1** (Nova Micro) — intent + entities -2. **Call 2** (Claude 3 Haiku) — agenda replies with Sessions DB context -3. **Bookmark mutations** — `agenda_bookmark_add` / `agenda_bookmark_remove` write `SessionBookmark` records (no Call 2) -4. **Email OTP** — required for personal schedule, conflict check, and bookmark mutations; auth is stored on the web chat session +One Bedrock model (`BEDROCK_AGENT_MODEL_ID`, defaulting to Claude Haiku 4.5) chooses EventBuzz tools, the service executes them against Mongo (identity injected from the chat session), then the same model writes the visitor-facing reply. -Set `BEDROCK_CALL2_MODEL_ID`, `OTP_PEPPER`, and SMTP/`MAIL_*` vars in `.env` (see `.env.example`). Without SMTP, OTP codes are logged to the server console. +Default model: `global.anthropic.claude-haiku-4-5-20251001-v1:0` (global inference profile — required in `ap-south-1`). Prompt caching is on by default (`BEDROCK_PROMPT_CACHE=true`): tools, the system prompt, and the static EVENT brief are marked with `cachePoint` (1-hour TTL). Haiku 4.5 only records a cache hit after ~4096 tokens in that prefix; check `cacheReadInputTokens` / `cacheWriteInputTokens` in Bedrock logs. Claude 3 Haiku (20240307) cannot use `cachePoint`. + +Tools: `get_my_tickets`, `get_my_agenda`, `email_my_ticket`, `get_agenda`, `get_speakers`, `get_exhibitors`, `get_venue`, `get_local`, `get_tech_help`, `search_faqs`, `save_session`, `remove_saved_session`, `contact_organizer`. + +Personal tools and mutations require email OTP (`identity.service`). Widget, WhatsApp, and LiveKit voice all share this runner. + +Set `BEDROCK_AGENT_MODEL_ID` or `BEDROCK_CALL2_MODEL_ID`, `OTP_PEPPER`, and SMTP/`MAIL_*` vars in `.env` (see `.env.example`). Without SMTP, OTP codes are logged to the server console. Reception WhatsApp: point the Meta webhook at `GET/POST /webhook/whatsapp` on this service (verify token `META_WHATSAPP_WEBHOOK_VERIFY_TOKEN`). Configure copy and drips in the dashboard **WhatsApp Chatbots → Reception** tab. WABA credentials come from Organisation WhatsApp settings. diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs index 0c3a1f2..a0e5054 100644 --- a/ecosystem.config.cjs +++ b/ecosystem.config.cjs @@ -1,11 +1,14 @@ /** - * PM2 — EventBuzz Virtual Concierge API (same server as backend). + * PM2 — EventBuzz Virtual Concierge API + LiveKit voice worker (same server as backend). * * cd /var/www/concierge && pm2 start ecosystem.config.cjs * pm2 save * - * Requires concierge/.env on the server (MONGODB_URI, AWS/Bedrock, MAIL_*). + * Requires concierge/.env on the server (MONGODB_URI, AWS/Bedrock, MAIL_*, LIVEKIT_*). * Prefer HOST=0.0.0.0 (or nginx → 127.0.0.1:3010 with HOST=localhost). + * + * LiveKit voice: LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET. + * One worker instance only (it registers with LiveKit Cloud). */ module.exports = { apps: [ @@ -21,5 +24,18 @@ module.exports = { NODE_ENV: 'production', }, }, + { + name: 'livekit-voice-worker', + script: 'src/workers/livekitVoiceAgent.js', + args: 'start', + instances: 1, + exec_mode: 'fork', + autorestart: true, + max_restarts: 20, + restart_delay: 5000, + env: { + NODE_ENV: 'production', + }, + }, ], }; diff --git a/eventbuzz-concierge-implementation-plan.md b/eventbuzz-concierge-implementation-plan.md new file mode 100644 index 0000000..75f7cd0 --- /dev/null +++ b/eventbuzz-concierge-implementation-plan.md @@ -0,0 +1,263 @@ +# Specification: Eventbuzz Virtual Concierge (Text & LiveKit Voice) + +## 1. System Overview & Architecture + +Build a multi-modal (Text & Voice) AI concierge agent for the **Eventbuzz** platform. The system uses an **Agentic Tool-Calling pattern** where a single LLM call acts as both intent recognizer and response generator. + +```text + ┌──────────────┐ + │ Client / Web │ + │ (Text / App) │ + └──────┬───────┘ + │ (HTTP/WS with JWT) + ▼ +┌──────────────┐ ┌────────────────────────────────────────────────────────┐ +│ LiveKit │ │ Concierge Agent Gateway │ +│ Voice Agent ├─►│ 1. Extract & Verify Auth (User ID, Event ID) │ +│ (SIP/WebRTC) │ │ 2. Construct LLM Context & Bind User-Scoped Tools │ +└──────────────┘ └──────────────────────────┬─────────────────────────────┘ + │ + ▼ + ┌────────────────────┐ + │ LLM (Tool Calling) │ + └──────────┬─────────┘ + │ + ┌───────────────────────┴───────────────────────┐ + ▼ ▼ + [Personal Info Intent] [Event Knowledge Intent] + Tool: `get_my_registration` Tool: `search_event_faq` + Backend API / DB query Vector DB / Hybrid Search + (Hard-scoped to Auth Context) (Scoped to Event ID) + │ │ + └───────────────────────┬───────────────────────┘ + ▼ + ┌───────────────────────┐ + │ LLM Synthesis Call │ + └───────────┬───────────┘ + ▼ + Formatted Text / Audio Response +``` + +--- + +## 2. Core Project Structure + +```text +src/ +├── agent/ +│ ├── prompts.ts # System instructions and tone configuration +│ ├── runner.ts # Core LLM chat and tool execution loop +│ └── tools/ +│ ├── index.ts # Tool registry and dynamic context binding +│ ├── personal.tools.ts # Ticket, registration, and user-specific queries +│ └── knowledge.tools.ts # RAG / FAQ retrieval tools +├── auth/ +│ └── context.ts # Session extraction (userId, eventId, role) +├── livekit/ +│ └── voice_worker.ts # LiveKit Agents SDK worker integration +├── rag/ +│ └── retriever.ts # Vector search or semantic retrieval service +└── server.ts # WebSocket / HTTP entry point for text chat +``` + +--- + +## 3. Tool Declarations & Context Binding + +To prevent privilege escalation or data leakage, **the LLM must never supply the `userId` as an argument**. The execution layer injects user identity from the authenticated session. + +### Tool Schemas (Provided to LLM) + +```typescript +export const conciergeTools = [ + { + name: "get_my_registration_and_tickets", + description: "Retrieves the current attendee's ticket tier, QR code data, payment status, and registered sessions. Requires no input arguments.", + parameters: { + type: "object", + properties: {}, + required: [] + } + }, + { + name: "search_event_info", + description: "Searches event details such as venue policies, schedules, speaker bios, dress code, Wi-Fi details, and general FAQs.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "Specific search term or question regarding the event." + } + }, + required: ["query"] + } + }, + { + name: "get_agenda_schedule", + description: "Fetches schedule items for a specific date, stage, or track.", + parameters: { + type: "object", + properties: { + date: { type: "string", description: "Date in YYYY-MM-DD format (optional)" }, + track: { type: "string", description: "Track or stage name (optional)" } + }, + required: [] + } + } +]; +``` + +### Context-Bound Tool Handlers + +```typescript +export interface SessionContext { + userId: string; + eventId: string; +} + +export function createToolExecutors(ctx: SessionContext) { + return { + get_my_registration_and_tickets: async () => { + // Injects ctx.userId automatically; LLM cannot spoof another user + return await db.registrations.findFirst({ + where: { userId: ctx.userId, eventId: ctx.eventId }, + include: { tickets: true, assignedSessions: true } + }); + }, + + search_event_info: async ({ query }: { query: string }) => { + return await vectorStore.similaritySearch(query, { + k: 4, + filter: { eventId: ctx.eventId } + }); + }, + + get_agenda_schedule: async (filters: { date?: string; track?: string }) => { + return await db.sessions.findMany({ + where: { + eventId: ctx.eventId, + ...(filters.date && { date: filters.date }), + ...(filters.track && { track: filters.track }) + } + }); + } + }; +} +``` + +--- + +## 4. System Prompt Design + +```text +You are the Eventbuzz Virtual Concierge for the event identified as {{eventName}}. +Your role is to assist participants with both logistical event questions and personal ticketing/registration details. + +Tone & Demeanor: +- Welcoming, clear, concise, and professional. +- For voice delivery (via LiveKit), keep responses brief (under 3 sentences when possible) and avoid formatting characters like markdown tables, bullets, or raw URLs. + +Tool Rules: +1. Whenever the user asks about their own status, tickets, check-in QR, or seat, invoke `get_my_registration_and_tickets`. +2. When the user asks general event questions (parking, dress code, speaker line-ups), invoke `search_event_info`. +3. NEVER assume personal registration details or make up ticket numbers. If a tool returns no record, inform the participant politely and suggest visiting the physical Help Desk. +``` + +--- + +## 5. Execution Loop (Text & Voice) + +```typescript +export async function runConciergeTurn( + messages: ChatMessage[], + ctx: SessionContext +) { + const toolExecutors = createToolExecutors(ctx); + + // First call: LLM determines whether to respond directly or invoke a tool + const response = await llm.chat({ + messages, + tools: conciergeTools, + toolChoice: "auto" + }); + + const toolCalls = response.message.tool_calls; + + // Case 1: Direct response (chit-chat or clarifications) + if (!toolCalls || toolCalls.length === 0) { + return response.message.content; + } + + // Case 2: Intent matched to tool + const toolResults = await Promise.all( + toolCalls.map(async (call) => { + const fn = toolExecutors[call.function.name]; + if (!fn) throw new Error(`Unknown tool: ${call.function.name}`); + + const args = JSON.parse(call.function.arguments || "{}"); + const result = await fn(args); + + return { + tool_call_id: call.id, + role: "tool", + name: call.function.name, + content: JSON.stringify(result) + }; + }) + ); + + // Synthesis call: Feed tool outputs back to LLM for final natural response + const finalResponse = await llm.chat({ + messages: [ + ...messages, + response.message, + ...toolResults + ] + }); + + return finalResponse.message.content; +} +``` + +--- + +## 6. LiveKit Voice Integration Spec + +Connect the agent to LiveKit rooms using the LiveKit Agents framework: + +1. **Room Entry & Token Validation:** When dispatching the voice worker to a participant's room, pass `userId` and `eventId` inside the LiveKit participant metadata or worker job metadata. +2. **STT & TTS Pipeline:** + * **STT:** Deepgram / Whisper stream. + * **LLM Engine:** Hook `runConciergeTurn` directly into the agent callback. + * **TTS:** Cartesia / ElevenLabs stream with low-latency configuration. +3. **Interrupt Handling:** Ensure LiveKit's turn-detection/VAD (Voice Activity Detection) cancels inflight LLM synthesis if the participant interrupts mid-sentence. + +--- + +## 7. Step-by-Step Implementation Tasks for Cursor + +```text +- [ ] Task 1: Setup data layer for event knowledge and registrations + - Create mock/read queries for `registrations`, `tickets`, and `sessions`. + - Implement basic RAG retriever filtered by `eventId`. + +- [ ] Task 2: Implement Tool Definitions and Executor Mapping + - Define JSON schemas in `src/agent/tools/`. + - Implement context-injection factory in `src/agent/tools/index.ts`. + +- [ ] Task 3: Build the Agent Orchestrator + - Implement `runConciergeTurn` in `src/agent/runner.ts`. + - Add conversational state management (in-memory or Redis session store). + +- [ ] Task 4: Expose Text Chat API + - Create a POST `/api/concierge/chat` route with JWT validation. + - Test via curl: + 1. General query ("Where is the keynote?") + 2. Personal query ("Do I have a VIP pass?") + +- [ ] Task 5: Implement LiveKit Voice Worker + - Configure `livekit-agents` worker in `src/livekit/voice_worker.ts`. + - Bind participant metadata to `SessionContext`. + - Ensure tool outputs format cleanly for speech (no bullet points or markdown tables). +``` diff --git a/package-lock.json b/package-lock.json index 3f9140a..9264f5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,8 @@ "livekit-server-sdk": "^2.18.0", "mongoose": "^8.0.0", "nodemailer": "^6.9.7", - "qrcode": "^1.5.4" + "qrcode": "^1.5.4", + "simli-client": "^3.0.2" }, "engines": { "node": ">=20" @@ -2796,6 +2797,13 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, + "node_modules/@types/dom-mediacapture-record": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.22.tgz", + "integrity": "sha512-mUMZLK3NvwRLcAAT9qmcK+9p7tpU2FHdDsntR3YI4+GY88XrgG4XiE7u1Q2LAN2/FZOz/tdMDC3GQCR4T8nFuw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "16.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", @@ -4025,6 +4033,36 @@ "node": ">=12.0.0" } }, + "node_modules/livekit-client": { + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.22.3.tgz", + "integrity": "sha512-jw9zBKXY5Gtr5MZ7vEON3QhMNccuDvYHck1PFSyG1aaateQPqgKZFBMgZkFZaXHIf9RV4MDW5xpTK2b/+qbwOg==", + "license": "Apache-2.0", + "dependencies": { + "@livekit/mutex": "1.1.1", + "@livekit/protocol": "1.50.4", + "events": "^3.3.0", + "jose": "^6.1.0", + "loglevel": "^1.9.2", + "machina": "^7.0.0", + "sdp-transform": "^2.15.0", + "tslib": "2.8.1", + "typed-emitter": "^2.1.0", + "webrtc-adapter": "9.0.6" + }, + "peerDependencies": { + "@types/dom-mediacapture-record": "^1" + } + }, + "node_modules/livekit-client/node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/livekit-server-sdk": { "version": "2.18.0", "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-2.18.0.tgz", @@ -4076,6 +4114,19 @@ "node": ">=8" } }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/lru-cache": { "version": "11.5.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", @@ -4085,6 +4136,15 @@ "node": "20 || >=22" } }, + "node_modules/machina": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/machina/-/machina-7.0.1.tgz", + "integrity": "sha512-0Xkgs8NeBvO2hW6bjJT8ewLWrMfh1haMPstaghpASWjXkLjRhtDiaJG1Kb7mslaOHxZYxpH7PIjpmirRVYbMMA==", + "license": "MIT", + "engines": { + "node": ">=22.22" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4828,6 +4888,16 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "license": "ISC" }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4866,6 +4936,21 @@ "node": ">=11.0.0" } }, + "node_modules/sdp": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.2.tgz", + "integrity": "sha512-xZocWwfyp4hkbN4hLWxMjmv2Q8aNa9MhmOZ7L9aCZPT+dZsgRr6wZRrSYE3HTdyk/2pZKPSgqI7ns7Een1xMSA==", + "license": "MIT" + }, + "node_modules/sdp-transform": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.15.0.tgz", + "integrity": "sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==", + "license": "MIT", + "bin": { + "sdp-verify": "checker.js" + } + }, "node_modules/secure-json-parse": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", @@ -4945,6 +5030,15 @@ "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", "license": "MIT" }, + "node_modules/simli-client": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/simli-client/-/simli-client-3.0.2.tgz", + "integrity": "sha512-Evfxj1hr7r1rtk/HOK7Ub7WZZQStkrRBFvCvWG/zukaZUw1QJcRO2bH/YKUpU2beaLfothTCb2XegTV+YlhRFw==", + "license": "MIT", + "dependencies": { + "livekit-client": "^2.15.15" + } + }, "node_modules/sonic-boom": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", @@ -5092,6 +5186,15 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "license": "MIT", + "optionalDependencies": { + "rxjs": "*" + } + }, "node_modules/typescript": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", @@ -5151,6 +5254,19 @@ "node": ">=12" } }, + "node_modules/webrtc-adapter": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.6.tgz", + "integrity": "sha512-CHbl2ZQbxx164IgWRgzJno4hWtM4tFbRam1QfI3Yxhs3w/DvqluVxVWeXs3oL5/fbGkSNLKo0Ty5MgUWceNhog==", + "license": "BSD-3-Clause", + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, "node_modules/whatwg-fetch": { "version": "3.6.20", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", diff --git a/package.json b/package.json index 423e18f..3f19f38 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "eventbuzz-concierge", "version": "1.0.0", - "description": "EventBuzz Virtual Concierge — independent Bedrock two-call chatbot service", + "description": "EventBuzz Virtual Concierge — Bedrock tool-calling chatbot service", "main": "server.js", "engines": { "node": ">=20" @@ -10,7 +10,8 @@ "start": "node server.js", "free-port": "node scripts/free-port.js", "dev": "node scripts/free-port.js && node --watch server.js", - "livekit-voice-worker": "node src/workers/livekitVoiceAgent.js start" + "livekit-voice-worker": "node src/workers/livekitVoiceAgent.js start", + "bundle:simli": "npx esbuild node_modules/simli-client/lib/client.ts --bundle --format=esm --platform=browser --target=es2020 --minify --outfile=public/embed/simli-client.js" }, "dependencies": { "@aws-sdk/client-bedrock-runtime": "^3.1030.0", @@ -26,6 +27,7 @@ "livekit-server-sdk": "^2.18.0", "mongoose": "^8.0.0", "nodemailer": "^6.9.7", - "qrcode": "^1.5.4" + "qrcode": "^1.5.4", + "simli-client": "^3.0.2" } } diff --git a/public/embed-avatar.html b/public/embed-avatar.html new file mode 100644 index 0000000..fe024ae --- /dev/null +++ b/public/embed-avatar.html @@ -0,0 +1,47 @@ + + + + + + EventBuzz Concierge Avatar + + + +
+
+ + + +
Starting…
+
+
+
+ +
+
Concierge
+
Voice and chat
+
+
+
+ +
+ + +
+
+
+ + + + + + + diff --git a/public/embed/avatar-page.css b/public/embed/avatar-page.css new file mode 100644 index 0000000..afc6e31 --- /dev/null +++ b/public/embed/avatar-page.css @@ -0,0 +1,299 @@ +:root { + --eba-accent: rgb(13, 148, 136); + --eba-bg: #0b1220; + --eba-chat-bg: #f8fafc; + --eba-text: #0f172a; + --eba-muted: #64748b; + --eba-border: #e2e8f0; +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + height: 100%; + overflow: hidden; + font-family: "Segoe UI", system-ui, -apple-system, sans-serif; + color: var(--eba-text); + background: var(--eba-bg); +} + +#eba-root { + height: 100dvh; + display: grid; + grid-template-columns: 2fr 1fr; + grid-template-rows: 1fr; +} + +.eba-avatar { + position: relative; + background: #020617; + min-width: 0; + min-height: 0; +} + +#eba-video { + width: 100%; + height: 100%; + object-fit: cover; + background: #020617; +} + +#eba-audio { + position: absolute; + width: 1px; + height: 1px; + opacity: 0; + pointer-events: none; +} + +#eba-wave { + display: none; + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.eba-mode-wave #eba-video, +.eba-mode-wave #eba-audio { + display: none; +} + +.eba-mode-wave #eba-wave { + display: block; +} + +.eba-mode-video #eba-wave { + display: none; +} + +#eba-avatar-status { + position: absolute; + left: 16px; + bottom: 16px; + padding: 6px 10px; + border-radius: 999px; + background: rgba(2, 6, 23, 0.7); + color: #e2e8f0; + font-size: 13px; +} + +.eba-chat { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + background: var(--eba-chat-bg); + border-left: 1px solid var(--eba-border); +} + +.eba-chat-header { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 14px; + border-bottom: 1px solid var(--eba-border); + background: #fff; +} + +.eba-logo { + width: 32px; + height: 32px; + object-fit: contain; +} + +.eba-chat-title { + font-weight: 700; + font-size: 15px; +} + +.eba-chat-sub { + font-size: 12px; + color: var(--eba-muted); +} + +.eba-messages { + flex: 1; + overflow: auto; + padding: 12px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.eba-msg { + max-width: 92%; + padding: 8px 10px; + border-radius: 12px; + font-size: 14px; + line-height: 1.4; + white-space: pre-wrap; + word-break: break-word; +} + +.eba-msg.user { + align-self: flex-end; + background: var(--eba-accent); + color: #fff; +} + +.eba-msg.bot { + align-self: flex-start; + background: #fff; + border: 1px solid var(--eba-border); +} + +.eba-msg.bot strong { + font-weight: 700; +} + +.eba-cards { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 8px; +} + +.eba-card { + border: 1px solid var(--eba-border); + border-radius: 10px; + padding: 8px; + background: #fff; +} + +.eba-card-title { + font-weight: 650; +} + +.eba-card-meta { + font-size: 12px; + color: var(--eba-muted); + margin-top: 4px; +} + +.eba-card a { + color: var(--eba-accent); + font-size: 13px; +} + +.eba-card img { + display: block; + max-width: 100%; + border-radius: 8px; + margin-top: 6px; +} + +.eba-feedback { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; + font-size: 12px; + color: var(--eba-muted); +} + +.eba-feedback button { + border: 1px solid var(--eba-border); + background: #fff; + border-radius: 8px; + cursor: pointer; +} + +.eba-chips { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 0 12px 8px; +} + +.eba-chip { + border: 1px solid var(--eba-border); + background: #fff; + border-radius: 999px; + padding: 6px 10px; + font-size: 12px; + cursor: pointer; +} + +.eba-error { + color: #b91c1c; + font-size: 13px; + padding: 0 12px 8px; +} + +.eba-form { + display: flex; + gap: 8px; + padding: 10px 12px 12px; + border-top: 1px solid var(--eba-border); + background: #fff; +} + +#eba-input { + flex: 1; + border: 1px solid var(--eba-border); + border-radius: 10px; + padding: 10px 12px; + font: inherit; +} + +#eba-send { + border: 0; + border-radius: 10px; + padding: 10px 14px; + background: var(--eba-accent); + color: #fff; + font-weight: 650; + cursor: pointer; +} + +#eba-send:disabled, +#eba-input:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.eba-start { + position: fixed; + inset: 0; + z-index: 20; + border: 0; + background: rgba(2, 6, 23, 0.72); + color: #fff; + font-size: 22px; + font-weight: 700; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + padding: 24px; +} + +.eba-start-hint { + font-size: 14px; + font-weight: 500; + color: #cbd5e1; + max-width: 28rem; +} + +.eba-start[hidden] { + display: none; +} + +@media (max-width: 768px) { + #eba-root { + grid-template-columns: 1fr; + grid-template-rows: 2fr 1fr; + } + + .eba-chat { + border-left: 0; + border-top: 1px solid var(--eba-border); + } +} diff --git a/public/embed/avatar-page.js b/public/embed/avatar-page.js new file mode 100644 index 0000000..ad85fa1 --- /dev/null +++ b/public/embed/avatar-page.js @@ -0,0 +1,1071 @@ +const cfg = window.EB_CONCIERGE || {}; +const botId = cfg.botId; +const apiBase = String(cfg.apiBase || '').replace(/\/$/, ''); +const Livekit = window.LivekitClient || window.livekit; + +const videoEl = document.getElementById('eba-video'); +const audioEl = document.getElementById('eba-audio'); +const waveEl = document.getElementById('eba-wave'); +const messagesEl = document.getElementById('eba-messages'); +const errorEl = document.getElementById('eba-error'); +const formEl = document.getElementById('eba-form'); +const inputEl = document.getElementById('eba-input'); +const sendBtn = document.getElementById('eba-send'); +const startBtn = document.getElementById('eba-start'); +const startHintEl = document.getElementById('eba-start-hint'); +const statusEl = document.getElementById('eba-avatar-status'); +const titleEl = document.getElementById('eba-title'); +const subEl = document.getElementById('eba-sub'); + +let sessionId = null; +let busy = false; +let livekitRoom = null; +let simliClient = null; +let started = false; +let chatReady = false; +let pipedTrackId = null; +let seenTranscriptIds = {}; +let chipsEl = null; +const mutedLivekitAudioEls = []; +let pendingGreeting = ''; +let pendingQuickReplies = []; +let greetingShown = false; +let avatarReadySent = false; +let lastAgentMediaTrack = null; +let lastAgentTrackClone = null; +let sessionAlive = false; +let simliRestarting = false; +let simliIdle = false; +let simliGeneration = 0; +let videoAvatarOn = false; +let waveAudioCtx = null; +let userAnalyser = null; +let agentAnalyser = null; +let waveRaf = 0; +let simliInputEnabled = false; +let agentPipelineState = ''; +let micListening = true; +let micPublished = false; +let micResumeTimer = 0; +const SIMLI_AUDIO_BUFFER = 3000; +const AGENT_STATE_ATTR = 'lk.agent.state'; + +function setStatus(text) { + statusEl.textContent = text || ''; +} + +function setError(text) { + const msg = String(text || '').trim(); + errorEl.hidden = !msg; + errorEl.textContent = msg; +} + +function showStartOverlay(label, hint) { + startBtn.hidden = false; + const title = startBtn.querySelector('.eba-start-title'); + if (title) title.textContent = label || 'Tap to start'; + if (startHintEl) startHintEl.textContent = hint || ''; +} + +function hideStartOverlay() { + startBtn.hidden = true; + if (startHintEl) startHintEl.textContent = ''; +} + +function el(tag, attrs, children) { + const n = document.createElement(tag); + if (attrs) { + Object.keys(attrs).forEach((k) => n.setAttribute(k, attrs[k])); + } + (children || []).forEach((c) => { + n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c); + }); + return n; +} + +function appendFormattedText(parent, text) { + const s = String(text || ''); + const re = /\*\*([^*]+)\*\*/g; + let last = 0; + let m; + while ((m = re.exec(s))) { + if (m.index > last) parent.appendChild(document.createTextNode(s.slice(last, m.index))); + parent.appendChild(el('strong', null, [m[1]])); + last = m.index + m[0].length; + } + if (last < s.length) parent.appendChild(document.createTextNode(s.slice(last))); + return parent; +} + +function addMessage(role, text, opts = {}) { + const bubble = el('div', { class: `eba-msg ${role === 'user' ? 'user' : 'bot'}` }); + if (text) { + const intro = el('div'); + appendFormattedText(intro, text); + bubble.appendChild(intro); + } + const cards = Array.isArray(opts.cards) ? opts.cards : []; + if (cards.length) { + const list = el('div', { class: 'eba-cards' }); + cards.forEach((card) => { + const node = renderCard(card); + if (node) list.appendChild(node); + }); + if (list.childNodes.length) bubble.appendChild(list); + } + if (opts.messageId && role !== 'user') { + bubble.appendChild(buildFeedback(String(opts.messageId))); + } + messagesEl.appendChild(bubble); + messagesEl.scrollTop = messagesEl.scrollHeight; + return bubble; +} + +function renderCard(card) { + if (!card) return null; + const d = card.data || {}; + const type = card.type; + const wrap = el('article', { class: 'eba-card' }); + + if (type === 'cta' && d.url) { + wrap.appendChild( + el( + 'a', + { href: String(d.url), target: '_blank', rel: 'noopener noreferrer' }, + [String(d.actionLabel || d.title || 'Open')], + ), + ); + return wrap; + } + + if (type === 'ticket_qr') { + wrap.appendChild(el('div', { class: 'eba-card-title' }, [d.name || d.ticketName || 'Ticket'])); + if (d.status) wrap.appendChild(el('div', { class: 'eba-card-meta' }, [String(d.status)])); + const img = d.qrImageUrl || d.imageUrl; + if (img) wrap.appendChild(el('img', { src: String(img), alt: 'Check-in QR' })); + return wrap; + } + + if (type === 'file') { + const title = String(d.title || 'Download').trim() || 'Download'; + wrap.appendChild(el('div', { class: 'eba-card-title' }, [title])); + if (d.url) { + wrap.appendChild( + el('a', { href: String(d.url), target: '_blank', rel: 'noopener noreferrer' }, ['Open']), + ); + } + return wrap; + } + + const title = d.title || d.name; + if (title) wrap.appendChild(el('div', { class: 'eba-card-title' }, [String(title)])); + const meta = [ + d.timeRangeLocal, + d.location, + d.track, + d.booth, + d.address, + d.organization || d.organizationName, + d.speakerRole, + ] + .filter(Boolean) + .join(' · '); + if (meta) wrap.appendChild(el('div', { class: 'eba-card-meta' }, [meta])); + if (Array.isArray(d.speakers) && d.speakers.length) { + wrap.appendChild( + el('div', { class: 'eba-card-meta' }, [`Speakers: ${d.speakers.slice(0, 4).join(', ')}`]), + ); + } + const href = d.url || d.mapsUrl || d.website; + if (href) { + wrap.appendChild( + el('a', { href: String(href), target: '_blank', rel: 'noopener noreferrer' }, [ + d.mapsUrl ? 'Open map' : 'Open', + ]), + ); + } + return wrap.childNodes.length ? wrap : null; +} + +function buildFeedback(messageId) { + const footer = el('div', { class: 'eba-feedback' }, ['Was this useful?']); + const up = el('button', { type: 'button', 'aria-label': 'Yes' }, ['👍']); + const down = el('button', { type: 'button', 'aria-label': 'No' }, ['👎']); + const send = async (satisfied) => { + up.disabled = true; + down.disabled = true; + try { + await api(`/public/concierge/${encodeURIComponent(botId)}/feedback`, { + method: 'POST', + body: { sessionId, messageId, satisfied }, + }); + } catch { + /* ignore */ + } + }; + up.addEventListener('click', () => send(true)); + down.addEventListener('click', () => send(false)); + footer.appendChild(up); + footer.appendChild(down); + return footer; +} + +function renderChips(items) { + if (chipsEl && chipsEl.parentNode) chipsEl.parentNode.removeChild(chipsEl); + chipsEl = null; + const rows = Array.isArray(items) ? items : []; + if (!rows.length) return; + chipsEl = el('div', { class: 'eba-chips' }); + rows.slice(0, 6).forEach((item) => { + const label = String(item.label || item.message || '').trim(); + if (!label) return; + const btn = el('button', { class: 'eba-chip', type: 'button' }, [label]); + btn.addEventListener('click', () => { + if (busy) return; + inputEl.value = String(item.message || label); + sendMessage(); + }); + chipsEl.appendChild(btn); + }); + if (!chipsEl.childNodes.length) { + chipsEl = null; + return; + } + messagesEl.appendChild(chipsEl); + messagesEl.scrollTop = messagesEl.scrollHeight; +} + +function normalizeSpeech(text) { + return String(text || '') + .replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/\s+/g, ' ') + .trim() + .toLowerCase(); +} + +function showPendingGreeting() { + if (greetingShown) return; + greetingShown = true; + if (pendingGreeting) addMessage('bot', pendingGreeting); + if (pendingQuickReplies.length) renderChips(pendingQuickReplies); +} + +async function signalAvatarReady(room) { + if (avatarReadySent || !room?.localParticipant) return; + avatarReadySent = true; + showPendingGreeting(); + const payload = new TextEncoder().encode(JSON.stringify({ type: 'avatar-ready' })); + const send = async () => { + try { + if (typeof room.localParticipant.publishData === 'function') { + await room.localParticipant.publishData(payload, { reliable: true }); + } + } catch (err) { + console.warn('[avatar] ready signal failed', err); + } + }; + await send(); + setTimeout(send, 1500); +} + +function rememberTranscript(id) { + if (!id) return false; + if (seenTranscriptIds[id]) return true; + seenTranscriptIds[id] = true; + return false; +} + +async function api(path, options) { + const hasBody = options?.body !== undefined; + const res = await fetch(apiBase + path, { + method: options?.method || 'GET', + headers: { + Accept: 'application/json', + ...(hasBody ? { 'Content-Type': 'application/json' } : {}), + }, + body: hasBody ? JSON.stringify(options.body) : undefined, + }); + const json = await res.json().catch(() => null); + if (!res.ok || !json || json.success === false) { + const msg = json?.error?.message || json?.message || `Request failed (${res.status})`; + throw new Error(msg); + } + return json.data; +} + +function setBusy(next) { + busy = next; + inputEl.disabled = next; + sendBtn.disabled = next; +} + +async function sendMessage() { + const text = String(inputEl.value || '').trim(); + if (!text || busy) return; + setError(''); + inputEl.value = ''; + renderChips([]); + addMessage('user', text); + ensureSimliConnected('chat'); + setBusy(true); + try { + const data = await api(`/public/concierge/${encodeURIComponent(botId)}/chat`, { + method: 'POST', + body: { message: text, sessionId: sessionId || undefined }, + }); + sessionId = data.sessionId || sessionId; + addMessage('bot', data.reply || 'No reply', { + cards: data.cards || [], + messageId: data.messageId || null, + }); + if (Array.isArray(data.quickReplies) && data.quickReplies.length) { + renderChips(data.quickReplies); + } + } catch (err) { + setError(err.message || 'Failed to send'); + } finally { + setBusy(false); + inputEl.focus(); + } +} + +function mediaTrackFrom(livekitTrack) { + if (!livekitTrack) return null; + return livekitTrack.mediaStreamTrack || livekitTrack.mediaStream?.getAudioTracks?.()[0] || null; +} + +function isLocalParticipant(room, participant) { + if (!room || !participant) return false; + const local = room.localParticipant; + if (local && (participant === local || (local.sid && participant.sid === local.sid))) { + return true; + } + const localId = local?.identity; + return Boolean(localId && participant.identity === localId); +} + +function stopAgentTrackClone() { + if (lastAgentTrackClone && typeof lastAgentTrackClone.stop === 'function') { + try { + lastAgentTrackClone.stop(); + } catch { + /* ignore */ + } + } + lastAgentTrackClone = null; +} + +function unmuteSimliPlayback() { + if (!audioEl) return; + audioEl.muted = false; + audioEl.defaultMuted = false; + audioEl.volume = 1; + audioEl.removeAttribute('muted'); +} + +function parseAgentStatePayload(raw) { + try { + const msg = JSON.parse(typeof raw === 'string' ? raw : new TextDecoder().decode(raw)); + if (msg && msg.type === 'agent-state' && msg.state) return String(msg.state).toLowerCase(); + } catch { + /* ignore */ + } + return ''; +} + +function readRemoteAgentState(room) { + let state = ''; + room?.remoteParticipants?.forEach((p) => { + if (!p || isLocalParticipant(room, p)) return; + const value = p.attributes?.[AGENT_STATE_ATTR]; + if (value) state = String(value).toLowerCase(); + }); + return state; +} + +function disconnectSimliTts() { + const src = simliClient?.sourceNode; + if (src && typeof src.disconnect === 'function') { + try { + src.disconnect(); + } catch { + /* ignore */ + } + } + if (simliClient) simliClient.sourceNode = null; + stopAgentTrackClone(); + simliInputEnabled = false; + pipedTrackId = null; +} + +function connectSimliTts(media) { + if (!videoAvatarOn || !media || !simliClient || typeof simliClient.listenToMediastreamTrack !== 'function') { + return; + } + if (simliIdle) { + ensureSimliConnected('agent-tts'); + return; + } + const id = media.id || media.label; + if (simliInputEnabled && pipedTrackId && pipedTrackId === id) return; + disconnectSimliTts(); + try { + const clone = typeof media.clone === 'function' ? media.clone() : media; + lastAgentTrackClone = clone; + if (clone) clone.enabled = true; + simliClient.listenToMediastreamTrack(clone); + simliInputEnabled = true; + pipedTrackId = id; + } catch (err) { + console.warn('[avatar] simli tts pipe failed', err); + } +} + +async function setMicListening(on) { + const next = Boolean(on); + if (micResumeTimer) { + clearTimeout(micResumeTimer); + micResumeTimer = 0; + } + if (!micPublished || !livekitRoom?.localParticipant) { + micListening = next; + return; + } + if (micListening === next) return; + micListening = next; + try { + if (typeof livekitRoom.localParticipant.setMicrophoneEnabled === 'function') { + await livekitRoom.localParticipant.setMicrophoneEnabled(next); + } + } catch (err) { + console.warn('[avatar] mic toggle failed', err); + } +} + +function applyAgentState(state) { + const next = String(state || '').toLowerCase(); + if (!next) return; + agentPipelineState = next; + const speaking = next === 'speaking'; + const holdMic = speaking || next === 'thinking'; + + if (videoAvatarOn) { + if (speaking && lastAgentMediaTrack) { + if (!simliInputEnabled) connectSimliTts(lastAgentMediaTrack); + } else if (!speaking) { + disconnectSimliTts(); + } + } + + if (holdMic) { + setMicListening(false); + } else { + micResumeTimer = window.setTimeout(() => setMicListening(true), 700); + } + + if (simliIdle || simliRestarting) return; + if (next === 'thinking') setStatus('Thinking'); + else if (speaking) setStatus('Speaking'); + else setStatus('Listening'); +} + +function makeSimliClient(SimliClient, LogLevel, token) { + return new SimliClient( + token, + videoEl, + audioEl, + null, + LogLevel?.INFO ?? 1, + 'livekit', + 'websockets', + 'wss://api.simli.ai', + SIMLI_AUDIO_BUFFER, + ); +} + +function attachRemoteAudio(track, audible) { + if (!track || typeof track.attach !== 'function') return; + try { + const node = track.attach(); + if (!node) return; + node.muted = !audible; + node.volume = audible ? 1 : 0; + node.autoplay = true; + node.playsInline = true; + node.style.position = 'absolute'; + node.style.width = '1px'; + node.style.height = '1px'; + node.style.opacity = '0'; + node.style.pointerEvents = 'none'; + document.body.appendChild(node); + mutedLivekitAudioEls.push(node); + if (audible && node.play) node.play().catch(() => {}); + } catch { + /* ignore */ + } +} + +function unmuteLivekitFallback() { + mutedLivekitAudioEls.forEach((node) => { + try { + node.muted = false; + node.volume = 1; + if (node.play) node.play().catch(() => {}); + } catch { + /* ignore */ + } + }); +} + +function muteLivekitPlayback() { + mutedLivekitAudioEls.forEach((node) => { + try { + node.muted = true; + node.volume = 0; + } catch { + /* ignore */ + } + }); +} + +function parseAccentRgb() { + const raw = getComputedStyle(document.documentElement).getPropertyValue('--eba-accent').trim(); + const m = raw.match(/(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/); + if (m) return { r: Number(m[1]), g: Number(m[2]), b: Number(m[3]) }; + return { r: 13, g: 148, b: 136 }; +} + +function ensureWaveAudioCtx() { + if (!waveAudioCtx) { + const Ctx = window.AudioContext || window.webkitAudioContext; + if (!Ctx) return null; + waveAudioCtx = new Ctx(); + } + if (waveAudioCtx.state === 'suspended') waveAudioCtx.resume().catch(() => {}); + return waveAudioCtx; +} + +function hookWaveAnalyser(mediaTrack, slot) { + if (!mediaTrack || videoAvatarOn) return; + const ctx = ensureWaveAudioCtx(); + if (!ctx) return; + try { + const clone = typeof mediaTrack.clone === 'function' ? mediaTrack.clone() : mediaTrack; + const source = ctx.createMediaStreamSource(new MediaStream([clone])); + const analyser = ctx.createAnalyser(); + analyser.fftSize = 256; + analyser.smoothingTimeConstant = 0.72; + source.connect(analyser); + if (slot === 'user') userAnalyser = analyser; + else agentAnalyser = analyser; + } catch (err) { + console.warn('[avatar] wave analyser failed', err); + } + startWaveLoop(); +} + +function analyserSnapshot(analyser) { + if (!analyser) return { rms: 0, bins: null }; + const bins = new Uint8Array(analyser.frequencyBinCount); + analyser.getByteFrequencyData(bins); + let sum = 0; + for (let i = 0; i < bins.length; i += 1) sum += bins[i] * bins[i]; + return { rms: Math.sqrt(sum / bins.length) / 255, bins }; +} + +function stopWaveLoop() { + if (waveRaf) { + cancelAnimationFrame(waveRaf); + waveRaf = 0; + } +} + +function startWaveLoop() { + if (!waveEl || videoAvatarOn || waveRaf) return; + const canvas = waveEl; + const ctx2d = canvas.getContext('2d'); + if (!ctx2d) return; + + const draw = () => { + waveRaf = requestAnimationFrame(draw); + const w = canvas.clientWidth || 1; + const h = canvas.clientHeight || 1; + if (canvas.width !== w || canvas.height !== h) { + canvas.width = w; + canvas.height = h; + } + const user = analyserSnapshot(userAnalyser); + const agent = analyserSnapshot(agentAnalyser); + const accent = parseAccentRgb(); + const t = Date.now() / 1000; + const idle = 0.1 + 0.04 * Math.sin(t * 2.2); + const userRms = Math.max(user.rms, 0); + const agentRms = Math.max(agent.rms, 0); + const level = Math.max(userRms, agentRms, idle * 0.35); + const agentLead = agentRms >= userRms; + const bins = (agentLead ? agent.bins : user.bins) || new Uint8Array(32); + const barCount = 36; + ctx2d.clearRect(0, 0, w, h); + const bg = ctx2d.createRadialGradient(w / 2, h / 2, 20, w / 2, h / 2, Math.max(w, h) * 0.55); + bg.addColorStop(0, `rgba(${accent.r},${accent.g},${accent.b},${0.12 + level * 0.28})`); + bg.addColorStop(1, 'rgba(2,6,23,0)'); + ctx2d.fillStyle = bg; + ctx2d.fillRect(0, 0, w, h); + + const cx = w / 2; + const cy = h / 2; + const radius = Math.min(w, h) * 0.16; + ctx2d.beginPath(); + ctx2d.arc(cx, cy, radius * (0.85 + level * 0.55), 0, Math.PI * 2); + ctx2d.fillStyle = agentLead + ? `rgba(${accent.r},${accent.g},${accent.b},${0.35 + agentRms * 0.5})` + : `rgba(226,232,240,${0.22 + userRms * 0.5})`; + ctx2d.fill(); + + const step = Math.max(1, Math.floor(bins.length / barCount)); + const gap = 6; + const barW = Math.max(4, (Math.min(w, h) * 0.72 - gap * (barCount - 1)) / barCount); + const totalW = barCount * barW + (barCount - 1) * gap; + const x0 = cx - totalW / 2; + const maxBar = Math.min(h, w) * 0.28; + for (let i = 0; i < barCount; i += 1) { + const v = (bins[i * step] || 0) / 255; + const amp = Math.max(idle * 0.25, v) * (0.55 + level); + const bh = Math.max(8, amp * maxBar); + const x = x0 + i * (barW + gap); + const y = cy - bh / 2; + ctx2d.fillStyle = agentLead + ? `rgba(${accent.r},${accent.g},${accent.b},${0.45 + v * 0.5})` + : `rgba(203,213,225,${0.4 + v * 0.55})`; + ctx2d.beginPath(); + if (ctx2d.roundRect) ctx2d.roundRect(x, y, barW, bh, 4); + else ctx2d.rect(x, y, barW, bh); + ctx2d.fill(); + } + }; + draw(); +} + +function applyStageMode() { + const root = document.getElementById('eba-root'); + if (!root) return; + root.classList.toggle('eba-mode-wave', !videoAvatarOn); + root.classList.toggle('eba-mode-video', videoAvatarOn); + if (videoAvatarOn) stopWaveLoop(); + else startWaveLoop(); +} + +function handleIncomingAudio(track, participant, room) { + if (!track || String(track.kind || '').toLowerCase() !== 'audio') return; + if (isLocalParticipant(room, participant)) return; + attachRemoteAudio(track, true); + const media = mediaTrackFrom(track); + lastAgentMediaTrack = media; + if (videoAvatarOn) { + if (agentPipelineState === 'speaking') connectSimliTts(media); + } else { + hookWaveAnalyser(media, 'agent'); + setTimeout(() => signalAvatarReady(room), 400); + } +} + +function ensureSimliConnected(reason) { + if (!videoAvatarOn) return; + if (!sessionAlive || !livekitRoom || simliRestarting) return; + if (!simliIdle && simliClient) return; + restartSimliAvatar(reason || 'session-restart'); +} + +async function restartSimliAvatar(reason) { + if (!sessionAlive || simliRestarting || !livekitRoom) return; + simliRestarting = true; + simliGeneration += 1; + setStatus('Reconnecting avatar…'); + console.warn('[avatar] restarting simli', reason); + try { + const prev = simliClient; + simliClient = null; + if (prev && typeof prev.stop === 'function') { + await prev.stop().catch(() => {}); + } + const simli = await api(`/public/concierge/${encodeURIComponent(botId)}/simli/token`, { + method: 'POST', + body: {}, + }); + if (!simli?.sessionToken) throw new Error('Avatar session token was empty'); + const { SimliClient, LogLevel } = await loadSimliClient(); + const next = makeSimliClient(SimliClient, LogLevel, simli.sessionToken); + bindSimliEvents(next); + simliClient = next; + await next.start(); + const played = await resumeSimliAudio(); + pipedTrackId = null; + if (played) muteLivekitPlayback(); + else unmuteLivekitFallback(); + simliIdle = false; + applyAgentState(readRemoteAgentState(livekitRoom) || agentPipelineState); + } catch (err) { + console.warn('[avatar] simli restart failed', err); + simliIdle = true; + setStatus('Avatar disconnected'); + } finally { + simliRestarting = false; + } +} + +function bindSimliEvents(client) { + if (!client || typeof client.on !== 'function') return; + const gen = simliGeneration; + let ready = false; + client.on('start', () => { + if (gen !== simliGeneration) return; + ready = true; + simliIdle = false; + muteLivekitPlayback(); + unmuteSimliPlayback(); + if (audioEl?.play) audioEl.play().catch(() => {}); + applyAgentState(readRemoteAgentState(livekitRoom) || agentPipelineState); + }); + client.on('stop', () => { + if (gen !== simliGeneration) return; + simliIdle = true; + setStatus('Idle — speak to continue'); + }); + client.on('error', (message) => { + if (gen !== simliGeneration) return; + console.warn('[avatar] simli error', message); + if (sessionAlive && ready) restartSimliAvatar(message || 'error'); + }); +} + +async function loadSimliClient() { + const mod = await import('/embed/simli-client.js'); + const SimliClient = mod.SimliClient || mod.default?.SimliClient || mod.default; + if (typeof SimliClient !== 'function') { + throw new Error('SIMLI client failed to load'); + } + return { SimliClient, LogLevel: mod.LogLevel || mod.default?.LogLevel }; +} + +async function resumeSimliAudio() { + const ctx = simliClient?.audioContext; + if (ctx && ctx.state === 'suspended' && typeof ctx.resume === 'function') { + await ctx.resume().catch(() => {}); + } + unmuteSimliPlayback(); + let played = false; + if (audioEl?.play) { + played = await audioEl.play().then(() => true).catch(() => false); + } + if (videoEl?.play) videoEl.play().catch(() => {}); + return played; +} + +async function startSimliOverlay() { + if (!videoAvatarOn || simliRestarting) return; + simliRestarting = true; + try { + setStatus('Connecting avatar…'); + const simli = await api(`/public/concierge/${encodeURIComponent(botId)}/simli/token`, { + method: 'POST', + body: {}, + }); + if (!simli?.sessionToken) throw new Error('Avatar session token was empty'); + const { SimliClient, LogLevel } = await loadSimliClient(); + const next = makeSimliClient(SimliClient, LogLevel, simli.sessionToken); + bindSimliEvents(next); + simliClient = next; + await next.start(); + const played = await resumeSimliAudio(); + pipedTrackId = null; + if (played) muteLivekitPlayback(); + else unmuteLivekitFallback(); + applyAgentState(readRemoteAgentState(livekitRoom) || agentPipelineState); + } catch (err) { + console.warn('[avatar] simli overlay failed, voice continues', err); + simliClient = null; + unmuteLivekitFallback(); + setStatus('Voice only'); + } finally { + simliRestarting = false; + } +} + +async function teardownMedia() { + sessionAlive = false; + simliIdle = false; + simliGeneration += 1; + if (micResumeTimer) { + clearTimeout(micResumeTimer); + micResumeTimer = 0; + } + disconnectSimliTts(); + stopWaveLoop(); + userAnalyser = null; + agentAnalyser = null; + if (waveAudioCtx) { + waveAudioCtx.close().catch(() => {}); + waveAudioCtx = null; + } + avatarReadySent = false; + agentPipelineState = ''; + micPublished = false; + micListening = true; + lastAgentMediaTrack = null; + stopAgentTrackClone(); + pipedTrackId = null; + mutedLivekitAudioEls.splice(0).forEach((node) => { + try { + if (node.srcObject) node.srcObject = null; + if (node.parentNode) node.parentNode.removeChild(node); + } catch { + /* ignore */ + } + }); + const room = livekitRoom; + livekitRoom = null; + if (room) { + try { + await room.disconnect(); + } catch { + /* ignore */ + } + } + const simli = simliClient; + simliClient = null; + if (simli && typeof simli.stop === 'function') { + try { + await simli.stop(); + } catch { + /* ignore */ + } + } +} + +async function maybeResume() { + const params = new URLSearchParams(window.location.search || ''); + const ref = String(params.get('session') || params.get('sessionId') || cfg.sessionId || '').trim(); + if (!ref) return false; + const data = await api( + `/public/concierge/${encodeURIComponent(botId)}/session/${encodeURIComponent(ref)}`, + ); + sessionId = data.sessionId || sessionId; + messagesEl.innerHTML = ''; + renderChips([]); + (data.history || []).forEach((m) => { + addMessage(m.role === 'user' ? 'user' : 'bot', m.text); + }); + return true; +} + +async function loadChat() { + if (chatReady) return; + const config = await api(`/public/concierge/${encodeURIComponent(botId)}/config`); + titleEl.textContent = config.eventName || 'Concierge'; + subEl.textContent = 'Voice and chat'; + if (config.maxMessageLength) inputEl.setAttribute('maxlength', String(config.maxMessageLength)); + if (config.accentColor) { + document.documentElement.style.setProperty('--eba-accent', String(config.accentColor)); + } + let resumed = false; + try { + resumed = await maybeResume(); + } catch { + resumed = false; + } + if (!resumed && config.greeting) pendingGreeting = String(config.greeting); + if (!resumed && Array.isArray(config.quickReplies)) pendingQuickReplies = config.quickReplies; + chatReady = true; + if (!config.voiceEnabled) { + throw new Error('Voice is not enabled for this chatbot'); + } + videoAvatarOn = Boolean(config.videoAvatarEnabled || config.avatarEnabled); + applyStageMode(); +} + +async function startSession() { + if (started) return; + started = true; + hideStartOverlay(); + setError(''); + setStatus('Connecting…'); + + if (!botId || !apiBase) { + throw new Error('Missing botId or apiBase'); + } + if (!Livekit || !Livekit.Room) { + throw new Error('Voice client failed to load'); + } + + await loadChat(); + + if (!navigator.mediaDevices?.getUserMedia) { + throw new Error('This browser cannot access the microphone'); + } + const preview = await navigator.mediaDevices.getUserMedia({ audio: true }); + preview.getTracks().forEach((t) => t.stop()); + + const granted = await api(`/public/concierge/${encodeURIComponent(botId)}/voice/token`, { + method: 'POST', + body: { sessionId: sessionId || undefined }, + }); + sessionId = granted.sessionId || sessionId; + + sessionAlive = true; + if (!videoAvatarOn) startWaveLoop(); + + const room = new Livekit.Room({ + adaptiveStream: false, + dynacast: false, + audioCaptureDefaults: { + echoCancellation: true, + noiseSuppression: true, + autoGainControl: true, + }, + }); + livekitRoom = room; + + room.on(Livekit.RoomEvent.Disconnected, () => { + if (livekitRoom === room) { + livekitRoom = null; + sessionAlive = false; + setStatus('Disconnected'); + } + }); + room.on(Livekit.RoomEvent.TrackSubscribed, (track, _pub, participant) => { + handleIncomingAudio(track, participant, room); + }); + if (Livekit.RoomEvent.LocalTrackPublished) { + room.on(Livekit.RoomEvent.LocalTrackPublished, (pub) => { + const media = mediaTrackFrom(pub?.track); + if (media) hookWaveAnalyser(media, 'user'); + }); + } + if (Livekit.RoomEvent.DataReceived) { + room.on(Livekit.RoomEvent.DataReceived, (payload, participant) => { + if (isLocalParticipant(room, participant)) return; + const state = parseAgentStatePayload(payload); + if (state) applyAgentState(state); + }); + } + if (Livekit.RoomEvent.ParticipantAttributesChanged) { + room.on(Livekit.RoomEvent.ParticipantAttributesChanged, (_changed, participant) => { + if (isLocalParticipant(room, participant)) return; + const state = participant?.attributes?.[AGENT_STATE_ATTR]; + if (state) applyAgentState(state); + }); + } + room.on(Livekit.RoomEvent.TranscriptionReceived, (segments, participant) => { + const local = isLocalParticipant(room, participant); + (segments || []).forEach((seg) => { + if (!seg || !seg.final) return; + if (agentPipelineState === 'speaking' && local) return; + ensureSimliConnected(local ? 'user-speech' : 'agent-speech'); + if (rememberTranscript(seg.id || `${local ? 'user' : 'bot'}:${seg.text}`)) return; + if (!local && pendingGreeting && normalizeSpeech(seg.text) === normalizeSpeech(pendingGreeting)) { + showPendingGreeting(); + return; + } + addMessage(local ? 'user' : 'bot', seg.text); + }); + }); + room.on(Livekit.RoomEvent.ActiveSpeakersChanged, (speakers) => { + if ((speakers || []).length) ensureSimliConnected('speakers'); + if (simliIdle || simliRestarting || agentPipelineState === 'speaking' || agentPipelineState === 'thinking') { + return; + } + if (videoAvatarOn) return; + const localId = room.localParticipant?.identity; + const agentSpeaking = (speakers || []).some((p) => p?.identity && p.identity !== localId); + setStatus(agentSpeaking ? 'Speaking' : 'Listening'); + }); + + await room.connect(granted.url, granted.token); + if (typeof room.startAudio === 'function') { + await room.startAudio().catch(() => {}); + } + room.remoteParticipants.forEach((participant) => { + participant.trackPublications.forEach((pub) => { + if (pub?.track) handleIncomingAudio(pub.track, participant, room); + }); + }); + + let micPub = null; + if (typeof room.localParticipant.setMicrophoneEnabled === 'function') { + micPub = await room.localParticipant.setMicrophoneEnabled(true); + } + if (!micPub && typeof Livekit.createLocalAudioTrack === 'function') { + const micTrack = await Livekit.createLocalAudioTrack({ + echoCancellation: true, + noiseSuppression: true, + autoGainControl: true, + }); + micPub = await room.localParticipant.publishTrack(micTrack); + } + if (!micPub) throw new Error('Microphone did not publish. Allow mic access and try again.'); + micPublished = true; + micListening = true; + const localMedia = mediaTrackFrom(micPub.track); + if (localMedia) hookWaveAnalyser(localMedia, 'user'); + room.remoteParticipants.forEach((participant) => { + const state = participant?.attributes?.[AGENT_STATE_ATTR]; + if (state && !isLocalParticipant(room, participant)) applyAgentState(state); + }); + if (!videoAvatarOn) { + const ctx = ensureWaveAudioCtx(); + if (ctx && ctx.state === 'suspended') await ctx.resume().catch(() => {}); + setTimeout(() => signalAvatarReady(room), 400); + setStatus('Listening'); + return; + } + setStatus('Connecting avatar…'); + try { + await Promise.race([ + startSimliOverlay(), + new Promise((resolve) => setTimeout(resolve, 2500)), + ]); + } catch (err) { + console.warn('[avatar] simli overlay', err); + } + await signalAvatarReady(room); + applyAgentState(readRemoteAgentState(room) || agentPipelineState); +} + +function needsGesture(err) { + const msg = String(err?.name || err?.message || err || '').toLowerCase(); + return /notallowed|permission|denied|gesture|autoplay|aborterror|notallowederror|securityerror/.test( + msg, + ); +} + +async function boot() { + try { + await startSession(); + } catch (err) { + started = false; + console.warn('[avatar] start failed', err); + await teardownMedia(); + const message = err.message || 'Could not start'; + setError(message); + setStatus('Waiting'); + showStartOverlay(needsGesture(err) ? 'Tap to start' : 'Tap to retry', message); + } +} + +formEl.addEventListener('submit', (e) => { + e.preventDefault(); + sendMessage(); +}); + +startBtn.addEventListener('click', () => { + hideStartOverlay(); + boot(); +}); + +if (!botId || !apiBase) { + setError('Missing window.EB_CONCIERGE.botId or apiBase'); + setStatus('Not configured'); + showStartOverlay('Not configured', 'Set botId and apiBase on this page.'); +} else { + boot(); +} diff --git a/public/embed/concierge-widget.css b/public/embed/concierge-widget.css index 1fb6baf..4c0eb50 100644 --- a/public/embed/concierge-widget.css +++ b/public/embed/concierge-widget.css @@ -188,6 +188,10 @@ white-space: pre-wrap; } +#eventbuzz-concierge-root .ebc-msg.bot strong { + font-weight: 700; +} + #eventbuzz-concierge-root .ebc-intro + .ebc-cards-list, #eventbuzz-concierge-root .ebc-intro + .ebc-feedback, #eventbuzz-concierge-root .ebc-cards-list + .ebc-feedback { diff --git a/public/embed/concierge-widget.js b/public/embed/concierge-widget.js index 4df93ec..4ab0a1d 100644 --- a/public/embed/concierge-widget.js +++ b/public/embed/concierge-widget.js @@ -38,6 +38,25 @@ return n; } + function appendFormattedText(parent, text) { + var s = String(text || ''); + if (!s) return parent; + var re = /\*\*([^*]+)\*\*/g; + var last = 0; + var m; + while ((m = re.exec(s))) { + if (m.index > last) { + parent.appendChild(document.createTextNode(s.slice(last, m.index))); + } + parent.appendChild(el('strong', null, [m[1]])); + last = m.index + m[0].length; + } + if (last < s.length) { + parent.appendChild(document.createTextNode(s.slice(last))); + } + return parent; + } + var embedOrigin = apiBase.replace(/\/api$/, ''); function addCssOnce() { @@ -46,7 +65,7 @@ el('link', { id: 'ebc-css', rel: 'stylesheet', - href: embedOrigin + '/embed/concierge-widget.css?v=22', + href: embedOrigin + '/embed/concierge-widget.css?v=23', }), ); } @@ -686,7 +705,9 @@ function addEphemeralBotMessage(text) { var bubble = el('div', { class: 'ebc-msg bot ebc-ephemeral' }); - bubble.appendChild(document.createTextNode(String(text || ''))); + var intro = el('div', { class: 'ebc-intro' }); + appendFormattedText(intro, text); + bubble.appendChild(intro); messages.appendChild(bubble); } @@ -763,9 +784,9 @@ }); if (role === 'bot' || role === 'assistant') { if (text) { - bubble.appendChild( - el('div', { class: cards.length || opts.messageId ? 'ebc-intro' : '' }, [String(text)]), - ); + var intro = el('div', { class: 'ebc-intro' }); + appendFormattedText(intro, text); + bubble.appendChild(intro); } if (cards.length) { var list = el('div', { class: 'ebc-cards-list' }); diff --git a/public/embed/simli-client.js b/public/embed/simli-client.js new file mode 100644 index 0000000..be3c1d2 --- /dev/null +++ b/public/embed/simli-client.js @@ -0,0 +1,66 @@ +var $l=Object.defineProperty;var Xl=(i,e,t)=>e in i?$l(i,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[e]=t;var N=(i,e,t)=>Xl(i,typeof e!="symbol"?e+"":e,t);function Zl(i,e){return e.forEach(function(t){t&&typeof t!="string"&&!Array.isArray(t)&&Object.keys(t).forEach(function(n){if(n!=="default"&&!(n in i)){var s=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(i,n,s.get?s:{enumerable:!0,get:function(){return t[n]}})}})}),Object.freeze(i)}var eu=Object.defineProperty,tu=(i,e,t)=>e in i?eu(i,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[e]=t,vo=(i,e,t)=>tu(i,typeof e!="symbol"?e+"":e,t),le=class{constructor(){vo(this,"_locking"),vo(this,"_locks"),this._locking=Promise.resolve(),this._locks=0}isLocked(){return this._locks>0}lock(){this._locks+=1;let e,t=new Promise(s=>e=()=>{this._locks-=1,s()}),n=this._locking.then(()=>e);return this._locking=this._locking.then(()=>t),n}};function ie(i,e){if(!i)throw new Error(e)}var nu=34028234663852886e22,iu=-34028234663852886e22,su=4294967295,ru=2147483647,au=-2147483648;function ri(i){if(typeof i!="number")throw new Error("invalid int 32: "+typeof i);if(!Number.isInteger(i)||i>ru||isu||i<0)throw new Error("invalid uint 32: "+i)}function Uc(i){if(typeof i!="number")throw new Error("invalid float 32: "+typeof i);if(Number.isFinite(i)&&(i>nu||i({no:s.no,name:s.name,localName:i[s.no]})))}function jc(i,e,t){let n=Object.create(null),s=Object.create(null),r=[];for(let a of e){let o=qc(a);r.push(o),n[a.name]=o,s[a.no]=o}return{typeName:i,values:r,findName(a){return n[a]},findNumber(a){return s[a]}}}function cu(i,e,t){let n={};for(let s of e){let r=qc(s);n[r.localName]=r.no,n[r.no]=r.localName}return Bc(n,i,e),n}function qc(i){return"localName"in i?i:Object.assign(Object.assign({},i),{localName:i.name})}var On=class{equals(e){return this.getType().runtime.util.equals(this.getType(),this,e)}clone(){return this.getType().runtime.util.clone(this)}fromBinary(e,t){let n=this.getType(),s=n.runtime.bin,r=s.makeReadOptions(t);return s.readMessage(this,r.readerFactory(e),e.byteLength,r),this}fromJson(e,t){let n=this.getType(),s=n.runtime.json,r=s.makeReadOptions(t);return s.readMessage(n,e,r,this),this}fromJsonString(e,t){let n;try{n=JSON.parse(e)}catch(s){throw new Error("cannot decode ".concat(this.getType().typeName," from JSON: ").concat(s instanceof Error?s.message:String(s)))}return this.fromJson(n,t)}toBinary(e){let t=this.getType(),n=t.runtime.bin,s=n.makeWriteOptions(e),r=s.writerFactory();return n.writeMessage(this,r,s),r.finish()}toJson(e){let t=this.getType(),n=t.runtime.json,s=n.makeWriteOptions(e);return n.writeMessage(this,s)}toJsonString(e){var t;let n=this.toJson(e);return JSON.stringify(n,null,(t=e?.prettySpaces)!==null&&t!==void 0?t:0)}toJSON(){return this.toJson({emitDefaultValues:!0})}getType(){return Object.getPrototypeOf(this).constructor}};function du(i,e,t,n){var s;let r=(s=n?.localName)!==null&&s!==void 0?s:e.substring(e.lastIndexOf(".")+1),a={[r]:function(o){i.util.initFields(this),i.util.initPartial(o,this)}}[r];return Object.setPrototypeOf(a.prototype,new On),Object.assign(a,{runtime:i,typeName:e,fields:i.util.newFieldList(t),fromBinary(o,c){return new a().fromBinary(o,c)},fromJson(o,c){return new a().fromJson(o,c)},fromJsonString(o,c){return new a().fromJsonString(o,c)},equals(o,c){return i.util.equals(a,o,c)}}),a}function lu(){let i=0,e=0;for(let n=0;n<28;n+=7){let s=this.buf[this.pos++];if(i|=(s&127)<>4,(t&128)==0)return this.assertBounds(),[i,e];for(let n=3;n<=31;n+=7){let s=this.buf[this.pos++];if(e|=(s&127)<>>r,o=!(!(a>>>7)&&e==0),c=(o?a|128:a)&255;if(t.push(c),!o)return}let n=i>>>28&15|(e&7)<<4,s=e>>3!=0;if(t.push((s?n|128:n)&255),!!s){for(let r=3;r<31;r=r+7){let a=e>>>r,o=!!(a>>>7),c=(o?a|128:a)&255;if(t.push(c),!o)return}t.push(e>>>31&1)}}var ai=4294967296;function bo(i){let e=i[0]==="-";e&&(i=i.slice(1));let t=1e6,n=0,s=0;function r(a,o){let c=Number(i.slice(a,o));s*=t,n=n*t+c,n>=ai&&(s=s+(n/ai|0),n=n%ai)}return r(-24,-18),r(-18,-12),r(-12,-6),r(-6),e?Wc(n,s):Ua(n,s)}function uu(i,e){let t=Ua(i,e),n=t.hi&2147483648;n&&(t=Wc(t.lo,t.hi));let s=Vc(t.lo,t.hi);return n?"-"+s:s}function Vc(i,e){var t=hu(i,e);if(i=t.lo,e=t.hi,e<=2097151)return String(ai*e+i);let n=i&16777215,s=(i>>>24|e<<8)&16777215,r=e>>16&65535,a=n+s*6777216+r*6710656,o=s+r*8147497,c=r*2,d=1e7;return a>=d&&(o+=Math.floor(a/d),a%=d),o>=d&&(c+=Math.floor(o/d),o%=d),c.toString()+yo(o)+yo(a)}function hu(i,e){return{lo:i>>>0,hi:e>>>0}}function Ua(i,e){return{lo:i|0,hi:e|0}}function Wc(i,e){return e=~e,i?i=~i+1:e+=1,Ua(i,e)}var yo=i=>{let e=String(i);return"0000000".slice(e.length)+e};function ko(i,e){if(i>=0){for(;i>127;)e.push(i&127|128),i=i>>>7;e.push(i)}else{for(let t=0;t<9;t++)e.push(i&127|128),i=i>>7;e.push(1)}}function mu(){let i=this.buf[this.pos++],e=i&127;if((i&128)==0)return this.assertBounds(),e;if(i=this.buf[this.pos++],e|=(i&127)<<7,(i&128)==0)return this.assertBounds(),e;if(i=this.buf[this.pos++],e|=(i&127)<<14,(i&128)==0)return this.assertBounds(),e;if(i=this.buf[this.pos++],e|=(i&127)<<21,(i&128)==0)return this.assertBounds(),e;i=this.buf[this.pos++],e|=(i&15)<<28;for(let t=5;(i&128)!==0&&t<10;t++)i=this.buf[this.pos++];if((i&128)!=0)throw new Error("invalid varint");return this.assertBounds(),e>>>0}function fu(){let i=new DataView(new ArrayBuffer(8));if(typeof BigInt=="function"&&typeof i.getBigInt64=="function"&&typeof i.getBigUint64=="function"&&typeof i.setBigInt64=="function"&&typeof i.setBigUint64=="function"&&(typeof process!="object"||typeof process.env!="object"||process.env.BUF_BIGINT_DISABLE!=="1")){let s=BigInt("-9223372036854775808"),r=BigInt("9223372036854775807"),a=BigInt("0"),o=BigInt("18446744073709551615");return{zero:BigInt(0),supported:!0,parse(c){let d=typeof c=="bigint"?c:BigInt(c);if(d>r||do||die(/^-?[0-9]+$/.test(s),"int64 invalid: ".concat(s)),n=s=>ie(/^[0-9]+$/.test(s),"uint64 invalid: ".concat(s));return{zero:"0",supported:!1,parse(s){return typeof s!="string"&&(s=s.toString()),t(s),s},uParse(s){return typeof s!="string"&&(s=s.toString()),n(s),s},enc(s){return typeof s!="string"&&(s=s.toString()),t(s),bo(s)},uEnc(s){return typeof s!="string"&&(s=s.toString()),n(s),bo(s)},dec(s,r){return uu(s,r)},uDec(s,r){return Vc(s,r)}}}var ee=fu(),w;(function(i){i[i.DOUBLE=1]="DOUBLE",i[i.FLOAT=2]="FLOAT",i[i.INT64=3]="INT64",i[i.UINT64=4]="UINT64",i[i.INT32=5]="INT32",i[i.FIXED64=6]="FIXED64",i[i.FIXED32=7]="FIXED32",i[i.BOOL=8]="BOOL",i[i.STRING=9]="STRING",i[i.BYTES=12]="BYTES",i[i.UINT32=13]="UINT32",i[i.SFIXED32=15]="SFIXED32",i[i.SFIXED64=16]="SFIXED64",i[i.SINT32=17]="SINT32",i[i.SINT64=18]="SINT64"})(w||(w={}));var Ct;(function(i){i[i.BIGINT=0]="BIGINT",i[i.STRING=1]="STRING"})(Ct||(Ct={}));function mt(i,e,t){if(e===t)return!0;if(i==w.BYTES){if(!(e instanceof Uint8Array)||!(t instanceof Uint8Array)||e.length!==t.length)return!1;for(let n=0;ni.length)&&(e=i.length);for(var t=0,n=Array(e);t>>0)}raw(e){return this.buf.length&&(this.chunks.push(new Uint8Array(this.buf)),this.buf=[]),this.chunks.push(e),this}uint32(e){for(tr(e);e>127;)this.buf.push(e&127|128),e=e>>>7;return this.buf.push(e),this}int32(e){return ri(e),ko(e,this.buf),this}bool(e){return this.buf.push(e?1:0),this}bytes(e){return this.uint32(e.byteLength),this.raw(e)}string(e){let t=this.textEncoder.encode(e);return this.uint32(t.byteLength),this.raw(t)}float(e){Uc(e);let t=new Uint8Array(4);return new DataView(t.buffer).setFloat32(0,e,!0),this.raw(t)}double(e){let t=new Uint8Array(8);return new DataView(t.buffer).setFloat64(0,e,!0),this.raw(t)}fixed32(e){tr(e);let t=new Uint8Array(4);return new DataView(t.buffer).setUint32(0,e,!0),this.raw(t)}sfixed32(e){ri(e);let t=new Uint8Array(4);return new DataView(t.buffer).setInt32(0,e,!0),this.raw(t)}sint32(e){return ri(e),e=(e<<1^e>>31)>>>0,ko(e,this.buf),this}sfixed64(e){let t=new Uint8Array(8),n=new DataView(t.buffer),s=ee.enc(e);return n.setInt32(0,s.lo,!0),n.setInt32(4,s.hi,!0),this.raw(t)}fixed64(e){let t=new Uint8Array(8),n=new DataView(t.buffer),s=ee.uEnc(e);return n.setInt32(0,s.lo,!0),n.setInt32(4,s.hi,!0),this.raw(t)}int64(e){let t=ee.enc(e);return ws(t.lo,t.hi,this.buf),this}sint64(e){let t=ee.enc(e),n=t.hi>>31,s=t.lo<<1^n,r=(t.hi<<1|t.lo>>>31)^n;return ws(s,r,this.buf),this}uint64(e){let t=ee.uEnc(e);return ws(t.lo,t.hi,this.buf),this}},sr=class{constructor(e,t){this.varint64=lu,this.uint32=mu,this.buf=e,this.len=e.length,this.pos=0,this.view=new DataView(e.buffer,e.byteOffset,e.byteLength),this.textDecoder=t??new TextDecoder}tag(){let e=this.uint32(),t=e>>>3,n=e&7;if(t<=0||n<0||n>5)throw new Error("illegal tag: field no "+t+" wire type "+n);return[t,n]}skip(e,t){let n=this.pos;switch(e){case ae.Varint:for(;this.buf[this.pos++]&128;);break;case ae.Bit64:this.pos+=4;case ae.Bit32:this.pos+=4;break;case ae.LengthDelimited:let s=this.uint32();this.pos+=s;break;case ae.StartGroup:for(;;){let r=this.tag(),a=L(r,2),o=a[0],c=a[1];if(c===ae.EndGroup){if(t!==void 0&&o!==t)throw new Error("invalid end group tag");break}this.skip(c,o)}break;default:throw new Error("cant skip wire type "+e)}return this.assertBounds(),this.buf.subarray(n,this.pos)}assertBounds(){if(this.pos>this.len)throw new RangeError("premature EOF")}int32(){return this.uint32()|0}sint32(){let e=this.uint32();return e>>>1^-(e&1)}int64(){return ee.dec(...this.varint64())}uint64(){return ee.uDec(...this.varint64())}sint64(){let e=this.varint64(),t=L(e,2),n=t[0],s=t[1],r=-(n&1);return n=(n>>>1|(s&1)<<31)^r,s=s>>>1^r,ee.dec(n,s)}bool(){let e=this.varint64(),t=L(e,2),n=t[0],s=t[1];return n!==0||s!==0}fixed32(){return this.view.getUint32((this.pos+=4)-4,!0)}sfixed32(){return this.view.getInt32((this.pos+=4)-4,!0)}fixed64(){return ee.uDec(this.sfixed32(),this.sfixed32())}sfixed64(){return ee.dec(this.sfixed32(),this.sfixed32())}float(){return this.view.getFloat32((this.pos+=4)-4,!0)}double(){return this.view.getFloat64((this.pos+=8)-8,!0)}bytes(){let e=this.uint32(),t=this.pos;return this.pos+=e,this.assertBounds(),this.buf.subarray(t,t+e)}string(){return this.textDecoder.decode(this.bytes())}};function wu(i,e,t,n){let s;return{typeName:e,extendee:t,get field(){if(!s){let r=typeof n=="function"?n():n;r.name=e.split(".").pop(),r.jsonName="[".concat(e,"]"),s=i.util.newFieldList([r]).list()[0]}return s},runtime:i}}function Jc(i){let e=i.field.localName,t=Object.create(null);return t[e]=_u(i),[t,()=>t[e]]}function _u(i){let e=i.field;if(e.repeated)return[];if(e.default!==void 0)return e.default;switch(e.kind){case"enum":return e.T.values[0].no;case"scalar":return rn(e.T,e.L);case"message":let t=e.T,n=new t;return t.fieldWrapper?t.fieldWrapper.unwrapField(n):n;case"map":throw"map fields are not allowed to be extensions"}}function Ru(i,e){if(!e.repeated&&(e.kind=="enum"||e.kind=="scalar")){for(let t=i.length-1;t>=0;--t)if(i[t].no==e.no)return[i[t]];return[]}return i.filter(t=>t.no===e.no)}var at="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),ss=[];for(let i=0;i>4,a=r,s=2;break;case 2:t[n++]=(a&15)<<4|(r&60)>>2,a=r,s=3;break;case 3:t[n++]=(a&3)<<6|r,s=0;break}}if(s==1)throw Error("invalid base64 string.");return t.subarray(0,n)},enc(i){let e="",t=0,n,s=0;for(let r=0;r>2],s=(n&3)<<4,t=1;break;case 1:e+=at[s|n>>4],s=(n&15)<<2,t=2;break;case 2:e+=at[s|n>>6],e+=at[n&63],t=0;break}return t&&(e+=at[s],e+="=",t==1&&(e+="=")),e}};function Pu(i,e,t){$c(e,i);let n=e.runtime.bin.makeReadOptions(t),s=Ru(i.getType().runtime.bin.listUnknownFields(i),e.field),r=Jc(e),a=L(r,2),o=a[0],c=a[1];for(let d of s)e.runtime.bin.readField(o,n.readerFactory(d.data),e.field,d.wireType,n);return c()}function Iu(i,e,t,n){$c(e,i);let s=e.runtime.bin.makeReadOptions(n),r=e.runtime.bin.makeWriteOptions(n);if(Yc(i,e)){let d=i.getType().runtime.bin.listUnknownFields(i).filter(l=>l.no!=e.field.no);i.getType().runtime.bin.discardUnknownFields(i);for(let l of d)i.getType().runtime.bin.onUnknownField(i,l.no,l.wireType,l.data)}let a=r.writerFactory(),o=e.field;!o.opt&&!o.repeated&&(o.kind=="enum"||o.kind=="scalar")&&(o=Object.assign(Object.assign({},e.field),{opt:!0})),e.runtime.bin.writeField(o,t,a,r);let c=s.readerFactory(a.finish());for(;c.posn.no==e.field.no)}function $c(i,e){ie(i.extendee.typeName==e.getType().typeName,"extension ".concat(i.typeName," can only be applied to message ").concat(i.extendee.typeName))}function Xc(i,e){let t=i.localName;if(i.repeated)return e[t].length>0;if(i.oneof)return e[i.oneof.localName].case===t;switch(i.kind){case"enum":case"scalar":return i.opt||i.req?e[t]!==void 0:i.kind=="enum"?e[t]!==i.T.values[0].no:!Hc(i.T,e[t]);case"message":return e[t]!==void 0;case"map":return Object.keys(e[t]).length>0}}function So(i,e){let t=i.localName,n=!i.opt&&!i.req;if(i.repeated)e[t]=[];else if(i.oneof)e[i.oneof.localName]={case:void 0};else switch(i.kind){case"map":e[t]={};break;case"enum":e[t]=n?i.T.values[0].no:void 0;break;case"scalar":e[t]=n?rn(i.T,i.L):void 0;break;case"message":e[t]=void 0;break}}function ot(i,e){if(i===null||typeof i!="object"||!Object.getOwnPropertyNames(On.prototype).every(n=>n in i&&typeof i[n]=="function"))return!1;let t=i.getType();return t===null||typeof t!="function"||!("typeName"in t)||typeof t.typeName!="string"?!1:e===void 0?!0:t.typeName==e.typeName}function Zc(i,e){return ot(e)||!i.fieldWrapper?e:i.fieldWrapper.wrapField(e)}w.DOUBLE,w.FLOAT,w.INT64,w.UINT64,w.INT32,w.UINT32,w.BOOL,w.STRING,w.BYTES;var Co={ignoreUnknownFields:!1},Eo={emitDefaultValues:!1,enumAsInteger:!1,useProtoFieldName:!1,prettySpaces:0};function Mu(i){return i?Object.assign(Object.assign({},Co),i):Co}function Au(i){return i?Object.assign(Object.assign({},Eo),i):Eo}var bi=Symbol(),oi=Symbol();function Ou(){return{makeReadOptions:Mu,makeWriteOptions:Au,readMessage(i,e,t,n){if(e==null||Array.isArray(e)||typeof e!="object")throw new Error("cannot decode message ".concat(i.typeName," from JSON: ").concat($e(e)));n=n??new i;let s=new Map,r=t.typeRegistry;for(let o of Object.entries(e)){var a=L(o,2);let c=a[0],d=a[1],l=i.fields.findJsonName(c);if(l){if(l.oneof){if(d===null&&l.kind=="scalar")continue;let u=s.get(l.oneof);if(u!==void 0)throw new Error("cannot decode message ".concat(i.typeName,' from JSON: multiple keys for oneof "').concat(l.oneof.name,'" present: "').concat(u,'", "').concat(c,'"'));s.set(l.oneof,c)}wo(n,d,l,t,i)}else{let u=!1;if(r?.findExtension&&c.startsWith("[")&&c.endsWith("]")){let h=r.findExtension(c.substring(1,c.length-1));if(h&&h.extendee.typeName==i.typeName){u=!0;let m=Jc(h),g=L(m,2),p=g[0],k=g[1];wo(p,d,h.field,t,h),Iu(n,h,k(),t)}}if(!u&&!t.ignoreUnknownFields)throw new Error("cannot decode message ".concat(i.typeName,' from JSON: key "').concat(c,'" is unknown'))}}return n},writeMessage(i,e){let t=i.getType(),n={},s;try{for(s of t.fields.byNumber()){if(!Xc(s,i)){if(s.req)throw"required field not set";if(!e.emitDefaultValues||!Lu(s))continue}let a=s.oneof?i[s.oneof.localName].value:i[s.localName],o=_o(s,a,e);o!==void 0&&(n[e.useProtoFieldName?s.name:s.jsonName]=o)}let r=e.typeRegistry;if(r?.findExtensionFor)for(let a of t.runtime.bin.listUnknownFields(i)){let o=r.findExtensionFor(t.typeName,a.no);if(o&&Yc(i,o)){let c=Pu(i,o,e),d=_o(o.field,c,e);d!==void 0&&(n[o.field.jsonName]=d)}}}catch(r){let a=s?"cannot encode field ".concat(t.typeName,".").concat(s.name," to JSON"):"cannot encode message ".concat(t.typeName," to JSON"),o=r instanceof Error?r.message:String(r);throw new Error(a+(o.length>0?": ".concat(o):""))}return n},readScalar(i,e,t){return Cn(i,e,t??Ct.BIGINT,!0)},writeScalar(i,e,t){if(e!==void 0&&(t||Hc(i,e)))return ci(i,e)},debug:$e}}function $e(i){if(i===null)return"null";switch(typeof i){case"object":return Array.isArray(i)?"array":"object";case"string":return i.length>100?"string":'"'.concat(i.split('"').join('\\"'),'"');default:return String(i)}}function wo(i,e,t,n,s){let r=t.localName;if(t.repeated){if(ie(t.kind!="map"),e===null)return;if(!Array.isArray(e))throw new Error("cannot decode field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(e)));let o=i[r];for(let c of e){if(c===null)throw new Error("cannot decode field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(c)));switch(t.kind){case"message":o.push(t.T.fromJson(c,n));break;case"enum":let d=_s(t.T,c,n.ignoreUnknownFields,!0);d!==oi&&o.push(d);break;case"scalar":try{o.push(Cn(t.T,c,t.L,!0))}catch(l){let u="cannot decode field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(c));throw l instanceof Error&&l.message.length>0&&(u+=": ".concat(l.message)),new Error(u)}break}}}else if(t.kind=="map"){if(e===null)return;if(typeof e!="object"||Array.isArray(e))throw new Error("cannot decode field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(e)));let o=i[r];for(let c of Object.entries(e)){var a=L(c,2);let d=a[0],l=a[1];if(l===null)throw new Error("cannot decode field ".concat(s.typeName,".").concat(t.name," from JSON: map value null"));let u;try{u=Du(t.K,d)}catch(h){let m="cannot decode map key for field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(e));throw h instanceof Error&&h.message.length>0&&(m+=": ".concat(h.message)),new Error(m)}switch(t.V.kind){case"message":o[u]=t.V.T.fromJson(l,n);break;case"enum":let h=_s(t.V.T,l,n.ignoreUnknownFields,!0);h!==oi&&(o[u]=h);break;case"scalar":try{o[u]=Cn(t.V.T,l,Ct.BIGINT,!0)}catch(m){let g="cannot decode map value for field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(e));throw m instanceof Error&&m.message.length>0&&(g+=": ".concat(m.message)),new Error(g)}break}}}else switch(t.oneof&&(i=i[t.oneof.localName]={case:r},r="value"),t.kind){case"message":let o=t.T;if(e===null&&o.typeName!="google.protobuf.Value")return;let c=i[r];ot(c)?c.fromJson(e,n):(i[r]=c=o.fromJson(e,n),o.fieldWrapper&&!t.oneof&&(i[r]=o.fieldWrapper.unwrapField(c)));break;case"enum":let d=_s(t.T,e,n.ignoreUnknownFields,!1);switch(d){case bi:So(t,i);break;case oi:break;default:i[r]=d;break}break;case"scalar":try{let l=Cn(t.T,e,t.L,!1);switch(l){case bi:So(t,i);break;default:i[r]=l;break}}catch(l){let u="cannot decode field ".concat(s.typeName,".").concat(t.name," from JSON: ").concat($e(e));throw l instanceof Error&&l.message.length>0&&(u+=": ".concat(l.message)),new Error(u)}break}}function Du(i,e){if(i===w.BOOL)switch(e){case"true":e=!0;break;case"false":e=!1;break}return Cn(i,e,Ct.BIGINT,!0).toString()}function Cn(i,e,t,n){if(e===null)return n?rn(i,t):bi;switch(i){case w.DOUBLE:case w.FLOAT:if(e==="NaN")return Number.NaN;if(e==="Infinity")return Number.POSITIVE_INFINITY;if(e==="-Infinity")return Number.NEGATIVE_INFINITY;if(e===""||typeof e=="string"&&e.trim().length!==e.length||typeof e!="string"&&typeof e!="number")break;let s=Number(e);if(Number.isNaN(s)||!Number.isFinite(s))break;return i==w.FLOAT&&Uc(s),s;case w.INT32:case w.FIXED32:case w.SFIXED32:case w.SINT32:case w.UINT32:let r;if(typeof e=="number"?r=e:typeof e=="string"&&e.length>0&&e.trim().length===e.length&&(r=Number(e)),r===void 0)break;return i==w.UINT32||i==w.FIXED32?tr(r):ri(r),r;case w.INT64:case w.SFIXED64:case w.SINT64:if(typeof e!="number"&&typeof e!="string")break;let a=ee.parse(e);return t?a.toString():a;case w.FIXED64:case w.UINT64:if(typeof e!="number"&&typeof e!="string")break;let o=ee.uParse(e);return t?o.toString():o;case w.BOOL:if(typeof e!="boolean")break;return e;case w.STRING:if(typeof e!="string")break;try{encodeURIComponent(e)}catch{throw new Error("invalid UTF8")}return e;case w.BYTES:if(e==="")return new Uint8Array(0);if(typeof e!="string")break;return Qc.dec(e)}throw new Error}function _s(i,e,t,n){if(e===null)return i.typeName=="google.protobuf.NullValue"?0:n?i.values[0].no:bi;switch(typeof e){case"number":if(Number.isInteger(e))return e;break;case"string":let s=i.findName(e);if(s!==void 0)return s.no;if(t)return oi;break}throw new Error("cannot decode enum ".concat(i.typeName," from JSON: ").concat($e(e)))}function Lu(i){return i.repeated||i.kind=="map"?!0:!(i.oneof||i.kind=="message"||i.opt||i.req)}function _o(i,e,t){if(i.kind=="map"){ie(typeof e=="object"&&e!=null);let a={},o=Object.entries(e);switch(i.V.kind){case"scalar":for(let d of o){var n=L(d,2);let l=n[0],u=n[1];a[l.toString()]=ci(i.V.T,u)}break;case"message":for(let d of o){var s=L(d,2);let l=s[0],u=s[1];a[l.toString()]=u.toJson(t)}break;case"enum":let c=i.V.T;for(let d of o){var r=L(d,2);let l=r[0],u=r[1];a[l.toString()]=Rs(c,u,t.enumAsInteger)}break}return t.emitDefaultValues||o.length>0?a:void 0}if(i.repeated){ie(Array.isArray(e));let a=[];switch(i.kind){case"scalar":for(let o=0;o0?a:void 0}switch(i.kind){case"scalar":return ci(i.T,e);case"enum":return Rs(i.T,e,t.enumAsInteger);case"message":return Zc(i.T,e).toJson(t)}}function Rs(i,e,t){var n;if(ie(typeof e=="number"),i.typeName=="google.protobuf.NullValue")return null;if(t)return e;let s=i.findNumber(e);return(n=s?.name)!==null&&n!==void 0?n:e}function ci(i,e){switch(i){case w.INT32:case w.SFIXED32:case w.SINT32:case w.FIXED32:case w.UINT32:return ie(typeof e=="number"),e;case w.FLOAT:case w.DOUBLE:return ie(typeof e=="number"),Number.isNaN(e)?"NaN":e===Number.POSITIVE_INFINITY?"Infinity":e===Number.NEGATIVE_INFINITY?"-Infinity":e;case w.STRING:return ie(typeof e=="string"),e;case w.BOOL:return ie(typeof e=="boolean"),e;case w.UINT64:case w.FIXED64:case w.INT64:case w.SFIXED64:case w.SINT64:return ie(typeof e=="bigint"||typeof e=="string"||typeof e=="number"),e.toString();case w.BYTES:return ie(e instanceof Uint8Array),Qc.enc(e)}}var Vt=Symbol("@bufbuild/protobuf/unknown-fields"),Ro={readUnknownFields:!0,readerFactory:i=>new sr(i)},Po={writeUnknownFields:!0,writerFactory:()=>new ir};function Nu(i){return i?Object.assign(Object.assign({},Ro),i):Ro}function xu(i){return i?Object.assign(Object.assign({},Po),i):Po}function Uu(){return{makeReadOptions:Nu,makeWriteOptions:xu,listUnknownFields(i){var e;return(e=i[Vt])!==null&&e!==void 0?e:[]},discardUnknownFields(i){delete i[Vt]},writeUnknownFields(i,e){let n=i[Vt];if(n)for(let s of n)e.tag(s.no,s.wireType).raw(s.data)},onUnknownField(i,e,t,n){let s=i;Array.isArray(s[Vt])||(s[Vt]=[]),s[Vt].push({no:e,wireType:t,data:n})},readMessage(i,e,t,n,s){let r=i.getType(),a=s?e.len:e.pos+t,o,c;for(;e.pos0&&(c=Bu),r){let g=i[a];if(n==ae.LengthDelimited&&o!=w.STRING&&o!=w.BYTES){let k=e.uint32()+e.pos;for(;e.posot(m,h)?m:new h(m));else{let m=o[r];h.fieldWrapper?h.typeName==="google.protobuf.BytesValue"?a[r]=pn(m):a[r]=m:a[r]=ot(m,h)?m:new h(m)}break}}},equals(i,e,t){return e===t?!0:!e||!t?!1:i.fields.byMember().every(n=>{let s=e[n.localName],r=t[n.localName];if(n.repeated){if(s.length!==r.length)return!1;switch(n.kind){case"message":return s.every((a,o)=>n.T.equals(a,r[o]));case"scalar":return s.every((a,o)=>mt(n.T,a,r[o]));case"enum":return s.every((a,o)=>mt(w.INT32,a,r[o]))}throw new Error("repeated cannot contain ".concat(n.kind))}switch(n.kind){case"message":let a=s,o=r;return n.T.fieldWrapper&&(a!==void 0&&!ot(a)&&(a=n.T.fieldWrapper.wrapField(a)),o!==void 0&&!ot(o)&&(o=n.T.fieldWrapper.wrapField(o))),n.T.equals(a,o);case"enum":return mt(w.INT32,s,r);case"scalar":return mt(n.T,s,r);case"oneof":if(s.case!==r.case)return!1;let c=n.findField(s.case);if(c===void 0)return!0;switch(c.kind){case"message":return c.T.equals(s.value,r.value);case"enum":return mt(w.INT32,s.value,r.value);case"scalar":return mt(c.T,s.value,r.value)}throw new Error("oneof cannot contain ".concat(c.kind));case"map":let d=Object.keys(s).concat(Object.keys(r));switch(n.V.kind){case"message":let l=n.V.T;return d.every(h=>l.equals(s[h],r[h]));case"enum":return d.every(h=>mt(w.INT32,s[h],r[h]));case"scalar":let u=n.V.T;return d.every(h=>mt(u,s[h],r[h]))}break}})},clone(i){let e=i.getType(),t=new e,n=t;for(let r of e.fields.byMember()){let a=i[r.localName],o;if(r.repeated)o=a.map($n);else if(r.kind=="map"){o=n[r.localName];for(let c of Object.entries(a)){var s=L(c,2);let d=s[0],l=s[1];o[d]=$n(l)}}else r.kind=="oneof"?o=r.findField(a.case)?{case:a.case,value:$n(a.value)}:{case:void 0}:o=$n(a);n[r.localName]=o}for(let r of e.runtime.bin.listUnknownFields(i))e.runtime.bin.onUnknownField(n,r.no,r.wireType,r.data);return t}}}function $n(i){if(i===void 0)return i;if(ot(i))return i.clone();if(i instanceof Uint8Array){let e=new Uint8Array(i.byteLength);return e.set(i),e}return i}function pn(i){return i instanceof Uint8Array?i:new Uint8Array(i)}function Wu(i,e,t){return{syntax:i,json:Ou(),bin:Uu(),util:Object.assign(Object.assign({},Vu()),{newFieldList:e,initFields:t}),makeMessageType(n,s,r){return du(this,n,s,r)},makeEnum:cu,makeEnumType:jc,getEnumType:ou,makeExtension(n,s,r){return wu(this,n,s,r)}}}var rr=class{constructor(e,t){this._fields=e,this._normalizer=t}findJsonName(e){if(!this.jsonNames){let t={};for(let n of this.list())t[n.jsonName]=t[n.name]=n;this.jsonNames=t}return this.jsonNames[e]}find(e){if(!this.numbers){let t={};for(let n of this.list())t[n.no]=n;this.numbers=t}return this.numbers[e]}list(){return this.all||(this.all=this._normalizer(this._fields)),this.all}byNumber(){return this.numbersAsc||(this.numbersAsc=this.list().concat().sort((e,t)=>e.no-t.no)),this.numbersAsc}byMember(){if(!this.members){this.members=[];let e=this.members,t;for(let n of this.list())n.oneof?n.oneof!==t&&(t=n.oneof,e.push(t)):e.push(n)}return this.members}};function td(i,e){let t=nd(i);return e?t:Qu(Ju(t))}function Hu(i){return td(i,!1)}var Ku=nd;function nd(i){let e=!1,t=[];for(let n=0;n"".concat(i,"$"),Ju=i=>zu.has(i)?id(i):i,Qu=i=>Gu.has(i)?id(i):i,ar=class{constructor(e){this.kind="oneof",this.repeated=!1,this.packed=!1,this.opt=!1,this.req=!1,this.default=void 0,this.fields=[],this.name=e,this.localName=Hu(e)}addField(e){ie(e.oneof===this,"field ".concat(e.name," not one of ").concat(this.name)),this.fields.push(e)}findField(e){if(!this._lookup){this._lookup=Object.create(null);for(let t=0;tnew rr(i,e=>Yu(e)),i=>{for(let e of i.getType().fields.byMember()){if(e.opt)continue;let t=e.localName,n=i;if(e.repeated){n[t]=[];continue}switch(e.kind){case"oneof":n[t]={case:void 0};break;case"enum":n[t]=0;break;case"map":n[t]={};break;case"scalar":n[t]=rn(e.T,e.L);break}}}),lt=class i extends On{constructor(e){super(),this.seconds=ee.zero,this.nanos=0,v.util.initPartial(e,this)}fromJson(e,t){if(typeof e!="string")throw new Error("cannot decode google.protobuf.Timestamp from JSON: ".concat(v.json.debug(e)));let n=e.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:Z|\.([0-9]{3,9})Z|([+-][0-9][0-9]:[0-9][0-9]))$/);if(!n)throw new Error("cannot decode google.protobuf.Timestamp from JSON: invalid RFC 3339 string");let s=Date.parse(n[1]+"-"+n[2]+"-"+n[3]+"T"+n[4]+":"+n[5]+":"+n[6]+(n[8]?n[8]:"Z"));if(Number.isNaN(s))throw new Error("cannot decode google.protobuf.Timestamp from JSON: invalid RFC 3339 string");if(sDate.parse("9999-12-31T23:59:59Z"))throw new Error("cannot decode message google.protobuf.Timestamp from JSON: must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive");return this.seconds=ee.parse(s/1e3),this.nanos=0,n[7]&&(this.nanos=parseInt("1"+n[7]+"0".repeat(9-n[7].length))-1e9),this}toJson(e){let t=Number(this.seconds)*1e3;if(tDate.parse("9999-12-31T23:59:59Z"))throw new Error("cannot encode google.protobuf.Timestamp to JSON: must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive");if(this.nanos<0)throw new Error("cannot encode google.protobuf.Timestamp to JSON: nanos must not be negative");let n="Z";if(this.nanos>0){let s=(this.nanos+1e9).toString().substring(1);s.substring(3)==="000000"?n="."+s.substring(0,3)+"Z":s.substring(6)==="000"?n="."+s.substring(0,6)+"Z":n="."+s+"Z"}return new Date(t).toISOString().replace(".000Z",n)}toDate(){return new Date(Number(this.seconds)*1e3+Math.ceil(this.nanos/1e6))}static now(){return i.fromDate(new Date)}static fromDate(e){let t=e.getTime();return new i({seconds:ee.parse(Math.floor(t/1e3)),nanos:t%1e3*1e6})}static fromBinary(e,t){return new i().fromBinary(e,t)}static fromJson(e,t){return new i().fromJson(e,t)}static fromJsonString(e,t){return new i().fromJsonString(e,t)}static equals(e,t){return v.util.equals(i,e,t)}};lt.runtime=v;lt.typeName="google.protobuf.Timestamp";lt.fields=v.util.newFieldList(()=>[{no:1,name:"seconds",kind:"scalar",T:3},{no:2,name:"nanos",kind:"scalar",T:5}]);var $u=v.makeMessageType("livekit.MetricsBatch",()=>[{no:1,name:"timestamp_ms",kind:"scalar",T:3},{no:2,name:"normalized_timestamp",kind:"message",T:lt},{no:3,name:"str_data",kind:"scalar",T:9,repeated:!0},{no:4,name:"time_series",kind:"message",T:Xu,repeated:!0},{no:5,name:"events",kind:"message",T:eh,repeated:!0}]),Xu=v.makeMessageType("livekit.TimeSeriesMetric",()=>[{no:1,name:"label",kind:"scalar",T:13},{no:2,name:"participant_identity",kind:"scalar",T:13},{no:3,name:"track_sid",kind:"scalar",T:13},{no:4,name:"samples",kind:"message",T:Zu,repeated:!0},{no:5,name:"rid",kind:"scalar",T:13}]),Zu=v.makeMessageType("livekit.MetricSample",()=>[{no:1,name:"timestamp_ms",kind:"scalar",T:3},{no:2,name:"normalized_timestamp",kind:"message",T:lt},{no:3,name:"value",kind:"scalar",T:2}]),eh=v.makeMessageType("livekit.EventMetric",()=>[{no:1,name:"label",kind:"scalar",T:13},{no:2,name:"participant_identity",kind:"scalar",T:13},{no:3,name:"track_sid",kind:"scalar",T:13},{no:4,name:"start_timestamp_ms",kind:"scalar",T:3},{no:5,name:"end_timestamp_ms",kind:"scalar",T:3,opt:!0},{no:6,name:"normalized_start_timestamp",kind:"message",T:lt},{no:7,name:"normalized_end_timestamp",kind:"message",T:lt,opt:!0},{no:8,name:"metadata",kind:"scalar",T:9},{no:9,name:"rid",kind:"scalar",T:13}]);var sd=v.makeEnum("livekit.BackupCodecPolicy",[{no:0,name:"PREFER_REGRESSION"},{no:1,name:"SIMULCAST"},{no:2,name:"REGRESSION"}]),qe=v.makeEnum("livekit.TrackType",[{no:0,name:"AUDIO"},{no:1,name:"VIDEO"},{no:2,name:"DATA"}]),de=v.makeEnum("livekit.TrackSource",[{no:0,name:"UNKNOWN"},{no:1,name:"CAMERA"},{no:2,name:"MICROPHONE"},{no:3,name:"SCREEN_SHARE"},{no:4,name:"SCREEN_SHARE_AUDIO"}]),Fa=v.makeEnum("livekit.VideoQuality",[{no:0,name:"LOW"},{no:1,name:"MEDIUM"},{no:2,name:"HIGH"},{no:3,name:"OFF"}]),Ht=v.makeEnum("livekit.ConnectionQuality",[{no:0,name:"POOR"},{no:1,name:"GOOD"},{no:2,name:"EXCELLENT"},{no:3,name:"LOST"}]),Dn=v.makeEnum("livekit.ClientConfigSetting",[{no:0,name:"UNSET"},{no:1,name:"DISABLED"},{no:2,name:"ENABLED"}]),Pe=v.makeEnum("livekit.DisconnectReason",[{no:0,name:"UNKNOWN_REASON"},{no:1,name:"CLIENT_INITIATED"},{no:2,name:"DUPLICATE_IDENTITY"},{no:3,name:"SERVER_SHUTDOWN"},{no:4,name:"PARTICIPANT_REMOVED"},{no:5,name:"ROOM_DELETED"},{no:6,name:"STATE_MISMATCH"},{no:7,name:"JOIN_FAILURE"},{no:8,name:"MIGRATION"},{no:9,name:"SIGNAL_CLOSE"},{no:10,name:"ROOM_CLOSED"},{no:11,name:"USER_UNAVAILABLE"},{no:12,name:"USER_REJECTED"},{no:13,name:"SIP_TRUNK_FAILURE"},{no:14,name:"CONNECTION_TIMEOUT"},{no:15,name:"MEDIA_FAILURE"},{no:16,name:"AGENT_ERROR"}]),it=v.makeEnum("livekit.ReconnectReason",[{no:0,name:"RR_UNKNOWN"},{no:1,name:"RR_SIGNAL_DISCONNECTED"},{no:2,name:"RR_PUBLISHER_FAILED"},{no:3,name:"RR_SUBSCRIBER_FAILED"},{no:4,name:"RR_SWITCH_CANDIDATE"}]),th=v.makeEnum("livekit.SubscriptionError",[{no:0,name:"SE_UNKNOWN"},{no:1,name:"SE_CODEC_UNSUPPORTED"},{no:2,name:"SE_TRACK_NOTFOUND"}]),me=v.makeEnum("livekit.AudioTrackFeature",[{no:0,name:"TF_STEREO"},{no:1,name:"TF_NO_DTX"},{no:2,name:"TF_AUTO_GAIN_CONTROL"},{no:3,name:"TF_ECHO_CANCELLATION"},{no:4,name:"TF_NOISE_SUPPRESSION"},{no:5,name:"TF_ENHANCED_NOISE_CANCELLATION"},{no:6,name:"TF_PRECONNECT_BUFFER"}]),an=v.makeEnum("livekit.PacketTrailerFeature",[{no:0,name:"PTF_USER_TIMESTAMP"},{no:1,name:"PTF_FRAME_ID"},{no:2,name:"PTF_USER_DATA"}]),rs=v.makeMessageType("livekit.Room",()=>[{no:1,name:"sid",kind:"scalar",T:9},{no:2,name:"name",kind:"scalar",T:9},{no:3,name:"empty_timeout",kind:"scalar",T:13},{no:14,name:"departure_timeout",kind:"scalar",T:13},{no:4,name:"max_participants",kind:"scalar",T:13},{no:5,name:"creation_time",kind:"scalar",T:3},{no:15,name:"creation_time_ms",kind:"scalar",T:3},{no:6,name:"turn_password",kind:"scalar",T:9},{no:7,name:"enabled_codecs",kind:"message",T:ki,repeated:!0},{no:8,name:"metadata",kind:"scalar",T:9},{no:9,name:"num_participants",kind:"scalar",T:13},{no:11,name:"num_publishers",kind:"scalar",T:13},{no:10,name:"active_recording",kind:"scalar",T:8},{no:13,name:"version",kind:"message",T:bd}]),ki=v.makeMessageType("livekit.Codec",()=>[{no:1,name:"mime",kind:"scalar",T:9},{no:2,name:"fmtp_line",kind:"scalar",T:9}]),nh=v.makeMessageType("livekit.ParticipantPermission",()=>[{no:1,name:"can_subscribe",kind:"scalar",T:8},{no:2,name:"can_publish",kind:"scalar",T:8},{no:3,name:"can_publish_data",kind:"scalar",T:8},{no:9,name:"can_publish_sources",kind:"enum",T:v.getEnumType(de),repeated:!0},{no:7,name:"hidden",kind:"scalar",T:8},{no:8,name:"recorder",kind:"scalar",T:8},{no:10,name:"can_update_metadata",kind:"scalar",T:8},{no:11,name:"agent",kind:"scalar",T:8},{no:12,name:"can_subscribe_metrics",kind:"scalar",T:8},{no:13,name:"can_manage_agent_session",kind:"scalar",T:8}]),xt=v.makeMessageType("livekit.ParticipantInfo",()=>[{no:1,name:"sid",kind:"scalar",T:9},{no:2,name:"identity",kind:"scalar",T:9},{no:3,name:"state",kind:"enum",T:v.getEnumType(zt)},{no:4,name:"tracks",kind:"message",T:Kt,repeated:!0},{no:5,name:"metadata",kind:"scalar",T:9},{no:6,name:"joined_at",kind:"scalar",T:3},{no:17,name:"joined_at_ms",kind:"scalar",T:3},{no:9,name:"name",kind:"scalar",T:9},{no:10,name:"version",kind:"scalar",T:13},{no:11,name:"permission",kind:"message",T:nh},{no:12,name:"region",kind:"scalar",T:9},{no:13,name:"is_publisher",kind:"scalar",T:8},{no:14,name:"kind",kind:"enum",T:v.getEnumType(Ln)},{no:15,name:"attributes",kind:"map",K:9,V:{kind:"scalar",T:9}},{no:16,name:"disconnect_reason",kind:"enum",T:v.getEnumType(Pe)},{no:18,name:"kind_details",kind:"enum",T:v.getEnumType(ih),repeated:!0},{no:19,name:"data_tracks",kind:"message",T:as,repeated:!0},{no:20,name:"client_protocol",kind:"scalar",T:5},{no:21,name:"capabilities",kind:"enum",T:v.getEnumType(Nn),repeated:!0}]),zt=v.makeEnum("livekit.ParticipantInfo.State",[{no:0,name:"JOINING"},{no:1,name:"JOINED"},{no:2,name:"ACTIVE"},{no:3,name:"DISCONNECTED"}]),Ln=v.makeEnum("livekit.ParticipantInfo.Kind",[{no:0,name:"STANDARD"},{no:1,name:"INGRESS"},{no:2,name:"EGRESS"},{no:3,name:"SIP"},{no:4,name:"AGENT"},{no:7,name:"CONNECTOR"},{no:8,name:"BRIDGE"}]),ih=v.makeEnum("livekit.ParticipantInfo.KindDetail",[{no:0,name:"CLOUD_AGENT"},{no:1,name:"FORWARDED"},{no:2,name:"CONNECTOR_WHATSAPP"},{no:3,name:"CONNECTOR_TWILIO"},{no:4,name:"BRIDGE_RTSP"},{no:5,name:"SIMULATION"}]),G=v.makeEnum("livekit.Encryption.Type",[{no:0,name:"NONE"},{no:1,name:"GCM"},{no:2,name:"CUSTOM"}]),sh=v.makeMessageType("livekit.SimulcastCodecInfo",()=>[{no:1,name:"mime_type",kind:"scalar",T:9},{no:2,name:"mid",kind:"scalar",T:9},{no:3,name:"cid",kind:"scalar",T:9},{no:4,name:"layers",kind:"message",T:bt,repeated:!0},{no:5,name:"video_layer_mode",kind:"enum",T:v.getEnumType(qa)},{no:6,name:"sdp_cid",kind:"scalar",T:9}]),Kt=v.makeMessageType("livekit.TrackInfo",()=>[{no:1,name:"sid",kind:"scalar",T:9},{no:2,name:"type",kind:"enum",T:v.getEnumType(qe)},{no:3,name:"name",kind:"scalar",T:9},{no:4,name:"muted",kind:"scalar",T:8},{no:5,name:"width",kind:"scalar",T:13},{no:6,name:"height",kind:"scalar",T:13},{no:7,name:"simulcast",kind:"scalar",T:8},{no:8,name:"disable_dtx",kind:"scalar",T:8},{no:9,name:"source",kind:"enum",T:v.getEnumType(de)},{no:10,name:"layers",kind:"message",T:bt,repeated:!0},{no:11,name:"mime_type",kind:"scalar",T:9},{no:12,name:"mid",kind:"scalar",T:9},{no:13,name:"codecs",kind:"message",T:sh,repeated:!0},{no:14,name:"stereo",kind:"scalar",T:8},{no:15,name:"disable_red",kind:"scalar",T:8},{no:16,name:"encryption",kind:"enum",T:v.getEnumType(G)},{no:17,name:"stream",kind:"scalar",T:9},{no:18,name:"version",kind:"message",T:bd},{no:19,name:"audio_features",kind:"enum",T:v.getEnumType(me),repeated:!0},{no:20,name:"backup_codec_policy",kind:"enum",T:v.getEnumType(sd)},{no:21,name:"packet_trailer_features",kind:"enum",T:v.getEnumType(an),repeated:!0}]),as=v.makeMessageType("livekit.DataTrackInfo",()=>[{no:1,name:"pub_handle",kind:"scalar",T:13},{no:2,name:"sid",kind:"scalar",T:9},{no:3,name:"name",kind:"scalar",T:9},{no:4,name:"encryption",kind:"enum",T:v.getEnumType(G)},{no:5,name:"frame_encoding",kind:"message",T:rd,opt:!0},{no:6,name:"schema",kind:"message",T:Ba,opt:!0}]),rd=v.makeMessageType("livekit.DataTrackFrameEncoding",()=>[{no:1,name:"well_known",kind:"enum",T:v.getEnumType(rh),oneof:"value"},{no:2,name:"custom",kind:"scalar",T:9,oneof:"value"}]),rh=v.makeEnum("livekit.DataTrackFrameEncoding.WellKnownFrameEncoding",[{no:0,name:"WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED",localName:"UNSPECIFIED"},{no:1,name:"WELL_KNOWN_FRAME_ENCODING_ROS1",localName:"ROS1"},{no:2,name:"WELL_KNOWN_FRAME_ENCODING_CDR",localName:"CDR"},{no:3,name:"WELL_KNOWN_FRAME_ENCODING_PROTOBUF",localName:"PROTOBUF"},{no:4,name:"WELL_KNOWN_FRAME_ENCODING_FLATBUFFER",localName:"FLATBUFFER"},{no:5,name:"WELL_KNOWN_FRAME_ENCODING_CBOR",localName:"CBOR"},{no:6,name:"WELL_KNOWN_FRAME_ENCODING_MSGPACK",localName:"MSGPACK"},{no:7,name:"WELL_KNOWN_FRAME_ENCODING_JSON",localName:"JSON"}]),ah=v.makeMessageType("livekit.DataTrackSchemaEncoding",()=>[{no:1,name:"well_known",kind:"enum",T:v.getEnumType(oh),oneof:"value"},{no:2,name:"custom",kind:"scalar",T:9,oneof:"value"}]),oh=v.makeEnum("livekit.DataTrackSchemaEncoding.WellKnownSchemaEncoding",[{no:0,name:"WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED",localName:"UNSPECIFIED"},{no:1,name:"WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF",localName:"PROTOBUF"},{no:2,name:"WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER",localName:"FLATBUFFER"},{no:3,name:"WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG",localName:"ROS1_MSG"},{no:4,name:"WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG",localName:"ROS2_MSG"},{no:5,name:"WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL",localName:"ROS2_IDL"},{no:6,name:"WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL",localName:"OMG_IDL"},{no:7,name:"WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA",localName:"JSON_SCHEMA"}]),Ba=v.makeMessageType("livekit.DataTrackSchemaId",()=>[{no:1,name:"name",kind:"scalar",T:9},{no:2,name:"encoding",kind:"message",T:ah}]),ch=v.makeMessageType("livekit.DataTrackSubscriptionOptions",()=>[{no:1,name:"target_fps",kind:"scalar",T:13,opt:!0}]),ja=v.makeMessageType("livekit.DataBlobKey",()=>[{no:1,name:"generic",kind:"scalar",T:9,oneof:"key"},{no:2,name:"schema_id",kind:"message",T:Ba,oneof:"key"}]),ad=v.makeMessageType("livekit.DataBlob",()=>[{no:1,name:"key",kind:"message",T:ja},{no:2,name:"contents",kind:"scalar",T:12}]),bt=v.makeMessageType("livekit.VideoLayer",()=>[{no:1,name:"quality",kind:"enum",T:v.getEnumType(Fa)},{no:2,name:"width",kind:"scalar",T:13},{no:3,name:"height",kind:"scalar",T:13},{no:4,name:"bitrate",kind:"scalar",T:13},{no:5,name:"ssrc",kind:"scalar",T:13},{no:6,name:"spatial_layer",kind:"scalar",T:5},{no:7,name:"rid",kind:"scalar",T:9},{no:8,name:"repair_ssrc",kind:"scalar",T:13}]),qa=v.makeEnum("livekit.VideoLayer.Mode",[{no:0,name:"MODE_UNUSED"},{no:1,name:"ONE_SPATIAL_LAYER_PER_STREAM"},{no:2,name:"MULTIPLE_SPATIAL_LAYERS_PER_STREAM"},{no:3,name:"ONE_SPATIAL_LAYER_PER_STREAM_INCOMPLETE_RTCP_SR"}]),Se=v.makeMessageType("livekit.DataPacket",()=>[{no:1,name:"kind",kind:"enum",T:v.getEnumType(yt)},{no:4,name:"participant_identity",kind:"scalar",T:9},{no:5,name:"destination_identities",kind:"scalar",T:9,repeated:!0},{no:2,name:"user",kind:"message",T:Va,oneof:"value"},{no:3,name:"speaker",kind:"message",T:dh,oneof:"value"},{no:6,name:"sip_dtmf",kind:"message",T:ld,oneof:"value"},{no:7,name:"transcription",kind:"message",T:lh,oneof:"value"},{no:8,name:"metrics",kind:"message",T:$u,oneof:"value"},{no:9,name:"chat_message",kind:"message",T:Ti,oneof:"value"},{no:10,name:"rpc_request",kind:"message",T:Wa,oneof:"value"},{no:11,name:"rpc_ack",kind:"message",T:os,oneof:"value"},{no:12,name:"rpc_response",kind:"message",T:Ha,oneof:"value"},{no:13,name:"stream_header",kind:"message",T:cs,oneof:"value"},{no:14,name:"stream_chunk",kind:"message",T:ct,oneof:"value"},{no:15,name:"stream_trailer",kind:"message",T:Ka,oneof:"value"},{no:18,name:"encrypted_packet",kind:"message",T:od,oneof:"value"},{no:16,name:"sequence",kind:"scalar",T:13},{no:17,name:"participant_sid",kind:"scalar",T:9}]),yt=v.makeEnum("livekit.DataPacket.Kind",[{no:0,name:"RELIABLE"},{no:1,name:"LOSSY"}]),od=v.makeMessageType("livekit.EncryptedPacket",()=>[{no:1,name:"encryption_type",kind:"enum",T:v.getEnumType(G)},{no:2,name:"iv",kind:"scalar",T:12},{no:3,name:"key_index",kind:"scalar",T:13},{no:4,name:"encrypted_value",kind:"scalar",T:12}]),cd=v.makeMessageType("livekit.EncryptedPacketPayload",()=>[{no:1,name:"user",kind:"message",T:Va,oneof:"value"},{no:3,name:"chat_message",kind:"message",T:Ti,oneof:"value"},{no:4,name:"rpc_request",kind:"message",T:Wa,oneof:"value"},{no:5,name:"rpc_ack",kind:"message",T:os,oneof:"value"},{no:6,name:"rpc_response",kind:"message",T:Ha,oneof:"value"},{no:7,name:"stream_header",kind:"message",T:cs,oneof:"value"},{no:8,name:"stream_chunk",kind:"message",T:ct,oneof:"value"},{no:9,name:"stream_trailer",kind:"message",T:Ka,oneof:"value"}]),dh=v.makeMessageType("livekit.ActiveSpeakerUpdate",()=>[{no:1,name:"speakers",kind:"message",T:dd,repeated:!0}]),dd=v.makeMessageType("livekit.SpeakerInfo",()=>[{no:1,name:"sid",kind:"scalar",T:9},{no:2,name:"level",kind:"scalar",T:2},{no:3,name:"active",kind:"scalar",T:8}]),Va=v.makeMessageType("livekit.UserPacket",()=>[{no:1,name:"participant_sid",kind:"scalar",T:9},{no:5,name:"participant_identity",kind:"scalar",T:9},{no:2,name:"payload",kind:"scalar",T:12},{no:3,name:"destination_sids",kind:"scalar",T:9,repeated:!0},{no:6,name:"destination_identities",kind:"scalar",T:9,repeated:!0},{no:4,name:"topic",kind:"scalar",T:9,opt:!0},{no:8,name:"id",kind:"scalar",T:9,opt:!0},{no:9,name:"start_time",kind:"scalar",T:4,opt:!0},{no:10,name:"end_time",kind:"scalar",T:4,opt:!0},{no:11,name:"nonce",kind:"scalar",T:12}]),ld=v.makeMessageType("livekit.SipDTMF",()=>[{no:3,name:"code",kind:"scalar",T:13},{no:4,name:"digit",kind:"scalar",T:9}]),lh=v.makeMessageType("livekit.Transcription",()=>[{no:2,name:"transcribed_participant_identity",kind:"scalar",T:9},{no:3,name:"track_id",kind:"scalar",T:9},{no:4,name:"segments",kind:"message",T:uh,repeated:!0}]),uh=v.makeMessageType("livekit.TranscriptionSegment",()=>[{no:1,name:"id",kind:"scalar",T:9},{no:2,name:"text",kind:"scalar",T:9},{no:3,name:"start_time",kind:"scalar",T:4},{no:4,name:"end_time",kind:"scalar",T:4},{no:5,name:"final",kind:"scalar",T:8},{no:6,name:"language",kind:"scalar",T:9}]),Ti=v.makeMessageType("livekit.ChatMessage",()=>[{no:1,name:"id",kind:"scalar",T:9},{no:2,name:"timestamp",kind:"scalar",T:3},{no:3,name:"edit_timestamp",kind:"scalar",T:3,opt:!0},{no:4,name:"message",kind:"scalar",T:9},{no:5,name:"deleted",kind:"scalar",T:8},{no:6,name:"generated",kind:"scalar",T:8}]),Wa=v.makeMessageType("livekit.RpcRequest",()=>[{no:1,name:"id",kind:"scalar",T:9},{no:2,name:"method",kind:"scalar",T:9},{no:3,name:"payload",kind:"scalar",T:9},{no:4,name:"response_timeout_ms",kind:"scalar",T:13},{no:5,name:"version",kind:"scalar",T:13},{no:6,name:"compressed_payload",kind:"scalar",T:12}]),os=v.makeMessageType("livekit.RpcAck",()=>[{no:1,name:"request_id",kind:"scalar",T:9}]),Ha=v.makeMessageType("livekit.RpcResponse",()=>[{no:1,name:"request_id",kind:"scalar",T:9},{no:2,name:"payload",kind:"scalar",T:9,oneof:"value"},{no:3,name:"error",kind:"message",T:ud,oneof:"value"},{no:4,name:"compressed_payload",kind:"scalar",T:12,oneof:"value"}]),ud=v.makeMessageType("livekit.RpcError",()=>[{no:1,name:"code",kind:"scalar",T:13},{no:2,name:"message",kind:"scalar",T:9},{no:3,name:"data",kind:"scalar",T:9}]),hd=v.makeMessageType("livekit.ParticipantTracks",()=>[{no:1,name:"participant_sid",kind:"scalar",T:9},{no:2,name:"track_sids",kind:"scalar",T:9,repeated:!0}]),md=v.makeMessageType("livekit.ServerInfo",()=>[{no:1,name:"edition",kind:"enum",T:v.getEnumType(fd)},{no:2,name:"version",kind:"scalar",T:9},{no:3,name:"protocol",kind:"scalar",T:5},{no:4,name:"region",kind:"scalar",T:9},{no:5,name:"node_id",kind:"scalar",T:9},{no:6,name:"debug_info",kind:"scalar",T:9},{no:7,name:"agent_protocol",kind:"scalar",T:5}]),fd=v.makeEnum("livekit.ServerInfo.Edition",[{no:0,name:"Standard"},{no:1,name:"Cloud"}]),pd=v.makeMessageType("livekit.ClientInfo",()=>[{no:1,name:"sdk",kind:"enum",T:v.getEnumType(gd)},{no:2,name:"version",kind:"scalar",T:9},{no:3,name:"protocol",kind:"scalar",T:5},{no:4,name:"os",kind:"scalar",T:9},{no:5,name:"os_version",kind:"scalar",T:9},{no:6,name:"device_model",kind:"scalar",T:9},{no:7,name:"browser",kind:"scalar",T:9},{no:8,name:"browser_version",kind:"scalar",T:9},{no:9,name:"address",kind:"scalar",T:9},{no:10,name:"network",kind:"scalar",T:9},{no:11,name:"other_sdks",kind:"scalar",T:9},{no:12,name:"client_protocol",kind:"scalar",T:5},{no:13,name:"capabilities",kind:"enum",T:v.getEnumType(Nn),repeated:!0}]),gd=v.makeEnum("livekit.ClientInfo.SDK",[{no:0,name:"UNKNOWN"},{no:1,name:"JS"},{no:2,name:"SWIFT"},{no:3,name:"ANDROID"},{no:4,name:"FLUTTER"},{no:5,name:"GO"},{no:6,name:"UNITY"},{no:7,name:"REACT_NATIVE"},{no:8,name:"RUST"},{no:9,name:"PYTHON"},{no:10,name:"CPP"},{no:11,name:"UNITY_WEB"},{no:12,name:"NODE"},{no:13,name:"UNREAL"},{no:14,name:"ESP32"}]),Nn=v.makeEnum("livekit.ClientInfo.Capability",[{no:0,name:"CAP_UNUSED"},{no:1,name:"CAP_PACKET_TRAILER"},{no:2,name:"CAP_COMPRESSION_DEFLATE_RAW"}]),vd=v.makeMessageType("livekit.ClientConfiguration",()=>[{no:1,name:"video",kind:"message",T:Oo},{no:2,name:"screen",kind:"message",T:Oo},{no:3,name:"resume_connection",kind:"enum",T:v.getEnumType(Dn)},{no:4,name:"disabled_codecs",kind:"message",T:hh},{no:5,name:"force_relay",kind:"enum",T:v.getEnumType(Dn)}]),Oo=v.makeMessageType("livekit.VideoConfiguration",()=>[{no:1,name:"hardware_encoder",kind:"enum",T:v.getEnumType(Dn)}]),hh=v.makeMessageType("livekit.DisabledCodecs",()=>[{no:1,name:"codecs",kind:"message",T:ki,repeated:!0},{no:2,name:"publish",kind:"message",T:ki,repeated:!0}]),bd=v.makeMessageType("livekit.TimedVersion",()=>[{no:1,name:"unix_micro",kind:"scalar",T:3},{no:2,name:"ticks",kind:"scalar",T:5}]),or=v.makeEnum("livekit.DataStream.OperationType",[{no:0,name:"CREATE"},{no:1,name:"UPDATE"},{no:2,name:"DELETE"},{no:3,name:"REACTION"}]),be=v.makeEnum("livekit.DataStream.CompressionType",[{no:0,name:"NONE"},{no:1,name:"DEFLATE_RAW"}]),yd=v.makeMessageType("livekit.DataStream.TextHeader",()=>[{no:1,name:"operation_type",kind:"enum",T:v.getEnumType(or)},{no:2,name:"version",kind:"scalar",T:5},{no:3,name:"reply_to_stream_id",kind:"scalar",T:9},{no:4,name:"attached_stream_ids",kind:"scalar",T:9,repeated:!0},{no:5,name:"generated",kind:"scalar",T:8}],{localName:"DataStream_TextHeader"}),kd=v.makeMessageType("livekit.DataStream.ByteHeader",()=>[{no:1,name:"name",kind:"scalar",T:9}],{localName:"DataStream_ByteHeader"}),cs=v.makeMessageType("livekit.DataStream.Header",()=>[{no:1,name:"stream_id",kind:"scalar",T:9},{no:2,name:"timestamp",kind:"scalar",T:3},{no:3,name:"topic",kind:"scalar",T:9},{no:4,name:"mime_type",kind:"scalar",T:9},{no:5,name:"total_length",kind:"scalar",T:4,opt:!0},{no:7,name:"encryption_type",kind:"enum",T:v.getEnumType(G)},{no:8,name:"attributes",kind:"map",K:9,V:{kind:"scalar",T:9}},{no:9,name:"text_header",kind:"message",T:yd,oneof:"content_header"},{no:10,name:"byte_header",kind:"message",T:kd,oneof:"content_header"},{no:11,name:"inline_content",kind:"scalar",T:12,opt:!0},{no:12,name:"compression",kind:"enum",T:v.getEnumType(be)}],{localName:"DataStream_Header"}),ct=v.makeMessageType("livekit.DataStream.Chunk",()=>[{no:1,name:"stream_id",kind:"scalar",T:9},{no:2,name:"chunk_index",kind:"scalar",T:4},{no:3,name:"content",kind:"scalar",T:12},{no:4,name:"version",kind:"scalar",T:5},{no:5,name:"iv",kind:"scalar",T:12,opt:!0}],{localName:"DataStream_Chunk"}),Ka=v.makeMessageType("livekit.DataStream.Trailer",()=>[{no:1,name:"stream_id",kind:"scalar",T:9},{no:2,name:"reason",kind:"scalar",T:9},{no:3,name:"attributes",kind:"map",K:9,V:{kind:"scalar",T:9}}],{localName:"DataStream_Trailer"});var mh=v.makeMessageType("livekit.SubscribedAudioCodec",()=>[{no:1,name:"codec",kind:"scalar",T:9},{no:2,name:"enabled",kind:"scalar",T:8}]);var Ve=v.makeEnum("livekit.SignalTarget",[{no:0,name:"PUBLISHER"},{no:1,name:"SUBSCRIBER"}]),cr=v.makeEnum("livekit.StreamState",[{no:0,name:"ACTIVE"},{no:1,name:"PAUSED"}]),fh=v.makeEnum("livekit.CandidateProtocol",[{no:0,name:"UDP"},{no:1,name:"TCP"},{no:2,name:"TLS"}]),ph=v.makeMessageType("livekit.SignalRequest",()=>[{no:1,name:"offer",kind:"message",T:Et,oneof:"message"},{no:2,name:"answer",kind:"message",T:Et,oneof:"message"},{no:3,name:"trickle",kind:"message",T:ds,oneof:"message"},{no:4,name:"add_track",kind:"message",T:xn,oneof:"message"},{no:5,name:"mute",kind:"message",T:ls,oneof:"message"},{no:6,name:"subscription",kind:"message",T:us,oneof:"message"},{no:7,name:"track_setting",kind:"message",T:Cd,oneof:"message"},{no:8,name:"leave",kind:"message",T:hs,oneof:"message"},{no:10,name:"update_layers",kind:"message",T:wd,oneof:"message"},{no:11,name:"subscription_permission",kind:"message",T:Pd,oneof:"message"},{no:12,name:"sync_state",kind:"message",T:Za,oneof:"message"},{no:13,name:"simulate",kind:"message",T:Ye,oneof:"message"},{no:14,name:"ping",kind:"scalar",T:3,oneof:"message"},{no:15,name:"update_metadata",kind:"message",T:$a,oneof:"message"},{no:16,name:"ping_req",kind:"message",T:Ad,oneof:"message"},{no:17,name:"update_audio_track",kind:"message",T:Ya,oneof:"message"},{no:18,name:"update_video_track",kind:"message",T:Ed,oneof:"message"},{no:19,name:"publish_data_track_request",kind:"message",T:Ga,oneof:"message"},{no:20,name:"unpublish_data_track_request",kind:"message",T:Ja,oneof:"message"},{no:21,name:"update_data_subscription",kind:"message",T:Td,oneof:"message"},{no:22,name:"store_data_blob_request",kind:"message",T:Ch,oneof:"message"},{no:23,name:"get_data_blob_request",kind:"message",T:wh,oneof:"message"}]),Do=v.makeMessageType("livekit.SignalResponse",()=>[{no:1,name:"join",kind:"message",T:yh,oneof:"message"},{no:2,name:"answer",kind:"message",T:Et,oneof:"message"},{no:3,name:"offer",kind:"message",T:Et,oneof:"message"},{no:4,name:"trickle",kind:"message",T:ds,oneof:"message"},{no:5,name:"update",kind:"message",T:Sh,oneof:"message"},{no:6,name:"track_published",kind:"message",T:Qa,oneof:"message"},{no:8,name:"leave",kind:"message",T:hs,oneof:"message"},{no:9,name:"mute",kind:"message",T:ls,oneof:"message"},{no:10,name:"speakers_changed",kind:"message",T:Rh,oneof:"message"},{no:11,name:"room_update",kind:"message",T:Ph,oneof:"message"},{no:12,name:"connection_quality",kind:"message",T:Mh,oneof:"message"},{no:13,name:"stream_state_update",kind:"message",T:Oh,oneof:"message"},{no:14,name:"subscribed_quality_update",kind:"message",T:Lh,oneof:"message"},{no:15,name:"subscription_permission_update",kind:"message",T:xh,oneof:"message"},{no:16,name:"refresh_token",kind:"scalar",T:9,oneof:"message"},{no:17,name:"track_unpublished",kind:"message",T:Th,oneof:"message"},{no:18,name:"pong",kind:"scalar",T:3,oneof:"message"},{no:19,name:"reconnect",kind:"message",T:kh,oneof:"message"},{no:20,name:"pong_resp",kind:"message",T:Fh,oneof:"message"},{no:21,name:"subscription_response",kind:"message",T:qh,oneof:"message"},{no:22,name:"request_response",kind:"message",T:Vh,oneof:"message"},{no:23,name:"track_subscribed",kind:"message",T:Wh,oneof:"message"},{no:24,name:"room_moved",kind:"message",T:Uh,oneof:"message"},{no:25,name:"media_sections_requirement",kind:"message",T:Gh,oneof:"message"},{no:26,name:"subscribed_audio_codec_update",kind:"message",T:Nh,oneof:"message"},{no:27,name:"publish_data_track_response",kind:"message",T:za,oneof:"message"},{no:28,name:"unpublish_data_track_response",kind:"message",T:gh,oneof:"message"},{no:29,name:"data_track_subscriber_handles",kind:"message",T:vh,oneof:"message"},{no:30,name:"store_data_blob_response",kind:"message",T:Eh,oneof:"message"},{no:31,name:"get_data_blob_response",kind:"message",T:_h,oneof:"message"}]),dr=v.makeMessageType("livekit.SimulcastCodec",()=>[{no:1,name:"codec",kind:"scalar",T:9},{no:2,name:"cid",kind:"scalar",T:9},{no:4,name:"layers",kind:"message",T:bt,repeated:!0},{no:5,name:"video_layer_mode",kind:"enum",T:v.getEnumType(qa)}]),xn=v.makeMessageType("livekit.AddTrackRequest",()=>[{no:1,name:"cid",kind:"scalar",T:9},{no:2,name:"name",kind:"scalar",T:9},{no:3,name:"type",kind:"enum",T:v.getEnumType(qe)},{no:4,name:"width",kind:"scalar",T:13},{no:5,name:"height",kind:"scalar",T:13},{no:6,name:"muted",kind:"scalar",T:8},{no:7,name:"disable_dtx",kind:"scalar",T:8},{no:8,name:"source",kind:"enum",T:v.getEnumType(de)},{no:9,name:"layers",kind:"message",T:bt,repeated:!0},{no:10,name:"simulcast_codecs",kind:"message",T:dr,repeated:!0},{no:11,name:"sid",kind:"scalar",T:9},{no:12,name:"stereo",kind:"scalar",T:8},{no:13,name:"disable_red",kind:"scalar",T:8},{no:14,name:"encryption",kind:"enum",T:v.getEnumType(G)},{no:15,name:"stream",kind:"scalar",T:9},{no:16,name:"backup_codec_policy",kind:"enum",T:v.getEnumType(sd)},{no:17,name:"audio_features",kind:"enum",T:v.getEnumType(me),repeated:!0},{no:18,name:"packet_trailer_features",kind:"enum",T:v.getEnumType(an),repeated:!0}]),Ga=v.makeMessageType("livekit.PublishDataTrackRequest",()=>[{no:1,name:"pub_handle",kind:"scalar",T:13},{no:2,name:"name",kind:"scalar",T:9},{no:3,name:"encryption",kind:"enum",T:v.getEnumType(G)},{no:4,name:"frame_encoding",kind:"message",T:rd,opt:!0},{no:5,name:"schema",kind:"message",T:Ba,opt:!0}]),za=v.makeMessageType("livekit.PublishDataTrackResponse",()=>[{no:1,name:"info",kind:"message",T:as}]),Ja=v.makeMessageType("livekit.UnpublishDataTrackRequest",()=>[{no:1,name:"pub_handle",kind:"scalar",T:13}]),gh=v.makeMessageType("livekit.UnpublishDataTrackResponse",()=>[{no:1,name:"info",kind:"message",T:as}]),vh=v.makeMessageType("livekit.DataTrackSubscriberHandles",()=>[{no:1,name:"sub_handles",kind:"map",K:13,V:{kind:"message",T:bh}}]),bh=v.makeMessageType("livekit.DataTrackSubscriberHandles.PublishedDataTrack",()=>[{no:1,name:"publisher_identity",kind:"scalar",T:9},{no:2,name:"publisher_sid",kind:"scalar",T:9},{no:3,name:"track_sid",kind:"scalar",T:9}],{localName:"DataTrackSubscriberHandles_PublishedDataTrack"}),ds=v.makeMessageType("livekit.TrickleRequest",()=>[{no:1,name:"candidateInit",kind:"scalar",T:9},{no:2,name:"target",kind:"enum",T:v.getEnumType(Ve)},{no:3,name:"final",kind:"scalar",T:8}]),ls=v.makeMessageType("livekit.MuteTrackRequest",()=>[{no:1,name:"sid",kind:"scalar",T:9},{no:2,name:"muted",kind:"scalar",T:8}]),yh=v.makeMessageType("livekit.JoinResponse",()=>[{no:1,name:"room",kind:"message",T:rs},{no:2,name:"participant",kind:"message",T:xt},{no:3,name:"other_participants",kind:"message",T:xt,repeated:!0},{no:4,name:"server_version",kind:"scalar",T:9},{no:5,name:"ice_servers",kind:"message",T:_d,repeated:!0},{no:6,name:"subscriber_primary",kind:"scalar",T:8},{no:7,name:"alternative_url",kind:"scalar",T:9},{no:8,name:"client_configuration",kind:"message",T:vd},{no:9,name:"server_region",kind:"scalar",T:9},{no:10,name:"ping_timeout",kind:"scalar",T:5},{no:11,name:"ping_interval",kind:"scalar",T:5},{no:12,name:"server_info",kind:"message",T:md},{no:13,name:"sif_trailer",kind:"scalar",T:12},{no:14,name:"enabled_publish_codecs",kind:"message",T:ki,repeated:!0},{no:15,name:"fast_publish",kind:"scalar",T:8}]),kh=v.makeMessageType("livekit.ReconnectResponse",()=>[{no:1,name:"ice_servers",kind:"message",T:_d,repeated:!0},{no:2,name:"client_configuration",kind:"message",T:vd},{no:3,name:"server_info",kind:"message",T:md},{no:4,name:"last_message_seq",kind:"scalar",T:13}]),Qa=v.makeMessageType("livekit.TrackPublishedResponse",()=>[{no:1,name:"cid",kind:"scalar",T:9},{no:2,name:"track",kind:"message",T:Kt}]),Th=v.makeMessageType("livekit.TrackUnpublishedResponse",()=>[{no:1,name:"track_sid",kind:"scalar",T:9}]),Et=v.makeMessageType("livekit.SessionDescription",()=>[{no:1,name:"type",kind:"scalar",T:9},{no:2,name:"sdp",kind:"scalar",T:9},{no:3,name:"id",kind:"scalar",T:13},{no:4,name:"mid_to_track_id",kind:"map",K:9,V:{kind:"scalar",T:9}}]),Sh=v.makeMessageType("livekit.ParticipantUpdate",()=>[{no:1,name:"participants",kind:"message",T:xt,repeated:!0}]),us=v.makeMessageType("livekit.UpdateSubscription",()=>[{no:1,name:"track_sids",kind:"scalar",T:9,repeated:!0},{no:2,name:"subscribe",kind:"scalar",T:8},{no:3,name:"participant_tracks",kind:"message",T:hd,repeated:!0}]),Td=v.makeMessageType("livekit.UpdateDataSubscription",()=>[{no:1,name:"updates",kind:"message",T:Sd,repeated:!0}]),Sd=v.makeMessageType("livekit.UpdateDataSubscription.Update",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"subscribe",kind:"scalar",T:8},{no:3,name:"options",kind:"message",T:ch}],{localName:"UpdateDataSubscription_Update"}),Ch=v.makeMessageType("livekit.StoreDataBlobRequest",()=>[{no:1,name:"request_id",kind:"scalar",T:13},{no:2,name:"blob",kind:"message",T:ad}]),Eh=v.makeMessageType("livekit.StoreDataBlobResponse",()=>[{no:1,name:"request_id",kind:"scalar",T:13},{no:2,name:"key",kind:"message",T:ja}]),wh=v.makeMessageType("livekit.GetDataBlobRequest",()=>[{no:1,name:"request_id",kind:"scalar",T:13},{no:2,name:"participant_identity",kind:"scalar",T:9},{no:3,name:"key",kind:"message",T:ja}]),_h=v.makeMessageType("livekit.GetDataBlobResponse",()=>[{no:1,name:"request_id",kind:"scalar",T:13},{no:2,name:"blob",kind:"message",T:ad}]),Cd=v.makeMessageType("livekit.UpdateTrackSettings",()=>[{no:1,name:"track_sids",kind:"scalar",T:9,repeated:!0},{no:3,name:"disabled",kind:"scalar",T:8},{no:4,name:"quality",kind:"enum",T:v.getEnumType(Fa)},{no:5,name:"width",kind:"scalar",T:13},{no:6,name:"height",kind:"scalar",T:13},{no:7,name:"fps",kind:"scalar",T:13},{no:8,name:"priority",kind:"scalar",T:13}]),Ya=v.makeMessageType("livekit.UpdateLocalAudioTrack",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"features",kind:"enum",T:v.getEnumType(me),repeated:!0}]),Ed=v.makeMessageType("livekit.UpdateLocalVideoTrack",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"width",kind:"scalar",T:13},{no:3,name:"height",kind:"scalar",T:13}]),hs=v.makeMessageType("livekit.LeaveRequest",()=>[{no:1,name:"can_reconnect",kind:"scalar",T:8},{no:2,name:"reason",kind:"enum",T:v.getEnumType(Pe)},{no:3,name:"action",kind:"enum",T:v.getEnumType(Jt)},{no:4,name:"regions",kind:"message",T:Bh}]),Jt=v.makeEnum("livekit.LeaveRequest.Action",[{no:0,name:"DISCONNECT"},{no:1,name:"RESUME"},{no:2,name:"RECONNECT"}]),wd=v.makeMessageType("livekit.UpdateVideoLayers",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"layers",kind:"message",T:bt,repeated:!0}]),$a=v.makeMessageType("livekit.UpdateParticipantMetadata",()=>[{no:1,name:"metadata",kind:"scalar",T:9},{no:2,name:"name",kind:"scalar",T:9},{no:3,name:"attributes",kind:"map",K:9,V:{kind:"scalar",T:9}},{no:4,name:"request_id",kind:"scalar",T:13}]),_d=v.makeMessageType("livekit.ICEServer",()=>[{no:1,name:"urls",kind:"scalar",T:9,repeated:!0},{no:2,name:"username",kind:"scalar",T:9},{no:3,name:"credential",kind:"scalar",T:9}]),Rh=v.makeMessageType("livekit.SpeakersChanged",()=>[{no:1,name:"speakers",kind:"message",T:dd,repeated:!0}]),Ph=v.makeMessageType("livekit.RoomUpdate",()=>[{no:1,name:"room",kind:"message",T:rs}]),Ih=v.makeMessageType("livekit.ConnectionQualityInfo",()=>[{no:1,name:"participant_sid",kind:"scalar",T:9},{no:2,name:"quality",kind:"enum",T:v.getEnumType(Ht)},{no:3,name:"score",kind:"scalar",T:2}]),Mh=v.makeMessageType("livekit.ConnectionQualityUpdate",()=>[{no:1,name:"updates",kind:"message",T:Ih,repeated:!0}]),Ah=v.makeMessageType("livekit.StreamStateInfo",()=>[{no:1,name:"participant_sid",kind:"scalar",T:9},{no:2,name:"track_sid",kind:"scalar",T:9},{no:3,name:"state",kind:"enum",T:v.getEnumType(cr)}]),Oh=v.makeMessageType("livekit.StreamStateUpdate",()=>[{no:1,name:"stream_states",kind:"message",T:Ah,repeated:!0}]),Xa=v.makeMessageType("livekit.SubscribedQuality",()=>[{no:1,name:"quality",kind:"enum",T:v.getEnumType(Fa)},{no:2,name:"enabled",kind:"scalar",T:8}]),Dh=v.makeMessageType("livekit.SubscribedCodec",()=>[{no:1,name:"codec",kind:"scalar",T:9},{no:2,name:"qualities",kind:"message",T:Xa,repeated:!0}]),Lh=v.makeMessageType("livekit.SubscribedQualityUpdate",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"subscribed_qualities",kind:"message",T:Xa,repeated:!0},{no:3,name:"subscribed_codecs",kind:"message",T:Dh,repeated:!0}]),Nh=v.makeMessageType("livekit.SubscribedAudioCodecUpdate",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"subscribed_audio_codecs",kind:"message",T:mh,repeated:!0}]),Rd=v.makeMessageType("livekit.TrackPermission",()=>[{no:1,name:"participant_sid",kind:"scalar",T:9},{no:2,name:"all_tracks",kind:"scalar",T:8},{no:3,name:"track_sids",kind:"scalar",T:9,repeated:!0},{no:4,name:"participant_identity",kind:"scalar",T:9}]),Pd=v.makeMessageType("livekit.SubscriptionPermission",()=>[{no:1,name:"all_participants",kind:"scalar",T:8},{no:2,name:"track_permissions",kind:"message",T:Rd,repeated:!0}]),xh=v.makeMessageType("livekit.SubscriptionPermissionUpdate",()=>[{no:1,name:"participant_sid",kind:"scalar",T:9},{no:2,name:"track_sid",kind:"scalar",T:9},{no:3,name:"allowed",kind:"scalar",T:8}]),Uh=v.makeMessageType("livekit.RoomMovedResponse",()=>[{no:1,name:"room",kind:"message",T:rs},{no:2,name:"token",kind:"scalar",T:9},{no:3,name:"participant",kind:"message",T:xt},{no:4,name:"other_participants",kind:"message",T:xt,repeated:!0}]),Za=v.makeMessageType("livekit.SyncState",()=>[{no:1,name:"answer",kind:"message",T:Et},{no:2,name:"subscription",kind:"message",T:us},{no:3,name:"publish_tracks",kind:"message",T:Qa,repeated:!0},{no:4,name:"data_channels",kind:"message",T:Md,repeated:!0},{no:5,name:"offer",kind:"message",T:Et},{no:6,name:"track_sids_disabled",kind:"scalar",T:9,repeated:!0},{no:7,name:"datachannel_receive_states",kind:"message",T:Id,repeated:!0},{no:8,name:"publish_data_tracks",kind:"message",T:za,repeated:!0}]),Id=v.makeMessageType("livekit.DataChannelReceiveState",()=>[{no:1,name:"publisher_sid",kind:"scalar",T:9},{no:2,name:"last_seq",kind:"scalar",T:13}]),Md=v.makeMessageType("livekit.DataChannelInfo",()=>[{no:1,name:"label",kind:"scalar",T:9},{no:2,name:"id",kind:"scalar",T:13},{no:3,name:"target",kind:"enum",T:v.getEnumType(Ve)}]),Ye=v.makeMessageType("livekit.SimulateScenario",()=>[{no:1,name:"speaker_update",kind:"scalar",T:5,oneof:"scenario"},{no:2,name:"node_failure",kind:"scalar",T:8,oneof:"scenario"},{no:3,name:"migration",kind:"scalar",T:8,oneof:"scenario"},{no:4,name:"server_leave",kind:"scalar",T:8,oneof:"scenario"},{no:5,name:"switch_candidate_protocol",kind:"enum",T:v.getEnumType(fh),oneof:"scenario"},{no:6,name:"subscriber_bandwidth",kind:"scalar",T:3,oneof:"scenario"},{no:7,name:"disconnect_signal_on_resume",kind:"scalar",T:8,oneof:"scenario"},{no:8,name:"disconnect_signal_on_resume_no_messages",kind:"scalar",T:8,oneof:"scenario"},{no:9,name:"leave_request_full_reconnect",kind:"scalar",T:8,oneof:"scenario"}]),Ad=v.makeMessageType("livekit.Ping",()=>[{no:1,name:"timestamp",kind:"scalar",T:3},{no:2,name:"rtt",kind:"scalar",T:3}]),Fh=v.makeMessageType("livekit.Pong",()=>[{no:1,name:"last_ping_timestamp",kind:"scalar",T:3},{no:2,name:"timestamp",kind:"scalar",T:3}]),Bh=v.makeMessageType("livekit.RegionSettings",()=>[{no:1,name:"regions",kind:"message",T:jh,repeated:!0}]),jh=v.makeMessageType("livekit.RegionInfo",()=>[{no:1,name:"region",kind:"scalar",T:9},{no:2,name:"url",kind:"scalar",T:9},{no:3,name:"distance",kind:"scalar",T:3}]),qh=v.makeMessageType("livekit.SubscriptionResponse",()=>[{no:1,name:"track_sid",kind:"scalar",T:9},{no:2,name:"err",kind:"enum",T:v.getEnumType(th)}]),Vh=v.makeMessageType("livekit.RequestResponse",()=>[{no:1,name:"request_id",kind:"scalar",T:13},{no:2,name:"reason",kind:"enum",T:v.getEnumType(Pt)},{no:3,name:"message",kind:"scalar",T:9},{no:4,name:"trickle",kind:"message",T:ds,oneof:"request"},{no:5,name:"add_track",kind:"message",T:xn,oneof:"request"},{no:6,name:"mute",kind:"message",T:ls,oneof:"request"},{no:7,name:"update_metadata",kind:"message",T:$a,oneof:"request"},{no:8,name:"update_audio_track",kind:"message",T:Ya,oneof:"request"},{no:9,name:"update_video_track",kind:"message",T:Ed,oneof:"request"},{no:10,name:"publish_data_track",kind:"message",T:Ga,oneof:"request"},{no:11,name:"unpublish_data_track",kind:"message",T:Ja,oneof:"request"}]),Pt=v.makeEnum("livekit.RequestResponse.Reason",[{no:0,name:"OK"},{no:1,name:"NOT_FOUND"},{no:2,name:"NOT_ALLOWED"},{no:3,name:"LIMIT_EXCEEDED"},{no:4,name:"QUEUED"},{no:5,name:"UNSUPPORTED_TYPE"},{no:6,name:"UNCLASSIFIED_ERROR"},{no:7,name:"INVALID_HANDLE"},{no:8,name:"INVALID_NAME"},{no:9,name:"DUPLICATE_HANDLE"},{no:10,name:"DUPLICATE_NAME"},{no:11,name:"INVALID_REQUEST"}]),Wh=v.makeMessageType("livekit.TrackSubscribed",()=>[{no:1,name:"track_sid",kind:"scalar",T:9}]),Od=v.makeMessageType("livekit.ConnectionSettings",()=>[{no:1,name:"auto_subscribe",kind:"scalar",T:8},{no:2,name:"adaptive_stream",kind:"scalar",T:8},{no:3,name:"subscriber_allow_pause",kind:"scalar",T:8,opt:!0},{no:4,name:"disable_ice_lite",kind:"scalar",T:8},{no:5,name:"auto_subscribe_data_track",kind:"scalar",T:8,opt:!0}]),Hh=v.makeMessageType("livekit.JoinRequest",()=>[{no:1,name:"client_info",kind:"message",T:pd},{no:2,name:"connection_settings",kind:"message",T:Od},{no:3,name:"metadata",kind:"scalar",T:9},{no:4,name:"participant_attributes",kind:"map",K:9,V:{kind:"scalar",T:9}},{no:5,name:"add_track_requests",kind:"message",T:xn,repeated:!0},{no:6,name:"publisher_offer",kind:"message",T:Et},{no:7,name:"reconnect",kind:"scalar",T:8},{no:8,name:"reconnect_reason",kind:"enum",T:v.getEnumType(it)},{no:9,name:"participant_sid",kind:"scalar",T:9},{no:10,name:"sync_state",kind:"message",T:Za}]),Kh=v.makeMessageType("livekit.WrappedJoinRequest",()=>[{no:1,name:"compression",kind:"enum",T:v.getEnumType(lr)},{no:2,name:"join_request",kind:"scalar",T:12}]),lr=v.makeEnum("livekit.WrappedJoinRequest.Compression",[{no:0,name:"NONE"},{no:1,name:"GZIP"}]),Gh=v.makeMessageType("livekit.MediaSectionsRequirement",()=>[{no:1,name:"num_audios",kind:"scalar",T:13},{no:2,name:"num_videos",kind:"scalar",T:13}]);function zh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var li={exports:{}},Jh=li.exports,Lo;function Qh(){return Lo||(Lo=1,function(i){(function(e,t){i.exports?i.exports=t():e.log=t()})(Jh,function(){var e=function(){},t="undefined",n=typeof window!==t&&typeof window.navigator!==t&&/Trident\/|MSIE /.test(window.navigator.userAgent),s=["trace","debug","info","warn","error"],r={},a=null;function o(p,k){var T=p[k];if(typeof T.bind=="function")return T.bind(p);try{return Function.prototype.bind.call(T,p)}catch{return function(){return Function.prototype.apply.apply(T,[p,arguments])}}}function c(){console.log&&(console.log.apply?console.log.apply(console,arguments):Function.prototype.apply.apply(console.log,[console,arguments])),console.trace&&console.trace()}function d(p){return p==="debug"&&(p="log"),typeof console===t?!1:p==="trace"&&n?c:console[p]!==void 0?o(console,p):console.log!==void 0?o(console,"log"):e}function l(){for(var p=this.getLevel(),k=0;k=0&&U<=T.levels.SILENT)return U;throw new TypeError("log.setLevel() called with invalid level: "+D)}T.name=p,T.levels={TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4,SILENT:5},T.methodFactory=k||h,T.getLevel=function(){return b??I??S},T.setLevel=function(D,U){return b=j(D),U!==!1&&C(b),l.call(T)},T.setDefaultLevel=function(D){I=j(D),M()||T.setLevel(D,!1)},T.resetLevel=function(){b=null,O(),l.call(T)},T.enableAll=function(D){T.setLevel(T.levels.TRACE,D)},T.disableAll=function(D){T.setLevel(T.levels.SILENT,D)},T.rebuild=function(){if(a!==T&&(S=j(a.getLevel())),l.call(T),a===T)for(var D in r)r[D].rebuild()},S=j(a?a.getLevel():"WARN");var B=M();B!=null&&(b=j(B)),l.call(T)}a=new m,a.getLogger=function(k){if(typeof k!="symbol"&&typeof k!="string"||k==="")throw new TypeError("You must supply a name when creating a logger.");var T=r[k];return T||(T=r[k]=new m(k,a.methodFactory)),T};var g=typeof window!==t?window.log:void 0;return a.noConflict=function(){return typeof window!==t&&window.log===a&&(window.log=g),a},a.getLoggers=function(){return r},a.default=a,a})}(li)),li.exports}var ms=Qh(),ur;(function(i){i[i.trace=0]="trace",i[i.debug=1]="debug",i[i.info=2]="info",i[i.warn=3]="warn",i[i.error=4]="error",i[i.silent=5]="silent"})(ur||(ur={}));var te;(function(i){i.Default="livekit",i.Room="livekit-room",i.TokenSource="livekit-token-source",i.Participant="livekit-participant",i.Track="livekit-track",i.Publication="livekit-track-publication",i.Engine="livekit-engine",i.Signal="livekit-signal",i.PCManager="livekit-pc-manager",i.PCTransport="livekit-pc-transport",i.E2EE="lk-e2ee",i.DataTracks="livekit-data-tracks",i.Region="livekit-region",i.ICE="livekit-ice",i.Stats="livekit-stats"})(te||(te={}));var W=ms.getLogger(te.Default),Yp=Object.values(te).map(i=>ms.getLogger(i));W.setDefaultLevel(ur.info);function he(i,e){let t=ms.getLogger(i);return t.setDefaultLevel(W.getLevel()),e?Yh(t,e):t}function Yh(i,e){let t=s=>(r,a)=>{let o=e(),c=o||a?Object.assign(Object.assign({},o),a):void 0;i[s](r,c)},n=Object.create(i);return n.trace=t("trace"),n.debug=t("debug"),n.info=t("info"),n.warn=t("warn"),n.error=t("error"),n}var on=ms.getLogger(te.E2EE),hr=new Set,$h=on.setLevel.bind(on);on.setLevel=(i,e)=>{$h(i,e);let t=on.getLevel();hr.forEach(n=>n(t))};function No(i){return hr.add(i),()=>{hr.delete(i)}}var gn=7e3,Xh=[0,300,2*2*300,3*3*300,4*4*300,gn,gn,gn,gn,gn],mr=class{constructor(e){this._retryDelays=e!==void 0?[...e]:Xh}nextRetryDelayInMs(e){if(e.retryCount>=this._retryDelays.length)return null;let t=this._retryDelays[e.retryCount];return e.retryCount<=1?t:t+Math.random()*1e3}};function Zh(i,e){var t={};for(var n in i)Object.prototype.hasOwnProperty.call(i,n)&&e.indexOf(n)<0&&(t[n]=i[n]);if(i!=null&&typeof Object.getOwnPropertySymbols=="function")for(var s=0,n=Object.getOwnPropertySymbols(i);s=i.length&&(i=void 0),{value:i&&i[n++],done:!i}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function dt(i){return this instanceof dt?(this.v=i,this):new dt(i)}function Dd(i,e,t){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n=t.apply(i,e||[]),s,r=[];return s=Object.create((typeof AsyncIterator=="function"?AsyncIterator:Object).prototype),o("next"),o("throw"),o("return",a),s[Symbol.asyncIterator]=function(){return this},s;function a(m){return function(g){return Promise.resolve(g).then(m,u)}}function o(m,g){n[m]&&(s[m]=function(p){return new Promise(function(k,T){r.push([m,p,k,T])>1||c(m,p)})},g&&(s[m]=g(s[m])))}function c(m,g){try{d(n[m](g))}catch(p){h(r[0][3],p)}}function d(m){m.value instanceof dt?Promise.resolve(m.value.v).then(l,u):h(r[0][2],m)}function l(m){c("next",m)}function u(m){c("throw",m)}function h(m,g){m(g),r.shift(),r.length&&c(r[0][0],r[0][1])}}function em(i){var e,t;return e={},n("next"),n("throw",function(s){throw s}),n("return"),e[Symbol.iterator]=function(){return this},e;function n(s,r){e[s]=i[s]?function(a){return(t=!t)?{value:dt(i[s](a)),done:!1}:r?r(a):a}:r}}function De(i){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var e=i[Symbol.asyncIterator],t;return e?e.call(i):(i=typeof xo=="function"?xo(i):i[Symbol.iterator](),t={},n("next"),n("throw"),n("return"),t[Symbol.asyncIterator]=function(){return this},t);function n(r){t[r]=i[r]&&function(a){return new Promise(function(o,c){a=i[r](a),s(o,c,a.done,a.value)})}}function s(r,a,o,c){Promise.resolve(c).then(function(d){r({value:d,done:o})},a)}}var Xn={exports:{}},Uo;function tm(){if(Uo)return Xn.exports;Uo=1;var i=typeof Reflect=="object"?Reflect:null,e=i&&typeof i.apply=="function"?i.apply:function(y,C,M){return Function.prototype.apply.call(y,C,M)},t;i&&typeof i.ownKeys=="function"?t=i.ownKeys:Object.getOwnPropertySymbols?t=function(y){return Object.getOwnPropertyNames(y).concat(Object.getOwnPropertySymbols(y))}:t=function(y){return Object.getOwnPropertyNames(y)};function n(b){console&&console.warn&&console.warn(b)}var s=Number.isNaN||function(y){return y!==y};function r(){r.init.call(this)}Xn.exports=r,Xn.exports.once=T,r.EventEmitter=r,r.prototype._events=void 0,r.prototype._eventsCount=0,r.prototype._maxListeners=void 0;var a=10;function o(b){if(typeof b!="function")throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof b)}Object.defineProperty(r,"defaultMaxListeners",{enumerable:!0,get:function(){return a},set:function(b){if(typeof b!="number"||b<0||s(b))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+b+".");a=b}}),r.init=function(){(this._events===void 0||this._events===Object.getPrototypeOf(this)._events)&&(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},r.prototype.setMaxListeners=function(y){if(typeof y!="number"||y<0||s(y))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+y+".");return this._maxListeners=y,this};function c(b){return b._maxListeners===void 0?r.defaultMaxListeners:b._maxListeners}r.prototype.getMaxListeners=function(){return c(this)},r.prototype.emit=function(y){for(var C=[],M=1;M0&&(B=C[0]),B instanceof Error)throw B;var D=new Error("Unhandled error."+(B?" ("+B.message+")":""));throw D.context=B,D}var U=j[y];if(U===void 0)return!1;if(typeof U=="function")e(U,this,C);else for(var H=U.length,oe=g(U,H),M=0;M0&&B.length>O&&!B.warned){B.warned=!0;var D=new Error("Possible EventEmitter memory leak detected. "+B.length+" "+String(y)+" listeners added. Use emitter.setMaxListeners() to increase limit");D.name="MaxListenersExceededWarning",D.emitter=b,D.type=y,D.count=B.length,n(D)}return b}r.prototype.addListener=function(y,C){return d(this,y,C,!1)},r.prototype.on=r.prototype.addListener,r.prototype.prependListener=function(y,C){return d(this,y,C,!0)};function l(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function u(b,y,C){var M={fired:!1,wrapFn:void 0,target:b,type:y,listener:C},O=l.bind(M);return O.listener=C,M.wrapFn=O,O}r.prototype.once=function(y,C){return o(C),this.on(y,u(this,y,C)),this},r.prototype.prependOnceListener=function(y,C){return o(C),this.prependListener(y,u(this,y,C)),this},r.prototype.removeListener=function(y,C){var M,O,j,B,D;if(o(C),O=this._events,O===void 0)return this;if(M=O[y],M===void 0)return this;if(M===C||M.listener===C)--this._eventsCount===0?this._events=Object.create(null):(delete O[y],O.removeListener&&this.emit("removeListener",y,M.listener||C));else if(typeof M!="function"){for(j=-1,B=M.length-1;B>=0;B--)if(M[B]===C||M[B].listener===C){D=M[B].listener,j=B;break}if(j<0)return this;j===0?M.shift():p(M,j),M.length===1&&(O[y]=M[0]),O.removeListener!==void 0&&this.emit("removeListener",y,D||C)}return this},r.prototype.off=r.prototype.removeListener,r.prototype.removeAllListeners=function(y){var C,M,O;if(M=this._events,M===void 0)return this;if(M.removeListener===void 0)return arguments.length===0?(this._events=Object.create(null),this._eventsCount=0):M[y]!==void 0&&(--this._eventsCount===0?this._events=Object.create(null):delete M[y]),this;if(arguments.length===0){var j=Object.keys(M),B;for(O=0;O=0;O--)this.removeListener(y,C[O]);return this};function h(b,y,C){var M=b._events;if(M===void 0)return[];var O=M[y];return O===void 0?[]:typeof O=="function"?C?[O.listener||O]:[O]:C?k(O):g(O,O.length)}r.prototype.listeners=function(y){return h(this,y,!0)},r.prototype.rawListeners=function(y){return h(this,y,!1)},r.listenerCount=function(b,y){return typeof b.listenerCount=="function"?b.listenerCount(y):m.call(b,y)},r.prototype.listenerCount=m;function m(b){var y=this._events;if(y!==void 0){var C=y[b];if(typeof C=="function")return 1;if(C!==void 0)return C.length}return 0}r.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]};function g(b,y){for(var C=new Array(y),M=0;M=t&&parseFloat(n[t],10)}function qt(i,e,t){if(!i.RTCPeerConnection)return;if(!Object.getOwnPropertyDescriptor(EventTarget.prototype,"addEventListener").writable){eo("Unable to polyfill events");return}let s=i.RTCPeerConnection.prototype,r=s.addEventListener;s.addEventListener=function(o,c){if(o!==e)return r.apply(this,arguments);let d=l=>{let u=t(l);u&&(c.handleEvent?c.handleEvent(u):c(u))};return this._eventMap=this._eventMap||{},this._eventMap[e]||(this._eventMap[e]=new Map),this._eventMap[e].set(c,d),r.apply(this,[o,d])};let a=s.removeEventListener;s.removeEventListener=function(o,c){if(o!==e||!this._eventMap||!this._eventMap[e])return a.apply(this,arguments);if(!this._eventMap[e].has(c))return a.apply(this,arguments);let d=this._eventMap[e].get(c);return this._eventMap[e].delete(c),this._eventMap[e].size===0&&delete this._eventMap[e],Object.keys(this._eventMap).length===0&&delete this._eventMap,a.apply(this,[o,d])},Object.defineProperty(s,"on"+e,{get(){return this["_on"+e]},set(o){this["_on"+e]&&(this.removeEventListener(e,this["_on"+e]),delete this["_on"+e]),o&&this.addEventListener(e,this["_on"+e]=o)},enumerable:!0,configurable:!0})}function nm(i){return typeof i!="boolean"?new Error("Argument type: "+typeof i+". Please use a boolean."):(Ld=i,i?"adapter.js logging disabled":"adapter.js logging enabled")}function im(i){return typeof i!="boolean"?new Error("Argument type: "+typeof i+". Please use a boolean."):(Nd=!i,"adapter.js deprecation warnings "+(i?"disabled":"enabled"))}function eo(){if(typeof window=="object"){if(Ld)return;typeof console<"u"&&typeof console.log=="function"&&console.log.apply(console,arguments)}}function to(i,e){Nd&&console.warn(i+" is deprecated, please use "+e+" instead.")}function sm(i){let e={browser:null,version:null};if(typeof i>"u"||!i.navigator||!i.navigator.userAgent)return e.browser="Not a browser.",e;let t=i.navigator;if(t.userAgentData&&t.userAgentData.brands){let n=t.userAgentData.brands.find(s=>s.brand==="Chromium");if(n){let s=parseInt(n.version,10);if(s>=90)return{browser:"chrome",version:s}}}if(t.mozGetUserMedia)e.browser="firefox",e.version=parseInt(Sn(t.userAgent,/Firefox\/(\d+)\./,1));else if(t.webkitGetUserMedia||i.isSecureContext===!1&&i.webkitRTCPeerConnection)e.browser="chrome",e.version=parseInt(Sn(t.userAgent,/Chrom(e|ium)\/(\d+)\./,2))||null;else if(i.RTCPeerConnection&&t.userAgent.match(/AppleWebKit\/(\d+)\./))e.browser="safari",e.version=parseInt(Sn(t.userAgent,/AppleWebKit\/(\d+)\./,1)),e.supportsUnifiedPlan=i.RTCRtpTransceiver&&"currentDirection"in i.RTCRtpTransceiver.prototype,e._safariVersion=Sn(t.userAgent,/Version\/(\d+(\.?\d+))/,1);else return e.browser="Not a supported browser.",e;return e}function Fo(i){return Object.prototype.toString.call(i)==="[object Object]"}function xd(i){return Fo(i)?Object.keys(i).reduce(function(e,t){let n=Fo(i[t]),s=n?xd(i[t]):i[t],r=n&&!Object.keys(s).length;return s===void 0||r?e:Object.assign(e,{[t]:s})},{}):i}function fr(i,e,t){!e||t.has(e.id)||(t.set(e.id,e),Object.keys(e).forEach(n=>{n.endsWith("Id")?fr(i,i.get(e[n]),t):n.endsWith("Ids")&&e[n].forEach(s=>{fr(i,i.get(s),t)})}))}function Bo(i,e,t){let n=t?"outbound-rtp":"inbound-rtp",s=new Map;if(e===null)return s;let r=[];return i.forEach(a=>{a.type==="track"&&a.trackIdentifier===e.id&&r.push(a)}),r.forEach(a=>{i.forEach(o=>{o.type===n&&o.trackId===a.id&&fr(i,o,s)})}),s}var jo=eo;function Ud(i,e){if(e.version>=64)return;let t=i&&i.navigator;if(!t.mediaDevices)return;let n=function(o){if(typeof o!="object"||o.mandatory||o.optional)return o;let c={};return Object.keys(o).forEach(d=>{if(d==="require"||d==="advanced"||d==="mediaSource")return;let l=typeof o[d]=="object"?o[d]:{ideal:o[d]};l.exact!==void 0&&typeof l.exact=="number"&&(l.min=l.max=l.exact);let u=function(h,m){return h?h+m.charAt(0).toUpperCase()+m.slice(1):m==="deviceId"?"sourceId":m};if(l.ideal!==void 0){c.optional=c.optional||[];let h={};typeof l.ideal=="number"?(h[u("min",d)]=l.ideal,c.optional.push(h),h={},h[u("max",d)]=l.ideal,c.optional.push(h)):(h[u("",d)]=l.ideal,c.optional.push(h))}l.exact!==void 0&&typeof l.exact!="number"?(c.mandatory=c.mandatory||{},c.mandatory[u("",d)]=l.exact):["min","max"].forEach(h=>{l[h]!==void 0&&(c.mandatory=c.mandatory||{},c.mandatory[u(h,d)]=l[h])})}),o.advanced&&(c.optional=(c.optional||[]).concat(o.advanced)),c},s=function(o,c){if(e.version>=61)return c(o);if(o=JSON.parse(JSON.stringify(o)),o&&typeof o.audio=="object"){let d=function(l,u,h){u in l&&!(h in l)&&(l[h]=l[u],delete l[u])};o=JSON.parse(JSON.stringify(o)),d(o.audio,"autoGainControl","googAutoGainControl"),d(o.audio,"noiseSuppression","googNoiseSuppression"),o.audio=n(o.audio)}if(o&&typeof o.video=="object"){let d=o.video.facingMode;d=d&&(typeof d=="object"?d:{ideal:d});let l=e.version<66;if(d&&(d.exact==="user"||d.exact==="environment"||d.ideal==="user"||d.ideal==="environment")&&!(t.mediaDevices.getSupportedConstraints&&t.mediaDevices.getSupportedConstraints().facingMode&&!l)){delete o.video.facingMode;let u;if(d.exact==="environment"||d.ideal==="environment"?u=["back","rear"]:(d.exact==="user"||d.ideal==="user")&&(u=["front"]),u)return t.mediaDevices.enumerateDevices().then(h=>{h=h.filter(g=>g.kind==="videoinput");let m=h.find(g=>u.some(p=>g.label.toLowerCase().includes(p)));return!m&&h.length&&u.includes("back")&&(m=h[h.length-1]),m&&(o.video.deviceId=d.exact?{exact:m.deviceId}:{ideal:m.deviceId}),o.video=n(o.video),jo("chrome: "+JSON.stringify(o)),c(o)})}o.video=n(o.video)}return jo("chrome: "+JSON.stringify(o)),c(o)},r=function(o){return e.version>=64?o:{name:{PermissionDeniedError:"NotAllowedError",PermissionDismissedError:"NotAllowedError",InvalidStateError:"NotAllowedError",DevicesNotFoundError:"NotFoundError",ConstraintNotSatisfiedError:"OverconstrainedError",TrackStartError:"NotReadableError",MediaDeviceFailedDueToShutdown:"NotAllowedError",MediaDeviceKillSwitchOn:"NotAllowedError",TabCaptureError:"AbortError",ScreenCaptureError:"AbortError",DeviceCaptureError:"AbortError"}[o.name]||o.name,message:o.message,constraint:o.constraint||o.constraintName,toString(){return this.name+(this.message&&": ")+this.message}}},a=function(o,c,d){s(o,l=>{t.webkitGetUserMedia(l,c,u=>{d&&d(r(u))})})};if(t.getUserMedia=a.bind(t),t.mediaDevices.getUserMedia){let o=t.mediaDevices.getUserMedia.bind(t.mediaDevices);t.mediaDevices.getUserMedia=function(c){return s(c,d=>o(d).then(l=>{if(d.audio&&!l.getAudioTracks().length||d.video&&!l.getVideoTracks().length)throw l.getTracks().forEach(u=>{u.stop()}),new DOMException("","NotFoundError");return l},l=>Promise.reject(r(l))))}}}function Fd(i){i.MediaStream=i.MediaStream||i.webkitMediaStream}function Bd(i,e){if(!(e.version>102))if(typeof i=="object"&&i.RTCPeerConnection&&!("ontrack"in i.RTCPeerConnection.prototype)){Object.defineProperty(i.RTCPeerConnection.prototype,"ontrack",{get(){return this._ontrack},set(n){this._ontrack&&this.removeEventListener("track",this._ontrack),this.addEventListener("track",this._ontrack=n)},enumerable:!0,configurable:!0});let t=i.RTCPeerConnection.prototype.setRemoteDescription;i.RTCPeerConnection.prototype.setRemoteDescription=function(){return this._ontrackpoly||(this._ontrackpoly=s=>{s.stream.addEventListener("addtrack",r=>{let a;i.RTCPeerConnection.prototype.getReceivers?a=this.getReceivers().find(c=>c.track&&c.track.id===r.track.id):a={track:r.track};let o=new Event("track");o.track=r.track,o.receiver=a,o.transceiver={receiver:a},o.streams=[s.stream],this.dispatchEvent(o)}),s.stream.getTracks().forEach(r=>{let a;i.RTCPeerConnection.prototype.getReceivers?a=this.getReceivers().find(c=>c.track&&c.track.id===r.id):a={track:r};let o=new Event("track");o.track=r,o.receiver=a,o.transceiver={receiver:a},o.streams=[s.stream],this.dispatchEvent(o)})},this.addEventListener("addstream",this._ontrackpoly)),t.apply(this,arguments)}}else qt(i,"track",t=>(t.transceiver||Object.defineProperty(t,"transceiver",{value:{receiver:t.receiver}}),t))}function jd(i){if(typeof i=="object"&&i.RTCPeerConnection&&!("getSenders"in i.RTCPeerConnection.prototype)&&"createDTMFSender"in i.RTCPeerConnection.prototype){let e=function(s,r){return{track:r,get dtmf(){return this._dtmf===void 0&&(r.kind==="audio"?this._dtmf=s.createDTMFSender(r):this._dtmf=null),this._dtmf},_pc:s}};if(!i.RTCPeerConnection.prototype.getSenders){i.RTCPeerConnection.prototype.getSenders=function(){return this._senders=this._senders||[],this._senders.slice()};let s=i.RTCPeerConnection.prototype.addTrack;i.RTCPeerConnection.prototype.addTrack=function(o,c){let d=s.apply(this,arguments);return d||(d=e(this,o),this._senders.push(d)),d};let r=i.RTCPeerConnection.prototype.removeTrack;i.RTCPeerConnection.prototype.removeTrack=function(o){r.apply(this,arguments);let c=this._senders.indexOf(o);c!==-1&&this._senders.splice(c,1)}}let t=i.RTCPeerConnection.prototype.addStream;i.RTCPeerConnection.prototype.addStream=function(r){this._senders=this._senders||[],t.apply(this,[r]),r.getTracks().forEach(a=>{this._senders.push(e(this,a))})};let n=i.RTCPeerConnection.prototype.removeStream;i.RTCPeerConnection.prototype.removeStream=function(r){this._senders=this._senders||[],n.apply(this,[r]),r.getTracks().forEach(a=>{let o=this._senders.find(c=>c.track===a);o&&this._senders.splice(this._senders.indexOf(o),1)})}}else if(typeof i=="object"&&i.RTCPeerConnection&&"getSenders"in i.RTCPeerConnection.prototype&&"createDTMFSender"in i.RTCPeerConnection.prototype&&i.RTCRtpSender&&!("dtmf"in i.RTCRtpSender.prototype)){let e=i.RTCPeerConnection.prototype.getSenders;i.RTCPeerConnection.prototype.getSenders=function(){let n=e.apply(this,[]);return n.forEach(s=>s._pc=this),n},Object.defineProperty(i.RTCRtpSender.prototype,"dtmf",{get(){return this._dtmf===void 0&&(this.track.kind==="audio"?this._dtmf=this._pc.createDTMFSender(this.track):this._dtmf=null),this._dtmf}})}}function qd(i,e){if(e.version>=67||!(typeof i=="object"&&i.RTCPeerConnection&&i.RTCRtpSender&&i.RTCRtpReceiver))return;if(!("getStats"in i.RTCRtpSender.prototype)){let n=i.RTCPeerConnection.prototype.getSenders;n&&(i.RTCPeerConnection.prototype.getSenders=function(){let a=n.apply(this,[]);return a.forEach(o=>o._pc=this),a});let s=i.RTCPeerConnection.prototype.addTrack;s&&(i.RTCPeerConnection.prototype.addTrack=function(){let a=s.apply(this,arguments);return a._pc=this,a}),i.RTCRtpSender.prototype.getStats=function(){let a=this;return this._pc.getStats().then(o=>Bo(o,a.track,!0))}}if(!("getStats"in i.RTCRtpReceiver.prototype)){let n=i.RTCPeerConnection.prototype.getReceivers;n&&(i.RTCPeerConnection.prototype.getReceivers=function(){let r=n.apply(this,[]);return r.forEach(a=>a._pc=this),r}),qt(i,"track",s=>(s.receiver._pc=s.srcElement,s)),i.RTCRtpReceiver.prototype.getStats=function(){let r=this;return this._pc.getStats().then(a=>Bo(a,r.track,!1))}}if(!("getStats"in i.RTCRtpSender.prototype&&"getStats"in i.RTCRtpReceiver.prototype))return;let t=i.RTCPeerConnection.prototype.getStats;i.RTCPeerConnection.prototype.getStats=function(){if(arguments.length>0&&arguments[0]instanceof i.MediaStreamTrack){let s=arguments[0],r,a,o;return this.getSenders().forEach(c=>{c.track===s&&(r?o=!0:r=c)}),this.getReceivers().forEach(c=>(c.track===s&&(a?o=!0:a=c),c.track===s)),o||r&&a?Promise.reject(new DOMException("There are more than one sender or receiver for the track.","InvalidAccessError")):r?r.getStats():a?a.getStats():Promise.reject(new DOMException("There is no sender or receiver for the track.","InvalidAccessError"))}return t.apply(this,arguments)}}function Vd(i){i.RTCPeerConnection.prototype.getLocalStreams=function(){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},Object.keys(this._shimmedLocalStreams).map(a=>this._shimmedLocalStreams[a][0])};let e=i.RTCPeerConnection.prototype.addTrack;i.RTCPeerConnection.prototype.addTrack=function(a,o){if(!o)return e.apply(this,arguments);this._shimmedLocalStreams=this._shimmedLocalStreams||{};let c=e.apply(this,arguments);return this._shimmedLocalStreams[o.id]?this._shimmedLocalStreams[o.id].indexOf(c)===-1&&this._shimmedLocalStreams[o.id].push(c):this._shimmedLocalStreams[o.id]=[o,c],c};let t=i.RTCPeerConnection.prototype.addStream;i.RTCPeerConnection.prototype.addStream=function(a){this._shimmedLocalStreams=this._shimmedLocalStreams||{},a.getTracks().forEach(d=>{if(this.getSenders().find(u=>u.track===d))throw new DOMException("Track already exists.","InvalidAccessError")});let o=this.getSenders();t.apply(this,arguments);let c=this.getSenders().filter(d=>o.indexOf(d)===-1);this._shimmedLocalStreams[a.id]=[a].concat(c)};let n=i.RTCPeerConnection.prototype.removeStream;i.RTCPeerConnection.prototype.removeStream=function(a){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},delete this._shimmedLocalStreams[a.id],n.apply(this,arguments)};let s=i.RTCPeerConnection.prototype.removeTrack;i.RTCPeerConnection.prototype.removeTrack=function(a){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},a&&Object.keys(this._shimmedLocalStreams).forEach(o=>{let c=this._shimmedLocalStreams[o].indexOf(a);c!==-1&&this._shimmedLocalStreams[o].splice(c,1),this._shimmedLocalStreams[o].length===1&&delete this._shimmedLocalStreams[o]}),s.apply(this,arguments)}}function Wd(i,e){if(!i.RTCPeerConnection)return;if(i.RTCPeerConnection.prototype.addTrack&&e.version>=65)return Vd(i);let t=i.RTCPeerConnection.prototype.getLocalStreams;i.RTCPeerConnection.prototype.getLocalStreams=function(){let l=t.apply(this);return this._reverseStreams=this._reverseStreams||{},l.map(u=>this._reverseStreams[u.id])};let n=i.RTCPeerConnection.prototype.addStream;i.RTCPeerConnection.prototype.addStream=function(l){if(this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},l.getTracks().forEach(u=>{if(this.getSenders().find(m=>m.track===u))throw new DOMException("Track already exists.","InvalidAccessError")}),!this._reverseStreams[l.id]){let u=new i.MediaStream(l.getTracks());this._streams[l.id]=u,this._reverseStreams[u.id]=l,l=u}n.apply(this,[l])};let s=i.RTCPeerConnection.prototype.removeStream;i.RTCPeerConnection.prototype.removeStream=function(l){this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},s.apply(this,[this._streams[l.id]||l]),delete this._reverseStreams[this._streams[l.id]?this._streams[l.id].id:l.id],delete this._streams[l.id]},i.RTCPeerConnection.prototype.addTrack=function(l,u){if(this.signalingState==="closed")throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");let h=[].slice.call(arguments,1);if(h.length!==1||!h[0].getTracks().find(p=>p===l))throw new DOMException("The adapter.js addTrack polyfill only supports a single stream which is associated with the specified track.","NotSupportedError");if(this.getSenders().find(p=>p.track===l))throw new DOMException("Track already exists.","InvalidAccessError");this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{};let g=this._streams[u.id];if(g)g.addTrack(l),Promise.resolve().then(()=>{this.dispatchEvent(new Event("negotiationneeded"))});else{let p=new i.MediaStream([l]);this._streams[u.id]=p,this._reverseStreams[p.id]=u,this.addStream(p)}return this.getSenders().find(p=>p.track===l)};function r(d,l){let u=l.sdp;return Object.keys(d._reverseStreams||[]).forEach(h=>{let m=d._reverseStreams[h],g=d._streams[m.id];u=u.replace(new RegExp(g.id,"g"),m.id)}),new RTCSessionDescription({type:l.type,sdp:u})}function a(d,l){let u=l.sdp;return Object.keys(d._reverseStreams||[]).forEach(h=>{let m=d._reverseStreams[h],g=d._streams[m.id];u=u.replace(new RegExp(m.id,"g"),g.id)}),new RTCSessionDescription({type:l.type,sdp:u})}["createOffer","createAnswer"].forEach(function(d){let l=i.RTCPeerConnection.prototype[d],u={[d](){let h=arguments;return arguments.length&&typeof arguments[0]=="function"?l.apply(this,[g=>{let p=r(this,g);h[0].apply(null,[p])},g=>{h[1]&&h[1].apply(null,g)},arguments[2]]):l.apply(this,arguments).then(g=>r(this,g))}};i.RTCPeerConnection.prototype[d]=u[d]});let o=i.RTCPeerConnection.prototype.setLocalDescription;i.RTCPeerConnection.prototype.setLocalDescription=function(){return!arguments.length||!arguments[0].type?o.apply(this,arguments):(arguments[0]=a(this,arguments[0]),o.apply(this,arguments))};let c=Object.getOwnPropertyDescriptor(i.RTCPeerConnection.prototype,"localDescription");Object.defineProperty(i.RTCPeerConnection.prototype,"localDescription",{get(){let d=c.get.apply(this);return d.type===""?d:r(this,d)}}),i.RTCPeerConnection.prototype.removeTrack=function(l){if(this.signalingState==="closed")throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");if(!l._pc)throw new DOMException("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.","TypeError");if(!(l._pc===this))throw new DOMException("Sender was not created by this connection.","InvalidAccessError");this._streams=this._streams||{};let h;Object.keys(this._streams).forEach(m=>{this._streams[m].getTracks().find(p=>l.track===p)&&(h=this._streams[m])}),h&&(h.getTracks().length===1?this.removeStream(this._reverseStreams[h.id]):h.removeTrack(l.track),this.dispatchEvent(new Event("negotiationneeded")))}}function pr(i,e){!i.RTCPeerConnection&&i.webkitRTCPeerConnection&&(i.RTCPeerConnection=i.webkitRTCPeerConnection),i.RTCPeerConnection&&e.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){let n=i.RTCPeerConnection.prototype[t],s={[t](){return arguments[0]=new(t==="addIceCandidate"?i.RTCIceCandidate:i.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};i.RTCPeerConnection.prototype[t]=s[t]})}function Hd(i,e){e.version>102||qt(i,"negotiationneeded",t=>{let n=t.target;if(!((e.version<72||n.getConfiguration&&n.getConfiguration().sdpSemantics==="plan-b")&&n.signalingState!=="stable"))return t})}var qo=Object.freeze({__proto__:null,fixNegotiationNeeded:Hd,shimAddTrackRemoveTrack:Wd,shimAddTrackRemoveTrackWithNative:Vd,shimGetSendersWithDtmf:jd,shimGetUserMedia:Ud,shimMediaStream:Fd,shimOnTrack:Bd,shimPeerConnection:pr,shimSenderReceiverGetStats:qd});function Kd(i,e){let t=i&&i.navigator;if(!t.mediaDevices)return;let n=i&&i.MediaStreamTrack;if(t.getUserMedia=function(s,r,a){to("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),t.mediaDevices.getUserMedia(s).then(r,a)},!(e.version>55&&"autoGainControl"in t.mediaDevices.getSupportedConstraints())){let s=function(a,o,c){o in a&&!(c in a)&&(a[c]=a[o],delete a[o])},r=t.mediaDevices.getUserMedia.bind(t.mediaDevices);if(t.mediaDevices.getUserMedia=function(a){return typeof a=="object"&&typeof a.audio=="object"&&(a=JSON.parse(JSON.stringify(a)),s(a.audio,"autoGainControl","mozAutoGainControl"),s(a.audio,"noiseSuppression","mozNoiseSuppression")),r(a)},n&&n.prototype.getSettings){let a=n.prototype.getSettings;n.prototype.getSettings=function(){let o=a.apply(this,arguments);return s(o,"mozAutoGainControl","autoGainControl"),s(o,"mozNoiseSuppression","noiseSuppression"),o}}if(n&&n.prototype.applyConstraints){let a=n.prototype.applyConstraints;n.prototype.applyConstraints=function(o){return this.kind==="audio"&&typeof o=="object"&&(o=JSON.parse(JSON.stringify(o)),s(o,"autoGainControl","mozAutoGainControl"),s(o,"noiseSuppression","mozNoiseSuppression")),a.apply(this,[o])}}}}function rm(i,e){i.navigator.mediaDevices&&(i.navigator.mediaDevices&&"getDisplayMedia"in i.navigator.mediaDevices||(i.navigator.mediaDevices.getDisplayMedia=function(n){if(!(n&&n.video)){let s=new DOMException("getDisplayMedia without video constraints is undefined");return s.name="NotFoundError",s.code=8,Promise.reject(s)}return n.video===!0?n.video={mediaSource:e}:n.video.mediaSource=e,i.navigator.mediaDevices.getUserMedia(n)}))}function Gd(i){typeof i=="object"&&i.RTCTrackEvent&&"receiver"in i.RTCTrackEvent.prototype&&!("transceiver"in i.RTCTrackEvent.prototype)&&Object.defineProperty(i.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function gr(i,e){typeof i!="object"||!(i.RTCPeerConnection||i.mozRTCPeerConnection)||(!i.RTCPeerConnection&&i.mozRTCPeerConnection&&(i.RTCPeerConnection=i.mozRTCPeerConnection),e.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){let n=i.RTCPeerConnection.prototype[t],s={[t](){return arguments[0]=new(t==="addIceCandidate"?i.RTCIceCandidate:i.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};i.RTCPeerConnection.prototype[t]=s[t]}))}function zd(i,e){if(typeof i!="object"||!(i.RTCPeerConnection||i.mozRTCPeerConnection)||e.version>=151)return;let t={inboundrtp:"inbound-rtp",outboundrtp:"outbound-rtp",candidatepair:"candidate-pair",localcandidate:"local-candidate",remotecandidate:"remote-candidate"},n=i.RTCPeerConnection.prototype.getStats;i.RTCPeerConnection.prototype.getStats=function(){let r=Array.prototype.slice.call(arguments),a=r[0],o=r[1],c=r[2];return this.signalingState==="closed"?Promise.resolve(new Map):n.apply(this,[a||null]).then(d=>{if(e.version<53&&!o)try{d.forEach(l=>{l.type=t[l.type]||l.type})}catch(l){if(l.name!=="TypeError")throw l;d.forEach((u,h)=>{d.set(h,Object.assign({},u,{type:t[u.type]||u.type}))})}return d}).then(o,c)}}function Jd(i){if(!(typeof i=="object"&&i.RTCPeerConnection&&i.RTCRtpSender)||i.RTCRtpSender&&"getStats"in i.RTCRtpSender.prototype)return;let e=i.RTCPeerConnection.prototype.getSenders;e&&(i.RTCPeerConnection.prototype.getSenders=function(){let s=e.apply(this,[]);return s.forEach(r=>r._pc=this),s});let t=i.RTCPeerConnection.prototype.addTrack;t&&(i.RTCPeerConnection.prototype.addTrack=function(){let s=t.apply(this,arguments);return s._pc=this,s}),i.RTCRtpSender.prototype.getStats=function(){return this.track?this._pc.getStats(this.track):Promise.resolve(new Map)}}function Qd(i){if(!(typeof i=="object"&&i.RTCPeerConnection&&i.RTCRtpSender)||i.RTCRtpSender&&"getStats"in i.RTCRtpReceiver.prototype)return;let e=i.RTCPeerConnection.prototype.getReceivers;e&&(i.RTCPeerConnection.prototype.getReceivers=function(){let n=e.apply(this,[]);return n.forEach(s=>s._pc=this),n}),qt(i,"track",t=>(t.receiver._pc=t.srcElement,t)),i.RTCRtpReceiver.prototype.getStats=function(){return this._pc.getStats(this.track)}}function Yd(i){!i.RTCPeerConnection||"removeStream"in i.RTCPeerConnection.prototype||(i.RTCPeerConnection.prototype.removeStream=function(t){to("removeStream","removeTrack"),this.getSenders().forEach(n=>{n.track&&t.getTracks().includes(n.track)&&this.removeTrack(n)})})}function $d(i){i.DataChannel&&!i.RTCDataChannel&&(i.RTCDataChannel=i.DataChannel)}function Xd(i,e){if(!(typeof i=="object"&&i.RTCPeerConnection)||e.version>=110)return;let t=i.RTCPeerConnection.prototype.addTransceiver;t&&(i.RTCPeerConnection.prototype.addTransceiver=function(){this.setParametersPromises=[];let s=arguments[1]&&arguments[1].sendEncodings;s===void 0&&(s=[]),s=[...s];let r=s.length>0;r&&s.forEach(o=>{if("rid"in o&&!/^[a-z0-9]{0,16}$/i.test(o.rid))throw new TypeError("Invalid RID value provided.");if("scaleResolutionDownBy"in o&&!(parseFloat(o.scaleResolutionDownBy)>=1))throw new RangeError("scale_resolution_down_by must be >= 1.0");if("maxFramerate"in o&&!(parseFloat(o.maxFramerate)>=0))throw new RangeError("max_framerate must be >= 0.0")});let a=t.apply(this,arguments);if(r){let o=a.sender,c=o.getParameters();(!("encodings"in c)||c.encodings.length===1&&Object.keys(c.encodings[0]).length===0)&&(c.encodings=s,o.sendEncodings=s,this.setParametersPromises.push(o.setParameters(c).then(()=>{delete o.sendEncodings}).catch(()=>{delete o.sendEncodings})))}return a})}function Zd(i,e){if(!(typeof i=="object"&&i.RTCRtpSender)||e.version>=110)return;let t=i.RTCRtpSender.prototype.getParameters;t&&(i.RTCRtpSender.prototype.getParameters=function(){let s=t.apply(this,arguments);return"encodings"in s||(s.encodings=[].concat(this.sendEncodings||[{}])),s})}function el(i,e){if(!(typeof i=="object"&&i.RTCPeerConnection)||e.version>=110)return;let t=i.RTCPeerConnection.prototype.createOffer;i.RTCPeerConnection.prototype.createOffer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(()=>t.apply(this,arguments)).finally(()=>{this.setParametersPromises=[]}):t.apply(this,arguments)}}function tl(i,e){if(!(typeof i=="object"&&i.RTCPeerConnection)||e.version>=110)return;let t=i.RTCPeerConnection.prototype.createAnswer;i.RTCPeerConnection.prototype.createAnswer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(()=>t.apply(this,arguments)).finally(()=>{this.setParametersPromises=[]}):t.apply(this,arguments)}}var Vo=Object.freeze({__proto__:null,shimAddTransceiver:Xd,shimCreateAnswer:tl,shimCreateOffer:el,shimGetDisplayMedia:rm,shimGetParameters:Zd,shimGetStats:zd,shimGetUserMedia:Kd,shimOnTrack:Gd,shimPeerConnection:gr,shimRTCDataChannel:$d,shimReceiverGetStats:Qd,shimRemoveStream:Yd,shimSenderGetStats:Jd});function nl(i){if(!(typeof i!="object"||!i.RTCPeerConnection)){if("getLocalStreams"in i.RTCPeerConnection.prototype||(i.RTCPeerConnection.prototype.getLocalStreams=function(){return this._localStreams||(this._localStreams=[]),this._localStreams}),!("addStream"in i.RTCPeerConnection.prototype)){let e=i.RTCPeerConnection.prototype.addTrack;i.RTCPeerConnection.prototype.addStream=function(n){this._localStreams||(this._localStreams=[]),this._localStreams.includes(n)||this._localStreams.push(n),n.getAudioTracks().forEach(s=>e.call(this,s,n)),n.getVideoTracks().forEach(s=>e.call(this,s,n))},i.RTCPeerConnection.prototype.addTrack=function(n){for(var s=arguments.length,r=new Array(s>1?s-1:0),a=1;a{this._localStreams?this._localStreams.includes(o)||this._localStreams.push(o):this._localStreams=[o]}),e.apply(this,arguments)}}"removeStream"in i.RTCPeerConnection.prototype||(i.RTCPeerConnection.prototype.removeStream=function(t){this._localStreams||(this._localStreams=[]);let n=this._localStreams.indexOf(t);if(n===-1)return;this._localStreams.splice(n,1);let s=t.getTracks();this.getSenders().forEach(r=>{s.includes(r.track)&&this.removeTrack(r)})})}}function il(i){if(!(typeof i!="object"||!i.RTCPeerConnection)&&("getRemoteStreams"in i.RTCPeerConnection.prototype||(i.RTCPeerConnection.prototype.getRemoteStreams=function(){return this._remoteStreams?this._remoteStreams:[]}),!("onaddstream"in i.RTCPeerConnection.prototype))){Object.defineProperty(i.RTCPeerConnection.prototype,"onaddstream",{get(){return this._onaddstream},set(t){this._onaddstream&&(this.removeEventListener("addstream",this._onaddstream),this.removeEventListener("track",this._onaddstreampoly)),this.addEventListener("addstream",this._onaddstream=t),this.addEventListener("track",this._onaddstreampoly=n=>{n.streams.forEach(s=>{if(this._remoteStreams||(this._remoteStreams=[]),this._remoteStreams.includes(s))return;this._remoteStreams.push(s);let r=new Event("addstream");r.stream=s,this.dispatchEvent(r)})})}});let e=i.RTCPeerConnection.prototype.setRemoteDescription;i.RTCPeerConnection.prototype.setRemoteDescription=function(){let n=this;return this._onaddstreampoly||this.addEventListener("track",this._onaddstreampoly=function(s){s.streams.forEach(r=>{if(n._remoteStreams||(n._remoteStreams=[]),n._remoteStreams.indexOf(r)>=0)return;n._remoteStreams.push(r);let a=new Event("addstream");a.stream=r,n.dispatchEvent(a)})}),e.apply(n,arguments)}}}function sl(i){if(typeof i!="object"||!i.RTCPeerConnection)return;let e=i.RTCPeerConnection.prototype,t=e.createOffer,n=e.createAnswer,s=e.setLocalDescription,r=e.setRemoteDescription,a=e.addIceCandidate;e.createOffer=function(d,l){let u=arguments.length>=2?arguments[2]:arguments[0],h=t.apply(this,[u]);return l?(h.then(d,l),Promise.resolve()):h},e.createAnswer=function(d,l){let u=arguments.length>=2?arguments[2]:arguments[0],h=n.apply(this,[u]);return l?(h.then(d,l),Promise.resolve()):h};let o=function(c,d,l){let u=s.apply(this,[c]);return l?(u.then(d,l),Promise.resolve()):u};e.setLocalDescription=o,o=function(c,d,l){let u=r.apply(this,[c]);return l?(u.then(d,l),Promise.resolve()):u},e.setRemoteDescription=o,o=function(c,d,l){let u=a.apply(this,[c]);return l?(u.then(d,l),Promise.resolve()):u},e.addIceCandidate=o}function rl(i){let e=i&&i.navigator;if(e.mediaDevices&&e.mediaDevices.getUserMedia){let t=e.mediaDevices,n=t.getUserMedia.bind(t);e.mediaDevices.getUserMedia=s=>n(al(s))}!e.getUserMedia&&e.mediaDevices&&e.mediaDevices.getUserMedia&&(e.getUserMedia=function(n,s,r){e.mediaDevices.getUserMedia(n).then(s,r)}.bind(e))}function al(i){return i&&i.video!==void 0?Object.assign({},i,{video:xd(i.video)}):i}function ol(i){if(!i.RTCPeerConnection)return;let e=i.RTCPeerConnection;i.RTCPeerConnection=function(n,s){if(n&&n.iceServers){let r=[];for(let a=0;aa.receiver.track.kind==="audio");n.offerToReceiveAudio===!1&&s?s.direction==="sendrecv"?s.setDirection?s.setDirection("sendonly"):s.direction="sendonly":s.direction==="recvonly"&&(s.setDirection?s.setDirection("inactive"):s.direction="inactive"):n.offerToReceiveAudio===!0&&!s&&this.addTransceiver("audio",{direction:"recvonly"}),typeof n.offerToReceiveVideo<"u"&&(n.offerToReceiveVideo=!!n.offerToReceiveVideo);let r=this.getTransceivers().find(a=>a.receiver.track.kind==="video");n.offerToReceiveVideo===!1&&r?r.direction==="sendrecv"?r.setDirection?r.setDirection("sendonly"):r.direction="sendonly":r.direction==="recvonly"&&(r.setDirection?r.setDirection("inactive"):r.direction="inactive"):n.offerToReceiveVideo===!0&&!r&&this.addTransceiver("video",{direction:"recvonly"})}return e.apply(this,arguments)}}function ll(i){typeof i!="object"||i.AudioContext||(i.AudioContext=i.webkitAudioContext)}var Wo=Object.freeze({__proto__:null,shimAudioContext:ll,shimCallbacksAPI:sl,shimConstraints:al,shimCreateOfferLegacy:dl,shimGetUserMedia:rl,shimLocalStreamsAPI:nl,shimRTCIceServerUrls:ol,shimRemoteStreamsAPI:il,shimTrackEventTransceiver:cl}),Ps={exports:{}},Ho;function am(){return Ho||(Ho=1,function(i){let e={};e.generateIdentifier=function(){return Math.random().toString(36).substring(2,12)},e.localCName=e.generateIdentifier(),e.splitLines=function(t){return t.trim().split(` +`).map(n=>n.trim())},e.splitSections=function(t){return t.split(` +m=`).map((s,r)=>(r>0?"m="+s:s).trim()+`\r +`)},e.getDescription=function(t){let n=e.splitSections(t);return n&&n[0]},e.getMediaSections=function(t){let n=e.splitSections(t);return n.shift(),n},e.matchPrefix=function(t,n){return e.splitLines(t).filter(s=>s.indexOf(n)===0)},e.parseCandidate=function(t){let n;t.indexOf("a=candidate:")===0?n=t.substring(12).split(" "):n=t.substring(10).split(" ");let s={foundation:n[0],component:{1:"rtp",2:"rtcp"}[n[1]]||n[1],protocol:n[2].toLowerCase(),priority:parseInt(n[3],10),ip:n[4],address:n[4],port:parseInt(n[5],10),type:n[7]};for(let r=8;r0?n[0].split("/")[1]:"sendrecv",uri:n[1],attributes:n.slice(2).join(" ")}},e.writeExtmap=function(t){return"a=extmap:"+(t.id||t.preferredId)+(t.direction&&t.direction!=="sendrecv"?"/"+t.direction:"")+" "+t.uri+(t.attributes?" "+t.attributes:"")+`\r +`},e.parseFmtp=function(t){let n={},s,r=t.substring(t.indexOf(" ")+1).split(";");for(let a=0;a{t.parameters[a]!==void 0?r.push(a+"="+t.parameters[a]):r.push(a)}),n+="a=fmtp:"+s+" "+r.join(";")+`\r +`}return n},e.parseRtcpFb=function(t){let n=t.substring(t.indexOf(" ")+1).split(" ");return{type:n.shift(),parameter:n.join(" ")}},e.writeRtcpFb=function(t){let n="",s=t.payloadType;return t.preferredPayloadType!==void 0&&(s=t.preferredPayloadType),t.rtcpFeedback&&t.rtcpFeedback.length&&t.rtcpFeedback.forEach(r=>{n+="a=rtcp-fb:"+s+" "+r.type+(r.parameter&&r.parameter.length?" "+r.parameter:"")+`\r +`}),n},e.parseSsrcMedia=function(t){let n=t.indexOf(" "),s={ssrc:parseInt(t.substring(7,n),10)},r=t.indexOf(":",n);return r>-1?(s.attribute=t.substring(n+1,r),s.value=t.substring(r+1)):s.attribute=t.substring(n+1),s},e.parseSsrcGroup=function(t){let n=t.substring(13).split(" ");return{semantics:n.shift(),ssrcs:n.map(s=>parseInt(s,10))}},e.getMid=function(t){let n=e.matchPrefix(t,"a=mid:")[0];if(n)return n.substring(6)},e.parseFingerprint=function(t){let n=t.substring(14).split(" ");return{algorithm:n[0].toLowerCase(),value:n[1].toUpperCase()}},e.getDtlsParameters=function(t,n){return{role:"auto",fingerprints:e.matchPrefix(t+n,"a=fingerprint:").map(e.parseFingerprint)}},e.writeDtlsParameters=function(t,n){let s="a=setup:"+n+`\r +`;return t.fingerprints.forEach(r=>{s+="a=fingerprint:"+r.algorithm+" "+r.value+`\r +`}),s},e.parseCryptoLine=function(t){let n=t.substring(9).split(" ");return{tag:parseInt(n[0],10),cryptoSuite:n[1],keyParams:n[2],sessionParams:n.slice(3)}},e.writeCryptoLine=function(t){return"a=crypto:"+t.tag+" "+t.cryptoSuite+" "+(typeof t.keyParams=="object"?e.writeCryptoKeyParams(t.keyParams):t.keyParams)+(t.sessionParams?" "+t.sessionParams.join(" "):"")+`\r +`},e.parseCryptoKeyParams=function(t){if(t.indexOf("inline:")!==0)return null;let n=t.substring(7).split("|");return{keyMethod:"inline",keySalt:n[0],lifeTime:n[1],mkiValue:n[2]?n[2].split(":")[0]:void 0,mkiLength:n[2]?n[2].split(":")[1]:void 0}},e.writeCryptoKeyParams=function(t){return t.keyMethod+":"+t.keySalt+(t.lifeTime?"|"+t.lifeTime:"")+(t.mkiValue&&t.mkiLength?"|"+t.mkiValue+":"+t.mkiLength:"")},e.getCryptoParameters=function(t,n){return e.matchPrefix(t+n,"a=crypto:").map(e.parseCryptoLine)},e.getIceParameters=function(t,n){let s=e.matchPrefix(t+n,"a=ice-ufrag:")[0],r=e.matchPrefix(t+n,"a=ice-pwd:")[0];return s&&r?{usernameFragment:s.substring(12),password:r.substring(10)}:null},e.writeIceParameters=function(t){let n="a=ice-ufrag:"+t.usernameFragment+`\r +a=ice-pwd:`+t.password+`\r +`;return t.iceLite&&(n+=`a=ice-lite\r +`),n},e.parseRtpParameters=function(t){let n={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=e.splitLines(t)[0].split(" ");n.profile=r[2];for(let o=3;o{n.headerExtensions.push(e.parseExtmap(o))});let a=e.matchPrefix(t,"a=rtcp-fb:* ").map(e.parseRtcpFb);return n.codecs.forEach(o=>{a.forEach(c=>{o.rtcpFeedback.find(l=>l.type===c.type&&l.parameter===c.parameter)||o.rtcpFeedback.push(c)})}),n},e.writeRtpDescription=function(t,n){let s="";s+="m="+t+" ",s+=n.codecs.length>0?"9":"0",s+=" "+(n.profile||"UDP/TLS/RTP/SAVPF")+" ",s+=n.codecs.map(a=>a.preferredPayloadType!==void 0?a.preferredPayloadType:a.payloadType).join(" ")+`\r +`,s+=`c=IN IP4 0.0.0.0\r +`,s+=`a=rtcp:9 IN IP4 0.0.0.0\r +`,n.codecs.forEach(a=>{s+=e.writeRtpMap(a),s+=e.writeFmtp(a),s+=e.writeRtcpFb(a)});let r=0;return n.codecs.forEach(a=>{a.maxptime>r&&(r=a.maxptime)}),r>0&&(s+="a=maxptime:"+r+`\r +`),n.headerExtensions&&n.headerExtensions.forEach(a=>{s+=e.writeExtmap(a)}),s},e.parseRtpEncodingParameters=function(t){let n=[],s=e.parseRtpParameters(t),r=s.fecMechanisms.indexOf("RED")!==-1,a=s.fecMechanisms.indexOf("ULPFEC")!==-1,o=e.matchPrefix(t,"a=ssrc:").map(h=>e.parseSsrcMedia(h)).filter(h=>h.attribute==="cname"),c=o.length>0&&o[0].ssrc,d,l=e.matchPrefix(t,"a=ssrc-group:FID").map(h=>h.substring(17).split(" ").map(g=>parseInt(g,10)));l.length>0&&l[0].length>1&&l[0][0]===c&&(d=l[0][1]),s.codecs.forEach(h=>{if(h.name.toUpperCase()==="RTX"&&h.parameters.apt){let m={ssrc:c,codecPayloadType:parseInt(h.parameters.apt,10)};c&&d&&(m.rtx={ssrc:d}),n.push(m),r&&(m=JSON.parse(JSON.stringify(m)),m.fec={ssrc:c,mechanism:a?"red+ulpfec":"red"},n.push(m))}}),n.length===0&&c&&n.push({ssrc:c});let u=e.matchPrefix(t,"b=");return u.length&&(u[0].indexOf("b=TIAS:")===0?u=parseInt(u[0].substring(7),10):u[0].indexOf("b=AS:")===0?u=parseInt(u[0].substring(5),10)*1e3*.95-50*40*8:u=void 0,n.forEach(h=>{h.maxBitrate=u})),n},e.parseRtcpParameters=function(t){let n={},s=e.matchPrefix(t,"a=ssrc:").map(o=>e.parseSsrcMedia(o)).filter(o=>o.attribute==="cname")[0];s&&(n.cname=s.value,n.ssrc=s.ssrc);let r=e.matchPrefix(t,"a=rtcp-rsize");n.reducedSize=r.length>0,n.compound=r.length===0;let a=e.matchPrefix(t,"a=rtcp-mux");return n.mux=a.length>0,n},e.writeRtcpParameters=function(t){let n="";return t.reducedSize&&(n+=`a=rtcp-rsize\r +`),t.mux&&(n+=`a=rtcp-mux\r +`),t.ssrc!==void 0&&t.cname&&(n+="a=ssrc:"+t.ssrc+" cname:"+t.cname+`\r +`),n},e.parseMsid=function(t){let n,s=e.matchPrefix(t,"a=msid:");if(s.length===1)return n=s[0].substring(7).split(" "),{stream:n[0],track:n[1]};let r=e.matchPrefix(t,"a=ssrc:").map(a=>e.parseSsrcMedia(a)).filter(a=>a.attribute==="msid");if(r.length>0)return n=r[0].value.split(" "),{stream:n[0],track:n[1]}},e.parseSctpDescription=function(t){let n=e.parseMLine(t),s=e.matchPrefix(t,"a=max-message-size:"),r;s.length>0&&(r=parseInt(s[0].substring(19),10)),isNaN(r)&&(r=65536);let a=e.matchPrefix(t,"a=sctp-port:");if(a.length>0)return{port:parseInt(a[0].substring(12),10),protocol:n.fmt,maxMessageSize:r};let o=e.matchPrefix(t,"a=sctpmap:");if(o.length>0){let c=o[0].substring(10).split(" ");return{port:parseInt(c[0],10),protocol:c[1],maxMessageSize:r}}},e.writeSctpDescription=function(t,n){let s=[];return t.protocol!=="DTLS/SCTP"?s=["m="+t.kind+" 9 "+t.protocol+" "+n.protocol+`\r +`,`c=IN IP4 0.0.0.0\r +`,"a=sctp-port:"+n.port+`\r +`]:s=["m="+t.kind+" 9 "+t.protocol+" "+n.port+`\r +`,`c=IN IP4 0.0.0.0\r +`,"a=sctpmap:"+n.port+" "+n.protocol+` 65535\r +`],n.maxMessageSize!==void 0&&s.push("a=max-message-size:"+n.maxMessageSize+`\r +`),s.join("")},e.generateSessionId=function(){return Math.random().toString().substr(2,22)},e.writeSessionBoilerplate=function(t,n,s){let r,a=n!==void 0?n:2;return t?r=t:r=e.generateSessionId(),`v=0\r +o=`+(s||"thisisadapterortc")+" "+r+" "+a+` IN IP4 127.0.0.1\r +s=-\r +t=0 0\r +`},e.getDirection=function(t,n){let s=e.splitLines(t);for(let r=0;r(t.candidate&&Object.defineProperty(t,"candidate",{value:new i.RTCIceCandidate(t.candidate),writable:"false"}),t))}function vr(i){!i.RTCIceCandidate||i.RTCIceCandidate&&"relayProtocol"in i.RTCIceCandidate.prototype||qt(i,"icecandidate",e=>{if(e.candidate){let t=Qt.parseCandidate(e.candidate.candidate);t.type==="relay"&&(e.candidate.relayProtocol={0:"tls",1:"tcp",2:"udp"}[t.priority>>24])}return e})}function hi(i,e){if(!i.RTCPeerConnection||e.browser==="chrome"&&e.version>102||e.browser==="firefox"&&e.version>=113)return;"sctp"in i.RTCPeerConnection.prototype||Object.defineProperty(i.RTCPeerConnection.prototype,"sctp",{get(){return typeof this._sctp>"u"?null:this._sctp}});let t=function(o){if(!o||!o.sdp)return!1;let c=Qt.splitSections(o.sdp);return c.shift(),c.some(d=>{let l=Qt.parseMLine(d);return l&&l.kind==="application"&&l.protocol.indexOf("SCTP")!==-1})},n=function(o){let c=o.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);if(c===null||c.length<2)return-1;let d=parseInt(c[1],10);return d!==d?-1:d},s=function(o){let c=65536;return e.browser==="firefox"&&(e.version<57?o===-1?c=16384:c=2147483637:e.version<60?c=e.version===57?65535:65536:c=2147483637),c},r=function(o,c){let d=65536;e.browser==="firefox"&&e.version===57&&(d=65535);let l=Qt.matchPrefix(o.sdp,"a=max-message-size:");return l.length>0?d=parseInt(l[0].substring(19),10):e.browser==="firefox"&&c!==-1&&(d=2147483637),d},a=i.RTCPeerConnection.prototype.setRemoteDescription;i.RTCPeerConnection.prototype.setRemoteDescription=function(){if(this._sctp=null,e.browser==="chrome"&&e.version>=76&&this.getConfiguration().sdpSemantics==="plan-b"&&Object.defineProperty(this,"sctp",{get(){return typeof this._sctp>"u"?null:this._sctp},enumerable:!0,configurable:!0}),t(arguments[0])){let c=n(arguments[0]),d=s(c),l=r(arguments[0],c),u;d===0&&l===0?u=Number.POSITIVE_INFINITY:d===0||l===0?u=Math.max(d,l):u=Math.min(d,l);let h={};Object.defineProperty(h,"maxMessageSize",{get(){return u}}),this._sctp=h}return a.apply(this,arguments)}}function mi(i,e){if(!(i.RTCPeerConnection&&"createDataChannel"in i.RTCPeerConnection.prototype)||e.browser==="chrome"&&e.version>=149||e.browser==="firefox"&&e.version>60)return;function t(s,r){let a=s.send;s.send=function(){let c=arguments[0],d=c.length||c.size||c.byteLength;if(s.readyState==="open"&&r.sctp&&d>r.sctp.maxMessageSize)throw new TypeError("Message too large (can send a maximum of "+r.sctp.maxMessageSize+" bytes)");return a.apply(s,arguments)}}let n=i.RTCPeerConnection.prototype.createDataChannel;i.RTCPeerConnection.prototype.createDataChannel=function(){let r=n.apply(this,arguments);return t(r,this),r},qt(i,"datachannel",s=>(t(s.channel,s.target),s))}function br(i){if(!i.RTCPeerConnection||"connectionState"in i.RTCPeerConnection.prototype)return;let e=i.RTCPeerConnection.prototype;Object.defineProperty(e,"connectionState",{get(){return{completed:"connected",checking:"connecting"}[this.iceConnectionState]||this.iceConnectionState},enumerable:!0,configurable:!0}),Object.defineProperty(e,"onconnectionstatechange",{get(){return this._onconnectionstatechange||null},set(t){this._onconnectionstatechange&&(this.removeEventListener("connectionstatechange",this._onconnectionstatechange),delete this._onconnectionstatechange),t&&this.addEventListener("connectionstatechange",this._onconnectionstatechange=t)},enumerable:!0,configurable:!0}),["setLocalDescription","setRemoteDescription"].forEach(t=>{let n=e[t];e[t]=function(){return this._connectionstatechangepoly||(this._connectionstatechangepoly=s=>{let r=s.target;if(r._lastConnectionState!==r.connectionState){r._lastConnectionState=r.connectionState;let a=new Event("connectionstatechange",s);r.dispatchEvent(a)}return s},this.addEventListener("iceconnectionstatechange",this._connectionstatechangepoly)),n.apply(this,arguments)}})}function yr(i,e){if(!i.RTCPeerConnection||e.browser==="chrome"&&e.version>=71||e.browser==="safari"&&e._safariVersion>=13.1)return;let t=i.RTCPeerConnection.prototype.setRemoteDescription;i.RTCPeerConnection.prototype.setRemoteDescription=function(s){if(s&&s.sdp&&s.sdp.indexOf(` +a=extmap-allow-mixed`)!==-1){let r=s.sdp.split(` +`).filter(a=>a.trim()!=="a=extmap-allow-mixed").join(` +`);i.RTCSessionDescription&&s instanceof i.RTCSessionDescription?arguments[0]=new i.RTCSessionDescription({type:s.type,sdp:r}):s.sdp=r}return t.apply(this,arguments)}}function fi(i,e){if(!(i.RTCPeerConnection&&i.RTCPeerConnection.prototype))return;let t=i.RTCPeerConnection.prototype.addIceCandidate;!t||t.length===0||(i.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?(e.browser==="chrome"&&e.version<78||e.browser==="firefox"&&e.version<68||e.browser==="safari")&&arguments[0]&&arguments[0].candidate===""?Promise.resolve():t.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())})}function pi(i,e){if(!(i.RTCPeerConnection&&i.RTCPeerConnection.prototype))return;let t=i.RTCPeerConnection.prototype.setLocalDescription;!t||t.length===0||(i.RTCPeerConnection.prototype.setLocalDescription=function(){let s=arguments[0]||{};if(typeof s!="object"||s.type&&s.sdp)return t.apply(this,arguments);if(s={type:s.type,sdp:s.sdp},!s.type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":s.type="offer";break;default:s.type="answer";break}return s.sdp||s.type!=="offer"&&s.type!=="answer"?t.apply(this,[s]):(s.type==="offer"?this.createOffer:this.createAnswer).apply(this).then(a=>t.apply(this,[a]))})}var cm=Object.freeze({__proto__:null,removeExtmapAllowMixed:yr,shimAddIceCandidateNullOrEmpty:fi,shimConnectionState:br,shimMaxMessageSize:hi,shimParameterlessSetLocalDescription:pi,shimRTCIceCandidate:ui,shimRTCIceCandidateRelayProtocol:vr,shimSendThrowTypeError:mi});function dm(){let i=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},e=i.window,t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{shimChrome:!0,shimFirefox:!0,shimSafari:!0},n=eo,s=sm(e),r={browserDetails:s,commonShim:cm,extractVersion:Sn,disableLog:nm,disableWarnings:im,sdp:om};switch(s.browser){case"chrome":if(!qo||!pr||!t.shimChrome)return n("Chrome shim is not included in this adapter release."),r;if(s.version===null)return n("Chrome shim can not determine version, not shimming."),r;n("adapter.js shimming chrome."),r.browserShim=qo,fi(e,s),pi(e),Ud(e,s),Fd(e),pr(e,s),Bd(e,s),Wd(e,s),jd(e),qd(e,s),Hd(e,s),ui(e),vr(e),br(e),hi(e,s),mi(e,s),yr(e,s);break;case"firefox":if(!Vo||!gr||!t.shimFirefox)return n("Firefox shim is not included in this adapter release."),r;n("adapter.js shimming firefox."),r.browserShim=Vo,fi(e,s),pi(e),Kd(e,s),gr(e,s),zd(e,s),Gd(e),Yd(e),Jd(e),Qd(e),$d(e),Xd(e,s),Zd(e,s),el(e,s),tl(e,s),ui(e),br(e),hi(e,s),mi(e,s);break;case"safari":if(!Wo||!t.shimSafari)return n("Safari shim is not included in this adapter release."),r;n("adapter.js shimming safari."),r.browserShim=Wo,fi(e,s),pi(e),ol(e),dl(e),sl(e),nl(e),il(e),cl(e),rl(e),ll(e),ui(e),vr(e),hi(e,s),mi(e,s),yr(e,s);break;default:n("Unsupported browser!");break}return r}dm({window:typeof window>"u"?void 0:window});var kr,hl,ge=class extends(hl=Promise){constructor(e){super(e)}catch(e){return super.catch(e)}static reject(e){return super.reject(e)}static all(e){return super.all(e)}static race(e){return super.race(e)}};kr=ge;ge.resolve=i=>Reflect.get(hl,"resolve",kr).call(kr,i);var lm=/version\/(\d+(\.?_?\d+)+)/i,Is;function ye(i){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;if(typeof i>"u"&&typeof navigator>"u")return;let t=(i??navigator.userAgent).toLowerCase();if(Is===void 0||e){let n=um.find(s=>s.test.test(t));Is=n?.describe(t)}return Is}var um=[{test:/firefox|iceweasel|fxios/i,describe(i){return{name:"Firefox",version:gi(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i,i),os:i.toLowerCase().includes("fxios")?"iOS":void 0,osVersion:Ms(i)}}},{test:/chrom|crios|crmo/i,describe(i){return{name:"Chrome",version:gi(/(?:chrome|chromium|crios|crmo)\/(\d+(\.?_?\d+)+)/i,i),os:i.toLowerCase().includes("crios")?"iOS":void 0,osVersion:Ms(i)}}},{test:/safari|applewebkit/i,describe(i){return{name:"Safari",version:gi(lm,i),os:i.includes("mobile/")?"iOS":"macOS",osVersion:Ms(i)}}}];function gi(i,e){let t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:1,n=e.match(i);return n&&n.length>=t&&n[t]||""}function Ms(i){return i.includes("mac os")?gi(/\(.+?(\d+_\d+(:?_\d+)?)/,i,1).replace(/_/g,"."):void 0}var hm="2.22.3",mm=hm,fm=17,ml=0,Tr=1,fl=2,pm=fl,Ne=class extends Error{constructor(e,t,n){super(t||"an error has occurred"),this.name="LiveKitError",this.code=e,typeof n?.cause<"u"&&(this.cause=n?.cause)}},Ee=class extends Ne{};var Q;(function(i){i[i.NotAllowed=0]="NotAllowed",i[i.ServerUnreachable=1]="ServerUnreachable",i[i.InternalError=2]="InternalError",i[i.Cancelled=3]="Cancelled",i[i.LeaveRequest=4]="LeaveRequest",i[i.Timeout=5]="Timeout",i[i.WebSocket=6]="WebSocket",i[i.ServiceNotFound=7]="ServiceNotFound"})(Q||(Q={}));var F=class i extends Ee{constructor(e,t,n,s){super(1,e),this.name="ConnectionError",this.status=n,this.reason=t,this.context=s,this.reasonName=Q[t]}static notAllowed(e,t,n){return new i(e,Q.NotAllowed,t,n)}static timeout(e){return new i(e,Q.Timeout)}static leaveRequest(e,t){return new i(e,Q.LeaveRequest,void 0,t)}static internal(e,t){return new i(e,Q.InternalError,void 0,t)}static cancelled(e){return new i(e,Q.Cancelled)}static serverUnreachable(e,t){return new i(e,Q.ServerUnreachable,t)}static websocket(e,t,n){return new i(e,Q.WebSocket,t,n)}static serviceNotFound(e,t){return new i(e,Q.ServiceNotFound,void 0,t)}},Un=class extends Ne{constructor(e){super(21,e??"device is unsupported"),this.name="DeviceUnsupportedError"}},We=class extends Ne{constructor(e){super(20,e??"track is invalid"),this.name="TrackInvalidError"}},Sr=class extends Ne{constructor(e){super(10,e||"unsupported server"),this.name="UnsupportedServer"}},Y=class extends Ne{constructor(e){super(12,e||"unexpected connection state"),this.name="UnexpectedConnectionState"}},Ze=class extends Ne{constructor(e){super(13,e||"unable to negotiate"),this.name="NegotiationError"}},Cr=class extends Ne{constructor(e){super(14,e||"unable to publish data"),this.name="PublishDataError"}},Si=class extends Ne{constructor(e,t){super(15,e),this.name="PublishTrackError",this.status=t}},Ci=class extends Ee{constructor(e,t){super(15,e),this.name="SignalRequestError",this.reason=t,this.reasonName=typeof t=="string"?t:Pt[t]}},ne;(function(i){i[i.AlreadyOpened=0]="AlreadyOpened",i[i.AbnormalEnd=1]="AbnormalEnd",i[i.DecodeFailed=2]="DecodeFailed",i[i.LengthExceeded=3]="LengthExceeded",i[i.Incomplete=4]="Incomplete",i[i.HandlerAlreadyRegistered=7]="HandlerAlreadyRegistered",i[i.EncryptionTypeMismatch=8]="EncryptionTypeMismatch",i[i.HeaderTooLarge=9]="HeaderTooLarge",i[i.PayloadTooLarge=10]="PayloadTooLarge"})(ne||(ne={}));var re=class extends Ee{constructor(e,t){super(16,e),this.name="DataStreamError",this.reason=t,this.reasonName=ne[t]}},ft=class extends Ne{constructor(e){super(18,e),this.name="SignalReconnectError"}},Ei;(function(i){i.PermissionDenied="PermissionDenied",i.NotFound="NotFound",i.DeviceInUse="DeviceInUse",i.Other="Other"})(Ei||(Ei={}));(function(i){function e(t){if(t&&"name"in t)return t.name==="NotFoundError"||t.name==="DevicesNotFoundError"?i.NotFound:t.name==="NotAllowedError"||t.name==="PermissionDeniedError"?i.PermissionDenied:t.name==="NotReadableError"||t.name==="TrackStartError"?i.DeviceInUse:i.Other}i.getFailure=e})(Ei||(Ei={}));var z=class{};z.setTimeout=function(){return setTimeout(...arguments)};z.setInterval=function(){return setInterval(...arguments)};z.clearTimeout=function(){return clearTimeout(...arguments)};z.clearInterval=function(){return clearInterval(...arguments)};var _;(function(i){i.Connected="connected",i.Reconnecting="reconnecting",i.SignalReconnecting="signalReconnecting",i.Reconnected="reconnected",i.Disconnected="disconnected",i.ConnectionStateChanged="connectionStateChanged",i.Moved="moved",i.MediaDevicesChanged="mediaDevicesChanged",i.ParticipantConnected="participantConnected",i.ParticipantDisconnected="participantDisconnected",i.TrackPublished="trackPublished",i.TrackSubscribed="trackSubscribed",i.TrackSubscriptionFailed="trackSubscriptionFailed",i.TrackUnpublished="trackUnpublished",i.TrackUnsubscribed="trackUnsubscribed",i.TrackMuted="trackMuted",i.TrackUnmuted="trackUnmuted",i.LocalTrackPublished="localTrackPublished",i.LocalTrackUnpublished="localTrackUnpublished",i.LocalAudioSilenceDetected="localAudioSilenceDetected",i.ActiveSpeakersChanged="activeSpeakersChanged",i.ParticipantMetadataChanged="participantMetadataChanged",i.ParticipantNameChanged="participantNameChanged",i.ParticipantAttributesChanged="participantAttributesChanged",i.ParticipantActive="participantActive",i.RoomMetadataChanged="roomMetadataChanged",i.DataReceived="dataReceived",i.SipDTMFReceived="sipDTMFReceived",i.TranscriptionReceived="transcriptionReceived",i.ConnectionQualityChanged="connectionQualityChanged",i.TrackStreamStateChanged="trackStreamStateChanged",i.TrackSubscriptionPermissionChanged="trackSubscriptionPermissionChanged",i.TrackSubscriptionStatusChanged="trackSubscriptionStatusChanged",i.AudioPlaybackStatusChanged="audioPlaybackChanged",i.VideoPlaybackStatusChanged="videoPlaybackChanged",i.MediaDevicesError="mediaDevicesError",i.ParticipantPermissionsChanged="participantPermissionsChanged",i.SignalConnected="signalConnected",i.RecordingStatusChanged="recordingStatusChanged",i.ParticipantEncryptionStatusChanged="participantEncryptionStatusChanged",i.EncryptionError="encryptionError",i.DCBufferStatusChanged="dcBufferStatusChanged",i.ActiveDeviceChanged="activeDeviceChanged",i.ChatMessage="chatMessage",i.LocalTrackSubscribed="localTrackSubscribed",i.MetricsReceived="metricsReceived",i.DataTrackPublished="dataTrackPublished",i.DataTrackUnpublished="dataTrackUnpublished",i.LocalDataTrackPublished="localDataTrackPublished",i.LocalDataTrackUnpublished="localDataTrackUnpublished"})(_||(_={}));var A;(function(i){i.TrackPublished="trackPublished",i.TrackSubscribed="trackSubscribed",i.TrackSubscriptionFailed="trackSubscriptionFailed",i.TrackUnpublished="trackUnpublished",i.TrackUnsubscribed="trackUnsubscribed",i.TrackMuted="trackMuted",i.TrackUnmuted="trackUnmuted",i.LocalTrackPublished="localTrackPublished",i.LocalTrackUnpublished="localTrackUnpublished",i.LocalTrackCpuConstrained="localTrackCpuConstrained",i.LocalSenderCreated="localSenderCreated",i.ParticipantMetadataChanged="participantMetadataChanged",i.ParticipantNameChanged="participantNameChanged",i.DataReceived="dataReceived",i.SipDTMFReceived="sipDTMFReceived",i.TranscriptionReceived="transcriptionReceived",i.IsSpeakingChanged="isSpeakingChanged",i.ConnectionQualityChanged="connectionQualityChanged",i.TrackStreamStateChanged="trackStreamStateChanged",i.TrackSubscriptionPermissionChanged="trackSubscriptionPermissionChanged",i.TrackSubscriptionStatusChanged="trackSubscriptionStatusChanged",i.TrackCpuConstrained="trackCpuConstrained",i.MediaDevicesError="mediaDevicesError",i.AudioStreamAcquired="audioStreamAcquired",i.ParticipantPermissionsChanged="participantPermissionsChanged",i.PCTrackAdded="pcTrackAdded",i.AttributesChanged="attributesChanged",i.LocalTrackSubscribed="localTrackSubscribed",i.ChatMessage="chatMessage",i.Active="active"})(A||(A={}));var R;(function(i){i.TransportsCreated="transportsCreated",i.Connected="connected",i.Disconnected="disconnected",i.Resuming="resuming",i.Resumed="resumed",i.Restarting="restarting",i.Restarted="restarted",i.SignalResumed="signalResumed",i.SignalRestarted="signalRestarted",i.Closing="closing",i.MediaTrackAdded="mediaTrackAdded",i.ActiveSpeakersUpdate="activeSpeakersUpdate",i.DataPacketReceived="dataPacketReceived",i.RTPVideoMapUpdate="rtpVideoMapUpdate",i.DCBufferStatusChanged="dcBufferStatusChanged",i.ParticipantUpdate="participantUpdate",i.RoomUpdate="roomUpdate",i.SpeakersChanged="speakersChanged",i.StreamStateChanged="streamStateChanged",i.ConnectionQualityUpdate="connectionQualityUpdate",i.SubscriptionError="subscriptionError",i.SubscriptionPermissionUpdate="subscriptionPermissionUpdate",i.RemoteMute="remoteMute",i.SubscribedQualityUpdate="subscribedQualityUpdate",i.LocalTrackUnpublished="localTrackUnpublished",i.LocalTrackSubscribed="localTrackSubscribed",i.Offline="offline",i.SignalRequestResponse="signalRequestResponse",i.SignalConnected="signalConnected",i.RoomMoved="roomMoved",i.PublishDataTrackResponse="publishDataTrackResponse",i.UnPublishDataTrackResponse="unPublishDataTrackResponse",i.DataTrackSubscriberHandles="dataTrackSubscriberHandles",i.DataTrackPacketReceived="dataTrackPacketReceived",i.Joined="joined",i.TokenRefreshed="tokenRefreshed",i.ServerRegionsReported="serverRegionsReported"})(R||(R={}));var P;(function(i){i.Message="message",i.Muted="muted",i.Unmuted="unmuted",i.Restarted="restarted",i.Ended="ended",i.Subscribed="subscribed",i.Unsubscribed="unsubscribed",i.CpuConstrained="cpuConstrained",i.UpdateSettings="updateSettings",i.UpdateSubscription="updateSubscription",i.AudioPlaybackStarted="audioPlaybackStarted",i.AudioPlaybackFailed="audioPlaybackFailed",i.AudioSilenceDetected="audioSilenceDetected",i.VisibilityChanged="visibilityChanged",i.VideoDimensionsChanged="videoDimensionsChanged",i.VideoPlaybackStarted="videoPlaybackStarted",i.VideoPlaybackFailed="videoPlaybackFailed",i.ElementAttached="elementAttached",i.ElementDetached="elementDetached",i.UpstreamPaused="upstreamPaused",i.UpstreamResumed="upstreamResumed",i.SubscriptionPermissionChanged="subscriptionPermissionChanged",i.SubscriptionStatusChanged="subscriptionStatusChanged",i.SubscriptionFailed="subscriptionFailed",i.TrackProcessorUpdate="trackProcessorUpdate",i.AudioTrackFeatureUpdate="audioTrackFeatureUpdate",i.TranscriptionReceived="transcriptionReceived",i.TimeSyncUpdate="timeSyncUpdate",i.PreConnectBufferFlushed="preConnectBufferFlushed"})(P||(P={}));function As(i){let e={};for(let n of Object.entries(i)){var t=L(n,2);let s=t[0],r=t[1];r!==void 0&&(e[s]=r)}return e}function Os(i,e){return i&&e?"".concat(i,"x").concat(e):void 0}function Er(i){return Math.round(i*1e4)/1e4}function gm(i){let e=i.jitterBufferDelay,t=i.jitterBufferEmittedCount;return e!==void 0&&t?Er(e/t):void 0}function vm(i,e){if(i.playoutDelay!==void 0)return Er(i.playoutDelay);let t=e?.totalPlayoutDelay,n=e?.totalSamplesCount;return t!==void 0&&n?Er(t/n):void 0}function wr(i){var e,t;let n=new Map,s=[],r=[],a=[],o;i.forEach(p=>n.set(p.id,p));let c=p=>{var k;return p.codecId?(k=n.get(p.codecId))===null||k===void 0?void 0:k.mimeType:void 0},d=(p,k)=>p[k]?n.get(p[k]):void 0;i.forEach(p=>{switch(p.type){case"inbound-rtp":{let k=d(p,"playoutId");r.push(As({kind:p.kind,ssrc:p.ssrc,mid:p.mid,trackId:p.trackIdentifier,codec:c(p),decoder:p.decoderImplementation,resolution:Os(p.frameWidth,p.frameHeight),fps:p.framesPerSecond,bytesReceived:p.bytesReceived,packetsReceived:p.packetsReceived,packetsLost:p.packetsLost,packetsDiscarded:p.packetsDiscarded,framesReceived:p.framesReceived,framesDecoded:p.framesDecoded,framesDropped:p.framesDropped,keyFramesDecoded:p.keyFramesDecoded,freezeCount:p.freezeCount,totalFreezesDuration:p.totalFreezesDuration,pauseCount:p.pauseCount,nackCount:p.nackCount,pliCount:p.pliCount,firCount:p.firCount,jitter:p.jitter,jitterBuffer:gm(p),playoutDelay:vm(p,k),audioLevel:p.audioLevel,totalSamplesReceived:p.totalSamplesReceived,concealedSamples:p.concealedSamples}));break}case"outbound-rtp":{let k=d(p,"remoteId"),T=d(p,"mediaSourceId");a.push(As({kind:p.kind,ssrc:p.ssrc,mid:p.mid,rid:p.rid,trackId:T?.trackIdentifier,active:p.active,codec:c(p),encoder:p.encoderImplementation,resolution:Os(p.frameWidth,p.frameHeight),fps:p.framesPerSecond,captureResolution:Os(T?.width,T?.height),captureFps:T?.framesPerSecond,audioLevel:T?.audioLevel,targetBitrate:p.targetBitrate,bytesSent:p.bytesSent,packetsSent:p.packetsSent,retransmittedPacketsSent:p.retransmittedPacketsSent,framesEncoded:p.framesEncoded,keyFramesEncoded:p.keyFramesEncoded,limitedBy:p.qualityLimitationReason==="none"?void 0:p.qualityLimitationReason,nackCount:p.nackCount,pliCount:p.pliCount,firCount:p.firCount,remotePacketsLost:k?.packetsLost,remoteFractionLost:k?.fractionLost,remoteJitter:k?.jitter,remoteRoundTripTime:k?.roundTripTime}));break}case"transport":o=p;break;case"candidate-pair":s.push(p);break}});let l=o?.selectedCandidatePairId,u=(t=(e=l?n.get(l):void 0)!==null&&e!==void 0?e:s.find(p=>p.selected))!==null&&t!==void 0?t:s.find(p=>p.nominated),h=u?.localCandidateId?n.get(u.localCandidateId):void 0,m=u?.remoteCandidateId?n.get(u.remoteCandidateId):void 0,g=As({ice:o?.iceState,dtls:o?.dtlsState,route:h&&m?"".concat(h.candidateType,"/").concat(h.protocol," -> ").concat(m.candidateType):void 0,network:h?.networkType,currentRoundTripTime:u?.currentRoundTripTime,availableOutgoingBitrate:u?.availableOutgoingBitrate,availableIncomingBitrate:u?.availableIncomingBitrate,bytesSent:u?.bytesSent,bytesReceived:u?.bytesReceived,candidatePairChanges:o?.selectedCandidatePairChanges});return{connection:Object.keys(g).length>0?g:void 0,outbound:a.length>0?a:void 0,inbound:r.length>0?r:void 0}}function bm(i){return typeof i>"u"?i:typeof structuredClone=="function"?typeof i=="object"&&i!==null?structuredClone(Object.assign({},i)):structuredClone(i):JSON.parse(JSON.stringify(i))}var J=class{constructor(e,t,n,s,r){if(typeof e=="object")this.width=e.width,this.height=e.height,this.aspectRatio=e.aspectRatio,this.encoding={maxBitrate:e.maxBitrate,maxFramerate:e.maxFramerate,priority:e.priority};else if(t!==void 0&&n!==void 0)this.width=e,this.height=t,this.aspectRatio=e/t,this.encoding={maxBitrate:n,maxFramerate:s,priority:r};else throw new TypeError("Unsupported options: provide at least width, height and maxBitrate")}get resolution(){return{width:this.width,height:this.height,frameRate:this.encoding.maxFramerate,aspectRatio:this.aspectRatio}}};var ym=["vp8","h264"],km=["vp8","h264","vp9","av1","h265"];function pl(i){return!!ym.find(e=>e===i)}var Tm=pl,Ko;(function(i){i[i.PREFER_REGRESSION=0]="PREFER_REGRESSION",i[i.SIMULCAST=1]="SIMULCAST",i[i.REGRESSION=2]="REGRESSION"})(Ko||(Ko={}));var _r;(function(i){i.telephone={maxBitrate:12e3},i.speech={maxBitrate:24e3},i.music={maxBitrate:48e3},i.musicStereo={maxBitrate:64e3},i.musicHighQuality={maxBitrate:96e3},i.musicHighQualityStereo={maxBitrate:128e3}})(_r||(_r={}));var Fn={h90:new J(160,90,9e4,20),h180:new J(320,180,16e4,20),h216:new J(384,216,18e4,20),h360:new J(640,360,45e4,20),h540:new J(960,540,8e5,25),h720:new J(1280,720,17e5,30),h1080:new J(1920,1080,3e6,30),h1440:new J(2560,1440,5e6,30),h2160:new J(3840,2160,8e6,30)},Rr={h120:new J(160,120,7e4,20),h180:new J(240,180,125e3,20),h240:new J(320,240,14e4,20),h360:new J(480,360,33e4,20),h480:new J(640,480,5e5,20),h540:new J(720,540,6e5,25),h720:new J(960,720,13e5,30),h1080:new J(1440,1080,23e5,30),h1440:new J(1920,1440,38e5,30)},no={h360fps3:new J(640,360,2e5,3,"medium"),h360fps15:new J(640,360,4e5,15,"medium"),h720fps5:new J(1280,720,8e5,5,"medium"),h720fps15:new J(1280,720,15e5,15,"medium"),h720fps30:new J(1280,720,2e6,30,"medium"),h1080fps15:new J(1920,1080,25e5,15,"medium"),h1080fps30:new J(1920,1080,5e6,30,"medium"),original:new J(0,0,7e6,30,"medium")};function gl(i,e,t){var n,s,r,a;let o=yl(i??{}),c=o.optionsWithoutProcessor,d=o.audioProcessor,l=o.videoProcessor,u=e?.processor,h=t?.processor,m=c??{};return m.audio===!0&&(m.audio={}),m.video===!0&&(m.video={}),m.audio&&(Pr(m.audio,e),(n=(r=m.audio).deviceId)!==null&&n!==void 0||(r.deviceId={ideal:"default"}),(d||u)&&(m.audio.processor=d??u)),m.video&&(Pr(m.video,t),(s=(a=m.video).deviceId)!==null&&s!==void 0||(a.deviceId={ideal:"default"}),(l||h)&&(m.video.processor=l??h)),m}function Pr(i,e){return Object.keys(e).forEach(t=>{i[t]===void 0&&(i[t]=e[t])}),i}function io(i){var e,t,n,s;let r={};if(i.video)if(typeof i.video=="object"){let a={},o=a,c=i.video;Object.keys(c).forEach(d=>{switch(d){case"resolution":Pr(o,c.resolution);break;default:o[d]=c[d]}}),r.video=a,(e=(n=r.video).deviceId)!==null&&e!==void 0||(n.deviceId={ideal:"default"})}else r.video=i.video?{deviceId:{ideal:"default"}}:!1;else r.video=!1;return i.audio?typeof i.audio=="object"?(r.audio=i.audio,(t=(s=r.audio).deviceId)!==null&&t!==void 0||(s.deviceId={ideal:"default"})):r.audio={deviceId:{ideal:"default"}}:r.audio=!1,r}function vl(i){return f(this,arguments,void 0,function(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:200;return function*(){let n=bl();if(n){let s=n.createAnalyser();s.fftSize=2048;let r=s.frequencyBinCount,a=new Uint8Array(r);n.createMediaStreamSource(new MediaStream([e.mediaStreamTrack])).connect(s),yield ke(t),s.getByteTimeDomainData(a);let c=a.some(d=>d!==128&&d!==0);return n.close(),!c}return!1}()})}function bl(){var i;let e=typeof window<"u"&&(window.AudioContext||window.webkitAudioContext);if(e){let t=new e({latencyHint:"interactive"});if(t.state==="suspended"&&typeof window<"u"&&(!((i=window.document)===null||i===void 0)&&i.body)){let n=()=>f(this,void 0,void 0,function*(){var s;try{t.state==="suspended"&&(yield t.resume())}catch(r){console.warn("Error trying to auto-resume audio context",r)}finally{(s=window.document.body)===null||s===void 0||s.removeEventListener("click",n)}});t.addEventListener("statechange",()=>{var s;t.state==="closed"&&((s=window.document.body)===null||s===void 0||s.removeEventListener("click",n))}),window.document.body.addEventListener("click",n)}return t}}function Sm(i){return i==="audioinput"?E.Source.Microphone:i==="videoinput"?E.Source.Camera:E.Source.Unknown}function Ir(i){return i===E.Source.Microphone?"audioinput":i===E.Source.Camera?"videoinput":void 0}function Cm(i){var e,t;let n=(e=i.video)!==null&&e!==void 0?e:!0;return i.resolution&&i.resolution.width>0&&i.resolution.height>0&&(n=typeof n=="boolean"?{}:n,Ft()?n=Object.assign(Object.assign({},n),{width:{max:i.resolution.width},height:{max:i.resolution.height},frameRate:i.resolution.frameRate}):n=Object.assign(Object.assign({},n),{width:{ideal:i.resolution.width},height:{ideal:i.resolution.height},frameRate:i.resolution.frameRate})),{audio:(t=i.audio)!==null&&t!==void 0?t:!1,video:n,controller:i.controller,selfBrowserSurface:i.selfBrowserSurface,surfaceSwitching:i.surfaceSwitching,systemAudio:i.systemAudio,preferCurrentTab:i.preferCurrentTab}}function wn(i){return i.split("/")[1].toLowerCase()}function Em(i){let e=[];return i.forEach(t=>{t.track!==void 0&&e.push(new Qa({cid:t.track.mediaStreamID,track:t.trackInfo}))}),e}function q(i){return"mediaStreamTrack"in i?{trackID:i.sid,source:i.source,muted:i.isMuted,enabled:i.mediaStreamTrack.enabled,kind:i.kind,streamID:i.mediaStreamID,streamTrackID:i.mediaStreamTrack.id}:{trackID:i.trackSid,enabled:i.isEnabled,muted:i.isMuted,trackInfo:Object.assign({mimeType:i.mimeType,name:i.trackName,encrypted:i.isEncrypted,kind:i.kind,source:i.source},i.track?q(i.track):{})}}function wm(){return typeof RTCRtpReceiver<"u"&&typeof RTCRtpReceiver.prototype.getSynchronizationSources=="function"}function _m(i,e){var t;i===void 0&&(i={}),e===void 0&&(e={});let n=[...Object.keys(e),...Object.keys(i)],s={};for(let r of n)i[r]!==e[r]&&(s[r]=(t=e[r])!==null&&t!==void 0?t:"");return s}function yl(i){let e=Object.assign({},i),t,n;return typeof e.audio=="object"&&e.audio.processor&&(t=e.audio.processor,e.audio=Object.assign(Object.assign({},e.audio),{processor:void 0})),typeof e.video=="object"&&e.video.processor&&(n=e.video.processor,e.video=Object.assign(Object.assign({},e.video),{processor:void 0})),{audioProcessor:t,videoProcessor:n,optionsWithoutProcessor:bm(e)}}function Rm(i){switch(i){case de.CAMERA:return E.Source.Camera;case de.MICROPHONE:return E.Source.Microphone;case de.SCREEN_SHARE:return E.Source.ScreenShare;case de.SCREEN_SHARE_AUDIO:return E.Source.ScreenShareAudio;default:return E.Source.Unknown}}function Go(i,e){return i.width*i.heightn.quality===e)}var Im=5e3,vn=[],Ie;(function(i){i[i.LOW=0]="LOW",i[i.MEDIUM=1]="MEDIUM",i[i.HIGH=2]="HIGH"})(Ie||(Ie={}));var E=class i extends we.EventEmitter{get streamState(){return this._streamState}setStreamState(e){this._streamState!==e&&this.log.debug("stream state changed: ".concat(this._streamState," -> ").concat(e)),this._streamState=e}constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};var s;super(),this.attachedElements=[],this.isMuted=!1,this._streamState=i.StreamState.Active,this.isInBackground=!1,this._currentBitrate=0,this.finalStatsLogged=!1,this.log=W,this.appVisibilityChangedListener=()=>{this.backgroundTimeout&&clearTimeout(this.backgroundTimeout),document.visibilityState==="hidden"?this.backgroundTimeout=setTimeout(()=>this.handleAppVisibilityChanged(),Im):this.handleAppVisibilityChanged()},this.loggerContextCb=n.loggerContextCb,this.log=he((s=n.loggerName)!==null&&s!==void 0?s:te.Track,()=>this.logContext),this.setMaxListeners(100),this.kind=t,this._mediaStreamTrack=e,this._mediaStreamID=e.id,this.source=i.Source.Unknown}get logContext(){var e;return Object.assign(Object.assign({},(e=this.loggerContextCb)===null||e===void 0?void 0:e.call(this)),q(this))}get currentBitrate(){return this._currentBitrate}get mediaStreamTrack(){return this._mediaStreamTrack}get mediaStreamID(){return this._mediaStreamID}attach(e){let t="audio";this.kind===i.Kind.Video&&(t="video"),this.attachedElements.length===0&&this.kind===i.Kind.Video&&this.addAppVisibilityListener(),e||(t==="audio"&&(vn.forEach(r=>{r.parentElement===null&&!e&&(e=r)}),e&&vn.splice(vn.indexOf(e),1)),e||(e=document.createElement(t))),this.attachedElements.includes(e)||this.attachedElements.push(e),Gt(this.mediaStreamTrack,e);let n=e.srcObject.getTracks(),s=n.some(r=>r.kind==="audio");return e.play().then(()=>{this.emit(s?P.AudioPlaybackStarted:P.VideoPlaybackStarted)}).catch(r=>{r.name==="NotAllowedError"?this.emit(s?P.AudioPlaybackFailed:P.VideoPlaybackFailed,r):r.name==="AbortError"?this.log.debug("".concat(s?"audio":"video"," playback aborted, likely due to new play request")):this.log.warn("could not playback ".concat(s?"audio":"video"),{error:r}),s&&e&&n.some(a=>a.kind==="video")&&r.name==="NotAllowedError"&&(e.muted=!0,e.play().catch(()=>{}))}),this.emit(P.ElementAttached,e),e}detach(e){try{if(e){Yt(this.mediaStreamTrack,e);let n=this.attachedElements.indexOf(e);return n>=0&&(this.attachedElements.splice(n,1),this.recycleElement(e),this.emit(P.ElementDetached,e)),e}let t=[];return this.attachedElements.forEach(n=>{Yt(this.mediaStreamTrack,n),t.push(n),this.recycleElement(n),this.emit(P.ElementDetached,n)}),this.attachedElements=[],t}finally{this.attachedElements.length===0&&this.removeAppVisibilityListener()}}stop(){this.log.debug("stopping track"),this.stopMonitor(),this._mediaStreamTrack.stop()}enable(){this._mediaStreamTrack.enabled=!0}disable(){this._mediaStreamTrack.enabled=!1}stopMonitor(){this.monitorInterval&&clearInterval(this.monitorInterval),this.timeSyncHandle!==void 0&&(cancelAnimationFrame(this.timeSyncHandle),this.timeSyncHandle=void 0),this.logFinalStats()}logFinalStats(){this.finalStatsLogged||(this.finalStatsLogged=!0,this.getRTCStatsReport().then(e=>{e&&this.log.info("final track stats",wr(e))}).catch(e=>this.log.debug("could not collect final track stats",{error:e})))}updateLoggerOptions(e){e.loggerContextCb&&(this.loggerContextCb=e.loggerContextCb),e.loggerName&&(this.log=he(e.loggerName,()=>this.logContext))}recycleElement(e){if(e instanceof HTMLAudioElement){let t=!0;e.pause(),vn.forEach(n=>{n.parentElement||(t=!1)}),t&&vn.push(e)}}handleAppVisibilityChanged(){return f(this,void 0,void 0,function*(){this.isInBackground=document.visibilityState==="hidden",!this.isInBackground&&this.kind===i.Kind.Video&&setTimeout(()=>this.attachedElements.forEach(e=>e.play().catch(()=>{})),0)})}addAppVisibilityListener(){Ce()?(this.isInBackground=document.visibilityState==="hidden",document.addEventListener("visibilitychange",this.appVisibilityChangedListener)):this.isInBackground=!1}removeAppVisibilityListener(){Ce()&&document.removeEventListener("visibilitychange",this.appVisibilityChangedListener)}};function Gt(i,e){let t;e.srcObject instanceof MediaStream?t=e.srcObject:t=new MediaStream;let n;i.kind==="audio"?n=t.getAudioTracks():n=t.getVideoTracks(),n.includes(i)||(n.forEach(s=>{t.removeTrack(s)}),t.addTrack(i)),(!Ft()||!(e instanceof HTMLVideoElement))&&(e.autoplay=!0),e.muted=t.getAudioTracks().length===0,e instanceof HTMLVideoElement&&(e.playsInline=!0),e.srcObject!==t&&(e.srcObject=t,(Ft()||Ut())&&e instanceof HTMLVideoElement&&setTimeout(()=>{e.srcObject=t,e.play().catch(()=>{})},0))}function Yt(i,e){if(e.srcObject instanceof MediaStream){let t=e.srcObject;t.removeTrack(i),t.getTracks().length>0?e.srcObject=t:e.srcObject=null}}(function(i){let e;(function(d){d.Audio="audio",d.Video="video",d.Unknown="unknown"})(e=i.Kind||(i.Kind={}));let t;(function(d){d.Camera="camera",d.Microphone="microphone",d.ScreenShare="screen_share",d.ScreenShareAudio="screen_share_audio",d.Unknown="unknown"})(t=i.Source||(i.Source={}));let n;(function(d){d.Active="active",d.Paused="paused",d.Unknown="unknown"})(n=i.StreamState||(i.StreamState={}));function s(d){switch(d){case e.Audio:return qe.AUDIO;case e.Video:return qe.VIDEO;default:return qe.DATA}}i.kindToProto=s;function r(d){switch(d){case qe.AUDIO:return e.Audio;case qe.VIDEO:return e.Video;default:return e.Unknown}}i.kindFromProto=r;function a(d){switch(d){case t.Camera:return de.CAMERA;case t.Microphone:return de.MICROPHONE;case t.ScreenShare:return de.SCREEN_SHARE;case t.ScreenShareAudio:return de.SCREEN_SHARE_AUDIO;default:return de.UNKNOWN}}i.sourceToProto=a;function o(d){switch(d){case de.CAMERA:return t.Camera;case de.MICROPHONE:return t.Microphone;case de.SCREEN_SHARE:return t.ScreenShare;case de.SCREEN_SHARE_AUDIO:return t.ScreenShareAudio;default:return t.Unknown}}i.sourceFromProto=o;function c(d){switch(d){case cr.ACTIVE:return n.Active;case cr.PAUSED:return n.Paused;default:return n.Unknown}}i.streamStateFromProto=c})(E||(E={}));var Mm="|",$t="https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension";function kl(i){let e=i.split(Mm);return e.length>1?[e[0],i.substr(e[0].length+1)]:[i,""]}function ke(i){return new ge(e=>z.setTimeout(e,i))}function Mr(){return"addTransceiver"in RTCPeerConnection.prototype}function Ar(){return"addTrack"in RTCPeerConnection.prototype}function Am(){if(!("getCapabilities"in RTCRtpSender)||Ft()||Ut())return!1;let i=RTCRtpSender.getCapabilities("video"),e=!1;if(i){for(let t of i.codecs)if(t.mimeType.toLowerCase()==="video/av1"){e=!0;break}}return e}function Om(){if(!("getCapabilities"in RTCRtpSender)||Ut())return!1;if(Ft()){let t=ye();if(t?.version&&Ge(t.version,"16")<0||t?.os==="iOS"&&t?.osVersion&&Ge(t.osVersion,"16")<0)return!1}let i=RTCRtpSender.getCapabilities("video"),e=!1;if(i){for(let t of i.codecs)if(t.mimeType.toLowerCase()==="video/vp9"){e=!0;break}}return e}function wt(i){return i==="av1"||i==="vp9"}function Dm(i){var e;let t=(e=i.getHeaderExtensionsToNegotiate)===null||e===void 0?void 0:e.call(i);if(!t||!i.setHeaderExtensionsToNegotiate)return!1;let n=t.find(s=>s.uri===$t);if(!n)return!1;if(n.direction!=="stopped")return!0;n.direction="sendrecv";try{return i.setHeaderExtensionsToNegotiate(t),!0}catch{return!1}}function Xt(i,e){var t;return wt(i)&&!!e?.simulcast&&!!(!((t=e.scalabilityMode)===null||t===void 0)&&t.startsWith("L1T"))}function Tl(){let i=ye();return Bn()||Ke()||i?.name==="Chrome"&&Ge(i.version,"113")<0}var Lm="1.13.6";function Nm(i){return i?Ge(i,Lm)>0:!1}function Or(i){return!document||Bn()?!1:(i||(i=document.createElement("audio")),"setSinkId"in i)}function xm(){return typeof RTCPeerConnection>"u"?!1:Mr()||Ar()}function Ut(){var i;return((i=ye())===null||i===void 0?void 0:i.name)==="Firefox"}function Um(){let i=ye();return!!i&&i.name==="Chrome"&&i.os!=="iOS"}function Dr(){return typeof window<"u"&&typeof window.RTCRtpScriptTransform<"u"&&!Um()}function Ft(){var i;return((i=ye())===null||i===void 0?void 0:i.name)==="Safari"}function Bn(){let i=ye();return i?.name==="Safari"||i?.os==="iOS"}function Fm(){let i=ye();return i?.name==="Safari"&&i.version.startsWith("17.")||i?.os==="iOS"&&!!i?.osVersion&&Ge(i.osVersion,"17")>=0}function Bm(i){return i||(i=ye()),i?.name==="Safari"&&Ge(i.version,"18.3")>0||i?.os==="iOS"&&!!i?.osVersion&&Ge(i.osVersion,"18.3")>0}function Sl(){var i,e;return Ce()?(e=(i=navigator.userAgentData)===null||i===void 0?void 0:i.mobile)!==null&&e!==void 0?e:/Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent):!1}function jm(){let i=ye(),e="17.2";if(i)return i.name!=="Safari"&&i.os!=="iOS"||i.os==="iOS"&&i.osVersion&&Ge(i.osVersion,e)>=0?!0:i.name==="Safari"&&Ge(i.version,e)>=0}function Ce(){return typeof document<"u"}function Ke(){return navigator.product=="ReactNative"}function cn(i){return i.hostname.endsWith(".livekit.cloud")||i.hostname.endsWith(".livekit.run")}function Ds(i){return cn(i)?i.hostname.split(".")[0]:null}function Cl(){if(global&&global.LiveKitReactNativeGlobal)return global.LiveKitReactNativeGlobal}function El(){if(!Ke())return;let i=Cl();if(i)return i.platform}function zo(){if(Ce())return window.devicePixelRatio;if(Ke()){let i=Cl();if(i)return i.devicePixelRatio}return 1}function Ge(i,e){let t=i.split("."),n=e.split("."),s=Math.min(t.length,n.length);for(let r=0;ro)return 1;if(a(Ls||(Ls=new ResizeObserver(qm)),Ls),Ns=null,Qo=()=>(Ns||(Ns=new IntersectionObserver(Vm,{root:null,rootMargin:"0px"})),Ns);function Wm(i){var e;let t=new pd({capabilities:i,sdk:gd.JS,protocol:fm,clientProtocol:pm,version:mm});return Ke()&&(t.os=(e=El())!==null&&e!==void 0?e:""),t}function Yo(){let i=arguments.length>0&&arguments[0]!==void 0?arguments[0]:16,e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:16,t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!1,n=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1,s=document.createElement("canvas");s.width=i,s.height=e;let r=s.getContext("2d");r?.fillRect(0,0,s.width,s.height),n&&r&&(r.beginPath(),r.arc(i/2,e/2,50,0,Math.PI*2,!0),r.closePath(),r.fillStyle="grey",r.fill());let o=s.captureStream().getTracks(),c=L(o,1),d=c[0];if(!d)throw Error("Could not get empty media stream video track");return d.enabled=t,d}var bn;function xs(){if(!bn){let t=new AudioContext,n=t.createOscillator(),s=t.createGain();s.gain.setValueAtTime(0,0);let r=t.createMediaStreamDestination();n.connect(s),s.connect(r),n.start();var i=r.stream.getAudioTracks(),e=L(i,1);if(bn=e[0],!bn)throw Error("Could not get empty media stream audio track");bn.enabled=!1}return bn.clone()}var ue=class{get isResolved(){return this._isResolved}constructor(e,t){this._isResolved=!1,this.onFinally=t,this.promise=new Promise((n,s)=>f(this,void 0,void 0,function*(){this.resolve=n,this.reject=s,e&&(yield e(n,s))})).finally(()=>{var n;this._isResolved=!0,(n=this.onFinally)===null||n===void 0||n.call(this)})}};function wl(i){return km.includes(i)}function Zt(i){if(typeof i=="string"||typeof i=="number")return i;if(Array.isArray(i))return i[0];if(i.exact!==void 0)return Array.isArray(i.exact)?i.exact[0]:i.exact;if(i.ideal!==void 0)return Array.isArray(i.ideal)?i.ideal[0]:i.ideal;throw Error("could not unwrap constraint")}function Hm(i){return i.startsWith("http")?i.replace(/^(http)/,"ws"):i}function jn(i){return i.startsWith("ws")?i.replace(/^(ws)/,"http"):i}function Km(i,e){return i.segments.map(t=>{let n=t.id,s=t.text,r=t.language,a=t.startTime,o=t.endTime,c=t.final;var d;let l=(d=e.get(n))!==null&&d!==void 0?d:Date.now(),u=Date.now();return c?e.delete(n):e.set(n,l),{id:n,text:s,startTime:Number.parseInt(a.toString()),endTime:Number.parseInt(o.toString()),final:c,language:r,firstReceivedTime:l,lastReceivedTime:u}})}function Gm(i){let e=i.id,t=i.timestamp,n=i.message,s=i.editTimestamp;return{id:e,timestamp:Number.parseInt(t.toString()),editTimestamp:s?Number.parseInt(s.toString()):void 0,message:n}}function $o(i){switch(i.reason){case Q.LeaveRequest:return i.context;case Q.Cancelled:return Pe.CLIENT_INITIATED;case Q.NotAllowed:return Pe.USER_REJECTED;case Q.ServerUnreachable:return Pe.JOIN_FAILURE;default:return Pe.UNKNOWN_REASON}}function Wt(i){return i!==void 0?Number(i):void 0}function He(i){return i!==void 0?BigInt(i):void 0}function en(i){return!!i&&!(i instanceof MediaStreamTrack)&&i.isLocal}function et(i){return!!i&&i.kind==E.Kind.Audio}function ut(i){return!!i&&i.kind==E.Kind.Video}function Qe(i){return en(i)&&ut(i)}function st(i){return en(i)&&et(i)}function Lr(i){return!!i&&!i.isLocal}function zm(i){return!!i&&!i.isLocal}function Us(i){return Lr(i)&&ut(i)}function Jm(i){return i.isLocal}function Qm(i,e){let t=[],n=new TextEncoder().encode(i);for(;n.length>e;){let s=e;for(;s>0;){let r=n[s];if(r!==void 0&&(r&192)!==128)break;s--}t.push(n.slice(0,s)),n=n.slice(s)}return n.length>0&&t.push(n),t}function vi(i){return new ReadableStream({start(e){e.enqueue(i),e.close()}})}function Ym(i,e){return Dd(this,arguments,function*(){let n=i.getReader(),s=new Uint8Array(0);try{for(;;){let r=yield dt(n.read()),a=r.done,o=r.value;if(a)break;if(o.byteLength===0)continue;let c=new Uint8Array(s.byteLength+o.byteLength);for(c.set(s),c.set(o,s.byteLength),s=c;s.byteLength>=e;)yield yield dt(s.slice(0,e)),s=s.slice(e)}s.byteLength>0&&(yield yield dt(s))}finally{n.releaseLock()}})}function $m(i){var e;let t=i.get("Cache-Control");if(t){let n=(e=t.match(/(?:^|[,\s])max-age=(\d+)/))===null||e===void 0?void 0:e[1];if(n)return parseInt(n,10)}}function kt(){return typeof CompressionStream<"u"}function Xm(){return kt()&&!Ut()}function Xo(i,e){let t=kl(e.id),n=L(t,2),s=n[1];if(s?.startsWith("TR"))return s;if(i.id.startsWith("TR"))return i.id}function Zm(i,e){let t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!1,n=ef(i,e);return t?n:so(n,"v1")}function ef(i,e){let t=new URL(Hm(i));return e.forEach((n,s)=>{t.searchParams.set(s,n)}),so(t,"rtc")}function tf(i){let e=new URL(jn(i));return so(e,"validate")}function _l(i){return i.endsWith("/")?i:"".concat(i,"/")}function so(i,e){return i.pathname="".concat(_l(i.pathname)).concat(e),i}function Zo(i){if(typeof i=="string")return Do.fromJson(JSON.parse(i),{ignoreUnknownFields:!0});if(i instanceof ArrayBuffer)return Do.fromBinary(new Uint8Array(i));throw new Error("could not decode websocket message: ".concat(typeof i))}function nf(i){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Unknown reason";if(i instanceof Error)return i.message;if(!(i instanceof AbortSignal))return e;let t=i.reason;switch(typeof t){case"string":return t;case"object":return t instanceof Error?t.message:e;default:return"toString"in t?t.toString():e}}function sf(i,e){return i instanceof Error?i.name&&i.message?"".concat(i.name,": ").concat(i.message):i.name:"Encountered unknown ".concat(e," error: ").concat(String(i))}var rf=10,Zn="lk_e2ee",ei="lk_e2ee_track_id",af="LKFrameEncryptionKey",of={sharedKey:!1,ratchetSalt:af,ratchetWindowSize:8,failureTolerance:rf,keyringSize:16,keySize:128},Tt;(function(i){i.SetKey="setKey",i.RatchetRequest="ratchetRequest",i.KeyRatcheted="keyRatcheted"})(Tt||(Tt={}));var ec;(function(i){i.KeyRatcheted="keyRatcheted"})(ec||(ec={}));var gt;(function(i){i.ParticipantEncryptionStatusChanged="participantEncryptionStatusChanged",i.EncryptionError="encryptionError"})(gt||(gt={}));var tc;(function(i){i.Error="cryptorError"})(tc||(tc={}));function cf(){return ro()||df()}function df(){return typeof window<"u"&&typeof window.RTCRtpScriptTransform<"u"}function ro(){return typeof window<"u"&&typeof window.RTCRtpSender<"u"&&typeof window.RTCRtpSender.prototype.createEncodedStreams<"u"}function lf(i){var e,t,n,s,r;if(((e=i.value)===null||e===void 0?void 0:e.case)!=="sipDtmf"&&((t=i.value)===null||t===void 0?void 0:t.case)!=="metrics"&&((n=i.value)===null||n===void 0?void 0:n.case)!=="speaker"&&((s=i.value)===null||s===void 0?void 0:s.case)!=="transcription"&&((r=i.value)===null||r===void 0?void 0:r.case)!=="encryptedPacket")return new cd({value:i.value})}var nc=class extends we.EventEmitter{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};super(),this.latestManuallySetKeyIndex=0,this.onKeyRatcheted=(t,n,s)=>{W.debug("key ratcheted event received",{ratchetResult:t,participantId:n,keyIndex:s})},this.keyInfoMap=new Map,this.options=Object.assign(Object.assign({},of),e),this.on(Tt.KeyRatcheted,this.onKeyRatcheted)}onSetEncryptionKey(e,t,n){let s={key:e,participantIdentity:t,keyIndex:n};if(!this.options.sharedKey&&!t)throw new Error("participant identity needs to be passed for encryption key if sharedKey option is false");this.keyInfoMap.set("".concat(t??"shared","-").concat(n??0),s),n!==void 0&&(this.latestManuallySetKeyIndex=n),this.emit(Tt.SetKey,s,n!==void 0)}getKeys(){return Array.from(this.keyInfoMap.values())}getLatestManuallySetKeyIndex(){return this.latestManuallySetKeyIndex}getOptions(){return this.options}ratchetKey(e,t){this.emit(Tt.RatchetRequest,e,t)}};var wi;(function(i){i[i.InvalidKey=0]="InvalidKey",i[i.MissingKey=1]="MissingKey",i[i.InternalError=2]="InternalError"})(wi||(wi={}));var Nr=class extends Ne{constructor(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:wi.InternalError,n=arguments.length>2?arguments[2]:void 0;super(40,e),this.reason=t,this.participantIdentity=n}};function qn(){return Dr()}function Vn(i){return!!i?.worker&&(ro()||qn())}function _i(i){return!!(i?.timestamp||i?.frameId)}function uf(i){let e=[];return i?.timestamp&&e.push(an.PTF_USER_TIMESTAMP),i?.frameId&&e.push(an.PTF_FRAME_ID),e}function hf(i){if(!i||i.length===0)return;let e={};return i.includes(an.PTF_USER_TIMESTAMP)&&(e.timestamp=!0),i.includes(an.PTF_FRAME_ID)&&(e.frameId=!0),e.timestamp||e.frameId?e:void 0}function ao(i){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:50,t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};var n,s;let r,a=(n=t.isImmediate)!==null&&n!==void 0?n:!1,o=(s=t.callback)!==null&&s!==void 0?s:!1,c=t.maxWait,d=Date.now(),l=[];function u(){if(c!==void 0){let m=Date.now()-d;if(m+e>=c)return c-m}return e}let h=function(){for(var m=arguments.length,g=new Array(m),p=0;p{let I=function(){if(r=void 0,d=Date.now(),!a){let y=i.apply(k,g);o&&o(y),l.forEach(C=>{let M=C.resolve;return M(y)}),l=[]}},b=a&&r===void 0;if(r!==void 0&&z.clearTimeout(r),r=z.setTimeout(I,u()),b){let y=i.apply(k,g);return o&&o(y),T(y)}l.push({resolve:T,reject:S})})};return h.cancel=function(m){r!==void 0&&z.clearTimeout(r),l.forEach(g=>{let p=g.reject;return p(m)}),l=[]},h}var oo=2e3;function fs(i,e){if(!e)return 0;let t,n;return"bytesReceived"in i?(t=i.bytesReceived,n=e.bytesReceived):"bytesSent"in i&&(t=i.bytesSent,n=e.bytesSent),t===void 0||n===void 0||i.timestamp===void 0||e.timestamp===void 0?0:(t-n)*8*1e3/(i.timestamp-e.timestamp)}var Ri=class extends E{constructor(e,t,n,s,r){super(e,n,r),this.timeSyncLoop=()=>{var a;if(this.listenerCount(P.TimeSyncUpdate)===0){this.timeSyncHandle=void 0;return}this.timeSyncHandle=requestAnimationFrame(this.timeSyncLoop);let o=(a=this.receiver)===null||a===void 0?void 0:a.getSynchronizationSources()[0];if(o){let c=o.timestamp,d=o.rtpTimestamp;d&&this.rtpTimestamp!==d&&(this.emit(P.TimeSyncUpdate,{timestamp:c,rtpTimestamp:d}),this.rtpTimestamp=d)}},this.onTimeSyncListenerAdded=a=>{a===P.TimeSyncUpdate&&this.timeSyncHandle===void 0&&(this.timeSyncHandle=requestAnimationFrame(this.timeSyncLoop))},this.sid=t,this.receiver=s}get isLocal(){return!1}setMuted(e){this.isMuted!==e&&(this.isMuted=e,this._mediaStreamTrack.enabled=!e,this.emit(e?P.Muted:P.Unmuted,this))}setMediaStream(e){this.mediaStream=e;let t=n=>{n.track===this._mediaStreamTrack&&(e.removeEventListener("removetrack",t),this.receiver&&"playoutDelayHint"in this.receiver&&(this.receiver.playoutDelayHint=void 0),this.receiver=void 0,this._currentBitrate=0,this.emit(P.Ended,this))};e.addEventListener("removetrack",t)}start(){this.startMonitor(),super.enable()}stop(){this.stopMonitor(),super.disable()}getRTCStatsReport(){return f(this,void 0,void 0,function*(){var e;return!((e=this.receiver)===null||e===void 0)&&e.getStats?yield this.receiver.getStats():void 0})}setPlayoutDelay(e){this.receiver?"playoutDelayHint"in this.receiver?this.receiver.playoutDelayHint=e:this.log.warn("Playout delay not supported in this browser"):this.log.warn("Cannot set playout delay, track already ended")}getPlayoutDelay(){if(this.receiver){if("playoutDelayHint"in this.receiver)return this.receiver.playoutDelayHint;this.log.warn("Playout delay not supported in this browser")}else this.log.warn("Cannot get playout delay, track already ended");return 0}startMonitor(){this.monitorInterval||(this.monitorInterval=setInterval(()=>this.monitorReceiver(),oo)),wm()&&this.registerTimeSyncUpdate()}stopMonitor(){super.stopMonitor(),this.off("newListener",this.onTimeSyncListenerAdded)}registerTimeSyncUpdate(){let e=this;e.off("newListener",this.onTimeSyncListenerAdded),e.on("newListener",this.onTimeSyncListenerAdded),this.timeSyncHandle===void 0&&this.timeSyncLoop()}},Fs=100,Wn=class extends Ri{constructor(e,t,n,s,r){super(e,t,E.Kind.Video,n,r),this.elementInfos=[],this.monitorReceiver=()=>f(this,void 0,void 0,function*(){if(!this.receiver){this._currentBitrate=0;return}let a=yield this.getReceiverStats();a&&this.prevStats&&this.receiver&&(this._currentBitrate=fs(a,this.prevStats)),this.prevStats=a}),this.debouncedHandleResize=ao(()=>{this.updateDimensions()},Fs),this.adaptiveStreamSettings=s}get isAdaptiveStream(){return this.adaptiveStreamSettings!==void 0}lookupFrameMetadata(e){let t=e.rtpTimestamp;var n;return(n=this.frameMetadataExtractor)===null||n===void 0?void 0:n.lookupMetadata(t)}setStreamState(e){super.setStreamState(e),this.log.debug("setStreamState",e),this.isAdaptiveStream&&e===E.StreamState.Active&&this.updateVisibility()}get mediaStreamTrack(){return this._mediaStreamTrack}setMuted(e){super.setMuted(e),this.attachedElements.forEach(t=>{e?Yt(this._mediaStreamTrack,t):Gt(this._mediaStreamTrack,t)})}attach(e){if(e?super.attach(e):e=super.attach(),this.adaptiveStreamSettings&&this.elementInfos.find(t=>t.element===e)===void 0){let t=new xr(e);this.observeElementInfo(t)}return e}observeElementInfo(e){this.adaptiveStreamSettings&&this.elementInfos.find(t=>t===e)===void 0?(e.handleResize=()=>{this.debouncedHandleResize()},e.handleVisibilityChanged=()=>{this.updateVisibility()},this.elementInfos.push(e),e.observe(),this.debouncedHandleResize(),this.updateVisibility()):this.log.warn("visibility resize observer not triggered",this.logContext)}stopObservingElementInfo(e){if(!this.isAdaptiveStream){this.log.warn("stopObservingElementInfo ignored",this.logContext);return}let t=this.elementInfos.filter(n=>n===e);for(let n of t)n.stopObserving();this.elementInfos=this.elementInfos.filter(n=>n!==e),this.updateVisibility(),this.debouncedHandleResize()}detach(e){if(e)return this.stopObservingElement(e),super.detach(e);let t=super.detach();for(let n of t)this.stopObservingElement(n);return t}getDecoderImplementation(){var e;return(e=this.prevStats)===null||e===void 0?void 0:e.decoderImplementation}getReceiverStats(){return f(this,void 0,void 0,function*(){if(!this.receiver||!this.receiver.getStats)return;let e=yield this.receiver.getStats(),t,n="",s=new Map;return e.forEach(r=>{r.type==="inbound-rtp"?(n=r.codecId,t={type:"video",streamId:r.id,framesDecoded:r.framesDecoded,framesDropped:r.framesDropped,framesReceived:r.framesReceived,packetsReceived:r.packetsReceived,packetsLost:r.packetsLost,frameWidth:r.frameWidth,frameHeight:r.frameHeight,pliCount:r.pliCount,firCount:r.firCount,nackCount:r.nackCount,jitter:r.jitter,timestamp:r.timestamp,bytesReceived:r.bytesReceived,decoderImplementation:r.decoderImplementation}):r.type==="codec"&&s.set(r.id,r)}),t&&n!==""&&s.get(n)&&(t.mimeType=s.get(n).mimeType),t})}stopObservingElement(e){let t=this.elementInfos.filter(n=>n.element===e);for(let n of t)this.stopObservingElementInfo(n)}handleAppVisibilityChanged(){let e=Object.create(null,{handleAppVisibilityChanged:{get:()=>super.handleAppVisibilityChanged}});return f(this,void 0,void 0,function*(){yield e.handleAppVisibilityChanged.call(this),this.isAdaptiveStream&&this.updateVisibility()})}updateVisibility(e){var t,n;let s=this.elementInfos.reduce((c,d)=>Math.max(c,d.visibilityChangedAt||0),0),r=!((n=(t=this.adaptiveStreamSettings)===null||t===void 0?void 0:t.pauseVideoInBackground)!==null&&n!==void 0)||n?this.isInBackground:!1,a=this.elementInfos.some(c=>c.pictureInPicture),o=this.elementInfos.some(c=>c.visible)&&!r||a;if(!(this.lastVisible===o&&!e)){if(!o&&Date.now()-s{this.updateVisibility()},Fs);return}this.lastVisible=o,this.emit(P.VisibilityChanged,o,this)}}updateDimensions(){var e,t;let n=0,s=0,r=this.getPixelDensity();for(let a of this.elementInfos){let o=a.width()*r,c=a.height()*r;o+c>n+s&&(n=o,s=c)}((e=this.lastDimensions)===null||e===void 0?void 0:e.width)===n&&((t=this.lastDimensions)===null||t===void 0?void 0:t.height)===s||(this.lastDimensions={width:n,height:s},this.emit(P.VideoDimensionsChanged,this.lastDimensions,this))}getPixelDensity(){var e;let t=(e=this.adaptiveStreamSettings)===null||e===void 0?void 0:e.pixelDensity;return t==="screen"?zo():t||(zo()>2?2:1)}},xr=class{get visible(){return this.isPiP||this.isIntersecting}get pictureInPicture(){return this.isPiP}constructor(e,t){this.onVisibilityChanged=n=>{var s;let r=n.target,a=n.isIntersecting;r===this.element&&(this.isIntersecting=a,this.isPiP=yn(this.element),this.visibilityChangedAt=Date.now(),(s=this.handleVisibilityChanged)===null||s===void 0||s.call(this))},this.onEnterPiP=()=>{var n,s;(s=(n=window.documentPictureInPicture)===null||n===void 0?void 0:n.window)===null||s===void 0||s.addEventListener("pagehide",this.onLeavePiP),queueMicrotask(()=>{requestAnimationFrame(()=>{var r;this.isPiP=yn(this.element),(r=this.handleVisibilityChanged)===null||r===void 0||r.call(this)})})},this.onLeavePiP=()=>{var n;this.isPiP=yn(this.element),(n=this.handleVisibilityChanged)===null||n===void 0||n.call(this)},this.element=e,this.isIntersecting=t??Ur(e),this.isPiP=Ce()&&yn(e),this.visibilityChangedAt=0}width(){return this.element.clientWidth}height(){return this.element.clientHeight}observe(){var e,t,n;this.isIntersecting=Ur(this.element),this.isPiP=yn(this.element),this.element.handleResize=()=>{var s;(s=this.handleResize)===null||s===void 0||s.call(this)},this.element.handleVisibilityChanged=this.onVisibilityChanged,Qo().observe(this.element),Jo().observe(this.element),this.element.addEventListener("enterpictureinpicture",this.onEnterPiP),this.element.addEventListener("leavepictureinpicture",this.onLeavePiP),(e=window.documentPictureInPicture)===null||e===void 0||e.addEventListener("enter",this.onEnterPiP),(n=(t=window.documentPictureInPicture)===null||t===void 0?void 0:t.window)===null||n===void 0||n.addEventListener("pagehide",this.onLeavePiP)}stopObserving(){var e,t,n,s,r;(e=Qo())===null||e===void 0||e.unobserve(this.element),(t=Jo())===null||t===void 0||t.unobserve(this.element),this.element.removeEventListener("enterpictureinpicture",this.onEnterPiP),this.element.removeEventListener("leavepictureinpicture",this.onLeavePiP),(n=window.documentPictureInPicture)===null||n===void 0||n.removeEventListener("enter",this.onEnterPiP),(r=(s=window.documentPictureInPicture)===null||s===void 0?void 0:s.window)===null||r===void 0||r.removeEventListener("pagehide",this.onLeavePiP)}};function yn(i){var e,t;return document.pictureInPictureElement===i?!0:!((e=window.documentPictureInPicture)===null||e===void 0)&&e.window?Ur(i,(t=window.documentPictureInPicture)===null||t===void 0?void 0:t.window):!1}function Ur(i,e){let t=e||window,n=i.offsetTop,s=i.offsetLeft,r=i.offsetWidth,a=i.offsetHeight,o=i,c=o.hidden,d=getComputedStyle(i),l=d.display;for(;i.offsetParent;)i=i.offsetParent,n+=i.offsetTop,s+=i.offsetLeft;return nt.pageYOffset&&s+r>t.pageXOffset&&!c&&l!=="none"}var Pi=class i extends we.EventEmitter{get logContext(){var e,t;return{room:(e=this.room)===null||e===void 0?void 0:e.name,participant:(t=this.room)===null||t===void 0?void 0:t.localParticipant.identity}}constructor(e,t){super(),this.decryptDataRequests=new Map,this.encryptDataRequests=new Map,this.log=he(te.E2EE,()=>this.logContext),this.onWorkerMessage=n=>{var s,r;let a=n.data,o=a.kind,c=a.data;switch(o){case"error":if(c.uuid){let u=this.decryptDataRequests.get(c.uuid);if(u?.reject){u.reject(c.error);break}let h=this.encryptDataRequests.get(c.uuid);if(h?.reject){h.reject(c.error);break}}this.log.error(c.error.message),this.emit(gt.EncryptionError,c.error,c.participantIdentity);break;case"initAck":c.enabled&&this.keyProvider.getKeys().forEach(u=>{this.postKey(u,!1)});break;case"enable":if(c.enabled&&this.keyProvider.getKeys().forEach(u=>{this.postKey(u,!1)}),this.encryptionEnabled!==c.enabled&&c.participantIdentity===((s=this.room)===null||s===void 0?void 0:s.localParticipant.identity))this.emit(gt.ParticipantEncryptionStatusChanged,c.enabled,this.room.localParticipant),this.encryptionEnabled=c.enabled;else if(c.participantIdentity){let u=(r=this.room)===null||r===void 0?void 0:r.getParticipantByIdentity(c.participantIdentity);if(!u)throw TypeError("couldn't set encryption status, participant not found".concat(c.participantIdentity));this.emit(gt.ParticipantEncryptionStatusChanged,c.enabled,u)}break;case"ratchetKey":this.keyProvider.emit(Tt.KeyRatcheted,c.ratchetResult,c.participantIdentity,c.keyIndex);break;case"decryptDataResponse":let d=this.decryptDataRequests.get(c.uuid);d?.resolve&&d.resolve(c);break;case"encryptDataResponse":let l=this.encryptDataRequests.get(c.uuid);l?.resolve&&l.resolve(c);break;case"packetTrailerMetadata":this.handleFrameMetadata(c.trackId,c.rtpTimestamp,c.ssrc,c.metadata);break;case"log":on[c.level](c.msg,c.context);break}},this.onWorkerError=n=>{this.log.error("e2ee worker encountered an error:",{error:n.error}),this.emit(gt.EncryptionError,n.error,void 0)},this.keyProvider=e.keyProvider,this.worker=e.worker,this.encryptionEnabled=!1,this.dataChannelEncryptionEnabled=t}get isEnabled(){return this.encryptionEnabled}get isDataChannelEncryptionEnabled(){return this.isEnabled&&this.dataChannelEncryptionEnabled}setup(e){if(!cf())throw new Un("tried to setup end-to-end encryption on an unsupported browser");if(this.log.info("setting up e2ee"),e!==this.room){this.room=e,this.setupEventListeners(e,this.keyProvider);let t={kind:"init",data:{keyProviderOptions:this.keyProvider.getOptions(),loglevel:on.getLevel()}};this.worker&&(this.log.info("initializing worker",{worker:this.worker}),this.worker.onmessage=this.onWorkerMessage,this.worker.onerror=this.onWorkerError,this.worker.postMessage(t),this.subscribeToLogLevelChanges())}}subscribeToLogLevelChanges(){var e;(e=this.unsubscribeLogLevel)===null||e===void 0||e.call(this);let t;if(i.disposeRegistry){let n=new WeakRef(this.worker);t=No(s=>{let r=n.deref();if(!r){t?.();return}r.postMessage({kind:"setLogLevel",data:{level:s}})}),i.disposeRegistry.register(this,t,this)}else{let n=this.worker;t=No(s=>{n.postMessage({kind:"setLogLevel",data:{level:s}})})}this.unsubscribeLogLevel=t}dispose(){var e,t,n;(e=this.unsubscribeLogLevel)===null||e===void 0||e.call(this),this.unsubscribeLogLevel=void 0,i.disposeRegistry&&i.disposeRegistry.unregister(this);let s=new Nr("E2EEManager disposed",wi.InternalError);for(let r of[...this.encryptDataRequests.values()])(t=r.reject)===null||t===void 0||t.call(r,s);for(let r of[...this.decryptDataRequests.values()])(n=r.reject)===null||n===void 0||n.call(r,s);this.worker&&(this.worker.onmessage=null,this.worker.onerror=null),this.removeAllListeners()}setParticipantCryptorEnabled(e,t){this.log.debug("set e2ee to ".concat(e," for participant ").concat(t)),this.postEnable(e,t)}setSifTrailer(e){!e||e.length===0?this.log.warn("ignoring server sent trailer as it's empty"):this.postSifTrailer(e)}handleFrameMetadata(e,t,n,s){if(this.room){for(let r of[this.room.localParticipant,...this.room.remoteParticipants.values()])for(let a of r.trackPublications.values())if(a.track&&a.track.mediaStreamID===e&&a.track instanceof Wn&&a.track.frameMetadataExtractor){a.track.frameMetadataExtractor.storeMetadata(t,n,s);return}}}setupEngine(e){e.on(R.RTPVideoMapUpdate,t=>{this.postRTPMap(t)})}setupEventListeners(e,t){e.on(_.TrackPublished,(n,s)=>this.setParticipantCryptorEnabledForPublication(n,s.identity)),e.on(_.ConnectionStateChanged,n=>{n===K.Connected&&e.remoteParticipants.forEach(s=>{s.trackPublications.forEach(r=>{this.setParticipantCryptorEnabledForPublication(r,s.identity)})})}).on(_.TrackUnsubscribed,(n,s,r)=>{var a;let o={kind:"removeTransform",data:{participantIdentity:r.identity,trackId:n.mediaStreamID}};(a=this.worker)===null||a===void 0||a.postMessage(o)}).on(_.TrackSubscribed,(n,s,r)=>{this.setupE2EEReceiver(n,r.identity,s.trackInfo)}).on(_.SignalConnected,()=>{if(!this.room)throw new TypeError("expected room to be present on signal connect");let n=t.getLatestManuallySetKeyIndex();t.getKeys().forEach(s=>{var r;this.postKey(s,n===((r=s.keyIndex)!==null&&r!==void 0?r:0))}),this.setParticipantCryptorEnabled(this.room.localParticipant.isE2EEEnabled,this.room.localParticipant.identity)}),e.localParticipant.on(A.LocalSenderCreated,(n,s)=>f(this,void 0,void 0,function*(){this.setupE2EESender(s,n)})),e.localParticipant.on(A.LocalTrackPublished,n=>{if(!ut(n.track)||!Bn())return;let s={kind:"updateCodec",data:{trackId:n.track.mediaStreamID,codec:wn(n.trackInfo.codecs[0].mimeType),participantIdentity:this.room.localParticipant.identity,hasPacketTrailer:!1}};this.worker.postMessage(s)}),t.on(Tt.SetKey,(n,s)=>this.postKey(n,s??!0)).on(Tt.RatchetRequest,(n,s)=>this.postRatchetRequest(n,s))}encryptData(e){return f(this,void 0,void 0,function*(){if(!this.worker)throw Error("could not encrypt data, worker is missing");let t=crypto.randomUUID(),n={kind:"encryptDataRequest",data:{uuid:t,payload:e,participantIdentity:this.room.localParticipant.identity}},s=new ue;return s.onFinally=()=>{this.encryptDataRequests.delete(t)},this.encryptDataRequests.set(t,s),this.worker.postMessage(n),s.promise})}handleEncryptedData(e,t,n,s){if(!this.worker)throw Error("could not handle encrypted data, worker is missing");let r=crypto.randomUUID(),a={kind:"decryptDataRequest",data:{uuid:r,payload:e,iv:t,participantIdentity:n,keyIndex:s}},o=new ue;return o.onFinally=()=>{this.decryptDataRequests.delete(r)},this.decryptDataRequests.set(r,o),this.worker.postMessage(a),o.promise}postRatchetRequest(e,t){if(!this.worker)throw Error("could not ratchet key, worker is missing");let n={kind:"ratchetRequest",data:{participantIdentity:e,keyIndex:t}};this.worker.postMessage(n)}postKey(e,t){let n=e.key,s=e.participantIdentity,r=e.keyIndex;var a;if(!this.worker)throw Error("could not set key, worker is missing");let o={kind:"setKey",data:{participantIdentity:s,isPublisher:s===((a=this.room)===null||a===void 0?void 0:a.localParticipant.identity),key:n,keyIndex:r,updateCurrentKeyIndex:t}};this.worker.postMessage(o)}postEnable(e,t){if(this.worker){let n={kind:"enable",data:{enabled:e,participantIdentity:t}};this.worker.postMessage(n)}else throw new ReferenceError("failed to enable e2ee, worker is not ready")}postRTPMap(e){var t;if(!this.worker)throw TypeError("could not post rtp map, worker is missing");if(!(!((t=this.room)===null||t===void 0)&&t.localParticipant.identity))throw TypeError("could not post rtp map, local participant identity is missing");let n={kind:"setRTPMap",data:{map:e,participantIdentity:this.room.localParticipant.identity}};this.worker.postMessage(n)}postSifTrailer(e){if(!this.worker)throw Error("could not post SIF trailer, worker is missing");let t={kind:"setSifTrailer",data:{trailer:e}};this.worker.postMessage(t)}setParticipantCryptorEnabledForPublication(e,t){if(!e.trackInfo){this.log.warn("skipping e2ee enabled update for publication without trackInfo",{trackSid:e.trackSid});return}this.setParticipantCryptorEnabled(e.trackInfo.encryption!==G.NONE,t)}setupE2EEReceiver(e,t,n){if(!e.receiver)return;if(!n?.mimeType||n.mimeType==="")throw new TypeError("MimeType missing from trackInfo, cannot set up E2EE cryptor");let s=e.kind==="video"&&!!n.packetTrailerFeatures&&n.packetTrailerFeatures.length>0;this.handleReceiver(e.receiver,e.mediaStreamID,t,e.kind==="video"?wn(n.mimeType):void 0,s)}setupE2EESender(e,t){var n,s,r;if(!en(e)||!t){t||this.log.warn("early return because sender is not ready");return}this.handleSender(t,e.mediaStreamID,void 0,ut(e)?(s=(n=e.publishOptions)===null||n===void 0?void 0:n.frameMetadata)!==null&&s!==void 0?s:(r=e.publishOptions)===null||r===void 0?void 0:r.packetTrailer:void 0)}handleReceiver(e,t,n,s,r){return f(this,void 0,void 0,function*(){if(this.worker){if(Dr()){let a={kind:"decode",participantIdentity:n,trackId:t,codec:s,hasPacketTrailer:r};e.transform=new RTCRtpScriptTransform(this.worker,a)}else{if(Zn in e&&ei in e){let d={kind:"updateCodec",data:{trackId:t,previousTrackId:e[ei],codec:s,participantIdentity:n,hasPacketTrailer:r}};this.worker.postMessage(d),e[ei]=t;return}let a=e.writableStream,o=e.readableStream;if(!a||!o){let d=e.createEncodedStreams();e.writableStream=d.writable,a=d.writable,e.readableStream=d.readable,o=d.readable}let c={kind:"decode",data:{readableStream:o,writableStream:a,trackId:t,codec:s,participantIdentity:n,hasPacketTrailer:r}};this.worker.postMessage(c,[o,a])}e[Zn]=!0,e[ei]=t}})}handleSender(e,t,n,s){var r;if(!(Zn in e||!this.worker)){if(!(!((r=this.room)===null||r===void 0)&&r.localParticipant.identity)||this.room.localParticipant.identity==="")throw TypeError("local identity needs to be known in order to set up encrypted sender");if(Dr()){this.log.info("initialize script transform");let a={kind:"encode",participantIdentity:this.room.localParticipant.identity,trackId:t,codec:n,hasPacketTrailer:_i(s),packetTrailer:s};e.transform=new RTCRtpScriptTransform(this.worker,a)}else{this.log.info("initialize encoded streams");let a=e.createEncodedStreams(),o={kind:"encode",data:{readableStream:a.readable,writableStream:a.writable,codec:n,trackId:t,participantIdentity:this.room.localParticipant.identity,hasPacketTrailer:_i(s),packetTrailer:s}};this.worker.postMessage(o,[a.readable,a.writable])}e[Zn]=!0}}};Pi.disposeRegistry=typeof FinalizationRegistry<"u"&&typeof WeakRef<"u"&&new FinalizationRegistry(i=>{i()});var mf=300,Fr=class{constructor(){this.metadataMap=new Map,this.activeSsrc=0}storeMetadata(e,t,n){for(this.activeSsrc!==0&&this.activeSsrc!==t&&this.metadataMap.clear(),this.activeSsrc=t;this.metadataMap.size>=mf;){let s=this.metadataMap.keys().next().value;this.metadataMap.delete(s)}this.metadataMap.set(e,n)}lookupMetadata(e){return this.metadataMap.get(e)}dispose(){this.metadataMap.clear(),this.activeSsrc=0}},Br=class{constructor(e){this.extractors=new Map,this.workerPipelines=new Map,this.onWorkerMessage=t=>{let n=t.data;if(n.kind==="metadata"){let s=this.extractors.get(n.data.trackId);s&&s.storeMetadata(n.data.rtpTimestamp,n.data.ssrc,n.data.metadata)}},this.onWorkerError=t=>{W.error("frame metadata worker encountered an error:",{error:t.error})},this.worker=e?.worker}setup(e){e!==this.room&&(this.room=e,this.worker&&(this.worker.onmessage=this.onWorkerMessage,this.worker.onerror=this.onWorkerError,this.worker.postMessage({kind:"init"})),e.on(_.TrackSubscribed,(t,n,s)=>{t.kind==="video"&&this.setupReceiver(t,n.trackInfo)}).on(_.TrackUnsubscribed,t=>{this.teardownTrack(t)}).on(_.Disconnected,()=>{this.cleanup()}))}setupReceiver(e,t){var n,s,r;let a=e.receiver;if(!a)return;if(!(!!t?.packetTrailerFeatures&&t.packetTrailerFeatures.length>0)){!((n=this.room)===null||n===void 0)&&n.hasE2EESetup||this.setupPassthroughReceiver(a,e.mediaStreamID);return}if(!Vn(this.worker?{worker:this.worker}:void 0)&&!(!((s=this.room)===null||s===void 0)&&s.hasE2EESetup)){W.warn("frame metadata transform not supported; skipping extraction");return}let c=new Fr,d=e.mediaStreamID;this.extractors.set(d,c),e.frameMetadataExtractor=c,!(!((r=this.room)===null||r===void 0)&&r.hasE2EESetup)&&this.setupWorkerReceiver(a,d,!0)}setupPassthroughReceiver(e,t){if(qn()){"transform"in e&&(e.transform=null);return}if(this.worker&&Vn({worker:this.worker})&&!this.workerPipelines.has(e)){this.setupWorkerReceiver(e,t,!1);return}this.worker&&this.workerPipelines.has(e)&&this.setupWorkerReceiver(e,t,!1)}setupWorkerReceiver(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,s=this.worker;if(!s)return;if(qn()){e.transform=new RTCRtpScriptTransform(s,{kind:"decode",trackId:t});return}let r=this.workerPipelines.get(e);if(r){let c={kind:"updateTrackId",data:{oldTrackId:r,newTrackId:t,hasPacketTrailer:n}};s.postMessage(c),this.workerPipelines.set(e,t);return}if(!("createEncodedStreams"in e)){W.warn("createEncodedStreams not supported");return}let a;try{a=e.createEncodedStreams()}catch(c){W.warn("failed to create encoded streams",{error:c});return}let o={kind:"decode",data:{readableStream:a.readable,writableStream:a.writable,trackId:t,hasPacketTrailer:n}};s.postMessage(o,[a.readable,a.writable]),this.workerPipelines.set(e,t)}teardownTrack(e){let t=e.mediaStreamID,n=this.extractors.get(t);n&&(n.dispose(),this.extractors.delete(t)),e instanceof Wn&&(e.frameMetadataExtractor=void 0)}cleanup(){var e;for(let t of this.extractors.values())t.dispose();this.extractors.clear(),this.workerPipelines.clear(),(e=this.worker)===null||e===void 0||e.terminate()}};var ff=500,pf=15e3,tn=class i{constructor(){this.failedConnectionAttempts=new Map,this.backOffPromises=new Map}static getInstance(){return this._instance||(this._instance=new i),this._instance}addFailedConnectionAttempt(e){var t;let n=new URL(e),s=Ds(n);if(!s)return;let r=(t=this.failedConnectionAttempts.get(s))!==null&&t!==void 0?t:0;this.failedConnectionAttempts.set(s,r+1),this.backOffPromises.set(s,ke(Math.min(ff*Math.pow(2,r),pf)))}getBackOffPromise(e){let t=new URL(e),n=t&&Ds(t);return n&&this.backOffPromises.get(n)||Promise.resolve()}resetFailedConnectionAttempts(e){let t=new URL(e),n=t&&Ds(t);n&&(this.failedConnectionAttempts.set(n,0),this.backOffPromises.set(n,Promise.resolve()))}resetAll(){this.backOffPromises.clear(),this.failedConnectionAttempts.clear()}};tn._instance=null;var Bs="default",Me=class i{constructor(){this._previousDevices=[]}static getInstance(){return this.instance===void 0&&(this.instance=new i),this.instance}get previousDevices(){return this._previousDevices}getDevices(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return function*(){var r;if(((r=i.userMediaPromiseMap)===null||r===void 0?void 0:r.size)>0){W.debug("awaiting getUserMedia promise");try{t?yield i.userMediaPromiseMap.get(t):yield Promise.all(i.userMediaPromiseMap.values())}catch{W.warn("error waiting for media permissons")}}let a=yield navigator.mediaDevices.enumerateDevices();if(s&&!(Ft()&&n.hasDeviceInUse(t))&&(a.filter(c=>c.kind===t).length===0||a.some(c=>{let d=c.label==="",l=t?c.kind===t:!0;return d&&l}))){let c={video:t!=="audioinput"&&t!=="audiooutput",audio:t!=="videoinput"&&{deviceId:{ideal:"default"}}},d=yield navigator.mediaDevices.getUserMedia(c);a=yield navigator.mediaDevices.enumerateDevices(),d.getTracks().forEach(l=>{l.stop()})}return n._previousDevices=a,t&&(a=a.filter(o=>o.kind===t)),a}()})}normalizeDeviceId(e,t,n){return f(this,void 0,void 0,function*(){if(t!==Bs)return t;let s=yield this.getDevices(e),r=s.find(o=>o.deviceId===Bs);if(!r){W.warn("could not reliably determine default device");return}let a=s.find(o=>o.deviceId!==Bs&&o.groupId===(n??r.groupId));if(!a){W.warn("could not reliably determine default device");return}return a?.deviceId})}hasDeviceInUse(e){return e?i.userMediaPromiseMap.has(e):i.userMediaPromiseMap.size>0}};Me.mediaDeviceKinds=["audioinput","audiooutput","videoinput"];Me.userMediaPromiseMap=new Map;var co=65535,Rl=4294967295,St=class i{static u16(e){return new i(e,co)}static u32(e){return new i(e,Rl)}constructor(e,t){if(this.value=e,e<0)throw new Error("WrapAroundUnsignedInt: cannot faithfully represent an integer smaller than 0");if(t>Number.MAX_SAFE_INTEGER)throw new Error("WrapAroundUnsignedInt: cannot faithfully represent an integer bigger than MAX_SAFE_INTEGER.");this.maxSize=t,this.clamp()}clamp(){for(;this.value>this.maxSize;)this.value-=this.maxSize+1;for(;this.value<0;)this.value+=this.maxSize+1}clone(){return new i(this.value,this.maxSize)}update(e){this.value=e(this.value),this.clamp()}increment(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:1;this.update(t=>t+e)}decrement(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:1;this.update(t=>t-e)}getThenIncrement(){let e=this.value;return this.increment(),new i(e,this.maxSize)}isBefore(e){let t=this.value>>>0,s=(e.value>>>0)-t>>>0;return s!==0&&sco)throw Hn.tooLarge();return i}},qr=class{constructor(){this.value=0}get(){return this.value+=1,this.value>co?null:this.value}reset(){this.value=0}},Mi={from(i){return{sid:i.sid,pubHandle:i.pubHandle,name:i.name,usesE2ee:i.encryption!==G.NONE}},toProtobuf(i){return new as({sid:i.sid,pubHandle:i.pubHandle,name:i.name,encryption:i.usesE2ee?G.GCM:G.NONE})}},_n;(function(i){i[i.WAITING=0]="WAITING",i[i.RUNNING=1]="RUNNING",i[i.COMPLETED=2]="COMPLETED"})(_n||(_n={}));var Vr=class{constructor(){this.pendingTasks=new Map,this.taskMutex=new le,this.nextTaskIndex=0}run(e){return f(this,void 0,void 0,function*(){let t={id:this.nextTaskIndex++,enqueuedAt:Date.now(),status:_n.WAITING};this.pendingTasks.set(t.id,t);let n=yield this.taskMutex.lock();try{return t.executedAt=Date.now(),t.status=_n.RUNNING,yield e()}finally{t.status=_n.COMPLETED,this.pendingTasks.delete(t.id),n()}})}flush(){return f(this,void 0,void 0,function*(){return this.run(()=>f(this,void 0,void 0,function*(){}))})}snapshot(){return Array.from(this.pendingTasks.values())}};var vf=["client"],Pl=class{constructor(){x(this,"listeners",new Map)}on(e,t){let n=this.listeners.get(e);return n||(n=new Set,this.listeners.set(e,n)),n.add(t),{off:()=>{n.delete(t)}}}emit(e,t){let n=this.listeners.get("*");if(n)for(let r of n)r(e,t);let s=this.listeners.get(e);if(s)for(let r of s)r(t)}clear(){this.listeners.clear()}},nn=class extends Error{constructor(i,e){super("non-serializable value at ".concat(i," (").concat(e,")")),this.path=i,this.label=e}},bf=(i,e)=>Wr(i,e,new Set),Wr=(i,e,t)=>{if(i===null)return null;switch(typeof i){case"string":case"boolean":return i;case"number":if(!Number.isFinite(i))throw new nn(e,kf(i));return i;case"object":return yf(i,e,t);default:throw new nn(e,typeof i)}},yf=(i,e,t)=>{var n,s;if(t.has(i))throw new nn(e,"circular reference");if(Array.isArray(i)){if(i.length!==Object.keys(i).length)throw new nn(e,"sparse array or array with non-index properties");t.add(i);let o=i.map((c,d)=>Wr(c,"".concat(e,"[").concat(d,"]"),t));return t.delete(i),o}let r=Object.getPrototypeOf(i);if(r!==Object.prototype&&r!==null)throw new nn(e,(n=(s=i.constructor)===null||s===void 0?void 0:s.name)!==null&&n!==void 0?n:"object");t.add(i);let a={};for(let o of Object.keys(i)){let c=Wr(i[o],"".concat(e,".").concat(o),t);Object.defineProperty(a,o,{value:c,enumerable:!0,writable:!0,configurable:!0})}return t.delete(i),a},kf=i=>Number.isNaN(i)?"NaN":i>0?"Infinity":"-Infinity",Hr=function(i){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:new Set;if(i===null||typeof i!="object"||e.has(i))return i;if(Array.isArray(i)){e.add(i);let s=i.map(r=>Hr(r,e));return e.delete(i),s}let t=Object.getPrototypeOf(i);if(t!==Object.prototype&&t!==null)return i;e.add(i);let n={};for(let s of Object.keys(i)){let r=Hr(i[s],e);Object.defineProperty(n,s,{value:r,enumerable:!0,writable:!0,configurable:!0})}return e.delete(i),n},Rn=Symbol("machina.type"),ic=20,Tf=class{constructor(e){x(this,"id",void 0),x(this,"initialState",void 0),x(this,Rn,"BehavioralFsm"),x(this,"states",void 0),x(this,"emitter",new Pl),x(this,"clients",new WeakMap),x(this,"knownClients",new Set),x(this,"childSubscriptions",[]),x(this,"disposed",!1),x(this,"transitionDepth",0),this.id=e.id,this.initialState=e.initialState,this.states=e.states,this.wrapChildLinks(),this.setupChildSubscriptions()}handle(e,t){var n;if(this.disposed)return;let s=this.getOrCreateClientMeta(e);for(var r=arguments.length,a=new Array(r>2?r-2:0),o=2;oic)throw this.transitionDepth=0,new Error("Max transition depth (".concat(ic,') exceeded in FSM "').concat(this.id,'". Likely an infinite _onEnter \u2192 transition loop.'));try{let r=this.states[s],a=this.states[t];if(r!=null&&r._onExit&&typeof r._onExit=="function"){let l=this.buildHandlerArgs(e,"",n);r._onExit(l)}n.state=t;let o={fromState:s,toState:t,client:e};this.emitter.emit("transitioning",o);let c;if(a!=null&&a._onEnter&&typeof a._onEnter=="function"){let l=this.buildHandlerArgs(e,"",n);c=a._onEnter(l)}this.emitter.emit("transitioned",o);let d=a?._child;d&&d.reset(e),this.processQueue(e,n),typeof c=="string"&&n.state===t&&this.transition(e,c)}finally{this.transitionDepth--}}}compositeState(e){var t;let n=this.clients.get(e);if(!n)return"";let s=(t=this.states[n.state])===null||t===void 0?void 0:t._child;if(s){let r=s.compositeState(e);if(r)return"".concat(n.state,".").concat(r)}return n.state}rehydrate(e,t){if(this.disposed)return;if(typeof t=="string"){this.rehydrateCompositePath(e,t);return}let n=this.planSnapshotWrites(e,t);for(let s of n)s()}dehydrate(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,n=this.clients.get(e);if(!n)return;let s={state:n.state,deferred:n.deferredQueue.map(a=>this.snapshotDeferredInput(a))},r=this.collectChildSnapshots(e,n.state,t);return r&&(s.children=r),s}planSnapshotWrites(e,t){if(this.disposed)return[];let n=t.state,s=t.deferred,r=t.children;if(!Object.hasOwn(this.states,n))throw new Error('rehydrate: unknown state "'.concat(n,'" in FSM "').concat(this.id,'". Valid states: ').concat(Object.keys(this.states).join(", ")));let a=[];if(r)for(let d of Object.keys(r)){var o;if(!Object.hasOwn(this.states,d))throw new Error('rehydrate: unknown state "'.concat(d,'" in FSM "').concat(this.id,'" referenced by snapshot.children.'));let l=(o=this.states[d])===null||o===void 0?void 0:o._child;if(!l)throw new Error('rehydrate: state "'.concat(d,'" in FSM "').concat(this.id,'" has no _child, but the snapshot has a children["').concat(d,'"] entry.'));a.push(...l.planRehydrate(e,r[d]))}let c=s.map(d=>bu({inputName:d.inputName,args:Hr(d.args)},d.untilState!==void 0?{untilState:d.untilState}:{}));return a.push(()=>{this.clients.has(e)||this.knownClients.add(new WeakRef(e)),this.clients.set(e,{state:n,deferredQueue:c})}),a}rehydrateCompositePath(e,t){let n=t.split("."),s=Tu(n),r=s[0],a=nr(s).slice(1);if(!Object.hasOwn(this.states,r))throw new Error('rehydrate: unknown state "'.concat(r,'" in FSM "').concat(this.id,'". Valid states: ').concat(Object.keys(this.states).join(", ")));if(a.length>0){var o;let c=a.join("."),d=(o=this.states[r])===null||o===void 0?void 0:o._child;if(!d)throw new Error('rehydrate: state "'.concat(r,'" in FSM "').concat(this.id,'" has no _child, but composite path "').concat(t,'" requires one.'));d.rehydrate(e,c)}this.clients.has(e)||this.knownClients.add(new WeakRef(e)),this.clients.set(e,{state:r,deferredQueue:[]})}collectChildSnapshots(e,t,n){let s=new Map;for(let o of Object.keys(this.states)){var r;let c=(r=this.states[o])===null||r===void 0?void 0:r._child;if(!c)continue;let d=s.get(c.instance);d?d.stateNames.push(o):s.set(c.instance,{childLink:c,stateNames:[o]})}let a;for(let o of s.values()){let c=o.childLink,d=o.stateNames,l=n&&d.includes(t);if(c.instance[Rn]==="Fsm"&&!l)continue;let u=c.dehydrate(e,l);if(u){a??(a={});for(let h of d)a[h]=u}}return a}snapshotDeferredInput(e){let t;try{t=bf(e.args,"args")}catch(s){if(!(s instanceof nn))throw s;let r=e.untilState?' (until "'.concat(e.untilState,'")'):"";throw new Error('dehydrate: deferred input "'.concat(e.inputName,'"').concat(r,' in FSM "').concat(this.id,'" has a non-serializable value at ').concat(s.path," (").concat(s.label,")"))}let n={inputName:e.inputName,args:t};return e.untilState!==void 0&&(n.untilState=e.untilState),n}on(e,t){return this.disposed?{off(){}}:this.emitter.on(e,t)}emit(e,t){this.disposed||this.emitter.emit(e,t)}dispose(e){this.disposed=!0;for(let n of this.childSubscriptions)n.off();if(!(e!=null&&e.preserveChildren)){let n=new Set;for(let s of Object.keys(this.states)){var t;let r=(t=this.states[s])===null||t===void 0?void 0:t._child;r&&!n.has(r.instance)&&(n.add(r.instance),r.dispose())}}this.emitter.clear()}wrapChildLinks(){for(let e of Object.keys(this.states)){let t=this.states[e],n=t?._child;if(n){if(typeof n!="object")throw new Error('State "'.concat(e,'"._child: expected an Fsm or BehavioralFsm instance, got ').concat(String(n)));if(!(Rn in n))throw new Error('State "'.concat(e,'"._child: expected an Fsm or BehavioralFsm instance, got a plain object'));t._child=Sf(n)}}}setupChildSubscriptions(){let e=new Set;for(let n of Object.keys(this.states)){var t;let s=(t=this.states[n])===null||t===void 0?void 0:t._child;if(!s||e.has(s.instance))continue;e.add(s.instance);let r=s.onAny((a,o)=>{if(a==="nohandler"){var c;let u=o;if(u.client!==void 0)this.bubbleNohandler(u.client,s,u.inputName,(c=u.args)!==null&&c!==void 0?c:[]);else for(let h of this.knownClients){var d;let m=h.deref();if(m===void 0){this.knownClients.delete(h);continue}this.bubbleNohandler(m,s,u.inputName,(d=u.args)!==null&&d!==void 0?d:[])}return}let l=o;if(l&&typeof l=="object"&&"client"in l)this.isChildActiveForClient(l.client,s)&&this.emitter.emit(a,o);else for(let u of this.knownClients){let h=u.deref();if(!h){this.knownClients.delete(u);continue}if(this.isChildActiveForClient(h,s)){this.emitter.emit(a,o);break}}});this.childSubscriptions.push(r)}}bubbleNohandler(e,t,n,s){if(!this.isChildActiveForClient(e,t))return;let r=this.clients.get(e);r.currentActionArgs=s,this.handleLocally(e,n,s,r)}isChildActiveForClient(e,t){var n;let s=this.clients.get(e);return s?((n=this.states[s.state])===null||n===void 0||(n=n._child)===null||n===void 0?void 0:n.instance)===t.instance:!1}handleLocally(e,t,n,s){var r;let a=this.states[s.state],o=(r=a?.[t])!==null&&r!==void 0?r:a?.["*"];if(!o){this.emitter.emit("nohandler",{inputName:t,args:n,client:e}),s.currentActionArgs=void 0;return}try{this.emitter.emit("handling",{inputName:t,client:e});let c=this.buildHandlerArgs(e,t,s),d;typeof o=="string"?d=o:typeof o=="function"&&(d=o(c,...n)),this.emitter.emit("handled",{inputName:t,client:e}),typeof d=="string"&&this.transition(e,d)}finally{s.currentActionArgs=void 0}}getOrCreateClientMeta(e){let t=this.clients.get(e);return t||(t={state:void 0,deferredQueue:[]},this.clients.set(e,t),this.knownClients.add(new WeakRef(e)),this.transition(e,this.initialState),t)}buildHandlerArgs(e,t,n){return{ctx:e,inputName:t,defer:s=>{if(!n.currentActionArgs)return;let r={inputName:t,args:[...n.currentActionArgs],untilState:s?.until};n.deferredQueue.push(r),this.emitter.emit("deferred",{inputName:t,client:e})},emit:(s,r)=>{this.emitter.emit(s,r)}}}processQueue(e,t){let n=[],s=[];for(let r of t.deferredQueue)r.untilState===void 0||r.untilState===t.state?n.push(r):s.push(r);t.deferredQueue=s;for(let r of n)this.handle(e,r.inputName,...r.args)}};function Sf(i){if(!i||typeof i!="object")throw new Error("createChildLink: expected an Fsm or BehavioralFsm instance, got ".concat(String(i)));let e=i[Rn];if(e==="BehavioralFsm")return{instance:i,canHandle(t,n){return i.canHandle(t,n)},handle(t,n){for(var s=arguments.length,r=new Array(s>2?s-2:0),a=2;a2?s-2:0),a=2;a{if(s&&typeof s=="object"&&"client"in s){s.client;let r=yu(s,vf);this.emitter.emit(n,r)}else this.emitter.emit(n,s)}),this.bfsm.transition(this.context,e.initialState)}handle(e){if(!this.disposed){for(var t=arguments.length,n=new Array(t>1?t-1:0),s=1;s{let t=i.ctx;if(Il(t,e))return"connected"},js=i=>{let e=i.ctx;return e.attemptId+=1,e.lastError=void 0,"connecting"},qs=i=>{let e=i.ctx;return e.attemptId+=1,e.lastError=void 0,"reconnecting"},kn=(i,e)=>{let t=i.ctx;return t.closeReason=e.reason,"disconnecting"},wf={new:{connect:js,close:kn},connecting:{connectComplete:sc,connectFailed:(i,e)=>{let t=i.ctx;return t.lastError=e.error,"closed"},close:kn},connected:{reconnect:qs,transportFailed:(i,e)=>{let t=i.ctx;if(Il(t,e))return t.lastError=e.reason,"offline"},close:kn},offline:{connect:js,reconnect:qs,close:kn},reconnecting:{reconnectComplete:sc,reconnectFailed:(i,e)=>{let t=i.ctx;return t.lastError=e.error,e.recoverable?"offline":"closed"},close:kn},disconnecting:{closeComplete:"closed"},closed:{connect:js,reconnect:qs}};function _f(){let i=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"new";return Ef({id:"signal",initialState:i,context:{attemptId:0},states:wf})}var Kr=class{get readyState(){return this.ws.readyState}constructor(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};var n,s;if(!((n=t.signal)===null||n===void 0)&&n.aborted)throw new DOMException("This operation was aborted","AbortError");this.url=e;let r=new WebSocket(e,(s=t.protocols)!==null&&s!==void 0?s:[]);r.binaryType="arraybuffer",this.ws=r;let a=function(){let o=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},c=o.closeCode,d=o.reason;return r.close(c,d)};this.opened=new ge((o,c)=>{let d=()=>{c(F.websocket("Encountered websocket error during connection establishment"))};r.onopen=()=>{o({readable:new ReadableStream({start(l){r.onmessage=u=>{let h=u.data;return l.enqueue(h)},r.onerror=u=>l.error(F.websocket(sf(u,"websocket"))),r.onclose=u=>{u.wasClean||u.code===1e3?l.close():l.error(F.websocket("WS closed unexpectedly with code ".concat(u.code)))}},cancel:a}),writable:new WritableStream({write(l){r.send(l)},abort(){r.close()},close:a}),protocol:r.protocol,extensions:r.extensions}),r.removeEventListener("error",d)},r.addEventListener("error",d)}),this.closed=new ge((o,c)=>{let d=()=>f(this,void 0,void 0,function*(){let l=new ge(h=>{r.readyState!==WebSocket.CLOSED&&r.addEventListener("close",m=>{h(m)},{once:!0})}),u=yield ge.race([ke(250),l]);u?o(u):c(F.websocket("Encountered unspecified websocket error without a timely close event"))});r.addEventListener("close",l=>{let u=l.code,h=l.reason;o({closeCode:u,reason:h}),r.removeEventListener("error",d)}),r.addEventListener("error",d)}),t.signal&&(t.signal.onabort=()=>r.close()),this.close=a}},Rf=["syncState","trickle","offer","answer","simulate","leave"];function Pf(i){return i instanceof F?i.reason!==Q.LeaveRequest&&i.reason!==Q.NotAllowed:!0}function If(i){let e=Rf.indexOf(i.case)>=0;return W.trace("request allowed to bypass queue:",{canPass:e,req:i}),e}var Te;(function(i){i[i.CONNECTING=0]="CONNECTING",i[i.CONNECTED=1]="CONNECTED",i[i.RECONNECTING=2]="RECONNECTING",i[i.DISCONNECTING=3]="DISCONNECTING",i[i.DISCONNECTED=4]="DISCONNECTED"})(Te||(Te={}));function Mf(i){switch(i){case"connected":return Te.CONNECTED;case"connecting":return Te.CONNECTING;case"reconnecting":return Te.RECONNECTING;case"disconnecting":return Te.DISCONNECTING;default:return Te.DISCONNECTED}}var Af=250,Of=5e3,Kn=class{get currentState(){return Mf(this.lifecycleState)}get isDisconnected(){let e=this.currentState;return e===Te.DISCONNECTING||e===Te.DISCONNECTED}get lifecycleState(){return this.machine.currentState()}get attemptId(){return this.machine.context.attemptId}sendLifecycleInput(e){let t=this.lifecycleState;return this.machine.handle(e.type,e),this.lifecycleState!==t}settleInFlightClose(){return f(this,void 0,void 0,function*(){this.log.debug("waiting for an in-flight close to settle before establishing a session"),(yield this.closingLock.lock())()})}get isEstablishingConnection(){return this.lifecycleState==="connecting"||this.lifecycleState==="reconnecting"}getNextRequestId(){return this._requestId+=1,this._requestId}constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1,t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};var n;this.rtt=0,this.log=W,this._requestId=0,this.useV0SignalPath=!1,this.resetCallbacks=()=>{this.onAnswer=void 0,this.onLeave=void 0,this.onLocalTrackPublished=void 0,this.onLocalTrackUnpublished=void 0,this.onNegotiateRequested=void 0,this.onOffer=void 0,this.onRemoteMuteChanged=void 0,this.onSubscribedQualityUpdate=void 0,this.onTokenRefresh=void 0,this.onTrickle=void 0,this.onClose=void 0,this.onMediaSectionsRequirement=void 0},this.loggerContextCb=t.loggerContextCb,this.log=he((n=t.loggerName)!==null&&n!==void 0?n:te.Signal,()=>this.logContext),this.useJSON=e,this.requestQueue=new Vr,this.queuedRequests=[],this.closingLock=new le,this.connectionLock=new le,this.machine=_f(),this.machine.on("transitioned",s=>{let r=s.fromState,a=s.toState;this.log.debug("signal lifecycle: ".concat(r," -> ").concat(a))}),this.machine.on("nohandler",s=>{let r=s.inputName;this.log.debug("ignoring signal lifecycle input ".concat(r," in state ").concat(this.lifecycleState))}),this.machine}get logContext(){var e,t;return(t=(e=this.loggerContextCb)===null||e===void 0?void 0:e.call(this))!==null&&t!==void 0?t:{}}join(e,t,n,s){return f(this,arguments,void 0,function(r,a,o,c){var d=this;let l=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!1,u=arguments.length>5?arguments[5]:void 0;return function*(){if(d.lifecycleState==="disconnecting"&&(yield d.settleInFlightClose()),!d.sendLifecycleInput({type:"connect"}))throw F.internal("cannot establish a signal session from '".concat(d.lifecycleState,"', close the current one first"));d.options=o;try{return yield d.connect(r,a,o,c,l,u)}catch(h){throw d.sendLifecycleInput({type:"connectFailed",error:h}),h}}()})}reconnect(e,t,n,s){return f(this,void 0,void 0,function*(){if(!this.options){this.log.warn("attempted to reconnect without signal options being set, ignoring");return}if(this.lifecycleState==="disconnecting"&&(yield this.settleInFlightClose()),!this.sendLifecycleInput({type:"reconnect"}))throw F.internal("cannot resume the signal session from '".concat(this.lifecycleState,"'"));this.clearPingInterval();try{return yield this.connect(e,t,Object.assign(Object.assign({},this.options),{reconnect:!0,sid:n,reconnectReason:s}),void 0,this.useV0SignalPath)}catch(r){throw this.sendLifecycleInput({type:"reconnectFailed",error:r,recoverable:Pf(r)}),r}})}connect(e,t,n,s){return f(this,arguments,void 0,function(r,a,o,c){var d=this;let l=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!1,u=arguments.length>5?arguments[5]:void 0;return function*(){let h=yield d.connectionLock.lock();d.connectOptions=o,d.useV0SignalPath=l;let m=Wm(o.clientInfoCapabilities),g=l?Df(a,m,o):yield Lf(a,m,o,u),p=Zm(r,g,l).toString(),k=tf(p).toString();return new Promise((T,S)=>f(d,void 0,void 0,function*(){var I,b;try{let y=!1,C=U=>f(this,void 0,void 0,function*(){if(y)return;y=!0;let H=U instanceof Event?U.currentTarget:U,oe=nf(H,"Abort handler called");this.streamWriter&&!this.isDisconnected?this.sendLeave().then(()=>this.close(oe)).catch(_e=>{this.log.error(_e),this.close()}):this.close(),M(),S(U instanceof F?U:F.cancelled(oe))});c?.addEventListener("abort",C);let M=()=>{clearTimeout(O),c?.removeEventListener("abort",C)},O=setTimeout(()=>{C(F.timeout("room connection has timed out (signal)"))},o.websocketTimeout),j=new URL(p);if(j.searchParams.has("access_token")&&j.searchParams.set("access_token",""),this.ws){let U=performance.now();yield this.teardownTransport("replaced by a new connection attempt"),this.log.debug("closed previous ws connection in ".concat(performance.now()-U,"ms"))}let B=this.attemptId;this.log.info("signal connecting to ".concat(j),{reconnect:o.reconnect,reconnectReason:o.reconnectReason}),this.ws=new Kr(p);let D=!1;this.ws.opened.catch(()=>{D=!0});try{this.ws.closed.then(fe=>{this.isEstablishingConnection&&!D&&S(F.internal("Websocket got closed during a (re)connection attempt: ".concat(fe.reason))),this.log.debug("websocket closed",{reason:fe.reason,code:fe.closeCode,attemptId:B,state:this.lifecycleState}),this.handleOnClose(fe.reason||(fe.closeCode===1e3?"server closed the signal connection":"Unexpected WS error"),B)}).catch(fe=>{this.isEstablishingConnection&&!D&&S(F.internal("Websocket error during a (re)connection attempt: ".concat(fe)))});let U=yield this.ws.opened.catch(fe=>f(this,void 0,void 0,function*(){if(this.lifecycleState!=="connected"){clearTimeout(O);let _t=yield this.handleConnectionError(fe,k);S(_t);return}this.handleWSError(fe),S(fe)}));if(clearTimeout(O),!U)return;let H=U.readable.getReader();this.streamWriter=U.writable.getWriter();let oe,_e;try{oe=yield Promise.race([H.read(),new Promise((fe,_t)=>{_e=setTimeout(()=>{_t(F.timeout("signal connection timed out while waiting for the first message"))},Of)})])}catch(fe){H.releaseLock(),S(fe),this.close();return}finally{clearTimeout(_e)}if(H.releaseLock(),!oe.value)throw F.internal("no message received as first message");let Je=Zo(oe.value),ht=this.validateFirstMessage(Je,(I=o.reconnect)!==null&&I!==void 0?I:!1);if(!ht.isValid){S(ht.error);return}((b=Je.message)===null||b===void 0?void 0:b.case)==="join"&&(this.pingTimeoutDuration=Je.message.value.pingTimeout,this.pingIntervalDuration=Je.message.value.pingInterval,this.pingTimeoutDuration&&this.pingTimeoutDuration>0&&this.log.debug("ping config",{timeout:this.pingTimeoutDuration,interval:this.pingIntervalDuration}),this.onJoined&&this.onJoined(Je.message.value));let Yn=ht.shouldProcessFirstMessage?Je:void 0;this.handleSignalConnected(U,O,B,Yn),T(ht.response)}catch(U){S(U)}finally{M()}}finally{h()}}))}()})}startReadingLoop(e,t){return f(this,void 0,void 0,function*(){t&&this.handleSignalResponse(t);let n=this.attemptId;for(;;){this.signalLatency&&(yield ke(this.signalLatency));try{let s=yield e.read(),r=s.done,a=s.value;if(r)break;let o=Zo(a);this.handleSignalResponse(o)}catch(s){this.log.error("error reading from signal stream",{error:s}),yield this.handleOnClose("error in reading loop",n);break}}})}close(){return f(this,arguments,void 0,function(){var e=this;let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!0,n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Close method called on signal client";return function*(){let s=t&&e.sendLifecycleInput({type:"close",reason:n}),r=yield e.closingLock.lock();try{yield e.teardownTransport(n)}finally{s&&e.sendLifecycleInput({type:"closeComplete"}),r()}}()})}teardownTransport(e){return f(this,void 0,void 0,function*(){try{if(this.clearPingInterval(),this.ws){this.ws.close({closeCode:1e3,reason:e});let t=this.ws.closed;this.ws=void 0,this.streamWriter=void 0,yield Promise.race([t,ke(Af)])}}catch(t){this.log.debug("websocket error while closing",{error:t})}})}sendOffer(e,t){this.log.debug("sending offer",{offerSdp:e.sdp}),this.sendRequest({case:"offer",value:It(e,t)})}sendAnswer(e,t){return this.log.debug("sending answer",{answerSdp:e.sdp}),this.sendRequest({case:"answer",value:It(e,t)})}sendIceCandidate(e,t){return this.log.debug("sending ice candidate",{candidate:e}),this.sendRequest({case:"trickle",value:new ds({candidateInit:JSON.stringify(e),target:t})})}sendMuteTrack(e,t){return this.sendRequest({case:"mute",value:new ls({sid:e,muted:t})})}sendAddTrack(e){return this.sendRequest({case:"addTrack",value:e})}sendUpdateLocalMetadata(e,t){return f(this,arguments,void 0,function(n,s){var r=this;let a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return function*(){let o=r.getNextRequestId();return yield r.sendRequest({case:"updateMetadata",value:new $a({requestId:o,metadata:n,name:s,attributes:a})}),o}()})}sendUpdateTrackSettings(e){this.sendRequest({case:"trackSetting",value:e})}sendUpdateSubscription(e){return this.sendRequest({case:"subscription",value:e})}sendSyncState(e){return this.sendRequest({case:"syncState",value:e})}sendUpdateVideoLayers(e,t){return this.sendRequest({case:"updateLayers",value:new wd({trackSid:e,layers:t})})}sendUpdateSubscriptionPermissions(e,t){return this.sendRequest({case:"subscriptionPermission",value:new Pd({allParticipants:e,trackPermissions:t})})}sendSimulateScenario(e){return this.sendRequest({case:"simulate",value:e})}sendPing(){return Promise.all([this.sendRequest({case:"ping",value:ee.parse(Date.now())}),this.sendRequest({case:"pingReq",value:new Ad({timestamp:ee.parse(Date.now()),rtt:ee.parse(this.rtt)})})])}sendUpdateLocalAudioTrack(e,t){return this.sendRequest({case:"updateAudioTrack",value:new Ya({trackSid:e,features:t})})}sendLeave(){return this.sendRequest({case:"leave",value:new hs({reason:Pe.CLIENT_INITIATED,action:Jt.DISCONNECT})})}sendPublishDataTrackRequest(e,t,n){return this.sendRequest({case:"publishDataTrackRequest",value:new Ga({pubHandle:e,name:t,encryption:n?G.GCM:G.NONE})})}sendUnPublishDataTrackRequest(e){return this.sendRequest({case:"unpublishDataTrackRequest",value:new Ja({pubHandle:e})})}sendUpdateDataSubscription(e,t){return this.sendRequest({case:"updateDataSubscription",value:new Td({updates:[new Sd({trackSid:e,subscribe:t})]})})}sendRequest(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1;return function*(){let r=!s&&!If(t),a=n.lifecycleState==="reconnecting"||n.queuedRequests.length>0;if(r&&a){n.queuedRequests.push(()=>f(n,void 0,void 0,function*(){yield this.sendRequest(t,!0)}));return}s||(yield n.requestQueue.flush()),n.signalLatency&&(yield ke(n.signalLatency));let o=t.case==="leave"&&!!n.streamWriter;if(n.isDisconnected&&!o){n.log.debug("skipping signal request (type: ".concat(t.case,") - SignalClient disconnected"));return}if(!n.streamWriter){n.log.error("cannot send signal request before connected, type: ".concat(t?.case));return}let c=new ph({message:t});try{n.useJSON?yield n.streamWriter.write(c.toJsonString()):yield n.streamWriter.write(c.toBinary().buffer)}catch(d){n.log.error("error sending signal message",{error:d})}}()})}handleSignalResponse(e){var t,n;let s=e.message;if(s==null){this.log.debug("received unsupported message");return}let r=!1;if(s.case==="answer"){let a=rc(s.value);this.onAnswer&&this.onAnswer(a,s.value.id,s.value.midToTrackId)}else if(s.case==="offer"){let a=rc(s.value);this.onOffer&&this.onOffer(a,s.value.id,s.value.midToTrackId)}else if(s.case==="trickle"){let a=JSON.parse(s.value.candidateInit);this.onTrickle&&this.onTrickle(a,s.value.target)}else s.case==="update"?this.onParticipantUpdate&&this.onParticipantUpdate((t=s.value.participants)!==null&&t!==void 0?t:[]):s.case==="trackPublished"?this.onLocalTrackPublished&&this.onLocalTrackPublished(s.value):s.case==="speakersChanged"?this.onSpeakersChanged&&this.onSpeakersChanged((n=s.value.speakers)!==null&&n!==void 0?n:[]):s.case==="leave"?this.onLeave&&this.onLeave(s.value):s.case==="mute"?this.onRemoteMuteChanged&&this.onRemoteMuteChanged(s.value.sid,s.value.muted):s.case==="roomUpdate"?this.onRoomUpdate&&s.value.room&&this.onRoomUpdate(s.value.room):s.case==="connectionQuality"?this.onConnectionQuality&&this.onConnectionQuality(s.value):s.case==="streamStateUpdate"?this.onStreamStateUpdate&&this.onStreamStateUpdate(s.value):s.case==="subscribedQualityUpdate"?this.onSubscribedQualityUpdate&&this.onSubscribedQualityUpdate(s.value):s.case==="subscriptionPermissionUpdate"?this.onSubscriptionPermissionUpdate&&this.onSubscriptionPermissionUpdate(s.value):s.case==="refreshToken"?this.onTokenRefresh&&this.onTokenRefresh(s.value):s.case==="trackUnpublished"?this.onLocalTrackUnpublished&&this.onLocalTrackUnpublished(s.value):s.case==="subscriptionResponse"?this.onSubscriptionError&&this.onSubscriptionError(s.value):s.case==="pong"||(s.case==="pongResp"?(this.rtt=Date.now()-Number.parseInt(s.value.lastPingTimestamp.toString()),this.resetPingTimeout(),r=!0):s.case==="requestResponse"?this.onRequestResponse&&this.onRequestResponse(s.value):s.case==="trackSubscribed"?this.onLocalTrackSubscribed&&this.onLocalTrackSubscribed(s.value.trackSid):s.case==="roomMoved"?(this.onTokenRefresh&&this.onTokenRefresh(s.value.token),this.onRoomMoved&&this.onRoomMoved(s.value)):s.case==="mediaSectionsRequirement"?this.onMediaSectionsRequirement&&this.onMediaSectionsRequirement(s.value):s.case==="publishDataTrackResponse"?this.onPublishDataTrackResponse&&this.onPublishDataTrackResponse(s.value):s.case==="unpublishDataTrackResponse"?this.onUnPublishDataTrackResponse&&this.onUnPublishDataTrackResponse(s.value):s.case==="dataTrackSubscriberHandles"?this.onDataTrackSubscriberHandles&&this.onDataTrackSubscriberHandles(s.value):this.log.debug("unsupported message",{msgCase:s.case}));r||this.resetPingTimeout()}setReconnected(){for(;this.queuedRequests.length>0;){let e=this.queuedRequests.shift();e&&this.requestQueue.run(e)}}handleOnClose(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:this.attemptId;return function*(){let r=n.onClose;if(!n.sendLifecycleInput({type:"transportFailed",attemptId:s,reason:t})){n.log.debug("ignoring transport close in state ".concat(n.lifecycleState),{reason:t,attemptId:s,currentAttemptId:n.attemptId});return}yield n.teardownTransport(t),n.log.info("websocket connection closed: ".concat(t),{reason:t}),r&&r(t)}()})}handleWSError(e){this.log.error("websocket error",{error:e})}resetPingTimeout(){if(this.clearPingTimeout(),!this.pingTimeoutDuration){this.log.warn("ping timeout duration not set");return}this.pingTimeout=z.setTimeout(()=>{this.log.warn("ping timeout triggered. last pong received at: ".concat(new Date(Date.now()-this.pingTimeoutDuration*1e3).toUTCString())),this.handleOnClose("ping timeout")},this.pingTimeoutDuration*1e3)}clearPingTimeout(){this.pingTimeout&&z.clearTimeout(this.pingTimeout)}startPingInterval(){if(this.clearPingInterval(),this.resetPingTimeout(),!this.pingIntervalDuration){this.log.warn("ping interval duration not set");return}this.log.debug("start ping interval"),this.pingInterval=z.setInterval(()=>{this.sendPing()},this.pingIntervalDuration*1e3)}clearPingInterval(){this.log.debug("clearing ping interval"),this.clearPingTimeout(),this.pingInterval&&z.clearInterval(this.pingInterval)}handleSignalConnected(e,t,n,s){if(clearTimeout(t),!this.sendLifecycleInput(this.lifecycleState==="reconnecting"?{type:"reconnectComplete",attemptId:n}:{type:"connectComplete",attemptId:n})){this.log.debug("discarding a connection whose attempt no longer owns the session",{attemptId:n,currentAttemptId:this.attemptId,state:this.lifecycleState});return}this.log.info("signal connected"),this.startPingInterval(),this.startReadingLoop(e.readable.getReader(),s)}validateFirstMessage(e,t){var n,s,r,a,o;return((n=e.message)===null||n===void 0?void 0:n.case)==="join"?{isValid:!0,response:e.message.value}:this.lifecycleState==="reconnecting"&&((s=e.message)===null||s===void 0?void 0:s.case)!=="leave"?((r=e.message)===null||r===void 0?void 0:r.case)==="reconnect"?{isValid:!0,response:e.message.value}:(this.log.debug("declaring signal reconnected without reconnect response received"),{isValid:!0,response:void 0,shouldProcessFirstMessage:!0}):this.isEstablishingConnection&&((a=e.message)===null||a===void 0?void 0:a.case)==="leave"?{isValid:!1,error:F.leaveRequest("Received leave request while trying to (re)connect",e.message.value.reason)}:t?{isValid:!1,error:F.internal("Unexpected first message")}:{isValid:!1,error:F.internal("did not receive join response, got ".concat((o=e.message)===null||o===void 0?void 0:o.case," instead"))}}handleConnectionError(e,t){return f(this,void 0,void 0,function*(){try{let n=yield fetch(t);switch(n.status){case 404:let s=yield n.text();return s.includes("requested room does not exist")?F.notAllowed(s,n.status):F.serviceNotFound("v1 RTC path not found. Consider upgrading your LiveKit server version","v0-rtc");case 401:case 403:let r=yield n.text();return F.notAllowed(r,n.status);default:break}return e instanceof F?e:F.internal("Encountered unknown websocket error during connection: ".concat(e),{status:n.status,statusText:n.statusText})}catch(n){return n instanceof F?n:F.serverUnreachable(n instanceof Error?n.message:"server was not reachable")}})}};function rc(i){let e={type:"offer",sdp:i.sdp};switch(i.type){case"answer":case"offer":case"pranswer":case"rollback":e.type=i.type;break}return e}function It(i,e){return new Et({sdp:i.sdp,type:i.type,id:e})}function Df(i,e,t){var n;let s=new URLSearchParams;return s.set("access_token",i),t.reconnect&&(s.set("reconnect","1"),t.sid&&s.set("sid",t.sid)),s.set("auto_subscribe",t.autoSubscribe?"1":"0"),s.set("sdk",Ke()?"reactnative":"js"),s.set("version",e.version),s.set("protocol",e.protocol.toString()),s.set("client_protocol",e.clientProtocol.toString()),e.deviceModel&&s.set("device_model",e.deviceModel),e.os&&s.set("os",e.os),e.osVersion&&s.set("os_version",e.osVersion),e.browser&&s.set("browser",e.browser),e.browserVersion&&s.set("browser_version",e.browserVersion),t.adaptiveStream&&s.set("adaptive_stream","1"),t.reconnectReason&&s.set("reconnect_reason",t.reconnectReason.toString()),!((n=navigator.connection)===null||n===void 0)&&n.type&&s.set("network",navigator.connection.type),s}function Lf(i,e,t,n){return f(this,void 0,void 0,function*(){let s=new URLSearchParams;s.set("access_token",i);let r=new Hh({clientInfo:e,connectionSettings:new Od({autoSubscribe:!!t.autoSubscribe,adaptiveStream:!!t.adaptiveStream}),reconnect:!!t.reconnect,participantSid:t.sid?t.sid:void 0,publisherOffer:n});t.reconnectReason&&(r.reconnectReason=t.reconnectReason);let a=r.toBinary(),o,c;if(kt()){let h=new CompressionStream("gzip"),m=h.writable.getWriter();m.write(new Uint8Array(a)),m.close();let g=[],p=h.readable.getReader();for(;;){let I=yield p.read(),b=I.done,y=I.value;if(b)break;g.push(y)}let k=g.reduce((I,b)=>I+b.length,0),T=new Uint8Array(k),S=0;for(let I of g)T.set(I,S),S+=I.length;o=T,c=lr.GZIP}else o=a,c=lr.NONE;let l=new Kh({joinRequest:o,compression:c}).toBinary(),u=h=>{let m=Array.from(h,g=>String.fromCodePoint(g)).join("");return btoa(m)};return s.set("join_request",u(l).replace(/\+/g,"-").replace(/\//g,"_")),s})}var Gr=class{constructor(e){this._map=new Map,this._lastCleanup=0,this.ttl=e}set(e,t){let n=Date.now();n-this._lastCleanup>this.ttl/2&&this.cleanup();let s=n+this.ttl;return this._map.set(e,{value:t,expiresAt:s}),this}get(e){let t=this._map.get(e);if(t){if(t.expiresAt=Date.now()&&e(r.value,s,this.asValueMap())}}map(e){this.cleanup();let t=[],n=this.asValueMap();for(let r of n.entries()){var s=L(r,2);let a=s[0],o=s[1];t.push(e(o,a,n))}return t}asValueMap(){let e=new Map;for(let n of this._map.entries()){var t=L(n,2);let s=t[0],r=t[1];r.expiresAt>=Date.now()&&e.set(s,r.value)}return e}},xe={},Vs={},Ws={exports:{}},ac;function uo(){if(ac)return Ws.exports;ac=1;var i=Ws.exports={v:[{name:"version",reg:/^(\d*)$/}],o:[{name:"origin",reg:/^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,names:["username","sessionId","sessionVersion","netType","ipVer","address"],format:"%s %s %d %s IP%d %s"}],s:[{name:"name"}],i:[{name:"description"}],u:[{name:"uri"}],e:[{name:"email"}],p:[{name:"phone"}],z:[{name:"timezones"}],r:[{name:"repeats"}],t:[{name:"timing",reg:/^(\d*) (\d*)/,names:["start","stop"],format:"%d %d"}],c:[{name:"connection",reg:/^IN IP(\d) (\S*)/,names:["version","ip"],format:"IN IP%d %s"}],b:[{push:"bandwidth",reg:/^(TIAS|AS|CT|RR|RS):(\d*)/,names:["type","limit"],format:"%s:%s"}],m:[{reg:/^(\w*) (\d*) ([\w/]*)(?: (.*))?/,names:["type","port","protocol","payloads"],format:"%s %d %s %s"}],a:[{push:"rtp",reg:/^rtpmap:(\d*) ([\w\-.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,names:["payload","codec","rate","encoding"],format:function(e){return e.encoding?"rtpmap:%d %s/%s/%s":e.rate?"rtpmap:%d %s/%s":"rtpmap:%d %s"}},{push:"fmtp",reg:/^fmtp:(\d*) ([\S| ]*)/,names:["payload","config"],format:"fmtp:%d %s"},{name:"control",reg:/^control:(.*)/,format:"control:%s"},{name:"rtcp",reg:/^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,names:["port","netType","ipVer","address"],format:function(e){return e.address!=null?"rtcp:%d %s IP%d %s":"rtcp:%d"}},{push:"rtcpFbTrrInt",reg:/^rtcp-fb:(\*|\d*) trr-int (\d*)/,names:["payload","value"],format:"rtcp-fb:%s trr-int %d"},{push:"rtcpFb",reg:/^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,names:["payload","type","subtype"],format:function(e){return e.subtype!=null?"rtcp-fb:%s %s %s":"rtcp-fb:%s %s"}},{push:"ext",reg:/^extmap:(\d+)(?:\/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?/,names:["value","direction","encrypt-uri","uri","config"],format:function(e){return"extmap:%d"+(e.direction?"/%s":"%v")+(e["encrypt-uri"]?" %s":"%v")+" %s"+(e.config?" %s":"")}},{name:"extmapAllowMixed",reg:/^(extmap-allow-mixed)/},{push:"crypto",reg:/^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,names:["id","suite","config","sessionConfig"],format:function(e){return e.sessionConfig!=null?"crypto:%d %s %s %s":"crypto:%d %s %s"}},{name:"setup",reg:/^setup:(\w*)/,format:"setup:%s"},{name:"connectionType",reg:/^connection:(new|existing)/,format:"connection:%s"},{name:"mid",reg:/^mid:([^\s]*)/,format:"mid:%s"},{name:"msid",reg:/^msid:(.*)/,format:"msid:%s"},{name:"ptime",reg:/^ptime:(\d*(?:\.\d*)*)/,format:"ptime:%d"},{name:"maxptime",reg:/^maxptime:(\d*(?:\.\d*)*)/,format:"maxptime:%d"},{name:"direction",reg:/^(sendrecv|recvonly|sendonly|inactive)/},{name:"icelite",reg:/^(ice-lite)/},{name:"iceUfrag",reg:/^ice-ufrag:(\S*)/,format:"ice-ufrag:%s"},{name:"icePwd",reg:/^ice-pwd:(\S*)/,format:"ice-pwd:%s"},{name:"fingerprint",reg:/^fingerprint:(\S*) (\S*)/,names:["type","hash"],format:"fingerprint:%s %s"},{push:"candidates",reg:/^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/,names:["foundation","component","transport","priority","ip","port","type","raddr","rport","tcptype","generation","network-id","network-cost"],format:function(e){var t="candidate:%s %d %s %d %s %d typ %s";return t+=e.raddr!=null?" raddr %s rport %d":"%v%v",t+=e.tcptype!=null?" tcptype %s":"%v",e.generation!=null&&(t+=" generation %d"),t+=e["network-id"]!=null?" network-id %d":"%v",t+=e["network-cost"]!=null?" network-cost %d":"%v",t}},{name:"endOfCandidates",reg:/^(end-of-candidates)/},{name:"remoteCandidates",reg:/^remote-candidates:(.*)/,format:"remote-candidates:%s"},{name:"iceOptions",reg:/^ice-options:(\S*)/,format:"ice-options:%s"},{push:"ssrcs",reg:/^ssrc:(\d*) ([\w_-]*)(?::(.*))?/,names:["id","attribute","value"],format:function(e){var t="ssrc:%d";return e.attribute!=null&&(t+=" %s",e.value!=null&&(t+=":%s")),t}},{push:"ssrcGroups",reg:/^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/,names:["semantics","ssrcs"],format:"ssrc-group:%s %s"},{name:"msidSemantic",reg:/^msid-semantic:\s?(\w*) (\S*)/,names:["semantic","token"],format:"msid-semantic: %s %s"},{push:"groups",reg:/^group:(\w*) (.*)/,names:["type","mids"],format:"group:%s %s"},{name:"rtcpMux",reg:/^(rtcp-mux)/},{name:"rtcpRsize",reg:/^(rtcp-rsize)/},{name:"sctpmap",reg:/^sctpmap:([\w_/]*) (\S*)(?: (\S*))?/,names:["sctpmapNumber","app","maxMessageSize"],format:function(e){return e.maxMessageSize!=null?"sctpmap:%s %s %s":"sctpmap:%s %s"}},{name:"xGoogleFlag",reg:/^x-google-flag:([^\s]*)/,format:"x-google-flag:%s"},{push:"rids",reg:/^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/,names:["id","direction","params"],format:function(e){return e.params?"rid:%s %s %s":"rid:%s %s"}},{push:"imageattrs",reg:new RegExp("^imageattr:(\\d+|\\*)[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?"),names:["pt","dir1","attrs1","dir2","attrs2"],format:function(e){return"imageattr:%s %s %s"+(e.dir2?" %s %s":"")}},{name:"simulcast",reg:new RegExp("^simulcast:(send|recv) ([a-zA-Z0-9\\-_~;,]+)(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?$"),names:["dir1","list1","dir2","list2"],format:function(e){return"simulcast:%s %s"+(e.dir2?" %s %s":"")}},{name:"simulcast_03",reg:/^simulcast:[\s\t]+([\S+\s\t]+)$/,names:["value"],format:"simulcast: %s"},{name:"framerate",reg:/^framerate:(\d+(?:$|\.\d+))/,format:"framerate:%s"},{name:"sourceFilter",reg:/^source-filter: *(excl|incl) (\S*) (IP4|IP6|\*) (\S*) (.*)/,names:["filterMode","netType","addressTypes","destAddress","srcList"],format:"source-filter: %s %s %s %s %s"},{name:"bundleOnly",reg:/^(bundle-only)/},{name:"label",reg:/^label:(.+)/,format:"label:%s"},{name:"sctpPort",reg:/^sctp-port:(\d+)$/,format:"sctp-port:%s"},{name:"maxMessageSize",reg:/^max-message-size:(\d+)$/,format:"max-message-size:%s"},{push:"tsRefClocks",reg:/^ts-refclk:([^\s=]*)(?:=(\S*))?/,names:["clksrc","clksrcExt"],format:function(e){return"ts-refclk:%s"+(e.clksrcExt!=null?"=%s":"")}},{name:"mediaClk",reg:/^mediaclk:(?:id=(\S*))? *([^\s=]*)(?:=(\S*))?(?: *rate=(\d+)\/(\d+))?/,names:["id","mediaClockName","mediaClockValue","rateNumerator","rateDenominator"],format:function(e){var t="mediaclk:";return t+=e.id!=null?"id=%s %s":"%v%s",t+=e.mediaClockValue!=null?"=%s":"",t+=e.rateNumerator!=null?" rate=%s":"",t+=e.rateDenominator!=null?"/%s":"",t}},{name:"keywords",reg:/^keywds:(.+)$/,format:"keywds:%s"},{name:"content",reg:/^content:(.+)/,format:"content:%s"},{name:"bfcpFloorCtrl",reg:/^floorctrl:(c-only|s-only|c-s)/,format:"floorctrl:%s"},{name:"bfcpConfId",reg:/^confid:(\d+)/,format:"confid:%s"},{name:"bfcpUserId",reg:/^userid:(\d+)/,format:"userid:%s"},{name:"bfcpFloorId",reg:/^floorid:(.+) (?:m-stream|mstrm):(.+)/,names:["id","mStream"],format:"floorid:%s mstrm:%s"},{push:"invalid",names:["value"]}]};return Object.keys(i).forEach(function(e){var t=i[e];t.forEach(function(n){n.reg||(n.reg=/(.*)/),n.format||(n.format="%s")})}),Ws.exports}var oc;function Nf(){return oc||(oc=1,function(i){var e=function(o){return String(Number(o))===o?Number(o):o},t=function(o,c,d,l){if(l&&!d)c[l]=e(o[1]);else for(var u=0;u1&&(o[d[0]]=void 0),o};i.parseParams=function(o){return o.split(/;\s?/).reduce(a,{})},i.parseFmtpConfig=i.parseParams,i.parsePayloads=function(o){return o.toString().split(" ").map(Number)},i.parseRemoteCandidates=function(o){for(var c=[],d=o.split(" ").map(e),l=0;l=d)return l;var u=c[o];switch(o+=1,l){case"%%":return"%";case"%s":return String(u);case"%d":return Number(u);case"%v":return""}})},n=function(a,o,c){var d=o.format instanceof Function?o.format(o.push?c:c[o.name]):o.format,l=[a+"="+d];if(o.names)for(var u=0;u4&&arguments[4]!==void 0?arguments[4]:!1;var r,a,o;if(!(!((r=i.msid)===null||r===void 0)&&r.includes(e)))return;let c=(o=(a=i.rtp.find(h=>h.codec.toUpperCase()===t.toUpperCase()))===null||a===void 0?void 0:a.payload)!==null&&o!==void 0?o:0;if(c===0)return 0;let d=Math.round(n*Ff),l=s?d:Math.min(d,Bf),u=i.fmtp.find(h=>h.payload===c);return u?u.config.includes("x-google-start-bitrate")||(u.config+=";x-google-start-bitrate=".concat(l)):i.fmtp.push({payload:c,config:"x-google-start-bitrate=".concat(l)}),c}var vt={NegotiationStarted:"negotiationStarted",NegotiationComplete:"negotiationComplete",OfferAnswered:"offerAnswered",RTPVideoPayloadTypes:"rtpVideoPayloadTypes"},Ai=class extends we.EventEmitter{get pc(){return this._pc||(this._pc=this.createPC()),this._pc}constructor(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};var n;super(),this.log=W,this.iceLog=W,this.ddExtID=0,this.latestOfferId=0,this.latestAcknowledgedOfferId=0,this.pendingCandidates=[],this.restartingIce=!1,this.renegotiate=!1,this.trackBitrates=[],this.remoteStereoMids=[],this.remoteNackMids=[],this.negotiate=ao(s=>f(this,void 0,void 0,function*(){this.emit(vt.NegotiationStarted);try{yield this.createAndSendOffer()}catch(r){if(s)s(r);else throw r}}),jf),this.close=()=>{this._pc&&(this.log.debug("closing peer connection"),this.pendingInitialOffer=void 0,this._pc.close(),this._pc.onconnectionstatechange=null,this._pc.oniceconnectionstatechange=null,this._pc.onicegatheringstatechange=null,this._pc.ondatachannel=null,this._pc.onnegotiationneeded=null,this._pc.onsignalingstatechange=null,this._pc.onicecandidate=null,this._pc.ondatachannel=null,this._pc.ontrack=null,this._pc.onconnectionstatechange=null,this._pc.oniceconnectionstatechange=null,this._pc=null)},this.loggerOptions=t,this.log=he((n=t.loggerName)!==null&&n!==void 0?n:te.PCTransport,()=>this.logContext),this.iceLog=he(te.ICE,()=>this.logContext),this.config=e,this._pc=this.createPC(),this.offerLock=new le}createPC(){let e=new RTCPeerConnection(this.config);return e.onicecandidate=t=>{var n;t.candidate&&(this.iceLog.debug("local ICE candidate gathered",{candidate:t.candidate.candidate}),(n=this.onIceCandidate)===null||n===void 0||n.call(this,t.candidate))},e.onicecandidateerror=t=>{var n;this.iceLog.debug("ICE candidate error",{event:t}),(n=this.onIceCandidateError)===null||n===void 0||n.call(this,t)},e.oniceconnectionstatechange=()=>{var t;this.iceLog.debug("ICE connection state: ".concat(e.iceConnectionState)),(t=this.onIceConnectionStateChange)===null||t===void 0||t.call(this,e.iceConnectionState)},e.onsignalingstatechange=()=>{var t;this.log.debug("signaling state: ".concat(e.signalingState)),(t=this.onSignalingStatechange)===null||t===void 0||t.call(this,e.signalingState)},e.onconnectionstatechange=()=>{var t;this.log.debug("connection state: ".concat(e.connectionState)),(t=this.onConnectionStateChange)===null||t===void 0||t.call(this,e.connectionState)},e.ondatachannel=t=>{var n;this.log.debug("data channel opened by peer",{label:t.channel.label,id:t.channel.id}),(n=this.onDataChannel)===null||n===void 0||n.call(this,t)},e.ontrack=t=>{var n;(n=this.onTrack)===null||n===void 0||n.call(this,t)},e}get logContext(){var e,t;return Object.assign({},(t=(e=this.loggerOptions).loggerContextCb)===null||t===void 0?void 0:t.call(e))}get isICEConnected(){return this._pc!==null&&(this.pc.iceConnectionState==="connected"||this.pc.iceConnectionState==="completed")}addIceCandidate(e){return f(this,void 0,void 0,function*(){if(this.pc.remoteDescription&&!this.restartingIce)return this.pc.addIceCandidate(e);this.iceLog.debug("queuing remote ICE candidate until remote description applied",{pendingCount:this.pendingCandidates.length+1}),this.pendingCandidates.push(e)})}setRemoteDescription(e,t){return f(this,void 0,void 0,function*(){var n,s;if(e.type==="answer"&&this.latestOfferId>0&&t>0&&t!==this.latestOfferId)return this.log.warn("ignoring answer for old offer",{offerId:t,latestOfferId:this.latestOfferId}),!1;let r;if(e.type==="offer"){let a=zf(e),o=a.stereoMids,c=a.nackMids;this.remoteStereoMids=o,this.remoteNackMids=c}else if(e.type==="answer"){if(this.pendingInitialOffer&&this._pc){let c=this.pendingInitialOffer;this.pendingInitialOffer=void 0;let d=Oe.parse((n=c.sdp)!==null&&n!==void 0?n:"");d.media.forEach(l=>{ti(l)}),this.log.debug("setting pending initial offer before processing answer"),yield this.setMungedSDP(c,Oe.write(d))}let a=Oe.parse((s=e.sdp)!==null&&s!==void 0?s:"");a.media.forEach(c=>{let d=Pn(c.mid);c.type==="audio"&&this.trackBitrates.some(l=>{if(!l.transceiver||d!=l.transceiver.mid)return!1;let u=0;if(c.rtp.some(m=>m.codec.toUpperCase()===l.codec.toUpperCase()?(u=m.payload,!0):!1),u===0)return!0;let h=!1;for(let m of c.fmtp)if(m.payload===u){m.config=m.config.split(";").filter(g=>!g.includes("maxaveragebitrate")).join(";"),l.maxbr>0&&(m.config+=";maxaveragebitrate=".concat(l.maxbr*1e3)),h=!0;break}return h||l.maxbr>0&&c.fmtp.push({payload:u,config:"maxaveragebitrate=".concat(l.maxbr*1e3)}),!0})});let o=this.getPlaceholderMids();o.size>0&&hc(a.media,c=>o.has(Pn(c.mid))),r=Oe.write(a)}return yield this.setMungedSDP(e,r,!0),this.pendingCandidates.length>0&&this.iceLog.debug("flushing queued ICE candidates",{count:this.pendingCandidates.length}),this.pendingCandidates.forEach(a=>{this.pc.addIceCandidate(a)}),this.pendingCandidates=[],this.restartingIce=!1,e.type==="answer"&&(this.latestAcknowledgedOfferId=t,this.emit(vt.OfferAnswered,t)),this.renegotiate?(this.renegotiate=!1,yield this.createAndSendOffer()):e.type==="answer"&&(this.emit(vt.NegotiationComplete),e.sdp&&Oe.parse(e.sdp).media.forEach(o=>{o.type==="video"&&this.emit(vt.RTPVideoPayloadTypes,o.rtp)})),!0})}createInitialOffer(){return f(this,void 0,void 0,function*(){var e;let t=yield this.offerLock.lock();try{if(this.pc.signalingState!=="stable"){this.log.warn("signaling state is not stable, cannot create initial offer");return}let n=this.latestOfferId+1;this.latestOfferId=n;let s=yield this.pc.createOffer();this.pendingInitialOffer={sdp:s.sdp,type:s.type};let r=Oe.parse((e=s.sdp)!==null&&e!==void 0?e:"");return r.media.forEach(a=>{ti(a)}),s.sdp=Oe.write(r),{offer:s,offerId:n}}finally{t()}})}createAndSendOffer(e){return f(this,void 0,void 0,function*(){var t;let n=yield this.offerLock.lock();try{if(this.onOffer===void 0)return;if(e?.iceRestart&&(this.iceLog.debug("restarting ICE"),this.restartingIce=!0),this._pc&&(this._pc.signalingState==="have-local-offer"||this.pendingInitialOffer)){let c=this._pc.remoteDescription;if(e?.iceRestart&&c)yield this._pc.setRemoteDescription(c);else{if(e?.iceRestart)throw new Ze("ICE restart requested without a remote description, peer connection must be recreated");this.renegotiate=!0,this.log.debug("requesting renegotiation");return}}else if(!this._pc||this._pc.signalingState==="closed"){this.log.warn("could not createOffer with closed peer connection");return}this.log.debug("starting to negotiate");let s=this.latestOfferId+1;this.latestOfferId=s;let r=yield this.pc.createOffer(e);this.log.debug("original offer",{sdp:r.sdp});let a=Oe.parse((t=r.sdp)!==null&&t!==void 0?t:"");a.media.forEach(c=>{ti(c),c.type==="audio"?uc(c,["all"],[]):c.type==="video"&&this.trackBitrates.some(d=>{if(!d.cid)return!1;let l=qf(c,d.cid,d.codec,d.maxbr,d.isScreenShare);return l===void 0?!1:(l>0&&wt(d.codec)&&!Ft()&&(this.ddExtID=Vf(c,a,this.ddExtID)),!0)})});let o=this.getPlaceholderMids();if(o.size>0&&hc(a.media,c=>o.has(Pn(c.mid))),this.latestOfferId>s){this.log.warn("latestOfferId mismatch",{latestOfferId:this.latestOfferId,offerId:s});return}yield this.setMungedSDP(r,Oe.write(a)),this.onOffer(r,this.latestOfferId)}finally{n()}})}createAndSetAnswer(){return f(this,void 0,void 0,function*(){var e;let t=yield this.pc.createAnswer(),n=Oe.parse((e=t.sdp)!==null&&e!==void 0?e:"");return n.media.forEach(s=>{ti(s),s.type==="audio"&&uc(s,this.remoteStereoMids,this.remoteNackMids)}),yield this.setMungedSDP(t,Oe.write(n)),t})}getPlaceholderMids(){var e,t;return Gf((t=(e=this._pc)===null||e===void 0?void 0:e.getTransceivers())!==null&&t!==void 0?t:[])}createDataChannel(e,t){return this.pc.createDataChannel(e,t)}addTransceiver(e,t){return this.pc.addTransceiver(e,t)}addTransceiverOfKind(e,t){return this.pc.addTransceiver(e,t)}addTrack(e){if(!this._pc)throw new Y("PC closed, cannot add track");return this._pc.addTrack(e)}setTrackCodecBitrate(e){this.trackBitrates.push(e)}setConfiguration(e){var t;if(!this._pc)throw new Y("PC closed, cannot configure");return(t=this._pc)===null||t===void 0?void 0:t.setConfiguration(e)}canRemoveTrack(){var e;return!!(!((e=this._pc)===null||e===void 0)&&e.removeTrack)}removeTrack(e){var t;return(t=this._pc)===null||t===void 0?void 0:t.removeTrack(e)}getConnectionState(){var e,t;return(t=(e=this._pc)===null||e===void 0?void 0:e.connectionState)!==null&&t!==void 0?t:"closed"}getICEConnectionState(){var e,t;return(t=(e=this._pc)===null||e===void 0?void 0:e.iceConnectionState)!==null&&t!==void 0?t:"closed"}getSignallingState(){var e,t;return(t=(e=this._pc)===null||e===void 0?void 0:e.signalingState)!==null&&t!==void 0?t:"closed"}getTransceivers(){var e,t;return(t=(e=this._pc)===null||e===void 0?void 0:e.getTransceivers())!==null&&t!==void 0?t:[]}getSenders(){var e,t;return(t=(e=this._pc)===null||e===void 0?void 0:e.getSenders())!==null&&t!==void 0?t:[]}getLocalDescription(){var e;return(e=this._pc)===null||e===void 0?void 0:e.localDescription}getRemoteDescription(){var e;return(e=this.pc)===null||e===void 0?void 0:e.remoteDescription}getStats(){var e;return(e=this._pc)===null||e===void 0?void 0:e.getStats()}getMaxMessageSize(){var e,t;return(t=(e=this._pc)===null||e===void 0?void 0:e.sctp)===null||t===void 0?void 0:t.maxMessageSize}getConnectedAddress(){return f(this,void 0,void 0,function*(){var e;if(!this._pc)return;let t="",n=new Map,s=new Map;if((yield this._pc.getStats()).forEach(o=>{switch(o.type){case"transport":t=o.selectedCandidatePairId;break;case"candidate-pair":t===""&&o.selected&&(t=o.id),n.set(o.id,o);break;case"remote-candidate":s.set(o.id,"".concat(o.address,":").concat(o.port));break}}),t==="")return;let a=(e=n.get(t))===null||e===void 0?void 0:e.remoteCandidateId;if(a!==void 0)return s.get(a)})}setMungedSDP(e,t,n){return f(this,void 0,void 0,function*(){var s,r;let a=e.sdp;if(t){e.sdp=t;try{this.log.debug("setting munged ".concat(n?"remote":"local"," description")),n?yield this.pc.setRemoteDescription(e):yield this.pc.setLocalDescription(e);return}catch(o){this.log.warn("not able to set ".concat(e.type,", falling back to unmodified sdp"),{error:o,mungedSdp:t,originalSdp:a}),e.sdp=a}}try{n?yield(s=this._pc)===null||s===void 0?void 0:s.setRemoteDescription(e):yield(r=this._pc)===null||r===void 0?void 0:r.setLocalDescription(e)}catch(o){let c="unknown error";o instanceof Error?c=o.message:typeof o=="string"&&(c=o);let d={error:c,sdp:e.sdp};throw t&&t!==a&&(d.mungedSdp=t),!n&&this.pc.remoteDescription&&(d.remoteSdp=this.pc.remoteDescription),this.log.error("unable to set ".concat(e.type),{fields:d}),new Ze(c)}})}};function Vf(i,e,t){var n,s;let r=Wf(e,t);return r===void 0?t:(!((n=i.ext)===null||n===void 0)&&n.some(a=>a.uri===$t)||((s=i.ext)!==null&&s!==void 0||(i.ext=[]),i.ext.push({value:r,uri:$t})),r)}function Wf(i,e){let t=Hf(i,$t);return t!==void 0?lc(i,t,$t)?void 0:t:e!==0&&!lc(i,e,$t)?e:Kf(i)}function Hf(i,e){var t;for(let n of i.media){let s=(t=n.ext)===null||t===void 0?void 0:t.find(r=>r.uri===e);if(s)return s.value}}function lc(i,e,t){return i.media.some(n=>{var s;return(s=n.ext)===null||s===void 0?void 0:s.some(r=>r.value===e&&r.uri!==t)})}function Kf(i){let e=0;return i.media.forEach(t=>{var n;(n=t.ext)===null||n===void 0||n.forEach(s=>{s.value>e&&(e=s.value)})}),e+1===15?16:e+1}function Ml(i,e){return i.split(";").some(t=>t.trim()===e)}function uc(i,e,t){let n=Pn(i.mid),s=0;i.rtp.some(r=>r.codec.toLowerCase()==="opus"?(s=r.payload,!0):!1),s>0&&(i.rtcpFb||(i.rtcpFb=[]),t.includes(n)&&!i.rtcpFb.some(r=>r.payload===s&&r.type==="nack")&&i.rtcpFb.push({payload:s,type:"nack"}),(e.includes(n)||e.length===1&&e[0]==="all")&&i.fmtp.some(r=>r.payload===s?(Ml(r.config,"stereo=1")||(r.config+=";stereo=1"),!0):!1))}function Gf(i){let e=new Set;for(let t of i)t.mid&&!t.sender.track&&e.add(t.mid);return e}function hc(i,e){var t,n;let s=new Map,r=new Set;for(let a of i){let o=e(a);for(let c of(t=a.fmtp)!==null&&t!==void 0?t:[])o?s.has(c.payload)||s.set(c.payload,c.config):(s.set(c.payload,c.config),r.add(c.payload))}if(s.size!==0){for(let a of i)if(e(a))for(let o of(n=a.fmtp)!==null&&n!==void 0?n:[]){let c=s.get(o.payload);c!==void 0&&o.config!==c&&(o.config=c)}}}function zf(i){var e;let t=[],n=[],s=Oe.parse((e=i.sdp)!==null&&e!==void 0?e:""),r=0;return s.media.forEach(a=>{var o;let c=Pn(a.mid);a.type==="audio"&&(a.rtp.some(d=>d.codec.toLowerCase()==="opus"?(r=d.payload,!0):!1),!((o=a.rtcpFb)===null||o===void 0)&&o.some(d=>d.payload===r&&d.type==="nack")&&n.push(c),a.fmtp.some(d=>d.payload===r?(Ml(d.config,"sprop-stereo=1")&&t.push(c),!0):!1))}),{stereoMids:t,nackMids:n}}function ti(i){if(i.connection){let e=i.connection.ip.indexOf(":")>=0;(i.connection.version===4&&e||i.connection.version===6&&!e)&&(i.connection.ip="0.0.0.0",i.connection.version=4)}}function Pn(i){return typeof i=="number"?i.toFixed(0):i}var zr="vp8",Jf={audioPreset:_r.music,dtx:!0,red:!0,forceStereo:!1,simulcast:!0,screenShareEncoding:no.h1080fps15.encoding,stopMicTrackOnMute:!1,videoCodec:zr,backupCodec:!0,preConnectBuffer:!1},Al={deviceId:{ideal:"default"},autoGainControl:!0,echoCancellation:!0,noiseSuppression:!0,voiceIsolation:!0},Ol={deviceId:{ideal:"default"},resolution:Fn.h720.resolution},Qf={adaptiveStream:!1,dynacast:!1,stopLocalTrackOnUnpublish:!0,reconnectPolicy:new mr,disconnectOnPageLeave:!0,webAudioMix:!1,singlePeerConnection:!0},ho={autoSubscribe:!0,maxRetries:1,peerConnectionTimeout:15e3,websocketTimeout:15e3},$;(function(i){i[i.NEW=0]="NEW",i[i.CONNECTING=1]="CONNECTING",i[i.CONNECTED=2]="CONNECTED",i[i.FAILED=3]="FAILED",i[i.CLOSING=4]="CLOSING",i[i.CLOSED=5]="CLOSED"})($||($={}));var Oi=class{get needsPublisher(){return this.isPublisherConnectionRequired}get needsSubscriber(){return this.isSubscriberConnectionRequired}get currentState(){return this.state}get mode(){return this._mode}constructor(e,t,n){var s;this.peerConnectionTimeout=ho.peerConnectionTimeout,this.log=W,this.iceLog=W,this.updateState=()=>{var r,a;let o=this.state,c=this.requiredTransports.map(d=>d.getConnectionState());c.every(d=>d==="connected")?this.state=$.CONNECTED:c.some(d=>d==="failed")?this.state=$.FAILED:c.some(d=>d==="connecting")?this.state=$.CONNECTING:c.every(d=>d==="closed")?this.state=$.CLOSED:c.some(d=>d==="closed")?this.state=$.CLOSING:c.every(d=>d==="new")&&(this.state=$.NEW),o!==this.state&&(this.log.debug("pc state change: from ".concat($[o]," to ").concat($[this.state])),(r=this.onStateChange)===null||r===void 0||r.call(this,this.state,this.publisher.getConnectionState(),(a=this.subscriber)===null||a===void 0?void 0:a.getConnectionState()))},this.loggerOptions=t,this.log=he((s=t.loggerName)!==null&&s!==void 0?s:te.PCManager,()=>this.logContext),this.iceLog=he(te.ICE,()=>this.logContext),this.isPublisherConnectionRequired=e!=="subscriber-primary",this.isSubscriberConnectionRequired=e==="subscriber-primary",this.publisher=new Ai(n,t),this._mode=e,e!=="publisher-only"&&(this.subscriber=new Ai(n,t),this.subscriber.onConnectionStateChange=this.updateState,this.subscriber.onIceConnectionStateChange=this.updateState,this.subscriber.onSignalingStatechange=this.updateState,this.subscriber.onIceCandidate=r=>{var a;(a=this.onIceCandidate)===null||a===void 0||a.call(this,r,Ve.SUBSCRIBER)},this.subscriber.onDataChannel=r=>{var a;(a=this.onDataChannel)===null||a===void 0||a.call(this,r)},this.subscriber.onTrack=r=>{var a;(a=this.onTrack)===null||a===void 0||a.call(this,r)}),this.publisher.onConnectionStateChange=this.updateState,this.publisher.onIceConnectionStateChange=this.updateState,this.publisher.onSignalingStatechange=this.updateState,this.publisher.onIceCandidate=r=>{var a;(a=this.onIceCandidate)===null||a===void 0||a.call(this,r,Ve.PUBLISHER)},this.publisher.onTrack=r=>{var a;(a=this.onTrack)===null||a===void 0||a.call(this,r)},this.publisher.onOffer=(r,a)=>{var o;(o=this.onPublisherOffer)===null||o===void 0||o.call(this,r,a)},this.state=$.NEW,this.connectionLock=new le,this.remoteOfferLock=new le}get logContext(){var e,t;return Object.assign({},(t=(e=this.loggerOptions).loggerContextCb)===null||t===void 0?void 0:t.call(e))}requirePublisher(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!0;this.isPublisherConnectionRequired=e,this.updateState()}createAndSendPublisherOffer(e){return this.publisher.createAndSendOffer(e)}setPublisherAnswer(e,t){return this.publisher.setRemoteDescription(e,t)}removeTrack(e){return this.publisher.removeTrack(e)}close(){return f(this,void 0,void 0,function*(){var e;if(this.publisher&&this.publisher.getSignallingState()!=="closed"){let t=this.publisher;for(let n of t.getSenders())try{t.canRemoveTrack()&&t.removeTrack(n)}catch(s){this.log.warn("could not removeTrack",{error:s})}}yield Promise.all([this.publisher.close(),(e=this.subscriber)===null||e===void 0?void 0:e.close()]),this.updateState()})}triggerIceRestart(){return f(this,void 0,void 0,function*(){this.iceLog.warn("triggering ICE restart"),this.needsPublisher&&(yield this.createAndSendPublisherOffer({iceRestart:!0}))})}addIceCandidate(e,t){return f(this,void 0,void 0,function*(){var n;this.iceLog.debug("adding remote ICE candidate",{target:t,candidate:e}),t===Ve.PUBLISHER?yield this.publisher.addIceCandidate(e):yield(n=this.subscriber)===null||n===void 0?void 0:n.addIceCandidate(e)})}createSubscriberAnswerFromOffer(e,t){return f(this,void 0,void 0,function*(){var n,s,r;this.log.debug("received server offer",{RTCSdpType:e.type,sdp:e.sdp,signalingState:(n=this.subscriber)===null||n===void 0?void 0:n.getSignallingState().toString()});let a=yield this.remoteOfferLock.lock();try{return(yield(s=this.subscriber)===null||s===void 0?void 0:s.setRemoteDescription(e,t))?yield(r=this.subscriber)===null||r===void 0?void 0:r.createAndSetAnswer():void 0}finally{a()}})}updateConfiguration(e,t){var n;this.log.debug("updating rtc configuration",{iceRestart:t}),this.publisher.setConfiguration(e),(n=this.subscriber)===null||n===void 0||n.setConfiguration(e),t&&this.triggerIceRestart()}ensurePCTransportConnection(e,t){return f(this,void 0,void 0,function*(){var n;let s=yield this.connectionLock.lock();try{this.isPublisherConnectionRequired&&this.publisher.getConnectionState()!=="connected"&&this.publisher.getConnectionState()!=="connecting"&&(this.log.debug("negotiation required, start negotiating"),this.publisher.negotiate()),yield Promise.all((n=this.requiredTransports)===null||n===void 0?void 0:n.map(r=>this.ensureTransportConnected(r,e,t)))}finally{s()}})}negotiate(e){return f(this,void 0,void 0,function*(){return new ge((t,n)=>{let s=this.publisher.latestOfferId;if(this.publisher.latestAcknowledgedOfferId>s){this.log.debug("negotiation already handled in more recent acknowledged offer",this.logContext),t();return}let r=!1,a=()=>{r||(r=!0,clearTimeout(d),this.publisher.off(vt.OfferAnswered,o),e.signal.removeEventListener("abort",c))},o=l=>{l>s&&(a(),t())},c=()=>{a(),n(new Ze("negotiation aborted"))},d=setTimeout(()=>{a(),n(new Ze("negotiation timed out"))},this.peerConnectionTimeout);e.signal.addEventListener("abort",c),this.publisher.on(vt.OfferAnswered,o),this.publisher.negotiate(l=>{a(),l instanceof Error?n(l):n(new Error(String(l)))})})})}addPublisherTransceiver(e,t){return this.publisher.addTransceiver(e,t)}addPublisherTransceiverOfKind(e,t){return this.publisher.addTransceiverOfKind(e,t)}getMidForReceiver(e){let n=(this.subscriber?this.subscriber.getTransceivers():this.publisher.getTransceivers()).find(s=>s.receiver===e);return n?.mid}getMaxPublisherMessageSize(){return this.publisher.getMaxMessageSize()}addPublisherTrack(e){return this.publisher.addTrack(e)}createPublisherDataChannel(e,t){return this.publisher.createDataChannel(e,t)}getConnectedAddress(e){return e===Ve.PUBLISHER?this.publisher.getConnectedAddress():e===Ve.SUBSCRIBER?this.publisher.getConnectedAddress():this.requiredTransports[0].getConnectedAddress()}get requiredTransports(){let e=[];return this.isPublisherConnectionRequired&&e.push(this.publisher),this.isSubscriberConnectionRequired&&this.subscriber&&e.push(this.subscriber),e}ensureTransportConnected(e,t){return f(this,arguments,void 0,function(n,s){var r=this;let a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:this.peerConnectionTimeout;return function*(){if(n.getConnectionState()!=="connected")return new Promise((c,d)=>f(r,void 0,void 0,function*(){let l=()=>{this.log.warn("abort transport connection"),z.clearTimeout(u),d(F.cancelled("room connection has been cancelled"))};s?.signal.aborted&&l(),s?.signal.addEventListener("abort",l);let u=z.setTimeout(()=>{s?.signal.removeEventListener("abort",l),d(F.internal("could not establish pc connection"))},a);for(;this.state!==$.CONNECTED;)if(yield ke(50),s?.signal.aborted){d(F.cancelled("room connection has been cancelled"));return}z.clearTimeout(u),s?.signal.removeEventListener("abort",l),c()}))}()})}},Di=class{constructor(e){this.bufferStatusLow=!0,this.headroomLock=new le,this.waiterAbortController=new AbortController,this.kind=e.kind,this.lowWaterMark=e.lowWaterMark,this.highWaterMark=e.highWaterMark,this.isEngineClosed=e.isEngineClosed,this.onBufferStatusChanged=e.onBufferStatusChanged}get channelHandle(){return this.handle}attach(e){this.handle&&this.handle!==e&&this.invalidateWaiters("data channel replaced"),this.handle=e}detach(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"data channel torn down";this.handle&&this.invalidateWaiters(e),this.handle=void 0}getChannel(){return this.handle}isBelowHighWaterMark(e){return e.bufferedAmount<=this.highWaterMark}isBelowLowWaterMark(e){return e.bufferedAmount<=e.bufferedAmountLowThreshold}lockHeadroom(){return this.headroomLock.lock()}waitForHeadroomWithLock(){return f(this,void 0,void 0,function*(){let e=yield this.lockHeadroom();try{yield this.waitForHeadroomWithoutLock()}finally{e()}})}waitForHeadroomWithoutLock(){return f(this,void 0,void 0,function*(){if(this.isEngineClosed())throw new Y("engine closed");let e=this.getChannel();if(!e)throw new Y("DataChannel not found, kind: ".concat(this.kind));if(this.isBelowHighWaterMark(e))return;let t=this.waiterAbortController.signal;yield new ge((n,s)=>{let r=()=>{c(),n()},a=()=>{c(),s(new Y("DataChannel ".concat(this.kind," closed while draining the buffer")))},o=()=>{c(),s(new Y("DataChannel ".concat(this.kind," was replaced or torn down while waiting for headroom")))},c=()=>{e.removeEventListener("bufferedamountlow",r),e.removeEventListener("close",a),t.removeEventListener("abort",o)};if(t.aborted){o();return}e.addEventListener("bufferedamountlow",r),e.addEventListener("close",a),t.addEventListener("abort",o)})})}invalidateWaiters(e){this.waiterAbortController.abort(e),this.waiterAbortController=new AbortController}refreshBufferStatus(){var e;let t=this.getChannel();if(!t)return;let n=this.isBelowLowWaterMark(t);n!==this.bufferStatusLow&&(this.bufferStatusLow=n,(e=this.onBufferStatusChanged)===null||e===void 0||e.call(this,n))}},Li=class extends Di{constructor(e){super(e),this.statCurrentBytes=0,this.statByterate=0,this.dropCount=0,this.bufferFullBehavior=e.bufferFullBehavior,this.shouldSkipSends=e.shouldSkipSends}send(e){return f(this,void 0,void 0,function*(){let t=this.getChannel();if(t){switch(this.bufferFullBehavior){case"wait":this.isBelowHighWaterMark(t)||(yield this.waitForHeadroomWithLock());break;case"drop":if(!this.isBelowLowWaterMark(t)){this.dropCount+=1,this.dropCount%100===0&&W.warn("dropping lossy data channel messages, total dropped: ".concat(this.dropCount));return}}if(this.statCurrentBytes+=e.byteLength,!this.shouldSkipSends())try{t.send(e),this.refreshBufferStatus()}catch(n){if(n instanceof TypeError)W.error(n);else throw n}}})}startThresholdTuning(){this.stopThresholdTuning(),this.statInterval=z.setInterval(()=>{this.statByterate=this.statCurrentBytes,this.statCurrentBytes=0;let e=this.getChannel();if(e){let t=this.statByterate/10;e.bufferedAmountLowThreshold=Math.min(Math.max(t,this.lowWaterMark),this.highWaterMark)}},1e3)}stopThresholdTuning(){this.statByterate=0,this.statCurrentBytes=0,this.statInterval&&(z.clearInterval(this.statInterval),this.statInterval=void 0),this.dropCount=0}},Ni=class{constructor(){this.buffer=[],this._totalSize=0,this._sentSize=0}push(e){this.buffer.push(e),this._totalSize+=e.data.byteLength,e.sent&&(this._sentSize+=e.data.byteLength)}pop(){let e=this.buffer.shift();return e&&(this._totalSize-=e.data.byteLength,e.sent&&(this._sentSize-=e.data.byteLength)),e}getAll(){return this.buffer.slice()}getUnsent(){return this.buffer.filter(e=>!e.sent)}markSent(e){e.sent||(e.sent=!0,this._sentSize+=e.data.byteLength)}markAllUnsent(){for(let e of this.buffer)e.sent=!1;this._sentSize=0}popToSequence(e){for(;this.buffer.length>0&&this.buffer[0].sequence<=e;)this.pop()}alignBufferedAmount(e){for(;this.buffer.length>0;){let t=this.buffer[0];if(!t.sent||this._sentSize-t.data.byteLength<=e)break;this.pop()}}get length(){return this.buffer.length}},Jr=class extends Di{constructor(e){super(e),this.messageBuffer=new Ni,this.sequence=1,this.isDeferringSends=e.isDeferringSends}nextSequence(){let e=this.sequence;return this.sequence+=1,e}send(e,t){return f(this,void 0,void 0,function*(){if(this.isDeferringSends()){this.messageBuffer.push({data:e,sequence:t,sent:!1});return}let n=this.getChannel();if(n){try{yield this.waitForHeadroomWithLock()}catch(s){if(this.isEngineClosed())throw s;this.messageBuffer.push({data:e,sequence:t,sent:!1});return}if(this.isDeferringSends()){this.messageBuffer.push({data:e,sequence:t,sent:!1});return}this.messageBuffer.push({data:e,sequence:t,sent:!0}),n.send(e),this.refreshBufferStatus()}})}replay(e){return f(this,void 0,void 0,function*(){let t=this.getChannel();if(!t)return;this.messageBuffer.popToSequence(e);let n=yield this.lockHeadroom();try{this.messageBuffer.markAllUnsent();for(let s=this.messageBuffer.getUnsent();s.length>0;s=this.messageBuffer.getUnsent())for(let r of s)yield this.waitForHeadroomWithoutLock(),t.send(r.data),this.messageBuffer.markSent(r)}finally{n()}this.refreshBufferStatus()})}refreshBufferStatus(){let e=this.channelHandle;e&&this.messageBuffer.alignBufferedAmount(e.bufferedAmount),super.refreshBufferStatus()}reset(){this.messageBuffer=new Ni,this.sequence=1}},V;(function(i){i[i.RELIABLE=0]="RELIABLE",i[i.LOSSY=1]="LOSSY",i[i.DATA_TRACK_LOSSY=2]="DATA_TRACK_LOSSY"})(V||(V={}));var Yf=64*1024,$f=1024*1024,Xf=8*1024,Zf=256*1024;function ep(i){return i===V.RELIABLE?Yf:Xf}function tp(i){return i===V.RELIABLE?$f:Zf}var mc="_lossy",fc="_reliable",pc="_data_track",Qr=class{constructor(e){this.opts=e;let t=n=>({kind:n,lowWaterMark:ep(n),highWaterMark:tp(n),isEngineClosed:e.isEngineClosed,onBufferStatusChanged:s=>e.onBufferStatusChanged(n,s)});this.reliable=new Jr(Object.assign(Object.assign({},t(V.RELIABLE)),{isDeferringSends:e.isReconnecting})),this.lossy=new Li(Object.assign(Object.assign({},t(V.LOSSY)),{bufferFullBehavior:"drop",shouldSkipSends:e.isReconnecting})),this.dataTrack=new Li(Object.assign(Object.assign({},t(V.DATA_TRACK_LOSSY)),{bufferFullBehavior:"wait",shouldSkipSends:e.isReconnecting}))}channelFor(e){switch(e){case V.RELIABLE:return this.reliable;case V.LOSSY:return this.lossy;case V.DATA_TRACK_LOSSY:return this.dataTrack}}getHandle(e){if(!(arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1))return this.channelFor(e).channelHandle;switch(e){case V.RELIABLE:return this.reliableSub;case V.LOSSY:return this.lossySub;case V.DATA_TRACK_LOSSY:return this.dataTrackSub}}get hasPublisherChannels(){return!!(this.reliable.channelHandle||this.lossy.channelHandle||this.dataTrack.channelHandle)}createPublisherChannels(e){for(let n of[this.lossy,this.reliable,this.dataTrack]){let s=n.channelHandle;s&&(s.onmessage=null,s.onerror=null,s.onclose=null)}let t=(n,s,r)=>{s.onmessage=r,s.onerror=this.opts.onDataError,s.onclose=()=>this.opts.onChannelClose(n.kind),s.bufferedAmountLowThreshold=n.lowWaterMark,s.onbufferedamountlow=()=>n.refreshBufferStatus(),n.attach(s)};t(this.lossy,e.createPublisherDataChannel(mc,{ordered:!1,maxRetransmits:0}),this.opts.onDataMessage),t(this.reliable,e.createPublisherDataChannel(fc,{ordered:!0}),this.opts.onDataMessage),t(this.dataTrack,e.createPublisherDataChannel(pc,{ordered:!1,maxRetransmits:0}),this.opts.onDataTrackMessage),this.lossy.startThresholdTuning()}adoptSubscriberChannel(e){let t;if(e.label===fc)this.reliableSub=e,t=this.opts.onDataMessage;else if(e.label===mc)this.lossySub=e,t=this.opts.onDataMessage;else if(e.label===pc)this.dataTrackSub=e,t=this.opts.onDataTrackMessage;else return!1;return e.onmessage=t,!0}teardown(){let e=t=>{t&&(t.onbufferedamountlow=null,t.onclose=null,t.onclosing=null,t.onerror=null,t.onmessage=null,t.onopen=null,t.close())};for(let t of[this.lossy,this.reliable,this.dataTrack]){let n=t.channelHandle;t.detach("peer connections cleaned up"),e(n)}e(this.lossySub),e(this.reliableSub),e(this.dataTrackSub),this.lossySub=void 0,this.reliableSub=void 0,this.dataTrackSub=void 0,this.reliable.reset()}},mo=typeof MediaRecorder<"u",Yr=class{constructor(){throw new Error("MediaRecorder is not available in this environment")}},np=mo?MediaRecorder:Yr,$r=class extends np{constructor(e,t){if(!mo)throw new Error("MediaRecorder is not available in this environment");super(new MediaStream([e.mediaStreamTrack]),t);let n,s,r=()=>s===void 0,a=()=>{this.removeEventListener("dataavailable",n),this.removeEventListener("stop",a),this.removeEventListener("error",o),s?.close(),s=void 0},o=c=>{s?.error(c),this.removeEventListener("dataavailable",n),this.removeEventListener("stop",a),this.removeEventListener("error",o),s=void 0};this.byteStream=new ReadableStream({start:c=>{s=c,n=d=>f(this,void 0,void 0,function*(){let l;if(d.data.arrayBuffer){let u=yield d.data.arrayBuffer();l=new Uint8Array(u)}else if(d.data.byteArray)l=d.data.byteArray;else throw new Error("no data available!");r()||c.enqueue(l)}),this.addEventListener("dataavailable",n)},cancel:()=>{a()}}),this.addEventListener("stop",a),this.addEventListener("error",o)}};function ip(){return mo}var sp=1e3,rp=1e4,xi=class extends E{get sender(){return this._sender}set sender(e){this._sender=e}get constraints(){return this._constraints}get hasPreConnectBuffer(){return!!this.localTrackRecorder}constructor(e,t,n){let s=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1,r=arguments.length>4?arguments[4]:void 0;super(e,t,r),this.manuallyStopped=!1,this.pendingDeviceChange=!1,this._isUpstreamPaused=!1,this.handleTrackMuteEvent=()=>this.debouncedTrackMuteHandler().catch(()=>this.log.debug("track mute bounce got cancelled by an unmute event",this.logContext)),this.debouncedTrackMuteHandler=ao(()=>f(this,void 0,void 0,function*(){yield this.pauseUpstream()}),5e3),this.handleTrackUnmuteEvent=()=>f(this,void 0,void 0,function*(){this.debouncedTrackMuteHandler.cancel("unmute"),yield this.resumeUpstream()}),this.handleEnded=()=>{this.isInBackground&&(this.reacquireTrack=!0),this._mediaStreamTrack.removeEventListener("mute",this.handleTrackMuteEvent),this._mediaStreamTrack.removeEventListener("unmute",this.handleTrackUnmuteEvent),this.emit(P.Ended,this)},this.reacquireTrack=!1,this.providedByUser=s,this.muteLock=new le,this.pauseUpstreamLock=new le,this.trackChangeLock=new le,this.trackChangeLock.lock().then(a=>f(this,void 0,void 0,function*(){try{yield this.setMediaStreamTrack(e,!0)}finally{a()}})),this._constraints=e.getConstraints(),n&&(this._constraints=n)}get id(){return this._mediaStreamTrack.id}get dimensions(){if(this.kind!==E.Kind.Video)return;let e=this._mediaStreamTrack.getSettings(),t=e.width,n=e.height;if(t&&n)return{width:t,height:n}}get isUpstreamPaused(){return this._isUpstreamPaused}get isUserProvided(){return this.providedByUser}get mediaStreamTrack(){var e,t;return(t=(e=this.processor)===null||e===void 0?void 0:e.processedTrack)!==null&&t!==void 0?t:this._mediaStreamTrack}get isLocal(){return!0}getSourceTrackSettings(){return this._mediaStreamTrack.getSettings()}setMediaStreamTrack(e,t,n){return f(this,void 0,void 0,function*(){var s;if(e===this._mediaStreamTrack&&!t)return;this._mediaStreamTrack&&(this.attachedElements.forEach(a=>{Yt(this._mediaStreamTrack,a)}),this.debouncedTrackMuteHandler.cancel("new-track"),this._mediaStreamTrack.removeEventListener("ended",this.handleEnded),this._mediaStreamTrack.removeEventListener("mute",this.handleTrackMuteEvent),this._mediaStreamTrack.removeEventListener("unmute",this.handleTrackUnmuteEvent)),this.mediaStream=new MediaStream([e]),e&&(e.addEventListener("ended",this.handleEnded),e.addEventListener("mute",this.handleTrackMuteEvent),e.addEventListener("unmute",this.handleTrackUnmuteEvent),this._constraints=e.getConstraints());let r;if(this.processor&&e){if(this.log.debug("restarting processor",this.logContext),this.kind==="unknown")throw TypeError("cannot set processor on track of unknown kind");this.processorElement&&(Gt(e,this.processorElement),this.processorElement.muted=!0),yield this.processor.restart({track:e,kind:this.kind,element:this.processorElement,localTrack:this}),r=this.processor.processedTrack}this.sender&&((s=this.sender.transport)===null||s===void 0?void 0:s.state)!=="closed"&&(yield this.sender.replaceTrack(r??e)),!this.providedByUser&&this._mediaStreamTrack!==e&&this._mediaStreamTrack.stop(),this._mediaStreamTrack=e,e&&(this._mediaStreamTrack.enabled=n?!0:!this.isMuted,yield this.resumeUpstream(),this.attachedElements.forEach(a=>{Gt(r??e,a)}))})}waitForDimensions(){return f(this,arguments,void 0,function(){var e=this;let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:sp;return function*(){var n;if(e.kind===E.Kind.Audio)throw new Error("cannot get dimensions for audio tracks");((n=ye())===null||n===void 0?void 0:n.os)==="iOS"&&(yield ke(10));let s=Date.now();for(;Date.now()-s0&&arguments[0]!==void 0?arguments[0]:!0;return function*(){if(e.source===E.Source.ScreenShare)return;let n=e._mediaStreamTrack.getSettings(),s=n.deviceId,r=n.groupId,a=e.kind===E.Kind.Audio?"audioinput":"videoinput";return t?Me.getInstance().normalizeDeviceId(a,s,r):s}()})}mute(){return f(this,void 0,void 0,function*(){return this.setTrackMuted(!0),this})}unmute(){return f(this,void 0,void 0,function*(){return this.setTrackMuted(!1),this})}replaceTrack(e,t){return f(this,void 0,void 0,function*(){let n=yield this.trackChangeLock.lock();try{if(!this.sender)throw new We("unable to replace an unpublished track");let s,r;typeof t=="boolean"?s=t:t!==void 0&&(s=t.userProvidedTrack,r=t.stopProcessor),this.providedByUser=s??!0,this.log.debug("replace MediaStreamTrack",this.logContext),yield this.setMediaStreamTrack(e),r&&this.processor&&(yield this.internalStopProcessor())}finally{n()}return yield this.onSenderTrackSwapped(),this})}onSenderTrackSwapped(){return f(this,void 0,void 0,function*(){})}restart(e,t){return f(this,void 0,void 0,function*(){this.manuallyStopped=!1;let n=yield this.trackChangeLock.lock();try{e||(e=this._constraints);let s=e,r=s.deviceId,a=s.facingMode,o=Zh(e,["deviceId","facingMode"]);this.log.debug("restarting track with constraints",Object.assign(Object.assign({},this.logContext),{constraints:e}));let c={audio:!1,video:!1};this.kind===E.Kind.Video?c.video=r||a?{deviceId:r,facingMode:a}:!0:c.audio=r?Object.assign({deviceId:r},o):!0,this.attachedElements.forEach(u=>{Yt(this.mediaStreamTrack,u)}),this._mediaStreamTrack.removeEventListener("ended",this.handleEnded),this._mediaStreamTrack.stop();let l=(yield navigator.mediaDevices.getUserMedia(c)).getTracks()[0];return this.kind===E.Kind.Video&&(yield l.applyConstraints(o)),l.addEventListener("ended",this.handleEnded),this.log.debug("re-acquired MediaStreamTrack",this.logContext),yield this.setMediaStreamTrack(l,!1,t),this._constraints=e,this.pendingDeviceChange=!1,this.emit(P.Restarted,this),this.manuallyStopped&&(this.log.warn("track was stopped during a restart, stopping restarted track",this.logContext),this.stop()),this}finally{n()}})}setTrackMuted(e){this.log.debug("setting ".concat(this.kind," track ").concat(e?"muted":"unmuted"),this.logContext),!(this.isMuted===e&&this._mediaStreamTrack.enabled!==e)&&(this.isMuted=e,this._mediaStreamTrack.enabled=!e,this.emit(e?P.Muted:P.Unmuted,this))}get needsReAcquisition(){return this._mediaStreamTrack.readyState!=="live"||this._mediaStreamTrack.muted||!this._mediaStreamTrack.enabled||this.reacquireTrack}handleAppVisibilityChanged(){let e=Object.create(null,{handleAppVisibilityChanged:{get:()=>super.handleAppVisibilityChanged}});return f(this,void 0,void 0,function*(){yield e.handleAppVisibilityChanged.call(this),Sl()&&(this.log.debug("visibility changed, is in Background: ".concat(this.isInBackground),this.logContext),!this.isInBackground&&this.needsReAcquisition&&!this.isUserProvided&&!this.isMuted&&(this.log.debug("track needs to be reacquired, restarting ".concat(this.source),this.logContext),yield this.restart(),this.reacquireTrack=!1))})}stop(){var e;this.manuallyStopped=!0,super.stop(),this._mediaStreamTrack.removeEventListener("ended",this.handleEnded),this._mediaStreamTrack.removeEventListener("mute",this.handleTrackMuteEvent),this._mediaStreamTrack.removeEventListener("unmute",this.handleTrackUnmuteEvent),(e=this.processor)===null||e===void 0||e.destroy(),this.processor=void 0}pauseUpstream(){return f(this,void 0,void 0,function*(){var e;let t=yield this.pauseUpstreamLock.lock();try{if(this._isUpstreamPaused===!0)return;if(!this.sender){this.log.warn("unable to pause upstream for an unpublished track",this.logContext);return}this._isUpstreamPaused=!0,this.emit(P.UpstreamPaused,this);let n=ye();if(n?.name==="Safari"&&Ge(n.version,"12.0")<0)throw new Un("pauseUpstream is not supported on Safari < 12.");((e=this.sender.transport)===null||e===void 0?void 0:e.state)!=="closed"&&(yield this.sender.replaceTrack(null))}finally{t()}})}resumeUpstream(){return f(this,void 0,void 0,function*(){var e;let t=yield this.pauseUpstreamLock.lock();try{if(this._isUpstreamPaused===!1)return;if(!this.sender){this.log.warn("unable to resume upstream for an unpublished track",this.logContext);return}this._isUpstreamPaused=!1,this.emit(P.UpstreamResumed,this),((e=this.sender.transport)===null||e===void 0?void 0:e.state)!=="closed"&&(yield this.sender.replaceTrack(this.mediaStreamTrack))}finally{t()}})}getRTCStatsReport(){return f(this,void 0,void 0,function*(){var e;return!((e=this.sender)===null||e===void 0)&&e.getStats?yield this.sender.getStats():void 0})}setProcessor(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return function*(){var r;let a=yield n.trackChangeLock.lock();try{n.log.debug("setting up processor",n.logContext);let o=document.createElement(n.kind),c={kind:n.kind,track:n._mediaStreamTrack,element:o,audioContext:n.audioContext,localTrack:n};if(yield t.init(c),n.log.debug("processor initialized",n.logContext),n.processor&&(yield n.internalStopProcessor()),n.kind==="unknown")throw TypeError("cannot set processor on track of unknown kind");if(Gt(n._mediaStreamTrack,o),o.muted=!0,o.play().catch(d=>{d instanceof DOMException&&d.name==="AbortError"?(n.log.warn("failed to play processor element, retrying",Object.assign(Object.assign({},n.logContext),{error:d})),setTimeout(()=>{o.play().catch(l=>{n.log.error("failed to play processor element",Object.assign(Object.assign({},n.logContext),{err:l}))})},100)):n.log.error("failed to play processor element",Object.assign(Object.assign({},n.logContext),{error:d}))}),n.processor=t,n.processorElement=o,n.processor.processedTrack){for(let d of n.attachedElements)d!==n.processorElement&&s&&(Yt(n._mediaStreamTrack,d),Gt(n.processor.processedTrack,d));yield(r=n.sender)===null||r===void 0?void 0:r.replaceTrack(n.processor.processedTrack)}n.emit(P.TrackProcessorUpdate,n.processor)}finally{a()}yield n.onSenderTrackSwapped()}()})}getProcessor(){return this.processor}stopProcessor(){return f(this,arguments,void 0,function(){var e=this;let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!0;return function*(){let n=yield e.trackChangeLock.lock();try{yield e.internalStopProcessor(t)}finally{n()}yield e.onSenderTrackSwapped()}()})}internalStopProcessor(){return f(this,arguments,void 0,function(){var e=this;let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!0;return function*(){var n,s;e.processor&&(e.log.debug("stopping processor",e.logContext),(n=e.processor.processedTrack)===null||n===void 0||n.stop(),yield e.processor.destroy(),e.processor=void 0,t||((s=e.processorElement)===null||s===void 0||s.remove(),e.processorElement=void 0),yield e._mediaStreamTrack.applyConstraints(e._constraints),yield e.setMediaStreamTrack(e._mediaStreamTrack,!0),e.emit(P.TrackProcessorUpdate))}()})}startPreConnectBuffer(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:100;if(!ip()){this.log.warn("MediaRecorder is not available, cannot start preconnect buffer",this.logContext);return}if(this.localTrackRecorder){this.log.warn("preconnect buffer already started");return}else{let t="audio/webm;codecs=opus";MediaRecorder.isTypeSupported(t)||(t="video/mp4"),this.localTrackRecorder=new $r(this,{mimeType:t})}this.localTrackRecorder.start(e),this.autoStopPreConnectBuffer=setTimeout(()=>{this.log.warn("preconnect buffer timed out, stopping recording automatically",this.logContext),this.stopPreConnectBuffer()},rp)}stopPreConnectBuffer(){clearTimeout(this.autoStopPreConnectBuffer),this.localTrackRecorder&&(this.localTrackRecorder.stop(),this.localTrackRecorder=void 0)}getPreConnectBuffer(){var e;return(e=this.localTrackRecorder)===null||e===void 0?void 0:e.byteStream}getPreConnectBufferMimeType(){var e;return(e=this.localTrackRecorder)===null||e===void 0?void 0:e.mimeType}},dn=class extends xi{get enhancedNoiseCancellation(){return this.isKrispNoiseFilterEnabled}constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,s=arguments.length>3?arguments[3]:void 0,r=arguments.length>4?arguments[4]:void 0;super(e,E.Kind.Audio,t,n,r),this.stopOnMute=!1,this.isKrispNoiseFilterEnabled=!1,this.monitorSender=()=>f(this,void 0,void 0,function*(){if(!this.sender){this._currentBitrate=0;return}let a;try{a=yield this.getSenderStats()}catch(o){this.log.error("could not get audio sender stats",Object.assign(Object.assign({},this.logContext),{error:o}));return}a&&this.prevStats&&(this._currentBitrate=fs(a,this.prevStats)),this.prevStats=a}),this.handleKrispNoiseFilterEnable=()=>{this.isKrispNoiseFilterEnabled=!0,this.log.debug("Krisp noise filter enabled",this.logContext),this.emit(P.AudioTrackFeatureUpdate,this,me.TF_ENHANCED_NOISE_CANCELLATION,!0)},this.handleKrispNoiseFilterDisable=()=>{this.isKrispNoiseFilterEnabled=!1,this.log.debug("Krisp noise filter disabled",this.logContext),this.emit(P.AudioTrackFeatureUpdate,this,me.TF_ENHANCED_NOISE_CANCELLATION,!1)},this.audioContext=s,this.checkForSilence()}mute(){let e=Object.create(null,{mute:{get:()=>super.mute}});return f(this,void 0,void 0,function*(){let t=yield this.muteLock.lock();try{return this.isMuted?(this.log.debug("Track already muted",this.logContext),this):(this.source===E.Source.Microphone&&this.stopOnMute&&!this.isUserProvided&&(this.log.debug("stopping mic track",this.logContext),this._mediaStreamTrack.stop()),yield e.mute.call(this),this)}finally{t()}})}unmute(){let e=Object.create(null,{unmute:{get:()=>super.unmute}});return f(this,void 0,void 0,function*(){let t=yield this.muteLock.lock();try{return this.isMuted?(this.source===E.Source.Microphone&&(this.stopOnMute||this._mediaStreamTrack.readyState==="ended"||this.pendingDeviceChange)&&!this.isUserProvided&&(this.log.debug("reacquiring mic track",this.logContext),yield this.restart(void 0,!0)),yield e.unmute.call(this),this):(this.log.debug("Track already unmuted",this.logContext),this)}finally{t()}})}restartTrack(e){return f(this,void 0,void 0,function*(){let t;if(e){let n=io({audio:e});typeof n.audio!="boolean"&&(t=n.audio)}yield this.restart(t)})}applyConstraints(e){return f(this,void 0,void 0,function*(){let t=yield this.trackChangeLock.lock();try{let n=yield this._mediaStreamTrack.applyConstraints(e);return this._constraints=Object.assign(Object.assign({},this._constraints),e),n}finally{t()}})}restart(e,t){let n=Object.create(null,{restart:{get:()=>super.restart}});return f(this,void 0,void 0,function*(){let s=yield n.restart.call(this,e,t);return this.checkForSilence(),s})}startMonitor(){Ce()&&(this.monitorInterval||(this.monitorInterval=setInterval(()=>{this.monitorSender()},oo)))}setProcessor(e){return f(this,void 0,void 0,function*(){var t;let n=yield this.trackChangeLock.lock();try{if(!Ke()&&!this.audioContext)throw Error("Audio context needs to be set on LocalAudioTrack in order to enable processors");this.processor&&(yield this.internalStopProcessor());let s={kind:this.kind,track:this._mediaStreamTrack,audioContext:this.audioContext,localTrack:this};this.log.debug("setting up audio processor ".concat(e.name),this.logContext),yield e.init(s),this.processor=e,this.processor.processedTrack&&(yield(t=this.sender)===null||t===void 0?void 0:t.replaceTrack(this.processor.processedTrack),this.processor.processedTrack.addEventListener("enable-lk-krisp-noise-filter",this.handleKrispNoiseFilterEnable),this.processor.processedTrack.addEventListener("disable-lk-krisp-noise-filter",this.handleKrispNoiseFilterDisable)),this.emit(P.TrackProcessorUpdate,this.processor)}finally{n()}})}setAudioContext(e){this.audioContext=e}getSenderStats(){return f(this,void 0,void 0,function*(){var e;if(!(!((e=this.sender)===null||e===void 0)&&e.getStats))return;let t=yield this.sender.getStats(),n;return t.forEach(s=>{if(s.type==="outbound-rtp"){n={type:"audio",streamId:s.id,packetsSent:s.packetsSent,bytesSent:s.bytesSent,timestamp:s.timestamp};let r=t.get(s.remoteId);r&&(n.packetsLost=r.packetsLost,n.jitter=r.jitter,n.roundTripTime=r.roundTripTime)}}),n})}checkForSilence(){return f(this,void 0,void 0,function*(){let e=yield vl(this);return e&&(this.isMuted||this.log.debug("silence detected on local audio track",this.logContext),this.emit(P.AudioSilenceDetected)),e})}};function ap(i,e,t){switch(i.kind){case"audio":return new dn(i,e,!1,void 0,t);case"video":return new ln(i,e,!1,t);default:throw new We("unsupported track type: ".concat(i.kind))}}var op=Object.values(Fn),cp=Object.values(Rr),dp=Object.values(no),lp=[Fn.h180,Fn.h360],up=[Rr.h180,Rr.h360],hp=i=>[{scaleResolutionDownBy:2,fps:i.encoding.maxFramerate}].map(t=>{var n,s;return new J(Math.floor(i.width/t.scaleResolutionDownBy),Math.floor(i.height/t.scaleResolutionDownBy),Math.max(15e4,Math.floor(i.encoding.maxBitrate/(Math.pow(t.scaleResolutionDownBy,2)*(((n=i.encoding.maxFramerate)!==null&&n!==void 0?n:30)/((s=t.fps)!==null&&s!==void 0?s:30))))),t.fps,i.encoding.priority)}),Xr=["q","h","f"];function Ui(i,e,t,n){var s,r;let a=n?.videoEncoding;i&&(a=n?.screenShareEncoding);let o=n?.simulcast,c=n?.scalabilityMode,d=n?.videoCodec,l=Xt(d,n);if(!a&&!o&&!c||!e||!t)return[{}];a||(a=fp(i,e,t,d),W.debug("using video encoding",a));let u=a.maxFramerate,h=new J(e,t,a.maxBitrate,a.maxFramerate,a.priority);if(c&&wt(d)&&!l){let S=new Fi(c),I=[];if(S.spatial>3)throw new Error("unsupported scalabilityMode: ".concat(c));let b=ye();if(Tl()){let y=S.suffix=="h"?2:3,C=Bm(b);for(let M=0;M(l&&S.forEach(I=>{I.scalabilityMode=c}),S),g;i?g=(s=vc(n?.screenShareSimulcastLayers))!==null&&s!==void 0?s:gc(i,h):g=(r=vc(n?.videoSimulcastLayers))!==null&&r!==void 0?r:gc(i,h);let p;if(g.length>0){let S=g[0];if(g.length>1){var k=g,T=L(k,2);p=T[1]}let I=Math.max(e,t);if(I>=960&&p)return m(Ks(e,t,[S,p,h],u));if(I>=480)return m(Ks(e,t,[S,h],u))}return m(Ks(e,t,[h]))}function mp(i,e,t){var n,s;return wt(i)&&!Xt(i,e)?(s=(n=t[0])===null||n===void 0?void 0:n.maxBitrate)!==null&&s!==void 0?s:0:t.reduce((r,a)=>{var o;return r+((o=a.maxBitrate)!==null&&o!==void 0?o:0)},0)}function Dl(i,e,t){var n,s,r,a;if(!t.backupCodec||t.backupCodec===!0||t.backupCodec.codec===t.videoCodec)return;e!==t.backupCodec.codec&&W.warn("requested a different codec than specified as backup",{serverRequested:e,backup:t.backupCodec.codec}),t.videoCodec=e,t.videoEncoding=t.backupCodec.encoding;let o=i.mediaStreamTrack.getSettings(),c=(n=o.width)!==null&&n!==void 0?n:(s=i.dimensions)===null||s===void 0?void 0:s.width,d=(r=o.height)!==null&&r!==void 0?r:(a=i.dimensions)===null||a===void 0?void 0:a.height;return i.source===E.Source.ScreenShare&&t.simulcast&&(t.simulcast=!1),Ui(i.source===E.Source.ScreenShare,c,d,t)}function fp(i,e,t,n){let s=pp(i,e,t),r=s[0].encoding,a=Math.max(e,t);for(let o=0;o=a)break}if(n)switch(n){case"av1":case"h265":r=Object.assign({},r),r.maxBitrate=r.maxBitrate*.7;break;case"vp9":r=Object.assign({},r),r.maxBitrate=r.maxBitrate*.85;break}return r}function pp(i,e,t){if(i)return dp;let n=e>t?e/t:t/e;return Math.abs(n-16/9)n?t/n:n/t;return Math.abs(s-16/9){if(a>=Xr.length)return;let o=Math.min(i,e),d={rid:Xr[a],scaleResolutionDownBy:Math.max(1,o/Math.min(r.width,r.height)),maxBitrate:r.encoding.maxBitrate},l=n&&r.encoding.maxFramerate?Math.min(n,r.encoding.maxFramerate):r.encoding.maxFramerate;l&&(d.maxFramerate=l);let u=ye(),h=u?.name==="Firefox"&&u.os!=="iOS"||a===0;r.encoding.priority&&h&&(d.priority=r.encoding.priority,d.networkPriority=r.encoding.priority),s.push(d)}),Ke()&&El()==="ios"){let r;s.forEach(o=>{r?o.maxFramerate&&o.maxFramerate>r&&(r=o.maxFramerate):r=o.maxFramerate});let a=!0;s.forEach(o=>{var c;o.maxFramerate!=r&&(a&&(a=!1,W.info("Simulcast on iOS React-Native requires all encodings to share the same framerate.")),W.info('Setting framerate of encoding "'.concat((c=o.rid)!==null&&c!==void 0?c:"",'" to ').concat(r)),o.maxFramerate=r)})}return s}function vc(i){if(i)return i.slice().sort((e,t)=>{let n=e.encoding,s=t.encoding;return n.maxBitrate>s.maxBitrate?1:n.maxBitrates.maxFramerate?1:-1:0})}var Fi=class{constructor(e){let t=e.match(/^L(\d)T(\d)(h|_KEY|_KEY_SHIFT){0,1}$/);if(!t)throw new Error("invalid scalability mode");if(this.spatial=parseInt(t[1]),this.temporal=parseInt(t[2]),t.length>3)switch(t[3]){case"h":case"_KEY":case"_KEY_SHIFT":this.suffix=t[3]}}toString(){var e;return"L".concat(this.spatial,"T").concat(this.temporal).concat((e=this.suffix)!==null&&e!==void 0?e:"")}};function gp(i){switch(i.source){case E.Source.Camera:return"maintain-framerate";case E.Source.ScreenShare:return"maintain-resolution";default:return"balanced"}}var vp=5e3,ln=class extends xi{get sender(){return this._sender}set sender(e){this._sender=e,this.degradationPreference&&this.setDegradationPreference(this.degradationPreference)}constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,s=arguments.length>3?arguments[3]:void 0;super(e,E.Kind.Video,t,n,s),this.simulcastCodecs=new Map,this.degradationPreference="balanced",this.isCpuConstrained=!1,this.optimizeForPerformance=!1,this.monitorSender=()=>f(this,void 0,void 0,function*(){if(!this.sender){this._currentBitrate=0;return}let r;try{r=yield this.getSenderStats()}catch(c){this.log.error("could not get video sender stats",Object.assign(Object.assign({},this.logContext),{error:c}));return}let a=new Map(r.map(c=>[c.rid,c])),o=r.some(c=>c.qualityLimitationReason==="cpu");if(o!==this.isCpuConstrained&&(this.isCpuConstrained=o,this.isCpuConstrained&&this.emit(P.CpuConstrained)),this.prevStats){let c=0;a.forEach((d,l)=>{var u;let h=(u=this.prevStats)===null||u===void 0?void 0:u.get(l);c+=fs(d,h)}),this._currentBitrate=c}this.prevStats=a}),this.senderLock=new le}get isSimulcast(){return!!(this.sender&&this.sender.getParameters().encodings.length>1)}startMonitor(e){var t;if(this.signalClient=e,!Ce())return;let n=(t=this.sender)===null||t===void 0?void 0:t.getParameters();n&&(this.encodings=n.encodings),!this.monitorInterval&&(this.monitorInterval=setInterval(()=>{this.monitorSender()},oo))}stop(){this._mediaStreamTrack.getConstraints(),this.simulcastCodecs.forEach(e=>{e.mediaStreamTrack.stop()}),super.stop()}pauseUpstream(){let e=Object.create(null,{pauseUpstream:{get:()=>super.pauseUpstream}});return f(this,void 0,void 0,function*(){var t,n,s,r,a;yield e.pauseUpstream.call(this);try{for(var o=!0,c=De(this.simulcastCodecs.values()),d;d=yield c.next(),t=d.done,!t;o=!0)r=d.value,o=!1,yield(a=r.sender)===null||a===void 0?void 0:a.replaceTrack(null)}catch(l){n={error:l}}finally{try{!o&&!t&&(s=c.return)&&(yield s.call(c))}finally{if(n)throw n.error}}})}resumeUpstream(){let e=Object.create(null,{resumeUpstream:{get:()=>super.resumeUpstream}});return f(this,void 0,void 0,function*(){var t,n,s,r,a;yield e.resumeUpstream.call(this);try{for(var o=!0,c=De(this.simulcastCodecs.values()),d;d=yield c.next(),t=d.done,!t;o=!0){r=d.value,o=!1;let l=r;yield(a=l.sender)===null||a===void 0?void 0:a.replaceTrack(l.mediaStreamTrack)}}catch(l){n={error:l}}finally{try{!o&&!t&&(s=c.return)&&(yield s.call(c))}finally{if(n)throw n.error}}})}mute(){let e=Object.create(null,{mute:{get:()=>super.mute}});return f(this,void 0,void 0,function*(){let t=yield this.muteLock.lock();try{return this.isMuted?(this.log.debug("Track already muted",this.logContext),this):(this.source===E.Source.Camera&&!this.isUserProvided&&(this.log.debug("stopping camera track",this.logContext),this._mediaStreamTrack.stop()),yield e.mute.call(this),this)}finally{t()}})}unmute(){let e=Object.create(null,{unmute:{get:()=>super.unmute}});return f(this,void 0,void 0,function*(){let t=yield this.muteLock.lock();try{return this.isMuted?(this.source===E.Source.Camera&&!this.isUserProvided&&(this.log.debug("reacquiring camera track",this.logContext),yield this.restart(void 0,!0)),yield e.unmute.call(this),this):(this.log.debug("Track already unmuted",this.logContext),this)}finally{t()}})}setTrackMuted(e){super.setTrackMuted(e);for(let t of this.simulcastCodecs.values())t.mediaStreamTrack.enabled=!e}getSenderStats(){return f(this,void 0,void 0,function*(){var e;if(!(!((e=this.sender)===null||e===void 0)&&e.getStats))return[];let t=[],n=yield this.sender.getStats();return n.forEach(s=>{var r;if(s.type==="outbound-rtp"){let a={type:"video",streamId:s.id,frameHeight:s.frameHeight,frameWidth:s.frameWidth,framesPerSecond:s.framesPerSecond,framesSent:s.framesSent,firCount:s.firCount,pliCount:s.pliCount,nackCount:s.nackCount,packetsSent:s.packetsSent,bytesSent:s.bytesSent,qualityLimitationReason:s.qualityLimitationReason,qualityLimitationDurations:s.qualityLimitationDurations,qualityLimitationResolutionChanges:s.qualityLimitationResolutionChanges,rid:(r=s.rid)!==null&&r!==void 0?r:s.id,retransmittedPacketsSent:s.retransmittedPacketsSent,targetBitrate:s.targetBitrate,timestamp:s.timestamp},o=n.get(s.remoteId);o&&(a.jitter=o.jitter,a.packetsLost=o.packetsLost,a.roundTripTime=o.roundTripTime),t.push(a)}}),t.sort((s,r)=>{var a,o;return((a=r.frameWidth)!==null&&a!==void 0?a:0)-((o=s.frameWidth)!==null&&o!==void 0?o:0)}),t})}isSvcPublish(e){return wt(e)&&!Xt(e,this.publishOptions)}setPublishingQuality(e){let t=[];for(let n=Ie.LOW;n<=Ie.HIGH;n+=1)t.push(new Xa({quality:n,enabled:n<=e}));this.log.debug("setting publishing quality. max quality ".concat(e),this.logContext),this.setPublishingLayers(this.isSvcPublish(this.codec),t)}restartTrack(e){return f(this,void 0,void 0,function*(){var t,n,s,r,a;let o;if(e){let u=io({video:e});typeof u.video!="boolean"&&(o=u.video)}yield this.restart(o),this.isCpuConstrained=!1;try{for(var c=!0,d=De(this.simulcastCodecs.values()),l;l=yield d.next(),t=l.done,!t;c=!0){r=l.value,c=!1;let u=r;u.sender&&((a=u.sender.transport)===null||a===void 0?void 0:a.state)!=="closed"&&(u.mediaStreamTrack=this.mediaStreamTrack.clone(),yield u.sender.replaceTrack(u.mediaStreamTrack))}}catch(u){n={error:u}}finally{try{!c&&!t&&(s=d.return)&&(yield s.call(d))}finally{if(n)throw n.error}}yield this.onSenderTrackSwapped()})}onSenderTrackSwapped(){return f(this,void 0,void 0,function*(){yield this.refreshSenderEncodings()})}refreshSenderEncodings(){return f(this,void 0,void 0,function*(){var e;if(!this.sender||!this.publishOptions||this.optimizeForPerformance)return;let t=yield this.senderLock.lock();try{let s;try{s=yield this.waitForDimensions()}catch(o){this.log.warn("could not determine new track dimensions, skipping encoding recompute",Object.assign(Object.assign({},this.logContext),{error:o}));return}if(this.lastEncodedDimensions&&this.lastEncodedDimensions.width===s.width&&this.lastEncodedDimensions.height===s.height)return;let r=this.source===E.Source.ScreenShare,a=Ui(r,s.width,s.height,Object.assign({},this.publishOptions));yield this.applyEncodingsToSender(this.sender,a),this.encodings=a,this.lastEncodedDimensions=s;for(let o of this.simulcastCodecs){var n=L(o,2);let c=n[0],d=n[1];if(!d.sender||((e=d.sender.transport)===null||e===void 0?void 0:e.state)==="closed"||!pl(c))continue;let l=Object.assign({},this.publishOptions),u=Dl(this,c,l);u&&(yield this.applyEncodingsToSender(d.sender,u),d.encodings=u)}}catch(s){this.log.warn("failed to apply recomputed encodings",Object.assign(Object.assign({},this.logContext),{error:s}))}finally{t()}})}applyEncodingsToSender(e,t){return f(this,void 0,void 0,function*(){let n=e.getParameters();!n.encodings||n.encodings.length!==t.length||(n.encodings.forEach((s,r)=>{if(s.active===!1)return;let a=t[r];a.scaleResolutionDownBy!==void 0&&(s.scaleResolutionDownBy=a.scaleResolutionDownBy),a.maxBitrate!==void 0&&(s.maxBitrate=a.maxBitrate),a.maxFramerate!==void 0&&(s.maxFramerate=a.maxFramerate),a.priority!==void 0&&(s.priority=a.priority,s.networkPriority=a.priority)}),this.log.debug("updating sender encodings after track restart",Object.assign(Object.assign({},this.logContext),{encodings:n.encodings})),yield e.setParameters(n))})}setProcessor(e){let t=Object.create(null,{setProcessor:{get:()=>super.setProcessor}});return f(this,arguments,void 0,function(n){var s=this;let r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return function*(){var a,o,c,d,l,u;if(yield t.setProcessor.call(s,n,r),!((l=s.processor)===null||l===void 0)&&l.processedTrack)try{for(var h=!0,m=De(s.simulcastCodecs.values()),g;g=yield m.next(),a=g.done,!a;h=!0)d=g.value,h=!1,yield(u=d.sender)===null||u===void 0?void 0:u.replaceTrack(s.processor.processedTrack)}catch(p){o={error:p}}finally{try{!h&&!a&&(c=m.return)&&(yield c.call(m))}finally{if(o)throw o.error}}}()})}setDegradationPreference(e){return f(this,void 0,void 0,function*(){this.degradationPreference=e,yield this.applyDegradationPreference(this.sender);for(let t of this.simulcastCodecs.values())yield this.applyDegradationPreference(t.sender)})}applyDegradationPreference(e){return f(this,void 0,void 0,function*(){if(e)try{this.log.debug("setting degradationPreference to ".concat(this.degradationPreference),this.logContext);let t=e.getParameters();t.degradationPreference=this.degradationPreference,yield e.setParameters(t)}catch(t){this.log.warn("failed to set degradationPreference",Object.assign({error:t},this.logContext))}})}addSimulcastTrack(e,t){if(this.simulcastCodecs.has(e)){this.log.error("".concat(e," already added, skipping adding simulcast codec"),this.logContext);return}let n={codec:e,mediaStreamTrack:this.mediaStreamTrack.clone(),sender:void 0,encodings:t};return this.simulcastCodecs.set(e,n),n}setSimulcastTrackSender(e,t){return f(this,void 0,void 0,function*(){let n=this.simulcastCodecs.get(e);n&&(n.sender=t,yield this.applyDegradationPreference(t),setTimeout(()=>{this.subscribedCodecs&&this.setPublishingCodecs(this.subscribedCodecs)},vp))})}setPublishingCodecs(e){return f(this,void 0,void 0,function*(){var t,n,s,r,a,o,c;if(this.log.debug("setting publishing codecs",Object.assign(Object.assign({},this.logContext),{codecs:e,currentCodec:this.codec})),!this.codec&&e.length>0)return yield this.setPublishingLayers(this.isSvcPublish(e[0].codec),e[0].qualities),[];this.subscribedCodecs=e;let d=[];try{for(t=!0,n=De(e);s=yield n.next(),r=s.done,!r;t=!0){c=s.value,t=!1;let l=c;if(!this.codec||this.codec===l.codec)yield this.setPublishingLayers(this.isSvcPublish(l.codec),l.qualities);else{let u=this.simulcastCodecs.get(l.codec);if(this.log.debug("try setPublishingCodec for ".concat(l.codec),Object.assign(Object.assign({},this.logContext),{simulcastCodecInfo:u})),!u||!u.sender){for(let h of l.qualities)if(h.enabled){d.push(l.codec);break}}else u.encodings&&(this.log.debug("try setPublishingLayersForSender ".concat(l.codec),this.logContext),yield bc(u.sender,u.encodings,l.qualities,this.senderLock,this.isSvcPublish(l.codec),this.log,this.logContext))}}}catch(l){a={error:l}}finally{try{!t&&!r&&(o=n.return)&&(yield o.call(n))}finally{if(a)throw a.error}}return d})}setPublishingLayers(e,t){return f(this,void 0,void 0,function*(){if(this.optimizeForPerformance){this.log.info("skipping setPublishingLayers due to optimized publishing performance",Object.assign(Object.assign({},this.logContext),{qualities:t}));return}this.log.debug("setting publishing layers",Object.assign(Object.assign({},this.logContext),{qualities:t})),!(!this.sender||!this.encodings)&&(yield bc(this.sender,this.encodings,t,this.senderLock,e,this.log,this.logContext))})}prioritizePerformance(){return f(this,void 0,void 0,function*(){if(!this.sender)throw new Error("sender not found");let e=yield this.senderLock.lock();try{this.optimizeForPerformance=!0;let t=this.sender.getParameters();t.encodings=t.encodings.map((n,s)=>{var r;return Object.assign(Object.assign({},n),{active:s===0,scaleResolutionDownBy:Math.max(1,Math.ceil(((r=this.mediaStreamTrack.getSettings().height)!==null&&r!==void 0?r:360)/360)),scalabilityMode:s===0&&wt(this.codec)?"L1T3":void 0,maxFramerate:s===0?15:0,maxBitrate:s===0?n.maxBitrate:0})}),this.log.debug("setting performance optimised encodings",Object.assign(Object.assign({},this.logContext),{encodings:t.encodings})),this.encodings=t.encodings,yield this.sender.setParameters(t)}catch(t){this.log.error("failed to set performance optimised encodings",Object.assign(Object.assign({},this.logContext),{error:t})),this.optimizeForPerformance=!1}finally{e()}})}handleAppVisibilityChanged(){let e=Object.create(null,{handleAppVisibilityChanged:{get:()=>super.handleAppVisibilityChanged}});return f(this,void 0,void 0,function*(){yield e.handleAppVisibilityChanged.call(this),Sl()&&this.isInBackground&&this.source===E.Source.Camera&&(this._mediaStreamTrack.enabled=!1)})}};function bc(i,e,t,n,s,r,a){return f(this,void 0,void 0,function*(){let o=yield n.lock();r.debug("setPublishingLayersForSender",Object.assign(Object.assign({},a),{sender:i,qualities:t,senderEncodings:e}));try{let c=i.getParameters(),d=c.encodings;if(!d)return;if(d.length!==e.length){r.warn("cannot set publishing layers, encodings mismatch",Object.assign(Object.assign({},a),{encodings:d,senderEncodings:e}));return}let l=!1;!1&&d[0].scalabilityMode||(s&&t.some(m=>m.enabled)&&t.forEach(m=>m.enabled=!0),d.forEach((h,m)=>{var g;let p=(g=h.rid)!==null&&g!==void 0?g:"";p===""&&(p="q");let k=Ll(p),T=t.find(S=>S.quality===k);T&&h.active!==T.enabled&&(l=!0,h.active=T.enabled,r.debug("setting layer ".concat(T.quality," to ").concat(h.active?"enabled":"disabled"),a),Ut()&&(T.enabled?(h.scaleResolutionDownBy=e[m].scaleResolutionDownBy,h.maxBitrate=e[m].maxBitrate,h.maxFrameRate=e[m].maxFrameRate):(h.scaleResolutionDownBy=4,h.maxBitrate=10,h.maxFrameRate=2)))})),l&&(c.encodings=d,r.debug("setting encodings",Object.assign(Object.assign({},a),{encodings:c.encodings})),yield i.setParameters(c))}finally{o()}})}function Ll(i){switch(i){case"f":return Ie.HIGH;case"h":return Ie.MEDIUM;case"q":return Ie.LOW;default:return Ie.HIGH}}function yc(i,e,t,n){if(!t)return[new bt({quality:Ie.HIGH,width:i,height:e,bitrate:0,ssrc:0})];if(n){let s=t[0].scalabilityMode,r=new Fi(s),a=[],o=r.suffix=="h"?1.5:2,c=r.suffix=="h"?2:3;for(let d=0;d{var r,a,o;let c=(r=s.scaleResolutionDownBy)!==null&&r!==void 0?r:1,d=Ll((a=s.rid)!==null&&a!==void 0?a:"");return new bt({quality:d,width:Math.ceil(i/c),height:Math.ceil(e/c),bitrate:(o=s.maxBitrate)!==null&&o!==void 0?o:0,ssrc:0})})}var bp=2*1e3,Gs="leave-reconnect",yp=10*1e3,kp=3e4,Tp=3,Sp=3,Re;(function(i){i[i.New=0]="New",i[i.Connected=1]="Connected",i[i.Disconnected=2]="Disconnected",i[i.Reconnecting=3]="Reconnecting",i[i.Closed=4]="Closed"})(Re||(Re={}));var kc=64e3,Zr=class extends we.EventEmitter{get isClosed(){return this._isClosed}get isNewlyCreated(){return this._isNewlyCreated}get pendingReconnect(){return!!this.reconnectTimeout}get serverVersion(){var e,t,n;return((t=(e=this.latestJoinResponse)===null||e===void 0?void 0:e.serverInfo)===null||t===void 0?void 0:t.version)||((n=this.latestJoinResponse)===null||n===void 0?void 0:n.serverVersion)||void 0}get reliableChannel(){return this.dataChannels.reliable}get lossyChannel(){return this.dataChannels.lossy}get dataTrackChannel(){return this.dataChannels.dataTrack}constructor(e){var t;super(),this.options=e,this.rtcConfig={},this.peerConnectionTimeout=ho.peerConnectionTimeout,this.fullReconnectOnNext=!1,this.latestRemoteOfferId=0,this.subscriberPrimary=!1,this.pcState=Re.New,this._isClosed=!0,this._isNewlyCreated=!0,this.pendingTrackResolvers={},this.reconnectAttempts=0,this.reconnectStart=0,this.attemptingReconnect=!1,this.joinAttempts=0,this.maxJoinAttempts=1,this.shouldFailNext=!1,this.shouldFailOnV1Path=!1,this.log=W,this.reliableReceivedState=new Gr(kp),this.midToTrackId={},this.isWaitingForNetworkReconnect=!1,this.handleDataChannel=n=>f(this,[n],void 0,function(s){var r=this;let a=s.channel;return function*(){a&&r.dataChannels.adoptSubscriberChannel(a)&&r.log.debug("on data channel ".concat(a.id,", ").concat(a.label))}()}),this.handleDataMessage=n=>f(this,void 0,void 0,function*(){var s,r,a,o;let c=yield this.dataProcessLock.lock();try{let d=yield this.decodeDataMessage(n);if(!d)return;let l=Se.fromBinary(d);if(l.sequence>0&&l.participantSid!==""){let u=this.reliableReceivedState.get(l.participantSid);if(u&&l.sequence<=u)return;this.reliableReceivedState.set(l.participantSid,l.sequence)}if(((s=l.value)===null||s===void 0?void 0:s.case)==="speaker")this.emit(R.ActiveSpeakersUpdate,l.value.value.speakers);else if(((r=l.value)===null||r===void 0?void 0:r.case)==="encryptedPacket"){if(!this.e2eeManager){this.log.error("Received encrypted packet but E2EE not set up");return}let u;try{u=yield this.e2eeManager.handleEncryptedData(l.value.value.encryptedValue,l.value.value.iv,l.participantIdentity,l.value.value.keyIndex)}catch(g){this.log.debug("failed to decrypt data packet",{error:g,participantIdentity:l.participantIdentity});return}let h=cd.fromBinary(u.payload),m=new Se({value:h.value,participantIdentity:l.participantIdentity,participantSid:l.participantSid});((a=m.value)===null||a===void 0?void 0:a.case)==="user"&&Tc(m,m.value.value),this.emit(R.DataPacketReceived,m,l.value.value.encryptionType)}else((o=l.value)===null||o===void 0?void 0:o.case)==="user"&&Tc(l,l.value.value),this.emit(R.DataPacketReceived,l,G.NONE)}finally{c()}}),this.handleDataTrackMessage=n=>f(this,void 0,void 0,function*(){let s=yield this.decodeDataMessage(n);s&&this.emit("dataTrackPacketReceived",s)}),this.handleDataError=n=>{if(this._isClosed)return;let r=n.currentTarget.maxRetransmits===0?"lossy":"reliable";if(typeof RTCErrorEvent<"u"&&n instanceof RTCErrorEvent&&n.error){let a=n.error;this.log.error("DataChannel error on ".concat(r,": ").concat(a.message),{error:a,errorDetail:a.errorDetail,sctpCauseCode:a.sctpCauseCode})}else this.log.error("Unknown DataChannel error on ".concat(r),{event:n})},this.handleDataChannelClose=n=>()=>{var s;!this._isClosed&&((s=this.pcManager)===null||s===void 0?void 0:s.publisher.getConnectionState())==="connected"&&this.log.error("publisher data channel '".concat(V[n],"' closed unexpectedly"),this.logContext)},this.handleDisconnect=(n,s)=>{if(this._isClosed)return;this.log.warn("".concat(n," disconnected")),this.reconnectAttempts===0&&(this.reconnectStart=Date.now());let r=c=>{this.log.warn("could not recover connection after ".concat(this.reconnectAttempts," attempts, ").concat(c,"ms. giving up")),this.emit(R.Disconnected),this.close("gave up reconnecting after ".concat(this.reconnectAttempts," attempts, ").concat(c,"ms"))},a=Date.now()-this.reconnectStart,o=this.getNextRetryDelay({elapsedMs:a,retryCount:this.reconnectAttempts});if(o===null){r(a);return}n===Gs&&(o=0),this.log.debug("reconnecting in ".concat(o,"ms")),this.clearReconnectTimeout(),this.token&&this.emit(R.TokenRefreshed,this.token),this.reconnectTimeout=z.setTimeout(()=>this.attemptReconnect(s).finally(()=>this.reconnectTimeout=void 0),o)},this.waitForRestarted=()=>new Promise((n,s)=>{this.pcState===Re.Connected&&n();let r=()=>{this.off(R.Disconnected,a),n()},a=()=>{this.off(R.Restarted,r),s()};this.once(R.Restarted,r),this.once(R.Disconnected,a)}),this.onRtpMapAvailable=n=>{let s=new Map;n.forEach(r=>{let a=r.codec.toLowerCase();wl(a)&&s.set(r.payload,a)}),this.emit(R.RTPVideoMapUpdate,s)},this.handleBrowserOnLine=()=>f(this,void 0,void 0,function*(){!this.url||!(yield fetch(jn(this.url),{method:"HEAD"}).then(s=>s.ok).catch(()=>!1))||(this.log.info("detected network reconnected"),(this.client.currentState===Te.RECONNECTING||this.isWaitingForNetworkReconnect&&this.client.currentState===Te.CONNECTED)&&(this.clearReconnectTimeout(),this.attemptReconnect(it.RR_SIGNAL_DISCONNECTED),this.isWaitingForNetworkReconnect=!1))}),this.handleBrowserOffline=()=>f(this,void 0,void 0,function*(){if(this.url)try{yield Promise.race([fetch(jn(this.url),{method:"HEAD"}),ke(4e3).then(()=>Promise.reject())])}catch{window.navigator.onLine===!1&&(this.log.info("detected network interruption"),this.isWaitingForNetworkReconnect=!0)}}),this.log=he((t=e.loggerName)!==null&&t!==void 0?t:te.Engine,()=>this.logContext),this.loggerOptions={loggerName:e.loggerName,loggerContextCb:()=>this.logContext},this.client=new Kn(void 0,this.loggerOptions),this.client.signalLatency=this.options.expSignalLatency,this.reconnectPolicy=this.options.reconnectPolicy,this.closingLock=new le,this.dataProcessLock=new le,this.dataChannels=new Qr({isEngineClosed:()=>this.isClosed,isReconnecting:()=>this.attemptingReconnect,onDataMessage:n=>this.handleDataMessage(n),onDataTrackMessage:n=>this.handleDataTrackMessage(n),onDataError:n=>this.handleDataError(n),onChannelClose:n=>this.handleDataChannelClose(n)(),onBufferStatusChanged:(n,s)=>this.emit(R.DCBufferStatusChanged,s,n)}),this.client.onParticipantUpdate=n=>this.emit(R.ParticipantUpdate,n),this.client.onConnectionQuality=n=>{this.handleLocalConnectionQuality(n),this.emit(R.ConnectionQualityUpdate,n)},this.client.onRoomUpdate=n=>this.emit(R.RoomUpdate,n),this.client.onSubscriptionError=n=>this.emit(R.SubscriptionError,n),this.client.onSubscriptionPermissionUpdate=n=>this.emit(R.SubscriptionPermissionUpdate,n),this.client.onSpeakersChanged=n=>this.emit(R.SpeakersChanged,n),this.client.onStreamStateUpdate=n=>this.emit(R.StreamStateChanged,n),this.client.onRequestResponse=n=>this.emit(R.SignalRequestResponse,n),this.client.onParticipantUpdate=n=>this.emit(R.ParticipantUpdate,n),this.client.onJoined=n=>this.emit(R.Joined,n)}get logContext(){var e,t,n,s,r,a;return{room:(t=(e=this.latestJoinResponse)===null||e===void 0?void 0:e.room)===null||t===void 0?void 0:t.name,roomID:(s=(n=this.latestJoinResponse)===null||n===void 0?void 0:n.room)===null||s===void 0?void 0:s.sid,participant:(a=(r=this.latestJoinResponse)===null||r===void 0?void 0:r.participant)===null||a===void 0?void 0:a.identity,participantID:this.participantSid}}join(e,t,n,s){return f(this,arguments,void 0,function(r,a,o,c){var d=this;let l=arguments.length>4&&arguments[4]!==void 0?arguments[4]:!1;return function*(){var u,h,m;d._isNewlyCreated=!1,d.url=r,d.token=a,d.signalOpts=o,d.maxJoinAttempts=o.maxRetries;try{d.joinAttempts+=1,d.setupSignalClientCallbacks();let g=!l&&Xm(),p;if(g){d.pcManager||(yield d.configure(),d.applyInitialPublisherLayout());let S=yield(u=d.pcManager)===null||u===void 0?void 0:u.publisher.createInitialOffer();S&&(p=It(S.offer,S.offerId))}if(c?.aborted)throw F.cancelled("Connection aborted");if(!l&&d.shouldFailOnV1Path)throw d.shouldFailOnV1Path=!1,F.serviceNotFound("Simulated v1 path failure","v0-rtc");let k=yield d.client.join(r,a,o,c,l,p);d._isClosed=!1,d.latestJoinResponse=k,d.participantSid=(h=k.participant)===null||h===void 0?void 0:h.sid,d.subscriberPrimary=k.subscriberPrimary,g?(m=d.pcManager)===null||m===void 0||m.updateConfiguration(d.makeRTCConfiguration(k)):(d.pcManager||(yield d.configure(k,!l),l||d.applyInitialPublisherLayout()),(!d.subscriberPrimary||k.fastPublish)&&d.negotiate().catch(S=>{d.log.error(S)})),d.registerOnLineListener(),d.clientConfiguration=k.clientConfiguration,d.emit(R.SignalConnected,k);let T=k.serverInfo;return T||(T={version:k.serverVersion,region:k.serverRegion}),d.log.info("connected to Livekit Server ".concat(Object.entries(T).map(S=>{let I=L(S,2),b=I[0],y=I[1];return"".concat(b,": ").concat(y)}).join(", "))),{joinResponse:k,serverInfo:T}}catch(g){if(g instanceof F){if(g.reason===Q.ServerUnreachable){if(d.log.warn("Couldn't connect to server, attempt ".concat(d.joinAttempts," of ").concat(d.maxJoinAttempts)),d.joinAttempts{let s=z.setTimeout(()=>{delete this.pendingTrackResolvers[e.cid],n(F.timeout("publication of local track timed out, no response from server"))},1e4);this.pendingTrackResolvers[e.cid]={resolve:r=>{z.clearTimeout(s),t(r)},reject:()=>{z.clearTimeout(s),n(new Error("Cancelled publication by calling unpublish"))}},this.client.sendAddTrack(e)})}removeTrack(e){if(e.track&&this.pendingTrackResolvers[e.track.id]){let t=this.pendingTrackResolvers[e.track.id].reject;t&&t(),delete this.pendingTrackResolvers[e.track.id]}try{return this.pcManager.removeTrack(e),!0}catch(t){this.log.warn("failed to remove track",{error:t})}return!1}updateMuteStatus(e,t){this.client.sendMuteTrack(e,t)}get dataSubscriberReadyState(){var e;return(e=this.dataChannelForKind(V.RELIABLE,!0))===null||e===void 0?void 0:e.readyState}getConnectedServerAddress(){return f(this,void 0,void 0,function*(){var e;return(e=this.pcManager)===null||e===void 0?void 0:e.getConnectedAddress()})}setRegionStrategy(e){this.regionStrategy=e}configure(e,t){return f(this,void 0,void 0,function*(){var n;if(!(this.pcManager&&this.pcManager.currentState!==$.NEW)){if(e){this.participantSid=(n=e.participant)===null||n===void 0?void 0:n.sid;let s=this.makeRTCConfiguration(e);this.pcManager=new Oi(t?"publisher-only":e.subscriberPrimary?"subscriber-primary":"publisher-primary",this.loggerOptions,s)}else{let s=this.makeRTCConfiguration();this.pcManager=new Oi("publisher-only",this.loggerOptions,s)}this.emit(R.TransportsCreated,this.pcManager.publisher,this.pcManager.subscriber),this.pcManager.onIceCandidate=(s,r)=>{this.client.sendIceCandidate(s,r)},this.pcManager.onPublisherOffer=(s,r)=>{this.client.sendOffer(s,r)},this.pcManager.onDataChannel=this.handleDataChannel,this.pcManager.onStateChange=(s,r,a)=>f(this,void 0,void 0,function*(){if(this.log.debug("primary PC state changed ".concat(s)),s===$.CONNECTING?this.transportConnectingSince=Date.now():this.transportConnectingSince=void 0,["closed","disconnected","failed"].includes(r)&&(this.publisherConnectionPromise=void 0),s===$.CONNECTED){let d=this.pcState===Re.New;this.pcState=Re.Connected,d&&this.emit(R.Connected,this.latestJoinResponse)}else s===$.FAILED&&(this.pcState===Re.Connected||this.pcState===Re.Reconnecting)&&(this.pcState=Re.Disconnected,this.handleDisconnect("peerconnection failed",a==="failed"?it.RR_SUBSCRIBER_FAILED:it.RR_PUBLISHER_FAILED));let o=this.client.isDisconnected||this.client.currentState===Te.RECONNECTING,c=[$.FAILED,$.CLOSING,$.CLOSED].includes(s);o&&c&&!this._isClosed&&this.emit(R.Offline)}),this.pcManager.onTrack=s=>{s.streams.length!==0&&this.emit(R.MediaTrackAdded,s.track,s.streams[0],s.receiver)}}})}setupSignalClientCallbacks(){this.client.onAnswer=(e,t,n)=>f(this,void 0,void 0,function*(){this.pcManager&&(this.log.debug("received server answer",{RTCSdpType:e.type,sdp:e.sdp,midToTrackId:n}),this.pcManager.mode==="publisher-only"&&(this.midToTrackId=n),yield this.pcManager.setPublisherAnswer(e,t))}),this.client.onTrickle=(e,t)=>{this.pcManager&&(this.log.debug("got ICE candidate from peer",{candidate:e,target:t}),this.pcManager.addIceCandidate(e,t))},this.client.onOffer=(e,t,n)=>f(this,void 0,void 0,function*(){if(this.latestRemoteOfferId=t,!this.pcManager)return;this.midToTrackId=n;let s=yield this.pcManager.createSubscriberAnswerFromOffer(e,t);s&&this.client.sendAnswer(s,t)}),this.client.onLocalTrackPublished=e=>{var t;if(this.log.debug("received trackPublishedResponse",{cid:e.cid,track:(t=e.track)===null||t===void 0?void 0:t.sid}),!this.pendingTrackResolvers[e.cid]){this.log.error("missing track resolver for ".concat(e.cid),{cid:e.cid});return}let n=this.pendingTrackResolvers[e.cid].resolve;delete this.pendingTrackResolvers[e.cid],n(e.track)},this.client.onLocalTrackUnpublished=e=>{this.emit(R.LocalTrackUnpublished,e)},this.client.onLocalTrackSubscribed=e=>{this.emit(R.LocalTrackSubscribed,e)},this.client.onTokenRefresh=e=>{this.token=e,this.emit(R.TokenRefreshed,e)},this.client.onRemoteMuteChanged=(e,t)=>{this.emit(R.RemoteMute,e,t)},this.client.onSubscribedQualityUpdate=e=>{this.emit(R.SubscribedQualityUpdate,e)},this.client.onRoomMoved=e=>{var t;this.participantSid=(t=e.participant)===null||t===void 0?void 0:t.sid,this.latestJoinResponse&&(this.latestJoinResponse.room=e.room),this.emit(R.RoomMoved,e)},this.client.onMediaSectionsRequirement=e=>{this.addMediaSections(e.numAudios,e.numVideos),this.negotiate()},this.client.onPublishDataTrackResponse=e=>{this.emit(R.PublishDataTrackResponse,e)},this.client.onUnPublishDataTrackResponse=e=>{this.emit(R.UnPublishDataTrackResponse,e)},this.client.onDataTrackSubscriberHandles=e=>{this.emit(R.DataTrackSubscriberHandles,e)},this.client.onClose=()=>{this.handleDisconnect("signal",it.RR_SIGNAL_DISCONNECTED)},this.client.onLeave=e=>{var t;switch(this.log.info("client leave request received (action=".concat(e?.action,")"),{reason:e?.reason}),e.regions&&(this.log.debug("updating regions"),this.emit(R.ServerRegionsReported,e.regions)),e.action){case Jt.DISCONNECT:this.emit(R.Disconnected,e?.reason),this.close("server leave: ".concat((t=Pe[e.reason])!==null&&t!==void 0?t:e.reason));break;case Jt.RECONNECT:this.fullReconnectOnNext=!0,this.handleDisconnect(Gs);break;case Jt.RESUME:this.handleDisconnect(Gs)}}}makeRTCConfiguration(e){var t;let n=Object.assign({},this.rtcConfig);if((((t=this.signalOpts)===null||t===void 0?void 0:t.e2eeEnabled)||this.frameMetadataWorker&&!qn())&&ro()&&(this.log.debug("E2EE - setting up transports with insertable streams"),n.encodedInsertableStreams=!0),n.sdpSemantics="unified-plan",n.continualGatheringPolicy="gather_continually",!e)return n;if(e.iceServers&&!n.iceServers){let r=[];e.iceServers.forEach(a=>{let o={urls:a.urls};a.username&&(o.username=a.username),a.credential&&(o.credential=a.credential),r.push(o)}),n.iceServers=r}return e.clientConfiguration&&e.clientConfiguration.forceRelay===Dn.ENABLED&&(n.iceTransportPolicy="relay"),n}applyInitialPublisherLayout(){this.createDataChannels(),Ke()||this.addMediaSections(Tp,Sp)}addMediaSections(e,t){var n,s,r;let a={direction:"recvonly"};for(let c=0;c1&&arguments[1]!==void 0?arguments[1]:{};var n,s,r;let a=this.frameMetadataWorker;if(!a||!((n=this.signalOpts)===null||n===void 0)&&n.e2eeEnabled)return;let o=(s=t.frameMetadata)!==null&&s!==void 0?s:t.packetTrailer,c=_i(o);if(qn()){c&&(e.transform=new RTCRtpScriptTransform(a,{kind:"encode",packetTrailer:o}));return}if(!Vn((r=this.options.frameMetadata)!==null&&r!==void 0?r:this.options.packetTrailer)||!("createEncodedStreams"in e)){c&&this.log.warn("frame metadata transform not supported; skipping write",this.logContext);return}let d=e.createEncodedStreams(),l=d.readable,u=d.writable;c?a.postMessage({kind:"encode",data:{readableStream:l,writableStream:u,packetTrailer:o}},[l,u]):l.pipeTo(u)}createTransceiverRTCRtpSender(e,t,n){return f(this,void 0,void 0,function*(){if(!this.pcManager)throw new Y("publisher is closed");let s=[];e.mediaStream&&s.push(e.mediaStream),ut(e)&&(e.codec=t.videoCodec);let r={direction:"sendonly",streams:s};return n&&(r.sendEncodings=n),(yield this.pcManager.addPublisherTransceiver(e.mediaStreamTrack,r)).sender})}createSimulcastTransceiverSender(e,t,n,s){return f(this,void 0,void 0,function*(){if(!this.pcManager)throw new Y("publisher is closed");let r={direction:"sendonly"};s&&(r.sendEncodings=s);let a=yield this.pcManager.addPublisherTransceiver(t.mediaStreamTrack,r);if(n.videoCodec)return yield e.setSimulcastTrackSender(n.videoCodec,a.sender),a.sender})}createRTCRtpSender(e){return f(this,void 0,void 0,function*(){if(!this.pcManager)throw new Y("publisher is closed");return this.pcManager.addPublisherTrack(e)})}handleLocalConnectionQuality(e){if(!this.participantSid)return;let t=e.updates.find(n=>n.participantSid===this.participantSid);t&&(t.quality===Ht.LOST?this.scheduleLostQualityReconnect():this.clearLostQualityTimeout())}scheduleLostQualityReconnect(){this.lostQualityTimeout||(this.lostQualityTimeout=z.setTimeout(()=>{this.lostQualityTimeout=void 0,!(this._isClosed||this.pcState!==Re.Connected||this.attemptingReconnect)&&this.hasActivePublisherSenders()&&(this.log.warn("local connection quality lost while publishing, triggering full reconnect",this.logContext),this.fullReconnectOnNext=!0,this.handleDisconnect("connection quality lost",it.RR_PUBLISHER_FAILED))},yp))}clearLostQualityTimeout(){this.lostQualityTimeout&&(z.clearTimeout(this.lostQualityTimeout),this.lostQualityTimeout=void 0)}hasActivePublisherSenders(){var e,t;return(t=(e=this.pcManager)===null||e===void 0?void 0:e.publisher.getSenders().some(n=>!!n.track&&n.track.readyState==="live"))!==null&&t!==void 0?t:!1}reconnect(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:it.RR_UNKNOWN;this.fullReconnectOnNext=!0,this.handleDisconnect("reconcile",e)}attemptReconnect(e){return f(this,void 0,void 0,function*(){var t,n,s;if(this._isClosed)return;if(this.attemptingReconnect){this.log.warn("already attempting reconnect, returning early");return}this.clearLostQualityTimeout(),(((t=this.clientConfiguration)===null||t===void 0?void 0:t.resumeConnection)===Dn.DISABLED||((s=(n=this.pcManager)===null||n===void 0?void 0:n.currentState)!==null&&s!==void 0?s:$.NEW)===$.NEW)&&(this.fullReconnectOnNext=!0);let r=this.fullReconnectOnNext;this.fullReconnectOnNext=!1;let a=!1;try{this.attemptingReconnect=!0,r?yield this.restartConnection():yield this.resumeConnection(e),this.clearPendingReconnect(),a=!0}catch(o){this.reconnectAttempts+=1;let c=!0;o instanceof Y?(this.log.debug("received unrecoverable error",{error:o}),c=!1):(r||!(o instanceof ft))&&(this.fullReconnectOnNext=!0),c?this.handleDisconnect("reconnect",it.RR_UNKNOWN):(this.log.info("could not recover connection after ".concat(this.reconnectAttempts," attempts, ").concat(Date.now()-this.reconnectStart,"ms. giving up")),this.emit(R.Disconnected),yield this.close("gave up reconnecting after ".concat(this.reconnectAttempts," attempts, ").concat(Date.now()-this.reconnectStart,"ms")))}finally{this.attemptingReconnect=!1,a&&this.fullReconnectOnNext&&!this._isClosed&&(this.log.debug("full reconnect requested during in-progress attempt, dispatching"),this.handleDisconnect("reconnect"))}})}getNextRetryDelay(e){try{return this.reconnectPolicy.nextRetryDelayInMs(e)}catch(t){this.log.warn("encountered error in reconnect policy",{error:t})}return null}restartConnection(e){return f(this,void 0,void 0,function*(){var t,n,s;try{if(!this.url||!this.token)throw new Y("could not reconnect, url or token not saved");this.log.info("reconnecting, attempt: ".concat(this.reconnectAttempts)),this.emit(R.Restarting),this.client.isDisconnected||(yield this.client.sendLeave()),yield this.cleanupPeerConnections(),yield this.cleanupClient();let r;try{if(!this.signalOpts)throw this.log.warn("attempted connection restart, without signal options present"),new ft;r=(yield this.join(e??this.url,this.token,this.signalOpts,void 0,!this.options.singlePeerConnection)).joinResponse}catch(a){throw a instanceof F&&a.reason===Q.NotAllowed?new Y("could not reconnect, token might be expired"):new ft}if(this.shouldFailNext)throw this.shouldFailNext=!1,new Error("simulated failure");if(this.client.setReconnected(),this.emit(R.SignalRestarted,r),yield this.waitForPCReconnected(),this.client.currentState!==Te.CONNECTED)throw new ft("Signal connection got severed during reconnect");(t=this.regionStrategy)===null||t===void 0||t.resetAttempts(),this.emit(R.Restarted)}catch(r){let a=yield(n=this.regionStrategy)===null||n===void 0?void 0:n.getNextUrl();if(a){yield this.restartConnection(a);return}else throw(s=this.regionStrategy)===null||s===void 0||s.resetAttempts(),r}})}resumeConnection(e){return f(this,void 0,void 0,function*(){if(!this.url||!this.token)throw new Y("could not reconnect, url or token not saved");if(!this.pcManager)throw new Y("publisher and subscriber connections unset");this.log.info("resuming signal connection, attempt ".concat(this.reconnectAttempts)),this.emit(R.Resuming);let t;try{this.setupSignalClientCallbacks(),t=yield this.client.reconnect(this.url,this.token,this.participantSid,e)}catch(s){let r="";throw s instanceof Error&&(r=s.message,this.log.error(s.message,{error:s})),s instanceof F&&s.reason===Q.NotAllowed?new Y("could not reconnect, token might be expired"):s instanceof F&&s.reason===Q.LeaveRequest?s:new ft(r)}if(this.emit(R.SignalResumed),t){let s=this.makeRTCConfiguration(t);this.pcManager.updateConfiguration(s),this.latestJoinResponse&&(this.latestJoinResponse.serverInfo=t.serverInfo)}else this.log.warn("Did not receive reconnect response");if(this.shouldFailNext)throw this.shouldFailNext=!1,new Error("simulated failure");if(yield this.pcManager.triggerIceRestart(),yield this.waitForPCReconnected(),this.client.currentState!==Te.CONNECTED)throw new ft("Signal connection got severed during reconnect");this.client.setReconnected();let n=this.dataChannelForKind(V.RELIABLE);n?.readyState==="open"&&n.id===null&&this.createDataChannels(),t?.lastMessageSeq&&this.resendReliableMessagesForResume(t.lastMessageSeq).catch(s=>{this.log.warn("failed to resend reliable messages after resume",Object.assign(Object.assign({},this.logContext),{error:s}))}),this.emit(R.Resumed)})}waitForPCInitialConnection(e,t){return f(this,void 0,void 0,function*(){if(!this.pcManager)throw new Y("PC manager is closed");yield this.pcManager.ensurePCTransportConnection(t,e)})}waitForPCReconnected(){return f(this,void 0,void 0,function*(){this.pcState=Re.Reconnecting,this.log.debug("waiting for peer connection to reconnect");try{if(yield ke(bp),!this.pcManager)throw new Y("PC manager is closed");yield this.pcManager.ensurePCTransportConnection(void 0,this.peerConnectionTimeout),this.pcState=Re.Connected}catch(e){throw this.pcState=Re.Disconnected,F.internal("could not establish PC connection, ".concat(e.message))}})}publishRpcAck(e,t){return f(this,void 0,void 0,function*(){let n=new Se({destinationIdentities:[e],kind:yt.RELIABLE,value:{case:"rpcAck",value:new os({requestId:t})}});yield this.sendDataPacket(n,V.RELIABLE)})}sendDataPacket(e,t){return f(this,void 0,void 0,function*(){var n,s;if(yield this.ensurePublisherConnected(t),this.e2eeManager&&this.e2eeManager.isDataChannelEncryptionEnabled){let o=lf(e);if(o){let c=yield this.e2eeManager.encryptData(o.toBinary());e.value={case:"encryptedPacket",value:new od({encryptedValue:c.payload,iv:c.iv,keyIndex:c.keyIndex})}}}t===V.RELIABLE&&(e.sequence=this.reliableChannel.nextSequence());let r=e.toBinary(),a=Math.min((s=(n=this.pcManager)===null||n===void 0?void 0:n.getMaxPublisherMessageSize())!==null&&s!==void 0?s:kc,kc);if(typeof a<"u"&&a!==0&&r.byteLength>a)throw new Cr("cannot publish data packet larger than ".concat(a," bytes (got ").concat(r.byteLength,")"));t===V.RELIABLE?yield this.reliableChannel.send(r,e.sequence):yield this.lossyChannel.send(r)})}sendDataTrackFrame(e){return f(this,void 0,void 0,function*(){yield this.ensurePublisherConnected(V.DATA_TRACK_LOSSY),yield this.dataTrackChannel.send(e)})}resendReliableMessagesForResume(e){return f(this,void 0,void 0,function*(){yield this.ensurePublisherConnected(V.RELIABLE),yield this.reliableChannel.replay(e)})}flowControlFor(e){return this.dataChannels.channelFor(e)}waitForBufferHeadroom(e){return f(this,void 0,void 0,function*(){return this.flowControlFor(e).waitForHeadroomWithLock()})}ensureDataTransportConnected(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:this.subscriberPrimary;return function*(){var r;if(!n.pcManager)throw new Y("PC manager is closed");let a=s?n.pcManager.subscriber:n.pcManager.publisher,o=s?"Subscriber":"Publisher";if(!a)throw F.internal("".concat(o," connection not set"));let c=!1;!s&&!n.dataChannelForKind(t,s)&&(n.createDataChannels(),c=!0),!c&&!s&&!n.pcManager.publisher.isICEConnected&&n.pcManager.publisher.getICEConnectionState()!=="checking"&&(c=!0),c&&n.negotiate().catch(u=>{n.log.error(u)});let d=n.dataChannelForKind(t,s);if(d?.readyState==="open")return;let l=new Date().getTime()+n.peerConnectionTimeout;for(;new Date().getTime()this.peerConnectionTimeout?(this.log.warn("transport stuck in connecting state",this.logContext),!1):!0}negotiate(){return f(this,void 0,void 0,function*(){return new ge((e,t)=>f(this,void 0,void 0,function*(){if(!this.pcManager){t(new Ze("PC manager is closed"));return}this.pcManager.requirePublisher(),this.pcManager.publisher.getTransceivers().length==0&&!this.dataChannels.hasPublisherChannels&&this.createDataChannels();let n=new AbortController,s=()=>{n.abort(),this.log.debug("engine disconnected while negotiation was ongoing"),e()};this.isClosed&&t(new Ze("cannot negotiate on closed engine")),this.on(R.Closing,s),this.on(R.Restarting,s),this.pcManager.publisher.off(vt.RTPVideoPayloadTypes,this.onRtpMapAvailable),this.pcManager.publisher.once(vt.RTPVideoPayloadTypes,this.onRtpMapAvailable);try{yield this.pcManager.negotiate(n),e()}catch(r){if(n.signal.aborted){e();return}r instanceof Ze&&(this.fullReconnectOnNext=!0),this.handleDisconnect("negotiation",it.RR_UNKNOWN),r instanceof Error?t(r):t(new Error(String(r)))}finally{this.off(R.Closing,s),this.off(R.Restarting,s)}}))})}dataChannelForKind(e,t){return this.dataChannels.getHandle(e,t)}sendSyncState(e,t,n){var s,r,a,o;if(!this.pcManager){this.log.warn("sync state cannot be sent without peer connection setup");return}let c=this.pcManager.publisher.getLocalDescription(),d=this.pcManager.publisher.getRemoteDescription(),l=(s=this.pcManager.subscriber)===null||s===void 0?void 0:s.getRemoteDescription(),u=(r=this.pcManager.subscriber)===null||r===void 0?void 0:r.getLocalDescription(),h=(o=(a=this.signalOpts)===null||a===void 0?void 0:a.autoSubscribe)!==null&&o!==void 0?o:!0,m=new Array,g=new Array;e.forEach(p=>{p.isDesired!==h&&m.push(p.trackSid),p.isEnabled||g.push(p.trackSid)}),this.client.sendSyncState(new Za({answer:this.pcManager.mode==="publisher-only"?d?It({sdp:d.sdp,type:d.type}):void 0:u?It({sdp:u.sdp,type:u.type}):void 0,offer:this.pcManager.mode==="publisher-only"?c?It({sdp:c.sdp,type:c.type}):void 0:l?It({sdp:l.sdp,type:l.type}):void 0,subscription:new us({trackSids:m,subscribe:!h,participantTracks:[]}),publishTracks:Em(t),dataChannels:this.dataChannelsInfo(),trackSidsDisabled:g,datachannelReceiveStates:this.reliableReceivedState.map((p,k)=>new Id({publisherSid:k,lastSeq:p})),publishDataTracks:n.map(p=>new za({info:Mi.toProtobuf(p)}))}))}failNext(){this.shouldFailNext=!0}failNextV1Path(){this.shouldFailOnV1Path=!0}dataChannelsInfo(){let e=[],t=(n,s)=>{n?.id!==void 0&&n.id!==null&&e.push(new Md({label:n.label,id:n.id,target:s}))};return t(this.dataChannelForKind(V.LOSSY),Ve.PUBLISHER),t(this.dataChannelForKind(V.RELIABLE),Ve.PUBLISHER),t(this.dataChannelForKind(V.LOSSY,!0),Ve.SUBSCRIBER),t(this.dataChannelForKind(V.RELIABLE,!0),Ve.SUBSCRIBER),e}clearReconnectTimeout(){this.reconnectTimeout&&z.clearTimeout(this.reconnectTimeout)}clearPendingReconnect(){this.clearReconnectTimeout(),this.reconnectAttempts=0}registerOnLineListener(){Ce()&&(window.addEventListener("online",this.handleBrowserOnLine),window.addEventListener("offline",this.handleBrowserOffline))}deregisterOnLineListener(){Ce()&&(window.removeEventListener("online",this.handleBrowserOnLine),window.removeEventListener("offline",this.handleBrowserOffline))}getTrackIdForReceiver(e){var t;let n=(t=this.pcManager)===null||t===void 0?void 0:t.getMidForReceiver(e);if(n){let s=Object.entries(this.midToTrackId).find(r=>L(r,1)[0]===n);if(s)return s[1]}}};function Tc(i,e){let t=i.participantIdentity?i.participantIdentity:e.participantIdentity;i.participantIdentity=t,e.participantIdentity=t;let n=i.destinationIdentities.length!==0?i.destinationIdentities:e.destinationIdentities;i.destinationIdentities=n,e.destinationIdentities=n}var ni=he(te.Region),ea=5e3,Cp=3e4,ze=class i{static fetchRegionSettings(e,t,n){return f(this,void 0,void 0,function*(){let s=yield i.fetchLock.lock();try{let r=yield fetch("".concat(Ep(e),"/regions"),{headers:{authorization:"Bearer ".concat(t)},signal:n});if(r.ok){let a=$m(r.headers),o=a?a*1e3:ea;return{regionSettings:yield r.json(),updatedAtInMs:Date.now(),maxAgeInMs:o}}else throw r.status===401?F.notAllowed("Could not fetch region settings: ".concat(r.statusText),r.status):F.internal("Could not fetch region settings: ".concat(r.statusText))}catch(r){throw r instanceof F?r:n?.aborted?F.cancelled("Region fetching was aborted"):F.serverUnreachable("Could not fetch region settings, ".concat(r instanceof Error?"".concat(r.name,": ").concat(r.message):r))}finally{s()}})}static scheduleRefetch(e,t,n){return f(this,void 0,void 0,function*(){let s=i.settingsTimeouts.get(e.hostname);clearTimeout(s),i.settingsTimeouts.set(e.hostname,setTimeout(()=>f(this,void 0,void 0,function*(){try{let r=yield i.fetchRegionSettings(e,t);i.updateCachedRegionSettings(e,t,r)}catch(r){if(r instanceof F&&r.reason===Q.NotAllowed){ni.debug("token is not valid, cancelling auto region refresh");return}ni.debug("auto refetching of region settings failed",{error:r}),i.scheduleRefetch(e,t,n)}}),n))})}static updateCachedRegionSettings(e,t,n){i.cache.set(e.hostname,n),i.scheduleRefetch(e,t,n.maxAgeInMs)}static stopRefetch(e){let t=i.settingsTimeouts.get(e);t&&(clearTimeout(t),i.settingsTimeouts.delete(e))}static scheduleCleanup(e){let t=i.connectionTrackers.get(e);t&&(t.cleanupTimeout&&clearTimeout(t.cleanupTimeout),t.cleanupTimeout=setTimeout(()=>{let n=i.connectionTrackers.get(e);n&&n.connectionCount===0&&(ni.debug("stopping region refetch after disconnect delay",{hostname:e}),i.stopRefetch(e)),n&&(n.cleanupTimeout=void 0)},Cp))}static cancelCleanup(e){let t=i.connectionTrackers.get(e);t?.cleanupTimeout&&(clearTimeout(t.cleanupTimeout),t.cleanupTimeout=void 0)}notifyConnected(){let e=this.serverUrl.hostname,t=i.connectionTrackers.get(e);t||(t={connectionCount:0},i.connectionTrackers.set(e,t)),t.connectionCount++,i.cancelCleanup(e)}notifyDisconnected(){let e=this.serverUrl.hostname,t=i.connectionTrackers.get(e);t&&(t.connectionCount=Math.max(0,t.connectionCount-1),t.connectionCount===0&&i.scheduleCleanup(e))}constructor(e,t){this.attemptedRegions=[],this.serverUrl=new URL(e),this.token=t}updateToken(e){var t;this.token=e;let n=this.getServerUrl(),s=i.cache.get(n.hostname);i.scheduleRefetch(this.serverUrl,this.token,(t=s?.maxAgeInMs)!==null&&t!==void 0?t:ea)}isCloud(){return cn(this.serverUrl)}getServerUrl(){return this.serverUrl}fetchRegionSettings(e){return f(this,void 0,void 0,function*(){return i.fetchRegionSettings(this.serverUrl,this.token,e)})}getNextBestRegionUrl(e){return f(this,void 0,void 0,function*(){if(!this.isCloud())throw Error("region availability is only supported for LiveKit Cloud domains");let t=i.cache.get(this.serverUrl.hostname);(!t||Date.now()-t.updatedAtInMs>t.maxAgeInMs)&&(t=yield this.fetchRegionSettings(e),i.updateCachedRegionSettings(this.serverUrl,this.token,t));let n=t.regionSettings.regions.filter(s=>!this.attemptedRegions.find(r=>r.url===s.url));if(n.length>0){let s=n[0];return this.attemptedRegions.push(s),ni.info("switching to region: ".concat(s.region),{region:s.region}),s.url}else return null})}resetAttempts(){this.attemptedRegions=[]}setServerReportedRegions(e){i.updateCachedRegionSettings(this.serverUrl,this.token,e)}};ze.cache=new Map;ze.settingsTimeouts=new Map;ze.connectionTrackers=new Map;ze.fetchLock=new le;function Ep(i){return"".concat(i.protocol.replace("ws","http"),"//").concat(i.host,"/settings")}function zs(){return new CompressionStream("deflate-raw")}function Nl(){return new DecompressionStream("deflate-raw")}function Sc(i,e){return f(this,void 0,void 0,function*(){let t=new DecompressionStream("deflate-raw"),n=t.writable.getWriter();return n.write(i).catch(()=>{}),n.close().catch(()=>{}),xl(t.readable,e)})}function xl(i,e){return f(this,void 0,void 0,function*(){let t=i.getReader(),n=[],s=0;for(;;){let o=yield t.read(),c=o.done,d=o.value;if(c)break;if(n.push(d),s+=d.byteLength,typeof e=="number"&&s>e)throw yield t.cancel(),new re("Decompressed payload exceeds the maximum payload size of ".concat(e," bytes"),ne.PayloadTooLarge)}let r=new Uint8Array(s),a=0;for(let o of n)r.set(o,a),a+=o.byteLength;return r})}var Mt=15e3,wp=5e9,Bi=class{get info(){return this._info}validateBytesReceived(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!1;if(!(typeof this.totalByteSize!="number"||this.totalByteSize===0)){if(e&&this.bytesReceivedthis.totalByteSize)throw new re("Extra chunk(s) received - expected ".concat(this.totalByteSize," bytes of data total, received ").concat(this.bytesReceived," bytes"),ne.LengthExceeded)}}constructor(e,t,n){this.reader=t,this.totalByteSize=n,this._info=e,this.bytesReceived=0}handleChunkReceived(e){var t;this.bytesReceived+=e.content.byteLength,this.validateBytesReceived();let n=this.totalByteSize?this.bytesReceived/this.totalByteSize:void 0;(t=this.onProgress)===null||t===void 0||t.call(this,n)}},ji=class extends Bi{[Symbol.asyncIterator](){let e=this.reader.getReader();e.closed.catch(()=>{});let t=()=>{e.releaseLock(),this.signal=void 0};return{next:()=>f(this,void 0,void 0,function*(){var n;try{let s=this.signal;if(s?.aborted)throw s.reason;let r=yield new Promise((a,o)=>{if(s){let c=()=>o(s.reason);s.addEventListener("abort",c,{once:!0}),e.read().then(a,o).finally(()=>{s.removeEventListener("abort",c)})}else e.read().then(a,o)});return r.done?(this.validateBytesReceived(!0),typeof this.totalByteSize=="number"&&((n=this.onProgress)===null||n===void 0||n.call(this,1)),{done:!0,value:void 0}):(this.handleChunkReceived(r.value),{done:!1,value:r.value.content})}catch(s){throw t(),s}}),return(){return f(this,void 0,void 0,function*(){return t(),{done:!0,value:void 0}})}}}withAbortSignal(e){return this.signal=e,this}readAll(){return f(this,arguments,void 0,function(){var e=this;let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return function*(){var n,s,r,a;let o=new Set,c=t.signal?e.withAbortSignal(t.signal):e;try{for(var d=!0,l=De(c),u;u=yield l.next(),n=u.done,!n;d=!0){a=u.value,d=!1;let h=a;o.add(h)}}catch(h){s={error:h}}finally{try{!d&&!n&&(r=l.return)&&(yield r.call(l))}finally{if(s)throw s.error}}return Array.from(o)}()})}},qi=class extends Bi{[Symbol.asyncIterator](){let e=this.reader.getReader();e.closed.catch(()=>{});let t=new TextDecoder("utf-8"),n=this.signal,s=()=>{e.releaseLock(),this.signal=void 0};return{next:()=>f(this,void 0,void 0,function*(){var r;try{if(n?.aborted)throw n.reason;let a=yield new Promise((o,c)=>{if(n){let d=()=>c(n.reason);n.addEventListener("abort",d,{once:!0}),e.read().then(o,c).finally(()=>{n.removeEventListener("abort",d)})}else e.read().then(o,c)});if(a.done)return this.validateBytesReceived(!0),typeof this.totalByteSize=="number"&&((r=this.onProgress)===null||r===void 0||r.call(this,1)),{done:!0,value:void 0};{this.handleChunkReceived(a.value);let o;try{o=t.decode(a.value.content)}catch(c){throw new re("Cannot decode datastream chunk ".concat(a.value.chunkIndex," as text: ").concat(c),ne.DecodeFailed)}return{done:!1,value:o}}}catch(a){throw s(),a}}),return(){return f(this,void 0,void 0,function*(){return s(),{done:!0,value:void 0}})}}}withAbortSignal(e){return this.signal=e,this}readAll(){return f(this,arguments,void 0,function(){var e=this;let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return function*(){var n,s,r,a;let o="",c=t.signal?e.withAbortSignal(t.signal):e;try{for(var d=!0,l=De(c),u;u=yield l.next(),n=u.done,!n;d=!0)a=u.value,d=!1,o+=a}catch(h){s={error:h}}finally{try{!d&&!n&&(r=l.return)&&(yield r.call(l))}finally{if(s)throw s.error}}return o}()})}},ta=class{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:wp;this.log=W,this.byteStreamControllers=new Map,this.textStreamControllers=new Map,this.byteStreamHandlers=new Map,this.textStreamHandlers=new Map,this.isConnected=!1,this.bufferedPackets=[],this.maxPayloadByteLength=e}setConnected(e){this.isConnected=e,e&&this.flushBufferedPackets()}flushBufferedPackets(){let e=this.bufferedPackets;this.bufferedPackets=[];for(let t of e){let n=t.packet,s=t.encryptionType;this.handleDataStreamPacket(n,s)}}registerTextStreamHandler(e,t){if(this.textStreamHandlers.has(e))throw new re('A text stream handler for topic "'.concat(e,'" has already been set.'),ne.HandlerAlreadyRegistered);this.textStreamHandlers.set(e,t)}unregisterTextStreamHandler(e){this.textStreamHandlers.delete(e)}registerByteStreamHandler(e,t){if(this.byteStreamHandlers.has(e))throw new re('A byte stream handler for topic "'.concat(e,'" has already been set.'),ne.HandlerAlreadyRegistered);this.byteStreamHandlers.set(e,t)}unregisterByteStreamHandler(e){this.byteStreamHandlers.delete(e)}clearControllers(){this.byteStreamControllers.clear(),this.textStreamControllers.clear(),this.bufferedPackets=[]}validateParticipantHasNoActiveDataStreams(e){let t=Array.from(this.textStreamControllers.entries()).filter(a=>a[1].sendingParticipantIdentity===e),n=Array.from(this.byteStreamControllers.entries()).filter(a=>a[1].sendingParticipantIdentity===e);if(t.length>0||n.length>0){let a=new re("Participant ".concat(e," unexpectedly disconnected in the middle of sending data"),ne.AbnormalEnd);for(let o of n){var s=L(o,2);let c=s[0];s[1].controller.error(a),this.byteStreamControllers.delete(c)}for(let o of t){var r=L(o,2);let c=r[0];r[1].controller.error(a),this.textStreamControllers.delete(c)}}}handleDataStreamPacket(e,t){if(!this.isConnected){this.bufferedPackets.push({packet:e,encryptionType:t});return}switch(e.value.case){case"streamHeader":return this.handleStreamHeader(e.value.value,e.participantIdentity,t);case"streamChunk":return this.handleStreamChunk(e.value.value,t);case"streamTrailer":return this.handleStreamTrailer(e.value.value,t);default:throw new Error('DataPacket of value "'.concat(e.value.case,'" is not data stream related!'))}}handleStreamHeader(e,t,n){var s;switch(e.contentHeader.case){case"byteHeader":{let r=this.byteStreamHandlers.get(e.topic);if(!r){this.log.debug("ignoring incoming byte stream due to no handler for topic",e.topic);return}let a,o={id:e.streamId,name:(s=e.contentHeader.value.name)!==null&&s!==void 0?s:"unknown",mimeType:e.mimeType,size:e.totalLength?Number(e.totalLength):void 0,topic:e.topic,timestamp:Wt(e.timestamp),attributes:e.attributes,encryptionType:n},c;switch(e.compression){case be.DEFLATE_RAW:if(!kt()){W.warn("Data stream ".concat(e.streamId," received with deflate-raw compression, but this browser does not have support for DecompressionStream. Dropping..."));return}c=!0;break;case be.NONE:c=!1;break;default:W.warn("Data stream ".concat(e.streamId," received with unknown compression type ").concat(e.compression,", dropping..."));return}let d=e.inlineContent;if(typeof d<"u"){r(new ji(o,Cc(e.streamId,c?Sc(d,this.maxPayloadByteLength):d),Wt(e.totalLength)),{identity:t});return}let l=new ReadableStream({start:u=>{if(a=u,this.byteStreamControllers.has(e.streamId))throw new re("A data stream read is already in progress for a stream with id ".concat(e.streamId,"."),ne.AlreadyOpened);this.byteStreamControllers.set(e.streamId,{info:o,controller:a,startTime:Date.now(),sendingParticipantIdentity:t})}});r(new ji(o,c?Pp(l,e.streamId,this.maxPayloadByteLength):l.pipeThrough(Vi(e.streamId)),Wt(e.totalLength)),{identity:t});return}case"textHeader":{let r=this.textStreamHandlers.get(e.topic);if(!r){this.log.debug("ignoring incoming text stream due to no handler for topic",e.topic);return}let a,o={id:e.streamId,mimeType:e.mimeType,size:e.totalLength?Number(e.totalLength):void 0,topic:e.topic,timestamp:Number(e.timestamp),attributes:e.attributes,encryptionType:n,attachedStreamIds:e.contentHeader.value.attachedStreamIds},c;switch(e.compression){case be.DEFLATE_RAW:if(!kt()){W.warn("Data stream ".concat(e.streamId," received with deflate-raw compression, but this browser does not have support for DecompressionStream. Dropping..."));return}c=!0;break;case be.NONE:c=!1;break;default:W.warn("Data stream ".concat(e.streamId," received with unknown compression type ").concat(e.compression,", dropping..."));return}let d=e.inlineContent;if(typeof d<"u"){let u=c?Sc(d,this.maxPayloadByteLength):d;r(new qi(o,Cc(e.streamId,u),Wt(e.totalLength)),{identity:t});return}let l=new ReadableStream({start:u=>{if(a=u,this.textStreamControllers.has(e.streamId))throw new re("A data stream read is already in progress for a stream with id ".concat(e.streamId,"."),ne.AlreadyOpened);this.textStreamControllers.set(e.streamId,{info:o,controller:a,startTime:Date.now(),sendingParticipantIdentity:t})}});r(new qi(o,c?Ip(l,e.streamId,this.maxPayloadByteLength):l.pipeThrough(Vi(e.streamId)),Wt(e.totalLength)),{identity:t});return}}}handleStreamChunk(e,t){let n=this.byteStreamControllers.get(e.streamId);n&&(n.info.encryptionType!==t?(n.controller.error(new re("Encryption type mismatch for stream ".concat(e.streamId,". Expected ").concat(t,", got ").concat(n.info.encryptionType),ne.EncryptionTypeMismatch)),this.byteStreamControllers.delete(e.streamId)):n.controller.enqueue(e));let s=this.textStreamControllers.get(e.streamId);s&&(s.info.encryptionType!==t?(s.controller.error(new re("Encryption type mismatch for stream ".concat(e.streamId,". Expected ").concat(t,", got ").concat(s.info.encryptionType),ne.EncryptionTypeMismatch)),this.textStreamControllers.delete(e.streamId)):s.controller.enqueue(e))}handleStreamTrailer(e,t){let n=this.textStreamControllers.get(e.streamId);n&&(n.info.encryptionType!==t?n.controller.error(new re("Encryption type mismatch for stream ".concat(e.streamId,". Expected ").concat(t,", got ").concat(n.info.encryptionType),ne.EncryptionTypeMismatch)):(n.info.attributes=Object.assign(Object.assign({},n.info.attributes),e.attributes),e.reason?n.controller.error(new re("Data stream ".concat(e.streamId," closed abnormally: ").concat(e.reason),ne.AbnormalEnd)):n.controller.close()),this.textStreamControllers.delete(e.streamId));let s=this.byteStreamControllers.get(e.streamId);s&&(s.info.encryptionType!==t?s.controller.error(new re("Encryption type mismatch for stream ".concat(e.streamId,". Expected ").concat(t,", got ").concat(s.info.encryptionType),ne.EncryptionTypeMismatch)):(s.info.attributes=Object.assign(Object.assign({},s.info.attributes),e.attributes),e.reason?s.controller.error(new re("Data stream ".concat(e.streamId," closed abnormally: ").concat(e.reason),ne.AbnormalEnd)):s.controller.close()),this.byteStreamControllers.delete(e.streamId))}};function Cc(i,e){return new ReadableStream({start:t=>f(this,void 0,void 0,function*(){let n=yield e;t.enqueue(new ct({streamId:i,chunkIndex:BigInt(0),content:n})),t.close()})})}function Vi(i){let e=-1;return new TransformStream({transform:(t,n)=>{let s=Wt(t.chunkIndex);if(s<=e){W.warn("ignoring duplicate chunk ".concat(s," ").concat(t.version>0?"(version ".concat(t.version,")"):""," for data stream ").concat(i," (last processed: ").concat(e,")"));return}if(s>e+1)throw new re("Missing chunk(s) ".concat(e+1,"..").concat(s-1," for data stream ").concat(i," - cannot reassemble payload"),ne.Incomplete);e=s,t.content.length!==0&&n.enqueue(t)}})}function Ul(){return new TransformStream({transform:(i,e)=>{e.enqueue(i.content)}})}function _p(i){let e=0;return new TransformStream({transform:(t,n)=>{t.byteLength>0&&(n.enqueue(new ct({streamId:i,chunkIndex:He(e),content:t})),e+=1)}})}function Rp(i){let e=new TextDecoder("utf-8"),t=new TextEncoder,n=0,s=r=>{try{return r?e.decode(r,{stream:!0}):e.decode()}catch(a){throw new re("Cannot decode compressed data stream ".concat(i," as text: ").concat(a),ne.DecodeFailed)}};return new TransformStream({transform:(r,a)=>{let o=s(r);o.length>0&&(a.enqueue(new ct({streamId:i,chunkIndex:He(n),content:t.encode(o)})),n+=1)},flush:r=>{let a=s();a.length>0&&(r.enqueue(new ct({streamId:i,chunkIndex:He(n),content:t.encode(a)})),n+=1)}})}function Pp(i,e,t){return i.pipeThrough(Vi(e)).pipeThrough(Ul()).pipeThrough(Nl()).pipeThrough(Fl(e,t)).pipeThrough(_p(e))}function Ip(i,e,t){return i.pipeThrough(Vi(e)).pipeThrough(Ul()).pipeThrough(Nl()).pipeThrough(Fl(e,t)).pipeThrough(Rp(e))}function Fl(i,e){let t=0;return new TransformStream({transform:(n,s)=>{if(t+=n.byteLength,t>e)throw new re("Data stream ".concat(i," exceeds the maximum payload size of ").concat(e," bytes"),ne.PayloadTooLarge);s.enqueue(n)}})}var Wi=class{constructor(e,t,n){this.writableStream=e,this.defaultWriter=e.getWriter(),this.onClose=n,this.info=t}write(e){return this.defaultWriter.write(e)}close(){return f(this,void 0,void 0,function*(){var e;yield this.defaultWriter.close(),this.defaultWriter.releaseLock(),(e=this.onClose)===null||e===void 0||e.call(this)})}},na=class extends Wi{},ia=class extends Wi{};function Js(i,e,t){var n;return new cs({streamId:i.id,mimeType:i.mimeType,topic:i.topic,timestamp:He(i.timestamp),totalLength:He(i.size),attributes:i.attributes,compression:(n=t?.compression)!==null&&n!==void 0?n:be.NONE,inlineContent:t?.inlineContent,contentHeader:{case:"textHeader",value:new yd({version:e?.version,attachedStreamIds:i.attachedStreamIds,replyToStreamId:e?.replyToStreamId,operationType:e?.type==="update"?or.UPDATE:or.CREATE})}})}function ii(i,e){var t;return new cs({streamId:i.id,mimeType:i.mimeType,topic:i.topic,timestamp:He(i.timestamp),totalLength:He(i.size),attributes:i.attributes,compression:(t=e?.compression)!==null&&t!==void 0?t:be.NONE,inlineContent:e?.inlineContent,contentHeader:{case:"byteHeader",value:new kd({name:i.name})}})}function Rt(i,e){return new Se({destinationIdentities:e,value:{case:"streamHeader",value:i}})}var Mp=new TextEncoder,sa=class{constructor(e,t,n,s,r){this.engine=e,this.log=t,this.getRemoteParticipantClientProtocol=n,this.getRemoteParticipantCapabilities=s,this.getAllRemoteParticipantIdentities=r}setupEngine(e){this.engine=e}sendText(e,t){return f(this,void 0,void 0,function*(){var n,s,r,a,o;let c=crypto.randomUUID(),d=Mp.encode(e),l=d.byteLength,u=(n=t?.compress)!==null&&n!==void 0?n:!0,h={id:c,mimeType:"text/plain",timestamp:Date.now(),topic:(s=t?.topic)!==null&&s!==void 0?s:"",size:l,attributes:t?.attributes,encryptionType:!((r=this.engine.e2eeManager)===null||r===void 0)&&r.isDataChannelEncryptionEnabled?G.GCM:G.NONE},g=u&&kt()&&this.allRecipientsSupportV2(t?.destinationIdentities)&&this.allRecipientsSupportCompression(t?.destinationIdentities)?Hi.fromStream(vi(d).pipeThrough(zs())):null;if((!t?.attachments||t.attachments.length===0)&&this.allRecipientsSupportV2(t?.destinationIdentities)){let b=d,y=be.NONE;if(g){let O=yield g.collect();O.byteLengthcrypto.randomUUID()),T=k?k.length+1:1,S=new Array(T).fill(0),I=(b,y)=>{var C;S[y]=b,(C=t?.onProgress)===null||C===void 0||C.call(t,S.reduce((M,O)=>M+O,0)/T)};if(g){h.attachedStreamIds=k;let b=Js(h,void 0,{compression:be.DEFLATE_RAW}),y=Rt(b,t?.destinationIdentities);yield this.sendChunkedByteStream(y,c,t?.destinationIdentities,g.stream().pipeThrough(Qs(d.length,C=>I(C,0)))),d.length===0&&I(1,0)}else{let b=yield this.streamText({streamId:c,totalSize:l,destinationIdentities:t?.destinationIdentities,topic:t?.topic,attachedStreamIds:k,attributes:t?.attributes});yield b.write(e),I(1,0),yield b.close(),h=b.info}return t?.attachments&&k&&(yield Promise.all(t.attachments.map((b,y)=>f(this,void 0,void 0,function*(){return this._sendFile(k[y],b,{topic:t.topic,mimeType:b.type,destinationIdentities:t.destinationIdentities,compress:t.compress,onProgress:C=>{I(C,y+1)}})})))),h})}sendBytes(e,t){return f(this,void 0,void 0,function*(){var n,s,r,a,o,c,d;let l=crypto.randomUUID(),u=t?.destinationIdentities,h=(n=t?.compress)!==null&&n!==void 0?n:!0,m={id:l,name:(s=t?.name)!==null&&s!==void 0?s:"unknown",mimeType:(r=t?.mimeType)!==null&&r!==void 0?r:"application/octet-stream",timestamp:Date.now(),topic:(a=t?.topic)!==null&&a!==void 0?a:"",size:e.byteLength,attributes:t?.attributes,encryptionType:!((o=this.engine.e2eeManager)===null||o===void 0)&&o.isDataChannelEncryptionEnabled?G.GCM:G.NONE},g=Qs(e.length,t?.onProgress),k=h&&kt()&&this.allRecipientsSupportV2(u)&&this.allRecipientsSupportCompression(u)?Hi.fromStream(vi(e).pipeThrough(g).pipeThrough(zs())):null;if(this.allRecipientsSupportV2(u)){let b=e,y=be.NONE;if(k){let O=yield k.collect();O.byteLength0?e:this.getAllRemoteParticipantIdentities()).every(n=>this.getRemoteParticipantClientProtocol(n)>=fl)}allRecipientsSupportCompression(e){return(e&&e.length>0?e:this.getAllRemoteParticipantIdentities()).every(n=>this.getRemoteParticipantCapabilities(n).includes(Nn.CAP_COMPRESSION_DEFLATE_RAW))}sendChunkedByteStream(e,t,n,s){return f(this,void 0,void 0,function*(){var r,a,o,c;let d=this.engine;yield Ys(d,e);let l=0;try{for(var u=!0,h=De(Ym(s,Mt)),m;m=yield h.next(),r=m.done,!r;u=!0){c=m.value,u=!1;let g=c,p=new Se({destinationIdentities:n,value:{case:"streamChunk",value:new ct({content:g,streamId:t,chunkIndex:He(l)})}});yield d.sendDataPacket(p,V.RELIABLE),l+=1}}catch(g){a={error:g}}finally{try{!u&&!r&&(o=h.return)&&(yield o.call(h))}finally{if(a)throw a.error}}yield $s(t,n,d)})}streamText(e){return f(this,void 0,void 0,function*(){var t,n,s;let r=(t=e?.streamId)!==null&&t!==void 0?t:crypto.randomUUID(),a=e?.destinationIdentities,o={id:r,mimeType:"text/plain",timestamp:Date.now(),topic:(n=e?.topic)!==null&&n!==void 0?n:"",size:e?.totalSize,attributes:e?.attributes,encryptionType:!((s=this.engine.e2eeManager)===null||s===void 0)&&s.isDataChannelEncryptionEnabled?G.GCM:G.NONE,attachedStreamIds:e?.attachedStreamIds},c=Js(o,e),d=Rt(c,a);yield Ys(this.engine,d);let l=0,u=this.engine,h=new WritableStream({write(p){return f(this,void 0,void 0,function*(){for(let k of Qm(p,Mt)){let T=new ct({content:k,streamId:r,chunkIndex:He(l)}),S=new Se({destinationIdentities:a,value:{case:"streamChunk",value:T}});yield u.sendDataPacket(S,V.RELIABLE),l+=1}})},close(){return f(this,void 0,void 0,function*(){yield $s(r,a,u)})},abort(p){console.log("Sink error:",p)}}),m=()=>f(this,void 0,void 0,function*(){yield g.close()});u.once(R.Closing,m);let g=new na(h,o,()=>this.engine.off(R.Closing,m));return g})}sendFile(e,t){return f(this,void 0,void 0,function*(){let n=crypto.randomUUID();return yield this._sendFile(n,e,t),{id:n}})}_sendFile(e,t,n){return f(this,void 0,void 0,function*(){var s,r,a,o,c;let d=n?.destinationIdentities,l=((s=n?.compress)!==null&&s!==void 0?s:!0)&&kt()&&this.allRecipientsSupportV2(d)&&this.allRecipientsSupportCompression(d),u={id:e,name:t.name,mimeType:(r=n?.mimeType)!==null&&r!==void 0?r:t.type,topic:(a=n?.topic)!==null&&a!==void 0?a:"",timestamp:Date.now(),size:t.size,encryptionType:!((o=this.engine.e2eeManager)===null||o===void 0)&&o.isDataChannelEncryptionEnabled?G.GCM:G.NONE},h=ii(u,{compression:l?be.DEFLATE_RAW:be.NONE}),m=Rt(h,d),g=t.stream().pipeThrough(Qs(t.size,n?.onProgress)),p=l?g.pipeThrough(zs()):g;return yield this.sendChunkedByteStream(m,e,d,p),t.size===0&&((c=n?.onProgress)===null||c===void 0||c.call(n,1)),u})}streamBytes(e){return f(this,void 0,void 0,function*(){var t,n,s,r,a;let o=(t=e?.streamId)!==null&&t!==void 0?t:crypto.randomUUID(),c=e?.destinationIdentities,d={id:o,mimeType:(n=e?.mimeType)!==null&&n!==void 0?n:"application/octet-stream",topic:(s=e?.topic)!==null&&s!==void 0?s:"",timestamp:Date.now(),attributes:e?.attributes,size:e?.totalSize,name:(r=e?.name)!==null&&r!==void 0?r:"unknown",encryptionType:!((a=this.engine.e2eeManager)===null||a===void 0)&&a.isDataChannelEncryptionEnabled?G.GCM:G.NONE},l=ii(d),u=Rt(l,c);yield Ys(this.engine,u);let h=0,m=new le,g=this.engine,p=this.log,k=new WritableStream({write(S){return f(this,void 0,void 0,function*(){let I=yield m.lock(),b=0;try{for(;b0&&e(Math.min(t/i,1)),s.enqueue(n)}})}function Ys(i,e){return f(this,void 0,void 0,function*(){if(e.toBinary().byteLength>Mt)throw new re("data stream header exceeds the ".concat(Mt,"-byte limit; reduce attribute size"),ne.HeaderTooLarge);yield i.sendDataPacket(e,V.RELIABLE)})}function $s(i,e,t){return f(this,void 0,void 0,function*(){let n=new Se({destinationIdentities:e,value:{case:"streamTrailer",value:new Ka({streamId:i})}});yield t.sendDataPacket(n,V.RELIABLE)})}function Bl(i){if(i.length===0)return new AbortController().signal;if(i.length===1)return i[0];for(let s of i)if(s.aborted)return s;let e=new AbortController,t=Array(i.length),n=()=>{for(let s of t)s()};return i.forEach((s,r)=>{let a=()=>{e.abort(s.reason),n()};s.addEventListener("abort",a),t[r]=()=>s.removeEventListener("abort",a)}),e.signal}function jl(i){let e=new AbortController;return setTimeout(()=>{e.abort(new DOMException("signal timed out after ".concat(i," ms"),"TimeoutError"))},i),e.signal}var se=1,nt=2,Ec=4,Ap=8,wc=0,_c=12,Rc=5,Op=7,Pc=3,Dp=3,Ic=2,Mc=1,Ac=0,Oc=3,si=2,Dc=2,Lp=1,Np=0,rt;(function(i){i[i.TooShort=0]="TooShort",i[i.HeaderOverrun=1]="HeaderOverrun",i[i.MissingExtWords=2]="MissingExtWords",i[i.UnsupportedVersion=3]="UnsupportedVersion",i[i.InvalidHandle=4]="InvalidHandle",i[i.MalformedExt=5]="MalformedExt"})(rt||(rt={}));var Xe=class i extends Ee{constructor(e,t,n){super(19,e,n),this.name="DataTrackDeserializeError",this.reason=t,this.reasonName=rt[t]}static tooShort(){return new i("Too short to contain a valid header",rt.TooShort)}static headerOverrun(){return new i("Header exceeds total packet length",rt.HeaderOverrun)}static missingExtWords(){return new i("Extension word indicator is missing",rt.MissingExtWords)}static unsupportedVersion(e){return new i("Unsupported version ".concat(e),rt.UnsupportedVersion)}static invalidHandle(e){return new i("invalid track handle: ".concat(e.message),rt.InvalidHandle,{cause:e})}static malformedExt(e){return new i("Extension with tag ".concat(e," is malformed"),rt.MalformedExt)}},In;(function(i){i[i.TooSmallForHeader=0]="TooSmallForHeader",i[i.TooSmallForPayload=1]="TooSmallForPayload"})(In||(In={}));var Ki=class i extends Ee{constructor(e,t,n){super(19,e,n),this.name="DataTrackSerializeError",this.reason=t,this.reasonName=In[t]}static tooSmallForHeader(){return new i("Buffer cannot fit header",In.TooSmallForHeader)}static tooSmallForPayload(){return new i("Buffer cannot fit payload",In.TooSmallForPayload)}},un=class{toBinary(){let e=this.toBinaryLengthBytes(),t=new ArrayBuffer(e),n=new DataView(t),s=this.toBinaryInto(n);if(e!==s)throw new Error("".concat(this.constructor.name,".toBinary: written bytes (").concat(s," bytes) not equal to allocated array buffer length (").concat(e," bytes)."));return new Uint8Array(t)}},hn;(function(i){i[i.UserTimestamp=2]="UserTimestamp",i[i.E2ee=1]="E2ee"})(hn||(hn={}));var Gi=class extends un{},Bt=class i extends Gi{constructor(e){super(),this.timestamp=e}toBinaryLengthBytes(){return se+se+i.lengthBytes}toBinaryInto(e){let t=0;e.setUint8(t,i.tag),t+=se,e.setUint8(t,i.lengthBytes),t+=se,e.setBigUint64(t,this.timestamp),t+=Ap;let n=this.toBinaryLengthBytes();if(t!==n)throw new Error("DataTrackUserTimestampExtension.toBinaryInto: Wrote ".concat(t," bytes but expected length was ").concat(n," bytes"));return t}toJSON(){return{tag:i.tag,lengthBytes:i.lengthBytes,timestamp:this.timestamp}}};Bt.tag=hn.UserTimestamp;Bt.lengthBytes=8;var jt=class i extends Gi{constructor(e,t){super(),this.keyIndex=e,this.iv=t}toBinaryLengthBytes(){return se+se+i.lengthBytes}toBinaryInto(e){let t=0;e.setUint8(t,i.tag),t+=se,e.setUint8(t,i.lengthBytes),t+=se,e.setUint8(t,this.keyIndex),t+=se;for(let s=0;s0&&arguments[0]!==void 0?arguments[0]:{};super(),this.userTimestamp=e.userTimestamp,this.e2ee=e.e2ee}toBinaryLengthBytes(){let e=0;return this.userTimestamp&&(e+=this.userTimestamp.toBinaryLengthBytes()),this.e2ee&&(e+=this.e2ee.toBinaryLengthBytes()),e}toBinaryInto(e){let t=0;if(this.e2ee){let s=this.e2ee.toBinaryInto(e);t+=s}if(this.userTimestamp){let s=this.userTimestamp.toBinaryInto(new DataView(e.buffer,e.byteOffset+t));t+=s}let n=this.toBinaryLengthBytes();if(t!==n)throw new Error("DataTrackExtensions.toBinaryInto: Wrote ".concat(t," bytes but expected length was ").concat(n," bytes"));return t}static fromBinary(e){let t=lo(e),n,s,r=0;for(;t.byteLength-r>=se+se;){let a=t.getUint8(r);r+=se;let o=t.getUint8(r);if(r+=se,a!==Np)switch(a){case hn.UserTimestamp:if(t.byteLength-r{}),s}catch(t){throw t}}setPipelineOptions(e){this.manager.setPipelineOptions(this.info.sid,e)}},Gn=class i extends un{constructor(e){var t;super(),this.marker=e.marker,this.trackHandle=e.trackHandle,this.sequence=e.sequence,this.frameNumber=e.frameNumber,this.timestamp=e.timestamp,this.extensions=(t=e.extensions)!==null&&t!==void 0?t:new sn}extensionsMetrics(){let e=this.extensions.toBinaryLengthBytes(),t=Math.ceil((si+e)/4),n=t*4-si-e;return{lengthBytes:e,lengthWords:t,paddingLengthBytes:n}}toBinaryLengthBytes(){let e=this.extensionsMetrics(),t=e.lengthBytes,n=e.paddingLengthBytes,s=_c;return t>0&&(s+=si+t+n),s}toBinaryInto(e){if(e.byteLength0&&(t|=1<0){let l=a-1;e.setUint16(c,l),c+=nt;let u=this.extensions.toBinaryInto(new DataView(e.buffer,e.byteOffset+c));c+=u;for(let h=0;h>Rc&Op;if(r>wc)throw Xe.unsupportedVersion(r);let a;switch(s>>Pc&Dp){case Ic:a=Z.Start;break;case Mc:a=Z.Final;break;case Oc:a=Z.Single;break;case Ac:default:a=Z.Inter;break}let o=(s>>Dc&Lp)>0;n+=se;let c;try{c=gf.fromNumber(t.getUint16(n))}catch(m){throw m instanceof Hn&&(m.isReason(Nt.Reserved)||m.isReason(Nt.TooLarge))?Xe.invalidHandle(m):m}n+=nt;let d=St.u16(t.getUint16(n));n+=nt;let l=St.u16(t.getUint16(n));n+=nt;let u=Ii.fromRtpTicks(t.getUint32(n));n+=Ec;let h=new sn;if(o){if(t.byteLength-nt.byteLength)throw Xe.headerOverrun();let k=new DataView(t.buffer,t.byteOffset+n,p),T=sn.fromBinary(k),S=L(T,2),I=S[0],b=S[1];h=I,n+=b}return[new i({marker:a,trackHandle:c,sequence:d,frameNumber:l,timestamp:u,extensions:h}),n]}toJSON(){return{marker:this.marker,trackHandle:this.trackHandle,sequence:this.sequence.value,frameNumber:this.frameNumber.value,timestamp:this.timestamp.asTicks(),extensions:this.extensions.toJSON()}}},Z;(function(i){i[i.Start=0]="Start",i[i.Inter=1]="Inter",i[i.Final=2]="Final",i[i.Single=3]="Single"})(Z||(Z={}));var Ji=class i extends un{constructor(e,t){super(),this.header=e,this.payload=t}toBinaryLengthBytes(){return this.header.toBinaryLengthBytes()+this.payload.byteLength}toBinaryInto(e){let t=0,n=this.header.toBinaryInto(e);if(t+=n,e.byteLength-t=s){let r=this.peekOldestPartialFrameNumber();if(typeof r!="number")throw new Error("Depacketizer.frameFromSingle: no oldest frame number found, but partials.size is ".concat(this.partials.size,"."));if(this.partials.delete(r),t?.throwOnInterruption)throw At.interrupted(r,e.header.frameNumber.value);Xs.warn("Data track frame ".concat(r," was interrupted by single-packet frame ").concat(e.header.frameNumber.value,", dropping."))}return{payload:e.payload,extensions:e.header.extensions}}beginPartial(e,t){var n;if(e.header.marker!==Z.Start)throw new Error("Depacketizer.beginPartial: packet.header.marker was not FrameMarker.Start, found ".concat(e.header.marker,"."));let s=e.header.sequence,r=e.header.frameNumber.value,a={startSequence:s,extensions:e.header.extensions,payloads:new Map([[s.value,e.payload]])},o=(n=t?.maxPartialFrames)!==null&&n!==void 0?n:1;for(;this.partials.size>=o;){let c=this.peekOldestPartialFrameNumber();if(typeof c!="number")break;if(this.partials.delete(c),t?.throwOnInterruption)throw At.interrupted(c,r);Xs.warn("Data track partials full (max ".concat(o,"), evicted oldest frame ").concat(c," to make room for new frame ").concat(r,"."))}return this.partials.set(r,a),null}pushToPartial(e){if(e.header.marker!==Z.Inter&&e.header.marker!==Z.Final)throw new Error("Depacketizer.pushToPartial: packet.header.marker was not FrameMarker.Inter or FrameMarker.Final, found ".concat(e.header.marker,"."));let t=e.header.frameNumber.value,n=this.partials.get(t);if(!n)throw this.partials.delete(t),At.unknownFrame(t);if(n.payloads.size>=i.MAX_BUFFER_PACKETS)throw this.partials.delete(t),At.bufferFull(t);return n.payloads.has(e.header.sequence.value)&&Xs.warn("Data track frame ".concat(t," received duplicate packet for sequence ").concat(e.header.sequence.value,", so replacing with newly received packet.")),n.payloads.set(e.header.sequence.value,e.payload),e.header.marker===Z.Final?this.finalize(t,n,e.header.sequence.value):null}finalize(e,t,n){let s=t.payloads.size,r=0;for(let d of t.payloads.values())r+=d.length;let a=new Uint8Array(r),o=t.startSequence.clone(),c=0;for(;;){let d=t.payloads.get(o.value);if(!d)break;t.payloads.delete(o.value);let l=a.length-c;if(d.length>l)throw new Error("Depacketizer.finalize: Expected at least ".concat(d.length," more bytes left in the payload buffer, only got ").concat(l," bytes."));if(a.set(d,c),c+=d.length,o.value!=n){o.increment();continue}return this.partials.delete(e),{payload:a,extensions:t.extensions}}throw this.partials.delete(e),At.incomplete(e,s,n-t.startSequence.value+1)}};Qi.MAX_BUFFER_PACKETS=128;var Dt;(function(i){i[i.Unpublished=0]="Unpublished",i[i.Timeout=1]="Timeout",i[i.Disconnected=2]="Disconnected",i[i.Cancelled=4]="Cancelled"})(Dt||(Dt={}));var Ue=class i extends Ee{constructor(e,t,n){super(22,e,n),this.name="DataTrackSubscribeError",this.reason=t,this.reasonName=Dt[t]}static unpublished(){return new i("The track has been unpublished and is no longer available",Dt.Unpublished)}static timeout(){return new i("Request to subscribe to data track timed-out",Dt.Timeout)}static disconnected(){return new i("Cannot subscribe to data track when disconnected",Dt.Disconnected)}static cancelled(){return new i("Subscription to data track cancelled by caller",Dt.Cancelled)}},Zs=he(te.DataTracks),ra=class{constructor(e){var t,n;let s=e.e2eeManager!==null;if(e.info.usesE2ee!==s)throw new Error("IncomingDataTrackPipeline: DataTrackInfo.usesE2ee must match presence of decryptionProvider");let r=new Qi;this.publisherIdentity=e.publisherIdentity,this.e2eeManager=(t=e.e2eeManager)!==null&&t!==void 0?t:null,this.depacketizer=r,this.options=(n=e.pipelineOptions)!==null&&n!==void 0?n:{}}updateE2eeManager(e){this.e2eeManager=e}setOptions(e){this.options=e}processPacket(e){return f(this,void 0,void 0,function*(){let t=this.depacketize(e);if(!t)return null;let n=yield this.decryptIfNeeded(t);return n||null})}depacketize(e){let t;try{t=this.depacketizer.push(e,{throwOnInterruption:!1,maxPartialFrames:this.options.maxPartialFrames})}catch(n){return Zs.warn("Data frame depacketize error: ".concat(n)),null}return t}decryptIfNeeded(e){return f(this,void 0,void 0,function*(){var t,n;let s=this.e2eeManager;if(!s)return e;let r=(n=(t=e.extensions)===null||t===void 0?void 0:t.e2ee)!==null&&n!==void 0?n:null;if(!r)return Zs.error("Missing E2EE meta"),null;let a;try{a=yield s.handleEncryptedData(e.payload,r.iv,this.publisherIdentity,r.keyIndex)}catch(o){return Zs.error("Error decrypting packet: ".concat(o)),null}return e.payload=a.payload,e})}},ce=he(te.DataTracks),xp=1e4,Up=16,aa=class extends we.EventEmitter{constructor(e){var t;super(),this.descriptors=new Map,this.subscriptionHandles=new Map,this.e2eeManager=(t=e?.e2eeManager)!==null&&t!==void 0?t:null}updateE2eeManager(e){this.e2eeManager=e;for(let t of this.descriptors.values())t.subscription.type==="active"&&t.subscription.pipeline.updateE2eeManager(e)}setPipelineOptions(e,t){let n=this.descriptors.get(e);if(!n){ce.warn("Unknown track ".concat(e,", cannot set pipeline options."));return}n.pipelineOptions=t,n.subscription.type==="active"&&n.subscription.pipeline.setOptions(t)}openSubscriptionStream(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:Up,s=null,r=new ue,a=this.descriptors.get(e),o=()=>{t?.removeEventListener("abort",d)},c=()=>{if(o(),!s){ce.warn("ReadableStream subscribed to ".concat(e," was not started."));return}if(!a||this.descriptors.get(a.info.sid)!==a){ce.warn("Unknown track ".concat(e,", skipping cancel..."));return}if(a.subscription.type!=="active"){ce.warn("Subscription for track ".concat(e," is not active, skipping cancel..."));return}a.subscription.streamControllers.delete(s),a.subscription.streamControllers.size===0&&this.unSubscribeRequest(a.info.sid)},d=()=>{var u;s&&(a?.subscription.type==="active"&&a.subscription.streamControllers.delete(s),s.error(Ue.cancelled()),(u=r.reject)===null||u===void 0||u.call(r,Ue.cancelled()),c())};return[new ReadableStream({start:u=>{s=u,this.subscribeRequest(e,t).then(()=>f(this,void 0,void 0,function*(){var h,m,g;if(!a||this.descriptors.get(a.info.sid)!==a){ce.error("Unknown track ".concat(e));let p=Ue.disconnected();u.error(p),(h=r.reject)===null||h===void 0||h.call(r,p);return}if(a.subscription.type!=="active"){ce.error("Subscription for track ".concat(e," is not active"));let p=Ue.disconnected();u.error(p),(m=r.reject)===null||m===void 0||m.call(r,p);return}if(t?.aborted){d();return}t?.addEventListener("abort",d),a.subscription.streamControllers.set(u,o),(g=r.resolve)===null||g===void 0||g.call(r)})).catch(h=>{var m;u.error(h),(m=r.reject)===null||m===void 0||m.call(r,h)})},cancel:()=>{c()}},new CountQueuingStrategy({highWaterMark:n})),r.promise]}subscribeRequest(e,t){return f(this,void 0,void 0,function*(){let n=this.descriptors.get(e);if(!n)throw new Error("Cannot subscribe to unknown track");let s=(r,a,o)=>f(this,void 0,void 0,function*(){if(r.subscription.type==="active")return;if(r.subscription.type!=="pending")throw new Error("Descriptor for track ".concat(e," is not pending, found ").concat(r.subscription.type));let c=Bl([a,o].filter(u=>typeof u<"u")),d=new ue;r.subscription.completionFuture.promise.then(()=>{var u;return(u=d.resolve)===null||u===void 0?void 0:u.call(d)}).catch(u=>{var h;return(h=d.reject)===null||h===void 0?void 0:h.call(d,u)});let l=()=>{var u;if(r.subscription.type==="pending"){if(r.subscription.pendingRequestCount-=1,o?.aborted){r.subscription.cancel();return}if(r.subscription.pendingRequestCount<=0){r.subscription.cancel();return}(u=d.reject)===null||u===void 0||u.call(d,Ue.cancelled())}};c.aborted&&l(),c.addEventListener("abort",l),yield d.promise,c.removeEventListener("abort",l)});switch(n.subscription.type){case"none":{n.subscription={type:"pending",completionFuture:new ue,pendingRequestCount:1,cancel:()=>{var a,o;let c=n.subscription;n.subscription={type:"none"},this.emit("sfuUpdateSubscription",{sid:n.info.sid,subscribe:!1}),c.type==="pending"&&((o=(a=c.completionFuture).reject)===null||o===void 0||o.call(a,r.aborted?Ue.timeout():Ue.cancelled()))}},this.emit("sfuUpdateSubscription",{sid:e,subscribe:!0});let r=jl(xp);yield s(n,t,r);return}case"pending":{n.subscription.pendingRequestCount+=1,yield s(n,t);return}case"active":return}})}querySubscribed(){return f(this,void 0,void 0,function*(){return Array.from(this.descriptors.values()).filter(t=>t.subscription.type==="active").map(t=>[t.info,t.publisherIdentity])})}unSubscribeRequest(e){var t;let n=this.descriptors.get(e);if(!n)throw new Error("Cannot subscribe to unknown track");if(n.subscription.type!=="active"){ce.warn("Unexpected descriptor state in unSubscribeRequest, expected active, found ".concat((t=n.subscription)===null||t===void 0?void 0:t.type));return}this.closeStreamControllers(n.subscription.streamControllers,e);let s=n.subscription;n.subscription={type:"none"},this.subscriptionHandles.delete(s.subcriptionHandle),this.emit("sfuUpdateSubscription",{sid:e,subscribe:!1})}closeStreamControllers(e,t){for(let s of e){var n=L(s,2);let r=n[0],a=n[1];a();try{r.close()}catch(o){ce.warn("Failed to close readable stream for track ".concat(t,": ").concat(o))}}}receiveSfuPublicationUpdates(e){return f(this,void 0,void 0,function*(){if(e.size===0)return;let t=new Map;for(let r of e.entries()){var n=L(r,2);let a=n[0],o=n[1],c=new Set;for(let d of o)c.add(d.sid),!this.descriptors.has(d.sid)&&(this.handleSidReassigned(a,d)||(yield this.handleTrackPublished(a,d)));t.set(a,c)}for(let r of t.entries()){var s=L(r,2);let a=s[0],o=s[1],d=Array.from(this.descriptors.entries()).filter(l=>{let u=L(l,2);return u[0],u[1].publisherIdentity===a}).map(l=>L(l,1)[0]).filter(l=>!o.has(l));for(let l of d)this.handleTrackUnpublished(l)}})}queryPublications(){return f(this,void 0,void 0,function*(){return Array.from(this.descriptors.values()).map(e=>e.info)})}handleTrackPublished(e,t){return f(this,void 0,void 0,function*(){if(this.descriptors.has(t.sid)){ce.error("Existing descriptor for track ".concat(t.sid));return}let n={info:t,publisherIdentity:e,subscription:{type:"none"},pipelineOptions:{}};this.descriptors.set(n.info.sid,n);let s=new zi(n.info,this,{publisherIdentity:e});this.emit("trackPublished",{track:s})})}handleSidReassigned(e,t){let n=Array.from(this.descriptors.entries()).find(u=>{let h=L(u,2);h[0];let m=h[1];return m.publisherIdentity===e&&m.info.pubHandle===t.pubHandle});if(!n)return!1;let s=L(n,2),r=s[0],a=s[1],o=a.info,c=o.name,d=o.usesE2ee;if(c!==t.name||d!==t.usesE2ee)return ce.warn("Info mismatch for ".concat(r,", treating as new publication")),!1;let l=t.sid;if(ce.debug("SID reassigned: ".concat(r," -> ").concat(l)),!this.descriptors.delete(r))return!1;switch(a.info.sid=l,a.subscription.type){case"none":break;case"pending":case"active":this.emit("sfuUpdateSubscription",{sid:l,subscribe:!0});break}return a.subscription.type==="active"&&this.subscriptionHandles.set(a.subscription.subcriptionHandle,l),this.descriptors.set(l,a),!0}handleTrackUnpublished(e){let t=this.descriptors.get(e);if(!t){ce.error("Unknown track ".concat(e));return}this.descriptors.delete(e),t.subscription.type==="active"&&(this.closeStreamControllers(t.subscription.streamControllers,e),this.subscriptionHandles.delete(t.subscription.subcriptionHandle)),this.emit("trackUnpublished",{sid:e,publisherIdentity:t.publisherIdentity})}receivedSfuSubscriberHandles(e){for(let n of e.entries()){var t=L(n,2);let s=t[0],r=t[1];this.registerSubscriberHandle(s,r)}}registerSubscriberHandle(e,t){var n,s;let r=this.descriptors.get(t);if(!r){ce.error("Unknown track ".concat(t));return}switch(r.subscription.type){case"none":{ce.warn("No subscription for ".concat(t));return}case"active":{this.subscriptionHandles.delete(r.subscription.subcriptionHandle),r.subscription.subcriptionHandle=e,this.subscriptionHandles.set(e,t);return}case"pending":{ce.debug("data track subscription activated",{sid:t,handle:e});let a=new ra({info:r.info,publisherIdentity:r.publisherIdentity,e2eeManager:this.e2eeManager,pipelineOptions:r.pipelineOptions}),o=r.subscription;r.subscription={type:"active",subcriptionHandle:e,pipeline:a,streamControllers:new Map},this.subscriptionHandles.set(e,t),(s=(n=o.completionFuture).resolve)===null||s===void 0||s.call(n)}}}packetReceived(e){return f(this,void 0,void 0,function*(){let t;try{var n=Ji.fromBinary(e),s=L(n,1);t=s[0]}catch(c){ce.error("Failed to deserialize packet: ".concat(c));return}let r=this.subscriptionHandles.get(t.header.trackHandle);if(!r){ce.warn("Unknown subscriber handle ".concat(t.header.trackHandle));return}let a=this.descriptors.get(r);if(!a){ce.error("Missing descriptor for track ".concat(r));return}if(a.subscription.type!=="active"){ce.warn("Received packet for track ".concat(r," without active subscription"));return}let o=yield a.subscription.pipeline.processPacket(t);if(o)for(let c of a.subscription.streamControllers.keys()){if(c.desiredSize!==null&&c.desiredSize<=0){ce.warn("Cannot send frame to subscribers: readable stream is full (desiredSize is ".concat(c.desiredSize,"). To increase this threshold, set a higher 'options.highWaterMark' when calling .subscribe()."));continue}let d=ql.lossyIntoFrame(o);c.enqueue(d)}})}resendSubscriptionUpdates(){for(let t of this.descriptors){var e=L(t,2);let n=e[0];e[1].subscription.type!=="none"&&this.emit("sfuUpdateSubscription",{sid:n,subscribe:!0})}}handleRemoteParticipantDisconnected(e){var t,n;for(let s of this.descriptors.values())if(s.publisherIdentity===e)switch(s.subscription.type){case"none":break;case"pending":(n=(t=s.subscription.completionFuture).reject)===null||n===void 0||n.call(t,Ue.disconnected());break;case"active":this.unSubscribeRequest(s.info.sid);break}}reset(){var e,t;for(let n of this.descriptors.values())this.emit("trackUnpublished",{sid:n.info.sid,publisherIdentity:n.publisherIdentity}),n.subscription.type==="pending"&&((t=(e=n.subscription.completionFuture).reject)===null||t===void 0||t.call(e,Ue.disconnected())),n.subscription.type==="active"&&this.closeStreamControllers(n.subscription.streamControllers,n.info.sid);this.descriptors.clear(),this.subscriptionHandles.clear()}},Yi=class i extends Ee{constructor(e,t,n){super(19,e,n),this.name="DataTrackPacketizerError",this.reason=t,this.reasonName=$i[t]}static mtuTooShort(){return new i("MTU is too short to send frame",$i.MtuTooShort)}},$i;(function(i){i[i.MtuTooShort=0]="MtuTooShort"})($i||($i={}));var oa=class i{constructor(e,t){this.sequence=St.u16(0),this.frameNumber=St.u16(0),this.clock=jr.rtpStartingNow(Ii.rtpRandom()),this.handle=e,this.mtuSizeBytes=t}static computeFrameMarker(e,t){return t<=1?Z.Single:e===0?Z.Start:e===t-1?Z.Final:Z.Inter}*packetize(e,t){var n;let s=this.frameNumber.getThenIncrement(),r={marker:Z.Inter,trackHandle:this.handle,sequence:St.u16(0),frameNumber:s,timestamp:(n=t?.now)!==null&&n!==void 0?n:this.clock.now(),extensions:e.extensions},a=new Gn(r).toBinaryLengthBytes();if(a>=this.mtuSizeBytes)throw Yi.mtuTooShort();let o=this.mtuSizeBytes-a,c=Math.ceil(e.payload.byteLength/o);for(let l=0,u=0;u{var n,s;(s=(n=this.flushedFuture).resolve)===null||s===void 0||s.call(n),this.manager.off("packetsFlushedChange",this.handleManagerPacketsFlushedChange),this.manager.off("reset",this.handleManagerReset)},this.handleManagerPacketsFlushedChange=n=>{var s,r;this.isFlushed=n.isFlushed,n.isFlushed&&((r=(s=this.flushedFuture).resolve)===null||r===void 0||r.call(s),this.flushedFuture=new ue)},this.options=e,this.manager=t,this.log=he(te.DataTracks),this.manager.on("packetsFlushedChange",this.handleManagerPacketsFlushedChange),this.manager.on("reset",this.handleManagerReset)}static withExplicitHandle(e,t,n){let s=new i(e,t);return s.handle=n,s}get info(){let e=this.descriptor;if(e?.type==="active")return e.info}get descriptor(){return this.handle?this.manager.getDescriptor(this.handle):null}publish(e){return f(this,void 0,void 0,function*(){try{this.handle=yield this.manager.publishRequest(this.options,e)}catch(t){throw t}})}isPublished(){var e;return((e=this.descriptor)===null||e===void 0?void 0:e.type)==="active"&&this.descriptor.publishState!=="unpublished"}tryPush(e){if(!this.handle)throw Lt.trackUnpublished();let t=ql.from(e);try{return this.manager.tryProcessAndSend(this.handle,t)}catch(n){throw n}}flush(){return f(this,void 0,void 0,function*(){if(!this.isFlushed)return this.flushedFuture.promise})}unpublish(){return f(this,void 0,void 0,function*(){if(!this.handle){W.warn('Data track "'.concat(this.options.name,'" is not published, so unpublishing has no effect.'));return}try{yield this.manager.unpublishRequest(this.handle)}catch(e){throw e}})}},es=class i{constructor(e){this.e2eeManager=e.e2eeManager,this.packetizer=new oa(e.info.pubHandle,i.TRANSPORT_MTU_BYTES)}updateE2eeManager(e){this.e2eeManager=e}processFrame(e){return Dd(this,arguments,function*(){let n=yield dt(this.encryptIfNeeded(e));try{yield dt(yield*em(De(this.packetizer.packetize(n))))}catch(s){throw s instanceof Yi?Xi.packetizer(s):s}})}encryptIfNeeded(e){return f(this,void 0,void 0,function*(){if(!this.e2eeManager)return e;let t;try{t=yield this.e2eeManager.encryptData(e.payload)}catch(n){throw Xi.encryption(n)}return e.payload=t.payload,e.extensions.e2ee=new jt(t.keyIndex,t.iv),e})}};es.TRANSPORT_MTU_BYTES=16e3;var Ae=he(te.DataTracks),Lc={pending(){return{type:"pending",completionFuture:new ue}},active(i,e){return{type:"active",info:i,publishState:"published",pipeline:new es({info:i,e2eeManager:e}),unpublishingFuture:new ue}}},Fp=1e4,ca=class i extends we.EventEmitter{constructor(e){var t;super(),this.handleAllocator=new qr,this.descriptors=new Map,this.inFlightPacketCounter=new Map,this.e2eeManager=(t=e?.e2eeManager)!==null&&t!==void 0?t:null}static withDescriptors(e){let t=new i;return t.descriptors=e,t}updateE2eeManager(e){this.e2eeManager=e;for(let t of this.descriptors.values())t.type==="active"&&t.pipeline.updateE2eeManager(e)}getDescriptor(e){var t;return(t=this.descriptors.get(e))!==null&&t!==void 0?t:null}tryProcessAndSend(e,t){return f(this,void 0,void 0,function*(){var n,s,r,a,o;let c=this.getDescriptor(e);if(c?.type!=="active"||c.publishState==="unpublished")throw Lt.trackUnpublished();if(c.publishState==="republishing")throw Lt.dropped("Data track republishing");try{try{for(var d=!0,l=De(c.pipeline.processFrame(t)),u;u=yield l.next(),n=u.done,!n;d=!0){a=u.value,d=!1;let h=a,m=(o=this.inFlightPacketCounter.get(e))!==null&&o!==void 0?o:0;this.inFlightPacketCounter.set(e,m+1),m===0&&this.emit("packetsFlushedChange",{handle:e,isFlushed:!1}),this.emit("packetAvailable",{handle:e,bytes:h.toBinary()})}}catch(h){s={error:h}}finally{try{!d&&!n&&(r=l.return)&&(yield r.call(l))}finally{if(s)throw s.error}}}catch(h){throw Lt.dropped(h)}})}handlePacketSendComplete(e){var t;let s=((t=this.inFlightPacketCounter.get(e))!==null&&t!==void 0?t:0)-1;s<0&&(Ae.warn("OutgoingDataTrackManager.handlePacketSendComplete: inFlightPacketCounter was decremented below 0 (got ".concat(this.inFlightPacketCounter," - resetting to 0. Were more packets send than were emitted?")),s=0),this.inFlightPacketCounter.set(e,s),s===0&&this.emit("packetsFlushedChange",{handle:e,isFlushed:!0})}publishRequest(e,t){return f(this,void 0,void 0,function*(){let n=this.handleAllocator.get();if(!n)throw Le.limitReached();let s=jl(Fp),r=t?Bl([t,s]):s;if(this.descriptors.has(n))throw new Error("Descriptor for handle already exists");let a=Lc.pending();this.descriptors.set(n,a);let o=()=>{var c,d;let l=this.descriptors.get(n);if(!l){Ae.warn("No descriptor for ".concat(n));return}this.descriptors.delete(n),this.emit("sfuUnpublishRequest",{handle:n}),l.type==="pending"&&((d=(c=l.completionFuture).reject)===null||d===void 0||d.call(c,s.aborted?Le.timeout():Le.cancelled()))};return r.aborted?(o(),a.completionFuture.promise.then(()=>n)):(r.addEventListener("abort",o),this.emit("sfuPublishRequest",{handle:n,name:e.name,usesE2ee:this.e2eeManager!==null}),yield a.completionFuture.promise,r.removeEventListener("abort",o),this.emit("trackPublished",{track:Zi.withExplicitHandle(e,this,n)}),n)})}queryPublished(){return Array.from(this.descriptors.values()).filter(t=>t.type==="active").map(t=>t.info)}unpublishRequest(e){return f(this,void 0,void 0,function*(){let t=this.descriptors.get(e);if(!t){Ae.warn("No descriptor for ".concat(e));return}if(t.type!=="active"){Ae.warn("Track ".concat(e," not active"));return}this.emit("sfuUnpublishRequest",{handle:e}),yield t.unpublishingFuture.promise,this.inFlightPacketCounter.delete(e),this.emit("trackUnpublished",{sid:t.info.sid})})}receivedSfuPublishResponse(e,t){var n,s,r,a;let o=this.descriptors.get(e);if(!o){Ae.warn("No descriptor for ".concat(e));return}switch(this.descriptors.delete(e),o.type){case"pending":{if(t.type==="ok"){let c=t.data;Ae.debug("SFU accepted publish request for handle ".concat(e),{sid:c.sid});let d=c.usesE2ee?this.e2eeManager:null;this.descriptors.set(c.pubHandle,Lc.active(c,d)),(s=(n=o.completionFuture).resolve)===null||s===void 0||s.call(n)}else Ae.debug("SFU rejected publish request for handle ".concat(e),{error:t.error}),(a=(r=o.completionFuture).reject)===null||a===void 0||a.call(r,t.error);return}case"active":{if(o.publishState!=="republishing"){Ae.warn("Track ".concat(e," already active"));return}if(t.type==="error"){Ae.warn("Republish failed for track ".concat(e));return}Ae.debug("Track ".concat(e," republished")),o.info.sid=t.data.sid,o.publishState="published",this.descriptors.set(o.info.pubHandle,o)}}}receivedSfuUnpublishResponse(e){var t,n;let s=this.descriptors.get(e);if(!s){Ae.warn("No descriptor for ".concat(e));return}if(this.descriptors.delete(e),s.type!=="active"){Ae.warn("Track ".concat(e," not active"));return}s.publishState="unpublished",(n=(t=s.unpublishingFuture).resolve)===null||n===void 0||n.call(t)}sfuWillRepublishTracks(){var e,t;for(let s of this.descriptors.entries()){var n=L(s,2);let r=n[0],a=n[1];switch(a.type){case"pending":this.descriptors.delete(r),(t=(e=a.completionFuture).reject)===null||t===void 0||t.call(e,Le.disconnected());break;case"active":a.publishState="republishing",this.emit("sfuPublishRequest",{handle:a.info.pubHandle,name:a.info.name,usesE2ee:a.info.usesE2ee})}}}reset(){return f(this,void 0,void 0,function*(){var e,t,n,s;this.handleAllocator.reset();for(let r of this.descriptors.values())switch(r.type){case"pending":(t=(e=r.completionFuture).reject)===null||t===void 0||t.call(e,Le.disconnected());break;case"active":(s=(n=r.unpublishingFuture).resolve)===null||s===void 0||s.call(n),yield this.unpublishRequest(r.info.pubHandle);break}this.descriptors.clear(),this.inFlightPacketCounter.clear(),this.emit("reset")})}},X=class i extends Error{constructor(e,t,n,s){super(t),this.code=e,this.message=Nc(t,i.MAX_MESSAGE_BYTES),this.data=n?Nc(n,i.MAX_DATA_BYTES):void 0,typeof s?.cause<"u"&&(this.cause=s?.cause)}static fromProto(e){return new i(e.code,e.message,e.data)}toProto(){return new ud({code:this.code,message:this.message,data:this.data})}static builtIn(e,t,n){return new i(i.ErrorCode[e],i.ErrorMessage[e],t,n)}};X.MAX_MESSAGE_BYTES=256;X.MAX_DATA_BYTES=15360;X.ErrorCode={APPLICATION_ERROR:1500,CONNECTION_TIMEOUT:1501,RESPONSE_TIMEOUT:1502,RECIPIENT_DISCONNECTED:1503,RESPONSE_PAYLOAD_TOO_LARGE:1504,SEND_FAILED:1505,UNSUPPORTED_METHOD:1400,RECIPIENT_NOT_FOUND:1401,REQUEST_PAYLOAD_TOO_LARGE:1402,UNSUPPORTED_SERVER:1403,UNSUPPORTED_VERSION:1404};X.ErrorMessage={APPLICATION_ERROR:"Application error in method handler",CONNECTION_TIMEOUT:"Connection timeout",RESPONSE_TIMEOUT:"Response timeout",RECIPIENT_DISCONNECTED:"Recipient disconnected",RESPONSE_PAYLOAD_TOO_LARGE:"Response payload too large",SEND_FAILED:"Failed to send",UNSUPPORTED_METHOD:"Method not supported at destination",RECIPIENT_NOT_FOUND:"Recipient not found",REQUEST_PAYLOAD_TOO_LARGE:"Request payload too large",UNSUPPORTED_SERVER:"RPC not supported by server",UNSUPPORTED_VERSION:"Unsupported RPC version"};var Hl=15360,Kl="lk.rpc_request",Gl="lk.rpc_response",pe;(function(i){i.RPC_REQUEST_ID="lk.rpc_request_id",i.RPC_REQUEST_METHOD="lk.rpc_request_method",i.RPC_REQUEST_RESPONSE_TIMEOUT_MS="lk.rpc_request_response_timeout_ms",i.RPC_REQUEST_VERSION="lk.rpc_request_version"})(pe||(pe={}));var Bp=1,zl=2;function fo(i){return new TextEncoder().encode(i).length}function Nc(i,e){if(fo(i)<=e)return i;let t=0,n=i.length,s=new TextEncoder;for(;tHl&&u{var b;n.pendingAcks.delete(p),(b=k.reject)===null||b===void 0||b.call(k,X.builtIn("CONNECTION_TIMEOUT")),n.pendingResponses.delete(p),T!==null&&clearTimeout(T)},7e3);n.pendingAcks.set(p,{resolve:()=>{clearTimeout(S)},participantIdentity:s}),n.pendingResponses.set(p,{completionFuture:k,participantIdentity:s}),yield n.publishRpcRequest(s,p,r,a,g,u),T=setTimeout(()=>{var b;n.pendingResponses.delete(p),(b=k.reject)===null||b===void 0||b.call(k,X.builtIn("RESPONSE_TIMEOUT"))},c);let I=k.promise.finally(()=>{clearTimeout(T),n.pendingAcks.has(p)&&(n.log.warn("RPC response received before ack",p),n.pendingAcks.delete(p),clearTimeout(S))});return[p,I]}()})}publishRpcRequest(e,t,n,s,r,a){return f(this,void 0,void 0,function*(){if(a>=Tr){yield this.outgoingDataStreamManager.sendText(s,{topic:Kl,destinationIdentities:[e],attributes:{[pe.RPC_REQUEST_ID]:t,[pe.RPC_REQUEST_METHOD]:n,[pe.RPC_REQUEST_RESPONSE_TIMEOUT_MS]:"".concat(r),[pe.RPC_REQUEST_VERSION]:"".concat(zl)}});return}this.emit("sendDataPacket",{packet:new Se({destinationIdentities:[e],kind:yt.RELIABLE,value:{case:"rpcRequest",value:new Wa({id:t,method:n,payload:s,responseTimeoutMs:r,version:Bp})}})})})}handleIncomingDataStream(e,t,n){return f(this,void 0,void 0,function*(){let s=n[pe.RPC_REQUEST_ID];if(!s){this.log.warn("RPC data stream malformed: ".concat(pe.RPC_REQUEST_ID," not set."));return}let r=this.pendingResponses.get(s);if(r&&r.participantIdentity!==t){this.log.warn("RPC response stream for ".concat(s," arrived from unexpected sender ").concat(t,", expected ").concat(r.participantIdentity,". Ignoring."));return}let a;try{a=yield e.readAll()}catch(o){this.log.warn("Error reading RPC response payload: ".concat(o)),this.handleIncomingRpcResponseFailure(s,X.builtIn("APPLICATION_ERROR","Error reading RPC response payload",{cause:o}));return}this.handleIncomingRpcResponseSuccess(s,a)})}handleIncomingRpcResponseSuccess(e,t){var n,s;let r=this.pendingResponses.get(e);r?((s=(n=r.completionFuture).resolve)===null||s===void 0||s.call(n,t),this.pendingResponses.delete(e)):this.log.error("Response received for unexpected RPC request",e)}handleIncomingRpcResponseFailure(e,t){var n,s;let r=this.pendingResponses.get(e);r?((s=(n=r.completionFuture).reject)===null||s===void 0||s.call(n,t),this.pendingResponses.delete(e)):this.log.error("Response received for unexpected RPC request",e)}handleIncomingRpcAck(e){let t=this.pendingAcks.get(e);t?(t.resolve(),this.pendingAcks.delete(e)):this.log.error("Ack received for unexpected RPC request: ".concat(e))}handleParticipantDisconnected(e){var t;for(let a of this.pendingAcks){var n=L(a,2);let o=n[0];n[1].participantIdentity===e&&this.pendingAcks.delete(o)}for(let a of this.pendingResponses){var s=L(a,2);let o=s[0];var r=s[1];let c=r.participantIdentity,d=r.completionFuture;c===e&&((t=d.reject)===null||t===void 0||t.call(d,X.builtIn("RECIPIENT_DISCONNECTED")),this.pendingResponses.delete(o))}}},la=class extends we.EventEmitter{constructor(e,t,n){super(),this.rpcHandlers=new Map,this.log=e,this.outgoingDataStreamManager=t,this.getRemoteParticipantClientProtocol=n}registerRpcMethod(e,t){if(this.rpcHandlers.has(e))throw Error("RPC handler already registered for method ".concat(e,", unregisterRpcMethod before trying to register again"));this.rpcHandlers.set(e,t)}unregisterRpcMethod(e){this.rpcHandlers.delete(e)}handleIncomingRpcRequest(e,t){return f(this,void 0,void 0,function*(){var n;if(this.publishRpcAck(e,t.id),t.version!==1){this.publishRpcResponsePacket(e,t.id,null,X.builtIn("UNSUPPORTED_VERSION"));return}let s=this.rpcHandlers.get(t.method);if(!s){this.publishRpcResponsePacket(e,t.id,null,X.builtIn("UNSUPPORTED_METHOD"));return}let r;try{r=yield s({requestId:t.id,callerIdentity:e,payload:t.payload,responseTimeout:t.responseTimeoutMs})}catch(a){let o;a instanceof X?o=a:(this.log.warn("Uncaught error returned by RPC handler for ".concat(t.method,". Returning APPLICATION_ERROR instead."),a),o=X.builtIn("APPLICATION_ERROR","Uncaught error: ".concat((n=a?.message)!==null&&n!==void 0?n:a),{cause:a})),this.publishRpcResponsePacket(e,t.id,null,o);return}yield this.publishRpcResponse(e,t.id,r??"")})}handleIncomingDataStream(e,t,n){return f(this,void 0,void 0,function*(){let s=n[pe.RPC_REQUEST_ID],r=n[pe.RPC_REQUEST_METHOD],a=parseInt(n[pe.RPC_REQUEST_RESPONSE_TIMEOUT_MS],10),o=parseInt(n[pe.RPC_REQUEST_VERSION],10);if(!s||!r||Number.isNaN(a)||Number.isNaN(o)){this.log.warn("RPC data stream malformed: ".concat(pe.RPC_REQUEST_ID," / ").concat(pe.RPC_REQUEST_METHOD," / ").concat(pe.RPC_REQUEST_RESPONSE_TIMEOUT_MS," / ").concat(pe.RPC_REQUEST_VERSION," not set.")),this.publishRpcResponsePacket(t,s,null,X.builtIn("APPLICATION_ERROR","RPC data stream malformed"));return}if(this.publishRpcAck(t,s),o!==zl){this.publishRpcResponsePacket(t,s,null,X.builtIn("UNSUPPORTED_VERSION"));return}let c;try{c=yield e.readAll()}catch(u){this.log.warn("Error reading RPC request payload: ".concat(u)),this.publishRpcResponsePacket(t,s,null,X.builtIn("APPLICATION_ERROR","Error reading RPC request payload",{cause:u}));return}let d=this.rpcHandlers.get(r);if(!d){this.publishRpcResponsePacket(t,s,null,X.builtIn("UNSUPPORTED_METHOD"));return}let l;try{l=yield d({requestId:s,callerIdentity:t,payload:c,responseTimeout:a})}catch(u){let h;u instanceof X?h=u:(this.log.warn("Uncaught error returned by RPC handler for ".concat(r,". Returning APPLICATION_ERROR instead."),u),h=X.builtIn("APPLICATION_ERROR")),this.publishRpcResponsePacket(t,s,null,h);return}yield this.publishRpcResponse(t,s,l??"")})}publishRpcAck(e,t){this.emit("sendDataPacket",{packet:new Se({destinationIdentities:[e],kind:yt.RELIABLE,value:{case:"rpcAck",value:new os({requestId:t})}})})}publishRpcResponsePacket(e,t,n,s){this.emit("sendDataPacket",{packet:new Se({destinationIdentities:[e],kind:yt.RELIABLE,value:{case:"rpcResponse",value:new Ha({requestId:t,value:s?{case:"error",value:s.toProto()}:{case:"payload",value:n??""}})}})})}publishRpcResponse(e,t,n){return f(this,void 0,void 0,function*(){if(this.getRemoteParticipantClientProtocol(e)>=Tr){yield this.outgoingDataStreamManager.sendText(n,{topic:Gl,destinationIdentities:[e],attributes:{[pe.RPC_REQUEST_ID]:t}});return}if(fo(n)>Hl){this.log.warn("RPC Response payload too large for request ".concat(t,". To send larger responses, consider updating the sending client.")),this.publishRpcResponsePacket(e,t,null,X.builtIn("RESPONSE_PAYLOAD_TOO_LARGE"));return}this.publishRpcResponsePacket(e,t,n,null)})}},ua=class extends Ri{constructor(e,t,n,s,r,a){super(e,t,E.Kind.Audio,n,a),this.monitorReceiver=()=>f(this,void 0,void 0,function*(){if(!this.receiver){this._currentBitrate=0;return}let o=yield this.getReceiverStats();o&&this.prevStats&&this.receiver&&(this._currentBitrate=fs(o,this.prevStats)),this.prevStats=o}),this.audioContext=s,this.webAudioPluginNodes=[],r&&(this.sinkId=r.deviceId)}setVolume(e){var t;for(let n of this.attachedElements)this.audioContext?(t=this.gainNode)===null||t===void 0||t.gain.setTargetAtTime(e,0,.1):n.volume=e;Ke()&&this._mediaStreamTrack._setVolume(e),this.elementVolume=e}getVolume(){if(this.elementVolume)return this.elementVolume;if(Ke())return 1;let e=0;return this.attachedElements.forEach(t=>{t.volume>e&&(e=t.volume)}),e}setSinkId(e){return f(this,void 0,void 0,function*(){this.sinkId=e,yield Promise.all(this.attachedElements.map(t=>{if(Or(t))return t.setSinkId(e)}))})}attach(e){let t=this.attachedElements.length===0;return e?super.attach(e):e=super.attach(),this.sinkId&&Or(e)&&e.setSinkId(this.sinkId).catch(n=>{this.log.error("Failed to set sink id on remote audio track",n,this.logContext)}),this.audioContext&&t&&(this.log.debug("using audio context mapping",this.logContext),this.connectWebAudio(this.audioContext,e),e.volume=0,e.muted=!0),this.elementVolume&&this.setVolume(this.elementVolume),e}detach(e){let t;return e?(t=super.detach(e),this.audioContext&&(this.attachedElements.length>0?this.connectWebAudio(this.audioContext,this.attachedElements[0]):this.disconnectWebAudio())):(t=super.detach(),this.disconnectWebAudio()),t}setAudioContext(e){this.audioContext=e,e&&this.attachedElements.length>0?this.connectWebAudio(e,this.attachedElements[0]):e||this.disconnectWebAudio()}setWebAudioPlugins(e){this.webAudioPluginNodes=e,this.attachedElements.length>0&&this.audioContext&&this.connectWebAudio(this.audioContext,this.attachedElements[0])}connectWebAudio(e,t){this.disconnectWebAudio(),this.sourceNode=e.createMediaStreamSource(t.srcObject);let n=this.sourceNode;this.webAudioPluginNodes.forEach(s=>{n.connect(s),n=s}),this.gainNode=e.createGain(),n.connect(this.gainNode),this.gainNode.connect(e.destination),this.elementVolume&&this.gainNode.gain.setTargetAtTime(this.elementVolume,0,.1),e.state!=="running"&&e.resume().then(()=>{e.state!=="running"&&this.emit(P.AudioPlaybackFailed,new Error("Audio Context couldn't be started automatically"))}).catch(s=>{this.emit(P.AudioPlaybackFailed,s)})}disconnectWebAudio(){var e,t;(e=this.gainNode)===null||e===void 0||e.disconnect(),(t=this.sourceNode)===null||t===void 0||t.disconnect(),this.gainNode=void 0,this.sourceNode=void 0}getReceiverStats(){return f(this,void 0,void 0,function*(){if(!this.receiver||!this.receiver.getStats)return;let e=yield this.receiver.getStats(),t;return e.forEach(n=>{n.type==="inbound-rtp"&&(t={type:"audio",streamId:n.id,timestamp:n.timestamp,jitter:n.jitter,bytesReceived:n.bytesReceived,concealedSamples:n.concealedSamples,concealmentEvents:n.concealmentEvents,silentConcealedSamples:n.silentConcealedSamples,silentConcealmentEvents:n.silentConcealmentEvents,totalAudioEnergy:n.totalAudioEnergy,totalSamplesDuration:n.totalSamplesDuration})}),t})}},je=class extends we.EventEmitter{constructor(e,t,n,s){var r;super(),this.metadataMuted=!1,this.encryption=G.NONE,this.log=W,this.handleMuted=()=>{this.emit(P.Muted)},this.handleUnmuted=()=>{this.emit(P.Unmuted)},this.log=he((r=s?.loggerName)!==null&&r!==void 0?r:te.Publication),this.loggerContextCb=this.loggerContextCb,this.setMaxListeners(100),this.kind=e,this.trackSid=t,this.trackName=n,this.source=E.Source.Unknown}setTrack(e){this.track&&(this.track.off(P.Muted,this.handleMuted),this.track.off(P.Unmuted,this.handleUnmuted)),this.track=e,e&&(e.on(P.Muted,this.handleMuted),e.on(P.Unmuted,this.handleUnmuted))}get logContext(){var e;return Object.assign(Object.assign({},(e=this.loggerContextCb)===null||e===void 0?void 0:e.call(this)),q(this))}get isMuted(){return this.metadataMuted}get isEnabled(){return!0}get isSubscribed(){return this.track!==void 0}get isEncrypted(){return this.encryption!==G.NONE}get audioTrack(){if(et(this.track))return this.track}get videoTrack(){if(ut(this.track))return this.track}updateInfo(e){this.trackSid=e.sid,this.trackName=e.name,this.source=E.sourceFromProto(e.source),this.mimeType=e.mimeType,this.kind===E.Kind.Video&&e.width>0&&(this.dimensions={width:e.width,height:e.height},this.simulcasted=e.simulcast),this.encryption=e.encryption,this.trackInfo=e,this.log.debug("update publication info",Object.assign(Object.assign({},this.logContext),{info:e}))}};(function(i){(function(e){e.Desired="desired",e.Subscribed="subscribed",e.Unsubscribed="unsubscribed"})(i.SubscriptionStatus||(i.SubscriptionStatus={})),function(e){e.Allowed="allowed",e.NotAllowed="not_allowed"}(i.PermissionStatus||(i.PermissionStatus={}))})(je||(je={}));var zn=class extends je{get isUpstreamPaused(){var e;return(e=this.track)===null||e===void 0?void 0:e.isUpstreamPaused}constructor(e,t,n,s){super(e,t.sid,t.name,s),this.track=void 0,this.handleTrackEnded=()=>{this.emit(P.Ended)},this.handleCpuConstrained=()=>{this.track&&ut(this.track)&&this.emit(P.CpuConstrained,this.track)},this.updateInfo(t),this.setTrack(n)}setTrack(e){this.track&&(this.track.off(P.Ended,this.handleTrackEnded),this.track.off(P.CpuConstrained,this.handleCpuConstrained)),super.setTrack(e),e&&(e.on(P.Ended,this.handleTrackEnded),e.on(P.CpuConstrained,this.handleCpuConstrained))}get isMuted(){return this.track?this.track.isMuted:super.isMuted}get audioTrack(){return super.audioTrack}get videoTrack(){return super.videoTrack}get isLocal(){return!0}mute(){return f(this,void 0,void 0,function*(){var e;return(e=this.track)===null||e===void 0?void 0:e.mute()})}unmute(){return f(this,void 0,void 0,function*(){var e;return(e=this.track)===null||e===void 0?void 0:e.unmute()})}pauseUpstream(){return f(this,void 0,void 0,function*(){var e;yield(e=this.track)===null||e===void 0?void 0:e.pauseUpstream()})}resumeUpstream(){return f(this,void 0,void 0,function*(){var e;yield(e=this.track)===null||e===void 0?void 0:e.resumeUpstream()})}getTrackFeatures(){var e;if(et(this.track)){let t=this.track.getSourceTrackSettings(),n=new Set;return t.autoGainControl&&n.add(me.TF_AUTO_GAIN_CONTROL),t.echoCancellation&&n.add(me.TF_ECHO_CANCELLATION),t.noiseSuppression&&n.add(me.TF_NOISE_SUPPRESSION),t.channelCount&&t.channelCount>1&&n.add(me.TF_STEREO),!((e=this.options)===null||e===void 0)&&e.dtx||n.add(me.TF_NO_DTX),this.track.enhancedNoiseCancellation&&n.add(me.TF_ENHANCED_NOISE_CANCELLATION),Array.from(n.values())}else return[]}};function ps(i,e){return f(this,void 0,void 0,function*(){i??(i={});let t=!1,n=yl(i),s=n.audioProcessor,r=n.videoProcessor,a=n.optionsWithoutProcessor,o=a.audio,c=a.video;if(s&&typeof a.audio=="object"&&(a.audio.processor=s),r&&typeof a.video=="object"&&(a.video.processor=r),i.audio&&typeof a.audio=="object"&&typeof a.audio.deviceId=="string"){let h=a.audio.deviceId;a.audio.deviceId={exact:h},t=!0,o=Object.assign(Object.assign({},a.audio),{deviceId:{ideal:h}})}if(a.video&&typeof a.video=="object"&&typeof a.video.deviceId=="string"){let h=a.video.deviceId;a.video.deviceId={exact:h},t=!0,c=Object.assign(Object.assign({},a.video),{deviceId:{ideal:h}})}a.audio===!0?a.audio={deviceId:"default"}:typeof a.audio=="object"&&a.audio!==null&&(a.audio=Object.assign(Object.assign({},a.audio),{deviceId:a.audio.deviceId||"default"})),a.video===!0?a.video={deviceId:"default"}:typeof a.video=="object"&&!a.video.deviceId&&(a.video.deviceId="default");let d=gl(a,Al,Ol),l=io(d),u=navigator.mediaDevices.getUserMedia(l);a.audio&&(Me.userMediaPromiseMap.set("audioinput",u),u.catch(()=>Me.userMediaPromiseMap.delete("audioinput"))),a.video&&(Me.userMediaPromiseMap.set("videoinput",u),u.catch(()=>Me.userMediaPromiseMap.delete("videoinput")));try{let h=yield u;return yield Promise.all(h.getTracks().map(m=>f(this,void 0,void 0,function*(){let g=m.kind==="audio",p,k=g?l.audio:l.video;typeof k!="boolean"&&(p=k);let T=m.getSettings().deviceId;p?.deviceId&&Zt(p.deviceId)!==T?p.deviceId=T:p||(p={deviceId:T});let S=ap(m,p,e);return S.kind===E.Kind.Video?S.source=E.Source.Camera:S.kind===E.Kind.Audio&&(S.source=E.Source.Microphone),S.mediaStream=h,et(S)&&s?yield S.setProcessor(s):ut(S)&&r&&(yield S.setProcessor(r)),S})))}catch(h){if(!t)throw h;return ps(Object.assign(Object.assign({},i),{audio:o,video:c}),e)}})}function jp(i){return f(this,void 0,void 0,function*(){return(yield ps({audio:!1,video:i??!0}))[0]})}function qp(i){return f(this,void 0,void 0,function*(){return(yield ps({audio:i??!0,video:!1}))[0]})}var pt;(function(i){i.Excellent="excellent",i.Good="good",i.Poor="poor",i.Lost="lost",i.Unknown="unknown"})(pt||(pt={}));function Vp(i){switch(i){case Ht.EXCELLENT:return pt.Excellent;case Ht.GOOD:return pt.Good;case Ht.POOR:return pt.Poor;case Ht.LOST:return pt.Lost;default:return pt.Unknown}}var ts=class extends we.EventEmitter{get logContext(){var e,t;return Object.assign({},(t=(e=this.loggerOptions)===null||e===void 0?void 0:e.loggerContextCb)===null||t===void 0?void 0:t.call(e))}get isEncrypted(){return this.trackPublications.size>0&&Array.from(this.trackPublications.values()).every(e=>e.isEncrypted)}get isAgent(){var e;return((e=this.permissions)===null||e===void 0?void 0:e.agent)||this.kind===Ln.AGENT}get isActive(){var e;return((e=this.participantInfo)===null||e===void 0?void 0:e.state)===zt.ACTIVE}get kind(){return this._kind}get attributes(){return Object.freeze(Object.assign({},this._attributes))}constructor(e,t,n,s,r,a){let o=arguments.length>6&&arguments[6]!==void 0?arguments[6]:Ln.STANDARD;var c;super(),this.audioLevel=0,this.isSpeaking=!1,this._connectionQuality=pt.Unknown,this.log=W,this.loggerOptions=a,this.log=he((c=a?.loggerName)!==null&&c!==void 0?c:te.Participant,()=>this.logContext),this.setMaxListeners(100),this.sid=e,this.identity=t,this.name=n,this.metadata=s,this.audioTrackPublications=new Map,this.videoTrackPublications=new Map,this.trackPublications=new Map,this._kind=o,this._attributes=r??{}}getTrackPublications(){return Array.from(this.trackPublications.values())}getTrackPublication(e){for(let n of this.trackPublications){var t=L(n,2);let s=t[1];if(s.source===e)return s}}getTrackPublicationByName(e){for(let n of this.trackPublications){var t=L(n,2);let s=t[1];if(s.trackName===e)return s}}waitUntilActive(){return this.isActive?Promise.resolve():this.activeFuture?this.activeFuture.promise:(this.activeFuture=new ue,this.once(A.Active,()=>{var e,t;(t=(e=this.activeFuture)===null||e===void 0?void 0:e.resolve)===null||t===void 0||t.call(e),this.activeFuture=void 0}),this.activeFuture.promise)}get connectionQuality(){return this._connectionQuality}get isCameraEnabled(){var e;let t=this.getTrackPublication(E.Source.Camera);return!(!((e=t?.isMuted)!==null&&e!==void 0)||e)}get isMicrophoneEnabled(){var e;let t=this.getTrackPublication(E.Source.Microphone);return!(!((e=t?.isMuted)!==null&&e!==void 0)||e)}get isScreenShareEnabled(){return!!this.getTrackPublication(E.Source.ScreenShare)}get isLocal(){return!1}get joinedAt(){return this.participantInfo?new Date(Number.parseInt(this.participantInfo.joinedAt.toString())*1e3):new Date}updateInfo(e){var t;return this.participantInfo&&this.participantInfo.sid===e.sid&&this.participantInfo.version>e.version?!1:(this.identity=e.identity,this.sid=e.sid,this._setName(e.name),this._setMetadata(e.metadata),this._setAttributes(e.attributes),e.state===zt.ACTIVE&&((t=this.participantInfo)===null||t===void 0?void 0:t.state)!==zt.ACTIVE&&this.emit(A.Active),e.permission&&this.setPermissions(e.permission),this.participantInfo=e,!0)}_setMetadata(e){let t=this.metadata!==e,n=this.metadata;this.metadata=e,t&&this.emit(A.ParticipantMetadataChanged,n)}_setName(e){let t=this.name!==e;this.name=e,t&&this.emit(A.ParticipantNameChanged,e)}_setAttributes(e){let t=_m(this.attributes,e);this._attributes=e,Object.keys(t).length>0&&this.emit(A.AttributesChanged,t)}setPermissions(e){var t,n,s,r,a,o;let c=this.permissions,d=e.canPublish!==((t=this.permissions)===null||t===void 0?void 0:t.canPublish)||e.canSubscribe!==((n=this.permissions)===null||n===void 0?void 0:n.canSubscribe)||e.canPublishData!==((s=this.permissions)===null||s===void 0?void 0:s.canPublishData)||e.hidden!==((r=this.permissions)===null||r===void 0?void 0:r.hidden)||e.recorder!==((a=this.permissions)===null||a===void 0?void 0:a.recorder)||e.canPublishSources.length!==this.permissions.canPublishSources.length||e.canPublishSources.some((l,u)=>{var h;return l!==((h=this.permissions)===null||h===void 0?void 0:h.canPublishSources[u])})||e.canSubscribeMetrics!==((o=this.permissions)===null||o===void 0?void 0:o.canSubscribeMetrics);return this.permissions=e,d&&this.emit(A.ParticipantPermissionsChanged,c),d}setIsSpeaking(e){e!==this.isSpeaking&&(this.isSpeaking=e,e&&(this.lastSpokeAt=new Date),this.emit(A.IsSpeakingChanged,e))}setConnectionQuality(e){let t=this._connectionQuality;this._connectionQuality=Vp(e),t!==this._connectionQuality&&this.emit(A.ConnectionQualityChanged,this._connectionQuality)}setDisconnected(){var e,t;this.activeFuture&&((t=(e=this.activeFuture).reject)===null||t===void 0||t.call(e,new Error("Participant disconnected")),this.activeFuture=void 0)}setAudioContext(e){this.audioContext=e,this.audioTrackPublications.forEach(t=>et(t.track)&&t.track.setAudioContext(e))}addTrackPublication(e){this.log.debug("adding track publication",{trackSid:e.trackSid,source:e.source,kind:e.kind}),e.on(P.Muted,()=>{this.emit(A.TrackMuted,e)}),e.on(P.Unmuted,()=>{this.emit(A.TrackUnmuted,e)});let t=e;switch(t.track&&(t.track.sid=e.trackSid),this.trackPublications.set(e.trackSid,e),e.kind){case E.Kind.Audio:this.audioTrackPublications.set(e.trackSid,e);break;case E.Kind.Video:this.videoTrackPublications.set(e.trackSid,e);break}}};function Wp(i){var e,t,n;if(!i.participantSid&&!i.participantIdentity)throw new Error("Invalid track permission, must provide at least one of participantIdentity and participantSid");return new Rd({participantIdentity:(e=i.participantIdentity)!==null&&e!==void 0?e:"",participantSid:(t=i.participantSid)!==null&&t!==void 0?t:"",allTracks:(n=i.allowAll)!==null&&n!==void 0?n:!1,trackSids:i.allowedTrackSids||[]})}var ha=class extends ts{constructor(e,t,n,s,r,a,o,c){super(e,t,void 0,void 0,void 0,{loggerName:s.loggerName,loggerContextCb:()=>this.engine.logContext}),this.pendingPublishing=new Set,this.pendingPublishPromises=new Map,this.participantTrackPermissions=[],this.allParticipantsAllowedToSubscribe=!0,this.encryptionType=G.NONE,this.e2eeStateMutex=new le,this.enabledPublishVideoCodecs=[],this.handleReconnecting=()=>{this.reconnectFuture||(this.reconnectFuture=new ue)},this.handleReconnected=()=>{var d,l;(l=(d=this.reconnectFuture)===null||d===void 0?void 0:d.resolve)===null||l===void 0||l.call(d),this.reconnectFuture=void 0,this.updateTrackSubscriptionPermissions()},this.handleClosing=()=>{var d,l,u,h,m,g;this.reconnectFuture&&(this.reconnectFuture.promise.catch(p=>this.log.warn(p.message)),(l=(d=this.reconnectFuture)===null||d===void 0?void 0:d.reject)===null||l===void 0||l.call(d,new Error("Got disconnected during reconnection attempt")),this.reconnectFuture=void 0),this.signalConnectedFuture&&((h=(u=this.signalConnectedFuture).reject)===null||h===void 0||h.call(u,new Error("Got disconnected without signal connected")),this.signalConnectedFuture=void 0),(g=(m=this.activeAgentFuture)===null||m===void 0?void 0:m.reject)===null||g===void 0||g.call(m,new Error("Got disconnected without active agent present")),this.activeAgentFuture=void 0,this.firstActiveAgent=void 0},this.handleSignalConnected=d=>{var l,u;d.participant&&this.updateInfo(d.participant),this.signalConnectedFuture||(this.signalConnectedFuture=new ue),(u=(l=this.signalConnectedFuture).resolve)===null||u===void 0||u.call(l)},this.handleSignalRequestResponse=d=>{let l=d.requestId,u=d.reason,h=d.message,m=this.pendingSignalRequests.get(l);switch(m&&(u!==Pt.OK&&m.reject(new Ci(h,u)),this.pendingSignalRequests.delete(l)),d.request.case){case"publishDataTrack":{let g;switch(d.reason){case Pt.NOT_ALLOWED:g=Le.notAllowed(d.message);break;case Pt.DUPLICATE_NAME:g=Le.duplicateName(d.message);break;case Pt.INVALID_NAME:g=Le.invalidName(d.message);break;case Pt.LIMIT_EXCEEDED:g=Le.limitReached(d.message);break;default:g=Le.unknown(d.reason,d.message);break}this.roomOutgoingDataTrackManager.receivedSfuPublishResponse(d.request.value.pubHandle,{type:"error",error:g});break}}},this.updateTrackSubscriptionPermissions=()=>{this.log.debug("updating track subscription permissions",{allParticipantsAllowed:this.allParticipantsAllowedToSubscribe,participantTrackPermissions:this.participantTrackPermissions}),this.engine.client.sendUpdateSubscriptionPermissions(this.allParticipantsAllowedToSubscribe,this.participantTrackPermissions.map(d=>Wp(d)))},this.onTrackUnmuted=d=>{this.onTrackMuted(d,d.isUpstreamPaused)},this.onTrackMuted=(d,l)=>{if(l===void 0&&(l=!0),!d.sid){this.log.error("could not update mute status for unpublished track",q(d));return}this.engine.updateMuteStatus(d.sid,l)},this.onTrackUpstreamPaused=d=>{this.log.debug("upstream paused",q(d)),this.onTrackMuted(d,!0)},this.onTrackUpstreamResumed=d=>{this.log.debug("upstream resumed",q(d)),this.onTrackMuted(d,d.isMuted)},this.onTrackFeatureUpdate=d=>{let l=this.audioTrackPublications.get(d.sid);if(!l){this.log.warn("Could not update local audio track settings, missing publication for track ".concat(d.sid));return}this.engine.client.sendUpdateLocalAudioTrack(l.trackSid,l.getTrackFeatures())},this.onTrackCpuConstrained=(d,l)=>{this.log.debug("track cpu constrained",q(l)),this.emit(A.LocalTrackCpuConstrained,d,l)},this.handleSubscribedQualityUpdate=d=>f(this,void 0,void 0,function*(){var l,u,h,m,g;if(!(!((g=this.roomOptions)===null||g===void 0)&&g.dynacast))return;let p=this.videoTrackPublications.get(d.trackSid);if(!p){this.log.warn("received subscribed quality update for unknown track",{trackSid:d.trackSid});return}if(!p.videoTrack)return;let k=yield p.videoTrack.setPublishingCodecs(d.subscribedCodecs);try{for(var T=!0,S=De(k),I;I=yield S.next(),l=I.done,!l;T=!0){m=I.value,T=!1;let b=m;Tm(b)&&(this.log.debug("publish ".concat(b," for ").concat(p.videoTrack.sid),q(p)),yield this.publishAdditionalCodecForTrack(p.videoTrack,b,p.options))}}catch(b){u={error:b}}finally{try{!T&&!l&&(h=S.return)&&(yield h.call(S))}finally{if(u)throw u.error}}}),this.handleLocalTrackUnpublished=d=>{let l=this.trackPublications.get(d.trackSid);if(!l){this.log.warn("received unpublished event for unknown track",{trackSid:d.trackSid});return}this.unpublishTrack(l.track)},this.handleTrackEnded=d=>f(this,void 0,void 0,function*(){if(d.source===E.Source.ScreenShare||d.source===E.Source.ScreenShareAudio)this.log.debug("unpublishing local track due to TrackEnded",q(d)),this.unpublishTrack(d);else if(d.isUserProvided)yield d.mute();else if(st(d)||Qe(d))try{if(Ce())try{let l=yield navigator?.permissions.query({name:d.source===E.Source.Camera?"camera":"microphone"});if(l&&l.state==="denied")throw this.log.warn("user has revoked access to ".concat(d.source),q(d)),l.onchange=()=>{l.state!=="denied"&&(d.isMuted||d.restartTrack(),l.onchange=null)},new Error("GetUserMedia Permission denied")}catch{}d.isMuted||(this.log.debug("track ended, attempting to use a different device",q(d)),st(d)?yield d.restartTrack({deviceId:"default"}):yield d.restartTrack())}catch{this.log.warn("could not restart track, muting instead",q(d)),yield d.mute()}}),this.audioTrackPublications=new Map,this.videoTrackPublications=new Map,this.trackPublications=new Map,this.engine=n,this.roomOptions=s,this.setupEngine(n),this.activeDeviceMap=new Map([["audioinput","default"],["videoinput","default"],["audiooutput","default"]]),this.pendingSignalRequests=new Map,this.roomOutgoingDataStreamManager=r,this.roomOutgoingDataTrackManager=a,this.rpcClientManager=o,this.rpcServerManager=c}get lastCameraError(){return this.cameraError}get lastMicrophoneError(){return this.microphoneError}get isE2EEEnabled(){return this.encryptionType!==G.NONE}getTrackPublication(e){let t=super.getTrackPublication(e);if(t)return t}getTrackPublicationByName(e){let t=super.getTrackPublicationByName(e);if(t)return t}setupEngine(e){var t;this.engine=e,this.engine.on(R.RemoteMute,(n,s)=>{let r=this.trackPublications.get(n);!r||!r.track||(s?r.mute():r.unmute())}),!((t=this.signalConnectedFuture)===null||t===void 0)&&t.isResolved&&(this.signalConnectedFuture=void 0),this.engine.on(R.Connected,this.handleReconnected).on(R.SignalConnected,this.handleSignalConnected).on(R.SignalRestarted,this.handleReconnected).on(R.SignalResumed,this.handleReconnected).on(R.Restarting,this.handleReconnecting).on(R.Resuming,this.handleReconnecting).on(R.LocalTrackUnpublished,this.handleLocalTrackUnpublished).on(R.SubscribedQualityUpdate,this.handleSubscribedQualityUpdate).on(R.Closing,this.handleClosing).on(R.SignalRequestResponse,this.handleSignalRequestResponse)}setMetadata(e){return f(this,void 0,void 0,function*(){yield this.requestMetadataUpdate({metadata:e})})}setName(e){return f(this,void 0,void 0,function*(){yield this.requestMetadataUpdate({name:e})})}setAttributes(e){return f(this,void 0,void 0,function*(){yield this.requestMetadataUpdate({attributes:e})})}requestMetadataUpdate(e){return f(this,arguments,void 0,function(t){var n=this;let s=t.metadata,r=t.name,a=t.attributes;return function*(){return new ge((o,c)=>f(n,void 0,void 0,function*(){var d,l;try{let u=!1,h=yield this.engine.client.sendUpdateLocalMetadata((d=s??this.metadata)!==null&&d!==void 0?d:"",(l=r??this.name)!==null&&l!==void 0?l:"",a),m=performance.now();for(this.pendingSignalRequests.set(h,{resolve:o,reject:g=>{c(g),u=!0},values:{name:r,metadata:s,attributes:a}});performance.now()-m<5e3&&!u;){if((!r||this.name===r)&&(!s||this.metadata===s)&&(!a||Object.entries(a).every(g=>{let p=L(g,2),k=p[0],T=p[1];return this.attributes[k]===T||T===""&&!this.attributes[k]}))){this.pendingSignalRequests.delete(h),o();return}yield ke(50)}c(new Ci("Request to update local metadata timed out","TimeoutError"))}catch(u){u instanceof Error?c(u):c(new Error(String(u)))}}))}()})}setCameraEnabled(e,t,n){return this.setTrackEnabled(E.Source.Camera,e,t,n)}setMicrophoneEnabled(e,t,n){return this.setTrackEnabled(E.Source.Microphone,e,t,n)}setScreenShareEnabled(e,t,n){return this.setTrackEnabled(E.Source.ScreenShare,e,t,n)}setE2EEEnabled(e){return f(this,void 0,void 0,function*(){let t=yield this.e2eeStateMutex.lock();try{if(this.encryptionType=e?G.GCM:G.NONE,yield Promise.all(this.pendingPublishPromises.values()),this.trackPublications.size===0||Array.from(this.trackPublications.values()).every(n=>n.isEncrypted===e))return;yield this.republishAllTracks(void 0,!1)}finally{t()}})}setTrackEnabled(e,t,n,s){return f(this,void 0,void 0,function*(){var r,a;this.log.debug("setTrackEnabled",{source:e,enabled:t}),this.republishPromise&&(yield this.republishPromise);let o=this.getTrackPublication(e);if(t)if(o)yield o.unmute();else{let u;if(this.pendingPublishing.has(e)){let h=yield this.waitForPendingPublicationOfSource(e);return h||this.log.info("waiting for pending publication promise timed out",{source:e}),yield h?.unmute(),h}this.pendingPublishing.add(e);try{switch(e){case E.Source.Camera:u=yield this.createTracks({video:(r=n)!==null&&r!==void 0?r:!0});break;case E.Source.Microphone:u=yield this.createTracks({audio:(a=n)!==null&&a!==void 0?a:!0});break;case E.Source.ScreenShare:u=yield this.createScreenTracks(Object.assign({},n));break;default:throw new We(e)}}catch(h){throw u?.forEach(m=>{m.stop()}),h instanceof Error&&this.emit(A.MediaDevicesError,h,Ir(e)),this.pendingPublishing.delete(e),h}for(let h of u){let m=Object.assign(Object.assign({},this.roomOptions.publishDefaults),n);e===E.Source.Microphone&&et(h)&&m.preConnectBuffer&&(this.log.info("starting preconnect buffer for microphone"),h.startPreConnectBuffer())}try{let h=[];for(let g of u)this.log.info("publishing track",q(g)),h.push(this.publishTrack(g,s));let m=yield Promise.all(h);var c=L(m,1);o=c[0]}catch(h){throw u?.forEach(m=>{m.stop()}),h}finally{this.pendingPublishing.delete(e)}}else if(!o?.track&&this.pendingPublishing.has(e)&&(o=yield this.waitForPendingPublicationOfSource(e),o||this.log.info("waiting for pending publication promise timed out",{source:e})),o&&o.track)if(e===E.Source.ScreenShare){let u=[this.unpublishTrack(o.track)],h=this.getTrackPublication(E.Source.ScreenShareAudio);h&&h.track&&u.push(this.unpublishTrack(h.track));var d=yield Promise.all(u),l=L(d,1);o=l[0]}else yield o.mute();return o})}enableCameraAndMicrophone(){return f(this,void 0,void 0,function*(){if(!(this.pendingPublishing.has(E.Source.Camera)||this.pendingPublishing.has(E.Source.Microphone))){this.pendingPublishing.add(E.Source.Camera),this.pendingPublishing.add(E.Source.Microphone);try{let e=yield this.createTracks({audio:!0,video:!0});yield Promise.all(e.map(t=>this.publishTrack(t)))}finally{this.pendingPublishing.delete(E.Source.Camera),this.pendingPublishing.delete(E.Source.Microphone)}}})}createTracks(e){return f(this,void 0,void 0,function*(){var t,n;e??(e={});let s=gl(e,(t=this.roomOptions)===null||t===void 0?void 0:t.audioCaptureDefaults,(n=this.roomOptions)===null||n===void 0?void 0:n.videoCaptureDefaults);try{return(yield ps(s,{loggerName:this.roomOptions.loggerName,loggerContextCb:()=>this.logContext})).map(o=>(et(o)&&(this.microphoneError=void 0,o.setAudioContext(this.audioContext),o.source=E.Source.Microphone,this.emit(A.AudioStreamAcquired)),ut(o)&&(this.cameraError=void 0,o.source=E.Source.Camera),o))}catch(r){throw r instanceof Error&&(e.audio&&(this.microphoneError=r),e.video&&(this.cameraError=r)),r}})}createScreenTracks(e){return f(this,void 0,void 0,function*(){if(e===void 0&&(e={}),navigator.mediaDevices.getDisplayMedia===void 0)throw new Un("getDisplayMedia not supported");e.resolution===void 0&&!Fm()&&(e.resolution=no.h1080fps30.resolution);let t=Cm(e),n=yield navigator.mediaDevices.getDisplayMedia(t),s=n.getVideoTracks();if(s.length===0)throw new We("no video track found");let r=new ln(s[0],void 0,!1,{loggerName:this.roomOptions.loggerName,loggerContextCb:()=>this.logContext});r.source=E.Source.ScreenShare,e.contentHint&&(r.mediaStreamTrack.contentHint=e.contentHint);let a=[r];if(n.getAudioTracks().length>0){this.emit(A.AudioStreamAcquired);let o=new dn(n.getAudioTracks()[0],void 0,!1,this.audioContext,{loggerName:this.roomOptions.loggerName,loggerContextCb:()=>this.logContext});o.source=E.Source.ScreenShareAudio,a.push(o)}return a})}publishTrack(e,t){return f(this,void 0,void 0,function*(){return this.publishOrRepublishTrack(e,t)})}waitForNextEngineRestart(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:15e3;return new Promise((t,n)=>{let s=()=>{clearTimeout(o),this.engine.off(R.Restarted,r),this.engine.off(R.Closing,a)},r=()=>{s(),t()},a=()=>{s(),n(new Error("engine closed before restart completed"))},o=setTimeout(()=>{s(),n(new Error("timed out waiting for engine restart"))},e);this.engine.once(R.Restarted,r),this.engine.once(R.Closing,a)})}publishOrRepublishTrack(e,t){return f(this,arguments,void 0,function(n,s){var r=this;let a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!1,o=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1;return function*(){var c,d,l,u;st(n)&&n.setAudioContext(r.audioContext),yield(c=r.reconnectFuture)===null||c===void 0?void 0:c.promise,r.republishPromise&&!a&&(yield r.republishPromise),en(n)&&r.pendingPublishPromises.has(n)&&(yield r.pendingPublishPromises.get(n));let h;if(n instanceof MediaStreamTrack)h=n.getConstraints();else{h=n.constraints;let S;switch(n.source){case E.Source.Microphone:S="audioinput";break;case E.Source.Camera:S="videoinput"}S&&r.activeDeviceMap.has(S)&&(h=Object.assign(Object.assign({},h),{deviceId:r.activeDeviceMap.get(S)}))}if(n instanceof MediaStreamTrack)switch(n.kind){case"audio":n=new dn(n,h,!0,r.audioContext,{loggerName:r.roomOptions.loggerName,loggerContextCb:()=>r.logContext});break;case"video":n=new ln(n,h,!0,{loggerName:r.roomOptions.loggerName,loggerContextCb:()=>r.logContext});break;default:throw new We("unsupported MediaStreamTrack kind ".concat(n.kind))}else n.updateLoggerOptions({loggerName:r.roomOptions.loggerName,loggerContextCb:()=>r.logContext});let m;if(r.trackPublications.forEach(S=>{S.track&&S.track===n&&(m=S)}),m)return r.log.warn("track has already been published, skipping",q(m)),m;let g=Object.assign(Object.assign({},r.roomOptions.publishDefaults),s),p="channelCount"in n.mediaStreamTrack.getSettings()&&n.mediaStreamTrack.getSettings().channelCount===2||n.mediaStreamTrack.getConstraints().channelCount===2,k=(d=g.forceStereo)!==null&&d!==void 0?d:p;k&&(g.dtx===void 0&&r.log.debug("Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.",q(n)),g.red===void 0&&r.log.debug("Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work."),(l=g.dtx)!==null&&l!==void 0||(g.dtx=!1),(u=g.red)!==null&&u!==void 0||(g.red=!1)),!jm()&&r.roomOptions.e2ee&&(r.log.info("End-to-end encryption is set up, simulcast publishing will be disabled on Safari versions and iOS browsers running iOS < v17.2"),g.simulcast=!1),g.source&&(n.source=g.source);let T=new Promise((S,I)=>f(r,void 0,void 0,function*(){try{if(this.engine.client.currentState!==Te.CONNECTED){this.log.debug("deferring track publication until signal is connected",{track:q(n)});let b=!1,y=setTimeout(()=>{b=!0,n.stop(),I(new Si("publishing rejected as engine not connected within timeout",408))},15e3);if(yield this.waitUntilEngineConnected(),clearTimeout(y),b)return;let C=yield this.publish(n,g,k);S(C)}else try{let b=yield this.publish(n,g,k);S(b)}catch(b){I(b)}}catch(b){I(b)}}));r.pendingPublishPromises.set(n,T);try{return yield T}catch(S){if(!o&&S instanceof Ze)return r.log.warn("negotiation due to track publish failed, retrying after reconnect",{error:S}),r.pendingPublishPromises.delete(n),yield r.waitForNextEngineRestart(),yield r.publishOrRepublishTrack(n,s,a,!0);throw S}finally{r.pendingPublishPromises.delete(n)}}()})}waitUntilEngineConnected(){return this.signalConnectedFuture||(this.signalConnectedFuture=new ue),this.signalConnectedFuture.promise}hasPermissionsToPublish(e){if(!this.permissions)return this.log.warn("no permissions present for publishing track",q(e)),!1;let t=this.permissions,n=t.canPublish,s=t.canPublishSources;return n&&(s.length===0||s.map(r=>Rm(r)).includes(e.source))?!0:(this.log.warn("insufficient permissions to publish",q(e)),!1)}publish(e,t,n){return f(this,void 0,void 0,function*(){var s,r,a,o,c,d,l,u,h,m,g;if(!this.hasPermissionsToPublish(e))throw new Si("failed to publish track, insufficient permissions",403);Array.from(this.trackPublications.values()).find(D=>en(e)&&D.source===e.source)&&e.source!==E.Source.Unknown&&this.log.info("publishing a second track with the same source: ".concat(e.source),q(e)),t.stopMicTrackOnMute&&et(e)&&(e.stopOnMute=!0),e.source===E.Source.ScreenShare&&Ut()&&(t.simulcast=!1),t.videoCodec==="av1"&&!Am()&&(t.videoCodec=void 0),t.videoCodec==="vp9"&&!Om()&&(t.videoCodec=void 0),t.videoCodec===void 0&&(t.videoCodec=zr),this.enabledPublishVideoCodecs.length>0&&(this.enabledPublishVideoCodecs.some(D=>t.videoCodec===wn(D.mime))||(t.videoCodec=wn(this.enabledPublishVideoCodecs[0].mime)));let k=t.videoCodec;e.on(P.Muted,this.onTrackMuted),e.on(P.Unmuted,this.onTrackUnmuted),e.on(P.Ended,this.handleTrackEnded),e.on(P.UpstreamPaused,this.onTrackUpstreamPaused),e.on(P.UpstreamResumed,this.onTrackUpstreamResumed),e.on(P.AudioTrackFeatureUpdate,this.onTrackFeatureUpdate);let T=[],S=!(!((s=t.dtx)!==null&&s!==void 0)||s),I=e.getSourceTrackSettings();I.autoGainControl&&T.push(me.TF_AUTO_GAIN_CONTROL),I.echoCancellation&&T.push(me.TF_ECHO_CANCELLATION),I.noiseSuppression&&T.push(me.TF_NOISE_SUPPRESSION),I.channelCount&&I.channelCount>1&&T.push(me.TF_STEREO),S&&T.push(me.TF_NO_DTX),st(e)&&e.hasPreConnectBuffer&&T.push(me.TF_PRECONNECT_BUFFER);let b=this.normalizeRequestedFrameMetadataOptions(e,t),y=new xn({cid:e.mediaStreamTrack.id,name:t.name,type:E.kindToProto(e.kind),muted:e.isMuted,source:E.sourceToProto(e.source),disableDtx:S,encryption:this.encryptionType,stereo:n,disableRed:this.isE2EEEnabled||!(!((r=t.red)!==null&&r!==void 0)||r),stream:t?.stream,backupCodecPolicy:t?.backupCodecPolicy,audioFeatures:T,packetTrailerFeatures:b}),C;if(e.kind===E.Kind.Video){let D;try{D=yield e.waitForDimensions()}catch{let H=(o=(a=this.roomOptions.videoCaptureDefaults)===null||a===void 0?void 0:a.resolution)!==null&&o!==void 0?o:Fn.h720.resolution;D={width:H.width,height:H.height},this.log.error("could not determine track dimensions, using defaults",Object.assign(Object.assign({},q(e)),{dims:D}))}if(y.width=D.width,y.height=D.height,Qe(e)){Xt(k,t)&&(Tl()||!Nm((c=this.engine)===null||c===void 0?void 0:c.serverVersion))&&(t.simulcast=!1,this.log.info("SVC simulcast is not supported, disabling simulcast.",q(e)));let U=Xt(k,t);wt(k)&&!U&&(e.source===E.Source.ScreenShare&&(t.scalabilityMode="L1T3","contentHint"in e.mediaStreamTrack&&(e.mediaStreamTrack.contentHint="motion",this.log.debug("forcing contentHint to motion for screenshare with SVC codecs",q(e)))),t.scalabilityMode=(d=t.scalabilityMode)!==null&&d!==void 0?d:"L3T3_KEY");let H=new dr({codec:k,cid:e.mediaStreamTrack.id});U&&(H.videoLayerMode=qa.ONE_SPATIAL_LAYER_PER_STREAM),y.simulcastCodecs=[H],t.backupCodec===!0&&(t.backupCodec={codec:zr}),t.backupCodec&&k!==t.backupCodec.codec&&y.encryption===G.NONE&&(this.roomOptions.dynacast||(this.roomOptions.dynacast=!0),y.simulcastCodecs.push(new dr({codec:t.backupCodec.codec,cid:""})))}C=Ui(e.source===E.Source.ScreenShare,y.width,y.height,t),y.layers=yc(y.width,y.height,C,wt(t.videoCodec)&&!Xt(t.videoCodec,t))}else e.kind===E.Kind.Audio&&(C=[{maxBitrate:(l=t.audioPreset)===null||l===void 0?void 0:l.maxBitrate,priority:(h=(u=t.audioPreset)===null||u===void 0?void 0:u.priority)!==null&&h!==void 0?h:"high",networkPriority:(g=(m=t.audioPreset)===null||m===void 0?void 0:m.priority)!==null&&g!==void 0?g:"high"}]);if(!this.engine||this.engine.isClosed)throw new Y("cannot publish track when not connected");let M=()=>f(this,void 0,void 0,function*(){var D,U;if(!this.engine.pcManager)throw new Y("pcManager is not ready");if(e.sender=yield this.engine.createSender(e,t,C),Qe(e)&&(e.publishOptions=t),this.emit(A.LocalSenderCreated,e.sender,e),Qe(e)&&((D=t.degradationPreference)!==null&&D!==void 0||(t.degradationPreference=gp(e)),e.setDegradationPreference(t.degradationPreference)),C){if(Ut()&&e.kind===E.Kind.Audio){let H;for(let oe of this.engine.pcManager.publisher.getTransceivers())if(oe.sender===e.sender){H=oe;break}H&&this.engine.pcManager.publisher.setTrackCodecBitrate({transceiver:H,codec:"opus",maxbr:!((U=C[0])===null||U===void 0)&&U.maxBitrate?C[0].maxBitrate/1e3:0})}else if(e.codec&&wl(e.codec)){let H=mp(e.codec,t,C);H>0&&this.engine.pcManager.publisher.setTrackCodecBitrate({cid:y.cid,codec:e.codec,maxbr:H/1e3,isScreenShare:e.source===E.Source.ScreenShare})}}yield this.engine.negotiate()}),O,j=new Promise((D,U)=>f(this,void 0,void 0,function*(){var H;try{O=yield this.engine.addTrack(y),D(O)}catch(oe){if(e.sender&&(!((H=this.engine.pcManager)===null||H===void 0)&&H.publisher)){try{this.engine.pcManager.publisher.removeTrack(e.sender)}catch(_e){this.log.error(_e)}yield this.engine.negotiate().catch(_e=>{this.log.error("failed to negotiate after removing track due to failed add track request",Object.assign(Object.assign({},q(e)),{error:_e}))})}U(oe)}}));if(this.enabledPublishVideoCodecs.length>0&&b.length===0)O=(yield Promise.all([j,M()]))[0];else{O=yield j;let D;if(O.codecs.forEach(U=>{D===void 0&&(D=U.mimeType)}),D&&e.kind===E.Kind.Video){let U=wn(D);U!==k&&(this.log.debug("falling back to server selected codec",Object.assign(Object.assign({},q(e)),{codec:U})),t.videoCodec=U,C=Ui(e.source===E.Source.ScreenShare,y.width,y.height,t))}yield M()}let B=new zn(e.kind,O,e,{loggerName:this.roomOptions.loggerName,loggerContextCb:()=>this.logContext});if(B.on(P.CpuConstrained,D=>this.onTrackCpuConstrained(D,B)),B.options=t,e.sid=O.sid,Qe(e)&&(e.publishOptions=t,y.width&&y.height&&(e.lastEncodedDimensions={width:y.width,height:y.height})),this.log.debug("publishing ".concat(e.kind," with encodings"),{encodings:C,trackInfo:O}),Qe(e)?e.startMonitor(this.engine.client):st(e)&&e.startMonitor(),this.addTrackPublication(B),this.emit(A.LocalTrackPublished,B),st(e)&&O.audioFeatures.includes(me.TF_PRECONNECT_BUFFER)){let D=e.getPreConnectBuffer(),U=e.getPreConnectBufferMimeType();this.on(A.LocalTrackSubscribed,H=>{if(H.trackSid===O.sid){if(!e.hasPreConnectBuffer){this.log.warn("subscribe event came to late, buffer already closed");return}this.log.debug("finished recording preconnect buffer",q(e)),e.stopPreConnectBuffer()}}),D&&new Promise((oe,_e)=>f(this,void 0,void 0,function*(){var Je,ht,Yn,fe,_t,ys;try{this.log.debug("waiting for agent",q(e));let Cs=setTimeout(()=>{_e(new Error("agent not active within 10 seconds"))},1e4),Yl=yield this.waitUntilActiveAgentPresent();clearTimeout(Cs),this.log.debug("sending preconnect buffer",q(e));let go=yield this.streamBytes({name:"preconnect-buffer",mimeType:U,topic:"lk.agent.pre-connect-audio-buffer",destinationIdentities:[Yl.identity],attributes:{trackId:B.trackSid,sampleRate:String((_t=I.sampleRate)!==null&&_t!==void 0?_t:"48000"),channels:String((ys=I.channelCount)!==null&&ys!==void 0?ys:"1")}});try{for(var ks=!0,Ts=De(D),Ss;Ss=yield Ts.next(),Je=Ss.done,!Je;ks=!0){fe=Ss.value,ks=!1;let Es=fe;yield go.write(Es)}}catch(Es){ht={error:Es}}finally{try{!ks&&!Je&&(Yn=Ts.return)&&(yield Yn.call(Ts))}finally{if(ht)throw ht.error}}yield go.close(),oe()}catch(Cs){_e(Cs)}})).then(()=>{this.log.debug("preconnect buffer sent successfully",q(e))}).catch(oe=>{this.log.error("error sending preconnect buffer",Object.assign(Object.assign({},q(e)),{error:oe}))})}return B})}canPublishFrameMetadata(){var e;return!!(this.roomOptions.e2ee||this.roomOptions.encryption||Vn((e=this.roomOptions.frameMetadata)!==null&&e!==void 0?e:this.roomOptions.packetTrailer))}normalizeRequestedFrameMetadataOptions(e,t){var n;let s=(n=t.frameMetadata)!==null&&n!==void 0?n:t.packetTrailer;if(e.kind!==E.Kind.Video||!_i(s))return t.frameMetadata=void 0,t.packetTrailer=void 0,[];if(!this.canPublishFrameMetadata())return this.log.warn("frame metadata transform not supported; not advertising features",Object.assign(Object.assign({},this.logContext),q(e))),t.frameMetadata=void 0,t.packetTrailer=void 0,[];let r=uf(s),a=hf(r);return t.frameMetadata=a,t.packetTrailer=a,r}get isLocal(){return!0}publishAdditionalCodecForTrack(e,t,n){return f(this,void 0,void 0,function*(){var s;if(this.encryptionType!==G.NONE)return;let r;if(this.trackPublications.forEach(g=>{g.track&&g.track===e&&(r=g)}),!r)throw new We("track is not published");if(!Qe(e))throw new We("track is not a video track");let a=Object.assign(Object.assign({},(s=this.roomOptions)===null||s===void 0?void 0:s.publishDefaults),n),o=Dl(e,t,a);if(!o){this.log.info("backup codec has been disabled, ignoring request to add additional codec for track",q(e));return}let c=e.addSimulcastTrack(t,o);if(!c)return;let d=this.normalizeRequestedFrameMetadataOptions(e,a),l=new xn({cid:c.mediaStreamTrack.id,type:E.kindToProto(e.kind),muted:e.isMuted,source:E.sourceToProto(e.source),sid:e.sid,packetTrailerFeatures:d,simulcastCodecs:[{codec:a.videoCodec,cid:c.mediaStreamTrack.id}]});if(l.layers=yc(l.width,l.height,o),!this.engine||this.engine.isClosed)throw new Y("cannot publish track when not connected");let u=()=>f(this,void 0,void 0,function*(){yield this.engine.createSimulcastSender(e,c,a,o),yield this.engine.negotiate()}),m=(yield Promise.all([this.engine.addTrack(l),u()]))[0];this.log.debug("published ".concat(t," for track ").concat(e.sid),{encodings:o,trackInfo:m})})}unpublishTrack(e,t){return f(this,void 0,void 0,function*(){var n,s;if(en(e)){let l=this.pendingPublishPromises.get(e);l&&(this.log.debug("awaiting publish promise before attempting to unpublish",q(e)),yield l)}let r=this.getPublicationForTrack(e),a=r?q(r):void 0;if(this.log.info("unpublishing track",a),!r||!r.track){this.log.warn("track was not unpublished because no publication was found",a);return}e=r.track,e.off(P.Muted,this.onTrackMuted),e.off(P.Unmuted,this.onTrackUnmuted),e.off(P.Ended,this.handleTrackEnded),e.off(P.UpstreamPaused,this.onTrackUpstreamPaused),e.off(P.UpstreamResumed,this.onTrackUpstreamResumed),e.off(P.AudioTrackFeatureUpdate,this.onTrackFeatureUpdate),t===void 0&&(t=(s=(n=this.roomOptions)===null||n===void 0?void 0:n.stopLocalTrackOnUnpublish)!==null&&s!==void 0?s:!0),t?e.stop():e.stopMonitor();let o=!1,c=e.sender;if(e.sender=void 0,this.engine.pcManager&&this.engine.pcManager.currentState<$.FAILED&&c)try{for(let l of this.engine.pcManager.publisher.getTransceivers())l.sender===c&&(l.direction="inactive",o=!0);try{o=this.engine.removeTrack(c)}catch(l){this.log.warn(l),o=!0}if(Qe(e)){for(let l of e.simulcastCodecs){var d=L(l,2);let u=d[1];if(u.sender){try{o=this.engine.removeTrack(u.sender)}catch(h){this.log.warn(h),o=!0}u.sender=void 0}}e.simulcastCodecs.clear()}}catch(l){this.log.warn("failed to unpublish track",Object.assign(Object.assign({},a),{error:l}))}switch(this.trackPublications.delete(r.trackSid),r.kind){case E.Kind.Audio:this.audioTrackPublications.delete(r.trackSid);break;case E.Kind.Video:this.videoTrackPublications.delete(r.trackSid);break}return this.emit(A.LocalTrackUnpublished,r),r.setTrack(void 0),o&&(yield this.engine.negotiate()),r})}unpublishTracks(e){return f(this,void 0,void 0,function*(){return(yield Promise.all(e.map(n=>this.unpublishTrack(n)))).filter(n=>!!n)})}republishAllTracks(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return function*(){n.republishPromise&&(yield n.republishPromise),n.republishPromise=new ge((r,a)=>f(n,void 0,void 0,function*(){try{let o=[];this.trackPublications.forEach(c=>{c.track&&(t&&(c.options=Object.assign(Object.assign({},c.options),t)),o.push(c))}),yield Promise.all(o.map(c=>f(this,void 0,void 0,function*(){let d=c.track;yield this.unpublishTrack(d,!1),s&&!d.isMuted&&d.source!==E.Source.ScreenShare&&d.source!==E.Source.ScreenShareAudio&&(st(d)||Qe(d))&&!d.isUserProvided&&(this.log.debug("restarting existing track",{track:c.trackSid}),yield d.restartTrack()),yield this.publishOrRepublishTrack(d,c.options,!0)}))),r()}catch(o){o instanceof Error?a(o):a(new Error(String(o)))}finally{this.republishPromise=void 0}})),yield n.republishPromise}()})}publishData(e){return f(this,arguments,void 0,function(t){var n=this;let s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return function*(){let r=s.reliable?V.RELIABLE:V.LOSSY,a=s.reliable?yt.RELIABLE:yt.LOSSY,o=s.destinationIdentities,c=s.topic,d=new Va({participantIdentity:n.identity,payload:t,destinationIdentities:o,topic:c}),l=new Se({kind:a,value:{case:"user",value:d}});yield n.engine.sendDataPacket(l,r)}()})}publishDtmf(e,t){return f(this,void 0,void 0,function*(){let n=new Se({kind:yt.RELIABLE,value:{case:"sipDtmf",value:new ld({code:e,digit:t})}});yield this.engine.sendDataPacket(n,V.RELIABLE)})}sendChatMessage(e,t){return f(this,void 0,void 0,function*(){let n={id:crypto.randomUUID(),message:e,timestamp:Date.now(),attachedFiles:t?.attachments},s=new Se({value:{case:"chatMessage",value:new Ti(Object.assign(Object.assign({},n),{timestamp:ee.parse(n.timestamp)}))}});return yield this.engine.sendDataPacket(s,V.RELIABLE),this.emit(A.ChatMessage,n),n})}editChatMessage(e,t){return f(this,void 0,void 0,function*(){let n=Object.assign(Object.assign({},t),{message:e,editTimestamp:Date.now()}),s=new Se({value:{case:"chatMessage",value:new Ti(Object.assign(Object.assign({},n),{timestamp:ee.parse(n.timestamp),editTimestamp:ee.parse(n.editTimestamp)}))}});return yield this.engine.sendDataPacket(s,V.RELIABLE),this.emit(A.ChatMessage,n),n})}sendText(e,t){return f(this,void 0,void 0,function*(){return this.roomOutgoingDataStreamManager.sendText(e,t)})}streamText(e){return f(this,void 0,void 0,function*(){return this.roomOutgoingDataStreamManager.streamText(e)})}sendFile(e,t){return f(this,void 0,void 0,function*(){return this.roomOutgoingDataStreamManager.sendFile(e,t)})}sendBytes(e,t){return f(this,void 0,void 0,function*(){return this.roomOutgoingDataStreamManager.sendBytes(e,t)})}streamBytes(e){return f(this,void 0,void 0,function*(){return this.roomOutgoingDataStreamManager.streamBytes(e)})}performRpc(e){return this.rpcClientManager.performRpc(e).then(t=>{let n=L(t,2);return n[0],n[1]})}registerRpcMethod(e,t){this.rpcServerManager.registerRpcMethod(e,t)}unregisterRpcMethod(e){this.rpcServerManager.unregisterRpcMethod(e)}setTrackSubscriptionPermissions(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[];this.participantTrackPermissions=t,this.allParticipantsAllowedToSubscribe=e,this.engine.client.isDisconnected||this.updateTrackSubscriptionPermissions()}setEnabledPublishCodecs(e){this.enabledPublishVideoCodecs=e.filter(t=>t.mime.split("/")[0].toLowerCase()==="video")}updateInfo(e){return super.updateInfo(e)?(e.tracks.forEach(t=>{var n,s;let r=this.trackPublications.get(t.sid);if(r){let a=r.isMuted||((s=(n=r.track)===null||n===void 0?void 0:n.isUpstreamPaused)!==null&&s!==void 0?s:!1);a!==t.muted&&(this.log.debug("updating server mute state after reconcile",Object.assign(Object.assign({},q(r)),{mutedOnServer:a})),this.engine.client.sendMuteTrack(t.sid,a))}}),!0):!1}setActiveAgent(e){var t,n,s,r;this.firstActiveAgent=e,e&&!this.firstActiveAgent&&(this.firstActiveAgent=e),e?(n=(t=this.activeAgentFuture)===null||t===void 0?void 0:t.resolve)===null||n===void 0||n.call(t,e):(r=(s=this.activeAgentFuture)===null||s===void 0?void 0:s.reject)===null||r===void 0||r.call(s,new Error("Agent disconnected")),this.activeAgentFuture=void 0}waitUntilActiveAgentPresent(){return this.firstActiveAgent?Promise.resolve(this.firstActiveAgent):(this.activeAgentFuture||(this.activeAgentFuture=new ue),this.activeAgentFuture.promise)}getPublicationForTrack(e){let t;return this.trackPublications.forEach(n=>{let s=n.track;s&&(e instanceof MediaStreamTrack?(st(s)||Qe(s))&&s.mediaStreamTrack===e&&(t=n):e===s&&(t=n))}),t}waitForPendingPublicationOfSource(e){return f(this,void 0,void 0,function*(){let n=Date.now();for(;Date.now()L(r,1)[0].source===e);if(s)return s[1];yield ke(20)}})}publishDataTrack(e){return f(this,void 0,void 0,function*(){let t=new Zi(e,this.roomOutgoingDataTrackManager);return yield t.publish(),t})}},ns=class extends DOMException{constructor(e,t){super(e,"AbortError"),this.reason=t}},ma=class extends Map{constructor(){super(...arguments),this.pending=new Map}set(e,t){var n,s;super.set(e,t);let r=(n=this.pending)===null||n===void 0?void 0:n.get(e);if(r){for(let a of r)a.isResolved||(s=a.resolve)===null||s===void 0||s.call(a,t);this.pending.delete(e)}return this}get[Symbol.toStringTag](){return"DeferrableMap"}getDeferred(e,t){return f(this,void 0,void 0,function*(){let n=this.get(e);if(typeof n<"u")return n;if(t?.aborted)throw new ns("The operation was aborted.",t.reason);let s=new ue(void 0,()=>{let a=this.pending.get(e);if(!a)return;let o=a.indexOf(s);o!==-1&&a.splice(o,1),a.length===0&&this.pending.delete(e)}),r=this.pending.get(e);if(r?r.push(s):this.pending.set(e,[s]),t){let a=()=>{var o;s.isResolved||(o=s.reject)===null||o===void 0||o.call(s,new ns("The operation was aborted.",t.reason))};t.addEventListener("abort",a,{once:!0}),s.promise.finally(()=>{t.removeEventListener("abort",a)})}return s.promise})}},fa=class extends je{constructor(e,t,n,s){super(e,t.sid,t.name,s),this.track=void 0,this.allowed=!0,this.requestedDisabled=void 0,this.visible=!0,this.handleEnded=r=>{this.setTrack(void 0),this.emit(P.Ended,r)},this.handleVisibilityChange=r=>{this.log.debug("adaptivestream video visibility ".concat(this.trackSid,", visible=").concat(r),this.logContext),this.visible=r,this.emitTrackUpdate()},this.handleVideoDimensionsChange=r=>{this.log.debug("adaptivestream video dimensions ".concat(r.width,"x").concat(r.height),this.logContext),this.videoDimensionsAdaptiveStream=r,this.emitTrackUpdate()},this.subscribed=n,this.updateInfo(t)}setSubscribed(e){let t=this.subscriptionStatus,n=this.permissionStatus;this.subscribed=e,e&&(this.allowed=!0);let s=new us({trackSids:[this.trackSid],subscribe:this.subscribed,participantTracks:[new hd({participantSid:"",trackSids:[this.trackSid]})]});this.emit(P.UpdateSubscription,s),this.emitSubscriptionUpdateIfChanged(t),this.emitPermissionUpdateIfChanged(n)}get subscriptionStatus(){return this.subscribed===!1?je.SubscriptionStatus.Unsubscribed:super.isSubscribed?je.SubscriptionStatus.Subscribed:je.SubscriptionStatus.Desired}get permissionStatus(){return this.allowed?je.PermissionStatus.Allowed:je.PermissionStatus.NotAllowed}get isSubscribed(){return this.subscribed===!1?!1:super.isSubscribed}get isDesired(){return this.subscribed!==!1}get isEnabled(){return this.requestedDisabled!==void 0?!this.requestedDisabled:this.isAdaptiveStream?this.visible:!0}get isLocal(){return!1}setEnabled(e){!this.isManualOperationAllowed()||this.requestedDisabled===!e||(this.requestedDisabled=!e,this.emitTrackUpdate())}setVideoQuality(e){!this.isManualOperationAllowed()||this.requestedMaxQuality===e||(this.requestedMaxQuality=e,this.requestedVideoDimensions=void 0,this.emitTrackUpdate())}setVideoDimensions(e){var t,n;this.isManualOperationAllowed()&&(((t=this.requestedVideoDimensions)===null||t===void 0?void 0:t.width)===e.width&&((n=this.requestedVideoDimensions)===null||n===void 0?void 0:n.height)===e.height||(Us(this.track)&&(this.requestedVideoDimensions=e),this.requestedMaxQuality=void 0,this.emitTrackUpdate()))}setVideoFPS(e){this.isManualOperationAllowed()&&Us(this.track)&&this.fps!==e&&(this.fps=e,this.emitTrackUpdate())}get videoQuality(){var e;return(e=this.requestedMaxQuality)!==null&&e!==void 0?e:Ie.HIGH}setTrack(e){let t=this.subscriptionStatus,n=this.permissionStatus,s=this.track;s!==e&&(s&&(s.off(P.VideoDimensionsChanged,this.handleVideoDimensionsChange),s.off(P.VisibilityChanged,this.handleVisibilityChange),s.off(P.Ended,this.handleEnded),s.detach(),s.stopMonitor(),this.emit(P.Unsubscribed,s)),super.setTrack(e),e&&(e.sid=this.trackSid,e.on(P.VideoDimensionsChanged,this.handleVideoDimensionsChange),e.on(P.VisibilityChanged,this.handleVisibilityChange),e.on(P.Ended,this.handleEnded),this.emit(P.Subscribed,e)),this.emitPermissionUpdateIfChanged(n),this.emitSubscriptionUpdateIfChanged(t))}setAllowed(e){let t=this.subscriptionStatus,n=this.permissionStatus;this.allowed=e,this.emitPermissionUpdateIfChanged(n),this.emitSubscriptionUpdateIfChanged(t)}setSubscriptionError(e){this.emit(P.SubscriptionFailed,e)}updateInfo(e){super.updateInfo(e);let t=this.metadataMuted;this.metadataMuted=e.muted,this.track?this.track.setMuted(e.muted):t!==e.muted&&this.emit(e.muted?P.Muted:P.Unmuted)}emitSubscriptionUpdateIfChanged(e){let t=this.subscriptionStatus;e!==t&&this.emit(P.SubscriptionStatusChanged,t,e)}emitPermissionUpdateIfChanged(e){this.permissionStatus!==e&&this.emit(P.SubscriptionPermissionChanged,this.permissionStatus,e)}isManualOperationAllowed(){return this.isDesired?!0:(this.log.warn("cannot update track settings when not subscribed",this.logContext),!1)}get isAdaptiveStream(){return Us(this.track)&&this.track.isAdaptiveStream}emitTrackUpdate(){let e=new Cd({trackSids:[this.trackSid],disabled:!this.isEnabled,fps:this.fps});if(this.kind===E.Kind.Video){let t=this.requestedVideoDimensions;if(this.videoDimensionsAdaptiveStream!==void 0)if(t)Go(this.videoDimensionsAdaptiveStream,t)&&(this.log.debug("using adaptive stream dimensions instead of requested",Object.assign(Object.assign({},this.logContext),this.videoDimensionsAdaptiveStream)),t=this.videoDimensionsAdaptiveStream);else if(this.requestedMaxQuality!==void 0&&this.trackInfo){let n=Pm(this.trackInfo,this.requestedMaxQuality);n&&Go(this.videoDimensionsAdaptiveStream,n)&&(this.log.debug("using adaptive stream dimensions instead of max quality layer",Object.assign(Object.assign({},this.logContext),this.videoDimensionsAdaptiveStream)),t=this.videoDimensionsAdaptiveStream)}else this.log.debug("using adaptive stream dimensions",Object.assign(Object.assign({},this.logContext),this.videoDimensionsAdaptiveStream)),t=this.videoDimensionsAdaptiveStream;t?(e.width=Math.ceil(t.width),e.height=Math.ceil(t.height)):this.requestedMaxQuality!==void 0?(this.log.debug("using requested max quality",Object.assign(Object.assign({},this.logContext),{quality:this.requestedMaxQuality})),e.quality=this.requestedMaxQuality):(this.log.debug("using default quality",Object.assign(Object.assign({},this.logContext),{quality:Ie.HIGH})),e.quality=Ie.HIGH)}this.emit(P.UpdateSettings,e)}},is=class i extends ts{static fromParticipantInfo(e,t,n,s){return new i(e,t.sid,t.identity,t.name,t.metadata,t.attributes,n,t.kind,t.dataTracks.map(r=>{let a=Mi.from(r);return new zi(a,s,{publisherIdentity:t.identity})}),t.clientProtocol,t.capabilities)}get logContext(){return Object.assign(Object.assign({},super.logContext),{remoteParticipantID:this.sid,remoteParticipant:this.identity})}constructor(e,t,n,s,r,a,o){let c=arguments.length>7&&arguments[7]!==void 0?arguments[7]:Ln.STANDARD,d=arguments.length>8&&arguments[8]!==void 0?arguments[8]:[],l=arguments.length>9&&arguments[9]!==void 0?arguments[9]:ml,u=arguments.length>10&&arguments[10]!==void 0?arguments[10]:[];super(t,n||"",s,r,a,o,c),this.signalClient=e,this.trackPublications=new Map,this.audioTrackPublications=new Map,this.videoTrackPublications=new Map,this.dataTracks=new ma(d.map(h=>[h.info.name,h])),this.volumeMap=new Map,this.clientProtocol=l,this.capabilities=u}addTrackPublication(e){super.addTrackPublication(e),e.on(P.UpdateSettings,t=>{this.log.debug("send update settings",Object.assign(Object.assign(Object.assign({},this.logContext),q(e)),{settings:t})),this.signalClient.sendUpdateTrackSettings(t)}),e.on(P.UpdateSubscription,t=>{t.participantTracks.forEach(n=>{n.participantSid=this.sid}),this.signalClient.sendUpdateSubscription(t)}),e.on(P.SubscriptionPermissionChanged,t=>{this.emit(A.TrackSubscriptionPermissionChanged,e,t)}),e.on(P.SubscriptionStatusChanged,t=>{this.emit(A.TrackSubscriptionStatusChanged,e,t)}),e.on(P.Subscribed,t=>{this.emit(A.TrackSubscribed,t,e)}),e.on(P.Unsubscribed,t=>{this.emit(A.TrackUnsubscribed,t,e)}),e.on(P.SubscriptionFailed,t=>{this.emit(A.TrackSubscriptionFailed,e.trackSid,t)})}getTrackPublication(e){let t=super.getTrackPublication(e);if(t)return t}getTrackPublicationByName(e){let t=super.getTrackPublicationByName(e);if(t)return t}setVolume(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:E.Source.Microphone;this.volumeMap.set(t,e);let n=this.getTrackPublication(t);n&&n.track&&n.track.setVolume(e)}getVolume(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:E.Source.Microphone,t=this.getTrackPublication(e);return t&&t.track?t.track.getVolume():this.volumeMap.get(e)}addSubscribedMediaTrack(e,t,n,s,r,a){let o=this.getTrackPublicationBySid(t);if(o||t.startsWith("TR")||this.trackPublications.forEach(l=>{!o&&e.kind===l.kind.toString()&&(o=l)}),!o){if(a===0){this.log.error("could not find published track",Object.assign(Object.assign({},this.logContext),{trackSid:t})),this.emit(A.TrackSubscriptionFailed,t);return}a===void 0&&(a=20),setTimeout(()=>{this.addSubscribedMediaTrack(e,t,n,s,r,a-1)},150);return}if(e.readyState==="ended"){this.log.error("unable to subscribe because MediaStreamTrack is ended. Do not call MediaStreamTrack.stop()",Object.assign(Object.assign({},this.logContext),q(o))),this.emit(A.TrackSubscriptionFailed,t);return}let c=e.kind==="video",d;return c?d=new Wn(e,t,s,r):d=new ua(e,t,s,this.audioContext,this.audioOutput),d.source=o.source,d.isMuted=o.isMuted,d.setMediaStream(n),d.start(),o.setTrack(d),this.volumeMap.has(o.source)&&Lr(d)&&et(d)&&d.setVolume(this.volumeMap.get(o.source)),o}get hasMetadata(){return!!this.participantInfo}getTrackPublicationBySid(e){return this.trackPublications.get(e)}updateInfo(e){if(!super.updateInfo(e))return!1;let t=new Map,n=new Map;return e.tracks.forEach(s=>{var r,a;let o=this.getTrackPublicationBySid(s.sid);if(o)o.updateInfo(s);else{let c=E.kindFromProto(s.type);if(!c)return;o=new fa(c,s,(r=this.signalClient.connectOptions)===null||r===void 0?void 0:r.autoSubscribe,{loggerContextCb:()=>this.logContext,loggerName:(a=this.loggerOptions)===null||a===void 0?void 0:a.loggerName}),o.updateInfo(s),n.set(s.sid,o);let d=Array.from(this.trackPublications.values()).find(l=>l.source===o?.source);d&&o.source!==E.Source.Unknown&&this.log.debug("received a second track publication for ".concat(this.identity," with the same source: ").concat(o.source),Object.assign(Object.assign({},this.logContext),{oldTrack:q(d),newTrack:q(o)})),this.addTrackPublication(o)}t.set(s.sid,o)}),this.trackPublications.forEach(s=>{t.has(s.trackSid)||(this.log.trace("detected removed track on remote participant, unpublishing",Object.assign(Object.assign({},this.logContext),q(s))),this.unpublishTrack(s.trackSid,!0))}),n.forEach(s=>{this.emit(A.TrackPublished,s)}),!0}unpublishTrack(e,t){let n=this.trackPublications.get(e);if(!n)return;let s=n.track;switch(s&&(s.stop(),n.setTrack(void 0)),this.trackPublications.delete(e),n.kind){case E.Kind.Audio:this.audioTrackPublications.delete(e);break;case E.Kind.Video:this.videoTrackPublications.delete(e);break}t&&this.emit(A.TrackUnpublished,n)}setAudioOutput(e){return f(this,void 0,void 0,function*(){this.audioOutput=e;let t=[];this.audioTrackPublications.forEach(n=>{var s;et(n.track)&&Lr(n.track)&&t.push(n.track.setSinkId((s=e.deviceId)!==null&&s!==void 0?s:"default"))}),yield Promise.all(t)})}addRemoteDataTrack(e){this.dataTracks.set(e.info.name,e)}removeRemoteDataTrack(e){for(let n of this.dataTracks.entries()){var t=L(n,2);let s=t[0],r=t[1];e===r.info.sid&&this.dataTracks.delete(s)}}emit(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),s=1;sf(this,void 0,void 0,function*(){var m;if(!xm())throw Ke()?Error("WebRTC isn't detected, have you called registerGlobals?"):Error("LiveKit doesn't seem to be supported on this browser. Try to update your browser and make sure no browser extensions are disabling webRTC.");let g=yield this.disconnectLock.lock();if(this.state===K.Connected)return this.log.info("already connected to room ".concat(this.name)),g(),Promise.resolve();if(this.connectFuture)return g(),this.connectFuture.promise;this.setAndEmitConnectionState(K.Connecting),((m=this.regionUrlProvider)===null||m===void 0?void 0:m.getServerUrl().toString())!==_l(l)&&(this.regionUrl=void 0,this.regionUrlProvider=void 0),cn(new URL(l))&&(this.regionUrlProvider===void 0?this.regionUrlProvider=new ze(l,u):this.regionUrlProvider.updateToken(u),this.regionUrlProvider.fetchRegionSettings().then(T=>{var S;(S=this.regionUrlProvider)===null||S===void 0||S.setServerReportedRegions(T)}).catch(T=>{this.log.warn("could not fetch region settings",{error:T})}));let p=(T,S,I)=>f(this,void 0,void 0,function*(){var b,y;this.abortController&&this.abortController.abort();let C=new AbortController;this.abortController=C,g?.();try{if(yield tn.getInstance().getBackOffPromise(l),C.signal.aborted)throw F.cancelled("Connection attempt aborted");yield this.attemptConnection(I??l,u,h,C),this.abortController=void 0,T()}catch(M){if(this.regionUrlProvider&&M instanceof F&&M.reason!==Q.Cancelled&&M.reason!==Q.NotAllowed){let O=null;try{this.log.debug("Fetching next region"),O=yield this.regionUrlProvider.getNextBestRegionUrl((b=this.abortController)===null||b===void 0?void 0:b.signal)}catch(j){if(j instanceof F&&(j.status===401||j.reason===Q.Cancelled)){this.handleDisconnect(this.options.stopLocalTrackOnUnpublish),S(j);return}}[Q.InternalError,Q.ServerUnreachable,Q.Timeout].includes(M.reason)&&(this.log.debug("Adding failed connection attempt to back off"),tn.getInstance().addFailedConnectionAttempt(l)),O&&!(!((y=this.abortController)===null||y===void 0)&&y.signal.aborted)?(this.log.info("Initial connection failed with ConnectionError: ".concat(M.message,". Retrying with another region: ").concat(O)),this.recreateEngine(!0),yield p(T,S,O)):(this.handleDisconnect(this.options.stopLocalTrackOnUnpublish,$o(M)),S(M))}else{let O=Pe.UNKNOWN_REASON;M instanceof F&&(O=$o(M)),this.handleDisconnect(this.options.stopLocalTrackOnUnpublish,O),S(M)}}}),k=this.regionUrl;return this.regionUrl=void 0,this.connectFuture=new ue((T,S)=>{p(T,S,k)},()=>{this.clearConnectionFutures()}),this.connectFuture.promise}),this.connectSignal=(l,u,h,m,g,p)=>f(this,void 0,void 0,function*(){let k=yield h.join(l,u,{autoSubscribe:m.autoSubscribe,adaptiveStream:typeof g.adaptiveStream=="object"?!0:g.adaptiveStream,clientInfoCapabilities:this.getClientInfoCapabilities(g),maxRetries:m.maxRetries,e2eeEnabled:!!this.e2eeManager,websocketTimeout:m.websocketTimeout},p.signal,!g.singlePeerConnection),T=k.joinResponse,S=k.serverInfo;if(this.serverInfo=S,!S.version)throw new Sr("unknown server version");return S.version==="0.15.1"&&this.options.dynacast&&(this.log.debug("disabling dynacast due to server version"),g.dynacast=!1),T}),this.applyJoinResponse=l=>{let u=l.participant;if(this.localParticipant.sid=u.sid,this.localParticipant.identity=u.identity,this.localParticipant.setEnabledPublishCodecs(l.enabledPublishCodecs),this.e2eeManager)try{this.e2eeManager.setSifTrailer(l.sifTrailer)}catch(h){this.log.error(h instanceof Error?h.message:"Could not set SifTrailer",{error:h})}this.handleParticipantUpdates([u,...l.otherParticipants]),l.room&&this.handleRoomUpdate(l.room)},this.attemptConnection=(l,u,h,m)=>f(this,void 0,void 0,function*(){var g,p;this.state===K.Reconnecting||this.isResuming||!((g=this.engine)===null||g===void 0)&&g.pendingReconnect?(this.log.info("Reconnection attempt replaced by new connection attempt"),this.recreateEngine(!0)):this.maybeCreateEngine(),!((p=this.regionUrlProvider)===null||p===void 0)&&p.isCloud()&&this.engine.setRegionStrategy(this.createRegionStrategy()),this.acquireAudioContext(),this.connOptions=Object.assign(Object.assign({},ho),h),this.connOptions.rtcConfig&&(this.engine.rtcConfig=this.connOptions.rtcConfig),this.connOptions.peerConnectionTimeout&&(this.engine.peerConnectionTimeout=this.connOptions.peerConnectionTimeout);try{let k=yield this.connectSignal(l,u,this.engine,this.connOptions,this.options,m);this.applyJoinResponse(k),this.setupLocalParticipantEvents(),this.emit(_.SignalConnected)}catch(k){yield this.engine.close(),this.recreateEngine();let T=m.signal.aborted?F.cancelled("Signal connection aborted"):F.serverUnreachable("could not establish signal connection");throw k instanceof Error&&(T.message="".concat(T.message,": ").concat(k.message)),k instanceof F&&(T.reason=k.reason,T.status=k.status),this.log.debug("error trying to establish signal connection",{error:k}),T}if(m.signal.aborted)throw yield this.engine.close(),this.recreateEngine(),F.cancelled("Connection attempt aborted");try{yield this.engine.waitForPCInitialConnection(this.connOptions.peerConnectionTimeout,m)}catch(k){throw yield this.engine.close(),this.recreateEngine(),k}Ce()&&this.options.disconnectOnPageLeave&&(window.addEventListener("pagehide",this.onPageLeave),window.addEventListener("beforeunload",this.onPageLeave)),Ce()&&window.addEventListener("freeze",this.onPageLeave),this.setAndEmitConnectionState(K.Connected),this.emit(_.Connected),tn.getInstance().resetFailedConnectionAttempts(l),this.registerConnectionReconcile(),this.regionUrlProvider&&this.regionUrlProvider.notifyConnected()}),this.disconnect=function(){for(var l=arguments.length,u=new Array(l),h=0;h0&&arguments[0]!==void 0?arguments[0]:!0;return function*(){var p,k,T;let S=yield m.disconnectLock.lock();try{if(m.state===K.Disconnected){m.log.debug("already disconnected");return}if(m.log.info("disconnect from room"),m.state===K.Connecting||m.state===K.Reconnecting||m.isResuming){let I="Abort connection attempt due to user initiated disconnect";m.log.warn(I),(p=m.abortController)===null||p===void 0||p.abort(I),(T=(k=m.connectFuture)===null||k===void 0?void 0:k.reject)===null||T===void 0||T.call(k,F.cancelled("Client initiated disconnect")),m.connectFuture=void 0}m.engine&&(m.engine.client.isDisconnected||(yield m.engine.client.sendLeave()),yield m.engine.close()),m.handleDisconnect(g,Pe.CLIENT_INITIATED),m.engine=void 0}finally{S()}}()})},this.onPageLeave=()=>f(this,void 0,void 0,function*(){this.log.info("Page leave detected, disconnecting"),yield this.disconnect()}),this.startAudio=()=>f(this,void 0,void 0,function*(){let l=[],u=ye();if(u&&u.os==="iOS"){let h="livekit-dummy-audio-el",m=document.getElementById(h);if(!m){m=document.createElement("audio"),m.id=h,m.autoplay=!0,m.hidden=!0;let g=xs();g.enabled=!0;let p=new MediaStream([g]);m.srcObject=p,document.addEventListener("visibilitychange",()=>{m&&(m.srcObject=document.hidden?null:p,document.hidden||(this.log.debug("page visible again, triggering startAudio to resume playback and update playback status"),this.startAudio()))}),document.body.append(m),this.once(_.Disconnected,()=>{m?.remove(),m=null})}l.push(m)}this.remoteParticipants.forEach(h=>{h.audioTrackPublications.forEach(m=>{m.track&&m.track.attachedElements.forEach(g=>{l.push(g)})})});try{yield Promise.all([this.acquireAudioContext(),...l.map(h=>(this.options.webAudioMix||(h.muted=!1),h.play()))]),this.handleAudioPlaybackStarted()}catch(h){throw this.handleAudioPlaybackFailed(h),h}}),this.startVideo=()=>f(this,void 0,void 0,function*(){let l=[];for(let u of this.remoteParticipants.values())u.videoTrackPublications.forEach(h=>{var m;(m=h.track)===null||m===void 0||m.attachedElements.forEach(g=>{l.includes(g)||l.push(g)})});yield Promise.all(l.map(u=>u.play())).then(()=>{this.handleVideoPlaybackStarted()}).catch(u=>{u.name==="NotAllowedError"?this.handleVideoPlaybackFailed():this.log.warn("Resuming video playback failed, make sure you call `startVideo` directly in a user gesture handler")})}),this.handleRestarting=()=>{this.clearConnectionReconcile(),this.isResuming=!1;for(let l of this.remoteParticipants.values())this.handleParticipantDisconnected(l.identity,l);this.setAndEmitConnectionState(K.Reconnecting)&&this.emit(_.Reconnecting)},this.handleRestarted=()=>{this.outgoingDataTrackManager.sfuWillRepublishTracks(),this.incomingDataTrackManager.resendSubscriptionUpdates()},this.handleSignalRestarted=l=>f(this,void 0,void 0,function*(){this.log.debug("signal reconnected to server, region ".concat(l.serverRegion),{region:l.serverRegion}),this.bufferedEvents=[],this.applyJoinResponse(l);try{yield this.localParticipant.republishAllTracks(void 0,!0)}catch(u){this.log.error("error trying to re-publish tracks after reconnection",{error:u})}try{yield this.engine.waitForRestarted(),this.log.debug("fully reconnected to server",{region:l.serverRegion})}catch{return}this.setAndEmitConnectionState(K.Connected),this.emit(_.Reconnected),this.registerConnectionReconcile(),this.emitBufferedEvents()}),this.handleParticipantUpdates=l=>{var u;for(let m of l){if(m.identity===this.localParticipant.identity){this.localParticipant.updateInfo(m);continue}m.identity===""&&(m.identity=(u=this.sidToIdentity.get(m.sid))!==null&&u!==void 0?u:"");let g=this.remoteParticipants.get(m.identity);m.state===zt.DISCONNECTED?this.handleParticipantDisconnected(m.identity,g,m.disconnectReason===Pe.UNKNOWN_REASON?void 0:m.disconnectReason):this.getOrCreateParticipant(m.identity,m)}let h=new Map(l.filter(m=>m.identity!==this.localParticipant.identity).map(m=>[m.identity,m.dataTracks.map(g=>Mi.from(g))]));this.incomingDataTrackManager.receiveSfuPublicationUpdates(h)},this.handleActiveSpeakersUpdate=l=>{let u=[],h={};l.forEach(m=>{if(h[m.sid]=!0,m.sid===this.localParticipant.sid)this.localParticipant.audioLevel=m.level,this.localParticipant.setIsSpeaking(!0),u.push(this.localParticipant);else{let g=this.getRemoteParticipantBySid(m.sid);g&&(g.audioLevel=m.level,g.setIsSpeaking(!0),u.push(g))}}),h[this.localParticipant.sid]||(this.localParticipant.audioLevel=0,this.localParticipant.setIsSpeaking(!1)),this.remoteParticipants.forEach(m=>{h[m.sid]||(m.audioLevel=0,m.setIsSpeaking(!1))}),this.activeSpeakers=u,this.emitWhenConnected(_.ActiveSpeakersChanged,u)},this.handleSpeakersChanged=l=>{let u=new Map;this.activeSpeakers.forEach(m=>{let g=this.remoteParticipants.get(m.identity);g&&g.sid!==m.sid||u.set(m.sid,m)}),l.forEach(m=>{let g=this.getRemoteParticipantBySid(m.sid);m.sid===this.localParticipant.sid&&(g=this.localParticipant),g&&(g.audioLevel=m.level,g.setIsSpeaking(m.active),m.active?u.set(m.sid,g):u.delete(m.sid))});let h=Array.from(u.values());h.sort((m,g)=>g.audioLevel-m.audioLevel),this.activeSpeakers=h,this.emitWhenConnected(_.ActiveSpeakersChanged,h)},this.handleStreamStateUpdate=l=>{l.streamStates.forEach(u=>{let h=this.getRemoteParticipantBySid(u.participantSid);if(!h)return;let m=h.getTrackPublicationBySid(u.trackSid);if(!m||!m.track)return;let g=E.streamStateFromProto(u.state),p=m.track.streamState;m.track.setStreamState(g),g!==p&&(h.emit(A.TrackStreamStateChanged,m,m.track.streamState),this.emitWhenConnected(_.TrackStreamStateChanged,m,m.track.streamState,h))})},this.handleSubscriptionPermissionUpdate=l=>{let u=this.getRemoteParticipantBySid(l.participantSid);if(!u)return;let h=u.getTrackPublicationBySid(l.trackSid);h&&h.setAllowed(l.allowed)},this.handleSubscriptionError=l=>{this.cancelPendingTrackAdded(l.trackSid);let u=Array.from(this.remoteParticipants.values()).find(m=>m.trackPublications.has(l.trackSid));if(!u)return;let h=u.getTrackPublicationBySid(l.trackSid);h&&h.setSubscriptionError(l.err)},this.handleDataPacket=(l,u)=>{let h=this.remoteParticipants.get(l.participantIdentity);if(l.value.case==="user")this.handleUserPacket(h,l.value.value,l.kind,u);else if(l.value.case==="transcription")this.handleTranscription(h,l.value.value);else if(l.value.case==="sipDtmf")this.handleSipDtmf(h,l.value.value);else if(l.value.case==="chatMessage")this.handleChatMessage(h,l.value.value);else if(l.value.case==="metrics")this.handleMetrics(l.value.value,h);else if(l.value.case==="streamHeader"||l.value.case==="streamChunk"||l.value.case==="streamTrailer")this.handleDataStream(l,u);else if(l.value.case==="rpcRequest"){let m=l.value.value;this.rpcServerManager.handleIncomingRpcRequest(l.participantIdentity,m)}else if(l.value.case==="rpcResponse"){let m=l.value.value;switch(m.value.case){case"payload":this.rpcClientManager.handleIncomingRpcResponseSuccess(m.requestId,m.value.value);break;case"error":this.rpcClientManager.handleIncomingRpcResponseFailure(m.requestId,X.fromProto(m.value.value));break;default:this.log.warn("Unknown rpcResponse.value.case: ".concat(m.value.case),this.logContext);break}}else l.value.case==="rpcAck"&&this.rpcClientManager.handleIncomingRpcAck(l.value.value.requestId)},this.handleUserPacket=(l,u,h,m)=>{this.emit(_.DataReceived,u.payload,l,h,u.topic,m),l?.emit(A.DataReceived,u.payload,h,m)},this.handleSipDtmf=(l,u)=>{this.emit(_.SipDTMFReceived,u,l),l?.emit(A.SipDTMFReceived,u)},this.handleTranscription=(l,u)=>{let h=u.transcribedParticipantIdentity===this.localParticipant.identity?this.localParticipant:this.getParticipantByIdentity(u.transcribedParticipantIdentity),m=h?.trackPublications.get(u.trackId),g=Km(u,this.transcriptionReceivedTimes);m?.emit(P.TranscriptionReceived,g),h?.emit(A.TranscriptionReceived,g,m),this.emit(_.TranscriptionReceived,g,h,m)},this.handleChatMessage=(l,u)=>{let h=Gm(u);this.emit(_.ChatMessage,h,l)},this.handleMetrics=(l,u)=>{this.emit(_.MetricsReceived,l,u)},this.handleDataStream=(l,u)=>{this.incomingDataStreamManager.handleDataStreamPacket(l,u)},this.bufferedSegments=new Map,this.handleAudioPlaybackStarted=()=>{this.canPlaybackAudio||(this.audioEnabled=!0,this.emit(_.AudioPlaybackStatusChanged,!0))},this.handleAudioPlaybackFailed=l=>{this.log.warn("could not playback audio",{error:l}),this.canPlaybackAudio&&(this.audioEnabled=!1,this.emit(_.AudioPlaybackStatusChanged,!1))},this.handleVideoPlaybackStarted=()=>{this.isVideoPlaybackBlocked&&(this.isVideoPlaybackBlocked=!1,this.emit(_.VideoPlaybackStatusChanged,!0))},this.handleVideoPlaybackFailed=()=>{this.isVideoPlaybackBlocked||(this.isVideoPlaybackBlocked=!0,this.emit(_.VideoPlaybackStatusChanged,!1))},this.handleDeviceChange=()=>f(this,void 0,void 0,function*(){var l;((l=ye())===null||l===void 0?void 0:l.os)!=="iOS"&&(yield this.selectDefaultDevices()),this.emit(_.MediaDevicesChanged)}),this.handleRoomUpdate=l=>{let u=this.roomInfo;this.roomInfo=l,u&&u.metadata!==l.metadata&&this.emitWhenConnected(_.RoomMetadataChanged,l.metadata),u?.activeRecording!==l.activeRecording&&this.emitWhenConnected(_.RecordingStatusChanged,l.activeRecording)},this.handleConnectionQualityUpdate=l=>{l.updates.forEach(u=>{if(u.participantSid===this.localParticipant.sid){this.localParticipant.setConnectionQuality(u.quality);return}let h=this.getRemoteParticipantBySid(u.participantSid);h&&h.setConnectionQuality(u.quality)})},this.getRemoteParticipantClientProtocol=l=>{var u,h;return(h=(u=this.remoteParticipants.get(l))===null||u===void 0?void 0:u.clientProtocol)!==null&&h!==void 0?h:ml},this.getRemoteParticipantCapabilities=l=>{var u,h;return(h=(u=this.remoteParticipants.get(l))===null||u===void 0?void 0:u.capabilities)!==null&&h!==void 0?h:[]},this.getAllRemoteParticipantIdentities=()=>Array.from(this.remoteParticipants.keys()),this.logWebRTCStats=()=>f(this,void 0,void 0,function*(){var l,u,h,m;let g=(l=this.engine)===null||l===void 0?void 0:l.pcManager;if(g)try{let p=yield Promise.all([g.publisher.getStats(),(u=g.subscriber)===null||u===void 0?void 0:u.getStats()]),k=L(p,2),T=k[0],S=k[1],I=T&&wr(T),b=S&&wr(S);this.statsLog.info("webrtc stats",{publisher:I?.connection,subscriber:b?.connection,inbound:[...(h=I?.inbound)!==null&&h!==void 0?h:[],...(m=b?.inbound)!==null&&m!==void 0?m:[]],outbound:I?.outbound})}catch(p){this.statsLog.debug("could not collect webrtc stats",{error:p})}}),this.onLocalParticipantMetadataChanged=l=>{this.emit(_.ParticipantMetadataChanged,l,this.localParticipant)},this.onLocalParticipantNameChanged=l=>{this.emit(_.ParticipantNameChanged,l,this.localParticipant)},this.onLocalAttributesChanged=l=>{this.emit(_.ParticipantAttributesChanged,l,this.localParticipant)},this.onLocalTrackMuted=l=>{this.emit(_.TrackMuted,l,this.localParticipant)},this.onLocalTrackUnmuted=l=>{this.emit(_.TrackUnmuted,l,this.localParticipant)},this.onTrackProcessorUpdate=l=>{var u;(u=l?.onPublish)===null||u===void 0||u.call(l,this)},this.onLocalTrackPublished=l=>f(this,void 0,void 0,function*(){var u,h,m,g,p,k;(u=l.track)===null||u===void 0||u.on(P.TrackProcessorUpdate,this.onTrackProcessorUpdate),(h=l.track)===null||h===void 0||h.on(P.Restarted,this.onLocalTrackRestarted),(p=(g=(m=l.track)===null||m===void 0?void 0:m.getProcessor())===null||g===void 0?void 0:g.onPublish)===null||p===void 0||p.call(g,this),this.emit(_.LocalTrackPublished,l,this.localParticipant),st(l.track)&&(yield l.track.checkForSilence())&&this.emit(_.LocalAudioSilenceDetected,l);let T=yield(k=l.track)===null||k===void 0?void 0:k.getDeviceId(!1),S=Ir(l.source);S&&T&&T!==this.localParticipant.activeDeviceMap.get(S)&&(this.localParticipant.activeDeviceMap.set(S,T),this.emit(_.ActiveDeviceChanged,S,T))}),this.onLocalTrackUnpublished=l=>{var u,h;(u=l.track)===null||u===void 0||u.off(P.TrackProcessorUpdate,this.onTrackProcessorUpdate),(h=l.track)===null||h===void 0||h.off(P.Restarted,this.onLocalTrackRestarted),this.emit(_.LocalTrackUnpublished,l,this.localParticipant)},this.onLocalTrackRestarted=l=>f(this,void 0,void 0,function*(){let u=yield l.getDeviceId(!1),h=Ir(l.source);h&&u&&u!==this.localParticipant.activeDeviceMap.get(h)&&(this.log.debug("local track restarted, setting ".concat(h," ").concat(u," active")),this.localParticipant.activeDeviceMap.set(h,u),this.emit(_.ActiveDeviceChanged,h,u))}),this.onLocalConnectionQualityChanged=l=>{this.emit(_.ConnectionQualityChanged,l,this.localParticipant)},this.onMediaDevicesError=(l,u)=>{this.emit(_.MediaDevicesError,l,u)},this.onLocalParticipantPermissionsChanged=l=>{this.emit(_.ParticipantPermissionsChanged,l,this.localParticipant)},this.onLocalChatMessageSent=l=>{this.emit(_.ChatMessage,l,this.localParticipant)},this.setMaxListeners(100),this.remoteParticipants=new Map,this.sidToIdentity=new Map,this.options=Object.assign(Object.assign({},Qf),e),this.log=he((n=this.options.loggerName)!==null&&n!==void 0?n:te.Room,()=>this.logContext),this.statsLog=he(te.Stats,()=>this.logContext),this.transcriptionReceivedTimes=new Map,this.options.audioCaptureDefaults=Object.assign(Object.assign({},Al),e?.audioCaptureDefaults),this.options.videoCaptureDefaults=Object.assign(Object.assign({},Ol),e?.videoCaptureDefaults),this.options.publishDefaults=Object.assign(Object.assign({},Jf),e?.publishDefaults),this.maybeCreateEngine(),this.incomingDataStreamManager=new ta((s=this.options.dataStream)===null||s===void 0?void 0:s.maxPayloadByteLength),this.outgoingDataStreamManager=new sa(this.engine,this.log,this.getRemoteParticipantClientProtocol,this.getRemoteParticipantCapabilities,this.getAllRemoteParticipantIdentities),this.incomingDataTrackManager=new aa({e2eeManager:this.e2eeManager}),this.incomingDataTrackManager.on("sfuUpdateSubscription",l=>{this.engine.client.sendUpdateDataSubscription(l.sid,l.subscribe)}).on("trackPublished",l=>{var u;l.track.publisherIdentity!==this.localParticipant.identity&&(this.emit(_.DataTrackPublished,l.track),(u=this.remoteParticipants.get(l.track.publisherIdentity))===null||u===void 0||u.addRemoteDataTrack(l.track))}).on("trackUnpublished",l=>{var u;l.publisherIdentity!==this.localParticipant.identity&&(this.emit(_.DataTrackUnpublished,l.sid),(u=this.remoteParticipants.get(l.publisherIdentity))===null||u===void 0||u.removeRemoteDataTrack(l.sid))}),this.outgoingDataTrackManager=new ca({e2eeManager:this.e2eeManager}),this.outgoingDataTrackManager.on("sfuPublishRequest",l=>{this.engine.client.sendPublishDataTrackRequest(l.handle,l.name,l.usesE2ee)}).on("sfuUnpublishRequest",l=>{this.engine.client.sendUnPublishDataTrackRequest(l.handle)}).on("trackPublished",l=>{this.emit(_.LocalDataTrackPublished,l.track)}).on("trackUnpublished",l=>{this.emit(_.LocalDataTrackUnpublished,l.sid)}).on("packetAvailable",l=>{let u=l.handle,h=l.bytes;this.engine.sendDataTrackFrame(h).finally(()=>this.outgoingDataTrackManager.handlePacketSendComplete(u))}),this.registerRpcDataStreamHandler(),this.rpcClientManager=new da(this.log,this.outgoingDataStreamManager,this.getRemoteParticipantClientProtocol,()=>{var l;return(l=this.engine)===null||l===void 0?void 0:l.serverVersion}),this.rpcClientManager.on("sendDataPacket",l=>{let u=l.packet;var h;(h=this.engine)===null||h===void 0||h.sendDataPacket(u,V.RELIABLE)}),this.rpcServerManager=new la(this.log,this.outgoingDataStreamManager,this.getRemoteParticipantClientProtocol),this.rpcServerManager.on("sendDataPacket",l=>{let u=l.packet;var h;(h=this.engine)===null||h===void 0||h.sendDataPacket(u,V.RELIABLE)}),this.disconnectLock=new le,this.localParticipant=new ha("","",this.engine,this.options,this.outgoingDataStreamManager,this.outgoingDataTrackManager,this.rpcClientManager,this.rpcServerManager),this.setupFrameMetadata(),(this.options.e2ee||this.options.encryption)&&this.setupE2EE(),this.engine.e2eeManager=this.e2eeManager,this.incomingDataTrackManager.updateE2eeManager((r=this.e2eeManager)!==null&&r!==void 0?r:null),this.outgoingDataTrackManager.updateE2eeManager((a=this.e2eeManager)!==null&&a!==void 0?a:null),this.options.videoCaptureDefaults.deviceId&&this.localParticipant.activeDeviceMap.set("videoinput",Zt(this.options.videoCaptureDefaults.deviceId)),this.options.audioCaptureDefaults.deviceId&&this.localParticipant.activeDeviceMap.set("audioinput",Zt(this.options.audioCaptureDefaults.deviceId)),!((o=this.options.audioOutput)===null||o===void 0)&&o.deviceId&&this.switchActiveDevice("audiooutput",Zt(this.options.audioOutput.deviceId)).catch(l=>this.log.warn("Could not set audio output: ".concat(l.message))),Ce()){let l=new AbortController,u;if(i.cleanupRegistry){let h=new WeakRef(this);u=()=>{let m=h.deref();m&&m.handleDeviceChange()},i.cleanupRegistry.register(this,()=>{l.abort()})}else u=this.handleDeviceChange;(d=(c=navigator.mediaDevices)===null||c===void 0?void 0:c.addEventListener)===null||d===void 0||d.call(c,"devicechange",u,{signal:l.signal})}}registerTextStreamHandler(e,t){return this.incomingDataStreamManager.registerTextStreamHandler(e,t)}unregisterTextStreamHandler(e){return this.incomingDataStreamManager.unregisterTextStreamHandler(e)}registerByteStreamHandler(e,t){return this.incomingDataStreamManager.registerByteStreamHandler(e,t)}unregisterByteStreamHandler(e){return this.incomingDataStreamManager.unregisterByteStreamHandler(e)}registerRpcMethod(e,t){this.rpcServerManager.registerRpcMethod(e,t)}unregisterRpcMethod(e){this.rpcServerManager.unregisterRpcMethod(e)}setE2EEEnabled(e){return f(this,void 0,void 0,function*(){let t=yield this.e2eeStateMutex.lock();try{if(this.e2eeManager)this.isE2EEEnabled!==e&&(yield this.localParticipant.setE2EEEnabled(e),this.localParticipant.identity!==""&&this.e2eeManager.setParticipantCryptorEnabled(e,this.localParticipant.identity));else throw Error("e2ee not configured, please set e2ee settings within the room options")}finally{t()}})}setupE2EE(){var e,t;let n=!!this.options.encryption,s=this.options.encryption||this.options.e2ee;s&&("e2eeManager"in s?(this.e2eeManager=s.e2eeManager,this.e2eeManager.isDataChannelEncryptionEnabled=n):this.e2eeManager=new Pi(s,n),this.e2eeManager.on(gt.ParticipantEncryptionStatusChanged,(r,a)=>{Jm(a)&&(this.isE2EEEnabled=r),this.emit(_.ParticipantEncryptionStatusChanged,r,a)}),this.e2eeManager.on(gt.EncryptionError,(r,a)=>{let o=a?this.getParticipantByIdentity(a):void 0;this.emit(_.EncryptionError,r,o)}),(e=this.e2eeManager)===null||e===void 0||e.setup(this),(t=this.e2eeManager)===null||t===void 0||t.setupEngine(this.engine))}setupFrameMetadata(){var e;let t=(e=this.options.frameMetadata)!==null&&e!==void 0?e:this.options.packetTrailer;this.frameMetadataManager=new Br(t),this.frameMetadataManager.setup(this)}get logContext(){var e,t,n;return{room:this.name,roomID:(e=this.roomInfo)===null||e===void 0?void 0:e.sid,participant:(t=this.localParticipant)===null||t===void 0?void 0:t.identity,participantID:(n=this.localParticipant)===null||n===void 0?void 0:n.sid}}get isRecording(){var e,t;return(t=(e=this.roomInfo)===null||e===void 0?void 0:e.activeRecording)!==null&&t!==void 0?t:!1}getSid(){return this.state===K.Disconnected?ge.resolve(""):this.roomInfo&&this.roomInfo.sid!==""?ge.resolve(this.roomInfo.sid):new ge((e,t)=>{let n=s=>{s.sid!==""&&(this.engine.off(R.RoomUpdate,n),e(s.sid))};this.engine.on(R.RoomUpdate,n),this.once(_.Disconnected,()=>{this.engine.off(R.RoomUpdate,n),t(new Y("Room disconnected before room server id was available"))})})}get name(){var e,t;return(t=(e=this.roomInfo)===null||e===void 0?void 0:e.name)!==null&&t!==void 0?t:""}get metadata(){var e;return(e=this.roomInfo)===null||e===void 0?void 0:e.metadata}get numParticipants(){var e,t;return(t=(e=this.roomInfo)===null||e===void 0?void 0:e.numParticipants)!==null&&t!==void 0?t:0}get numPublishers(){var e,t;return(t=(e=this.roomInfo)===null||e===void 0?void 0:e.numPublishers)!==null&&t!==void 0?t:0}maybeCreateEngine(){this.engine&&(this.engine.isNewlyCreated||!this.engine.isClosed)||(this.engine=new Zr(this.options),this.engine.e2eeManager=this.e2eeManager,this.engine.on(R.ParticipantUpdate,this.handleParticipantUpdates).on(R.RoomUpdate,this.handleRoomUpdate).on(R.SpeakersChanged,this.handleSpeakersChanged).on(R.StreamStateChanged,this.handleStreamStateUpdate).on(R.ConnectionQualityUpdate,this.handleConnectionQualityUpdate).on(R.SubscriptionError,this.handleSubscriptionError).on(R.SubscriptionPermissionUpdate,this.handleSubscriptionPermissionUpdate).on(R.MediaTrackAdded,(e,t,n)=>{this.onTrackAdded(e,t,n)}).on(R.Disconnected,e=>{this.handleDisconnect(this.options.stopLocalTrackOnUnpublish,e)}).on(R.ActiveSpeakersUpdate,this.handleActiveSpeakersUpdate).on(R.DataPacketReceived,this.handleDataPacket).on(R.Resuming,()=>{this.clearConnectionReconcile(),this.isResuming=!0,this.log.debug("Resuming signal connection"),this.setAndEmitConnectionState(K.SignalReconnecting)&&this.emit(_.SignalReconnecting)}).on(R.Resumed,()=>{this.registerConnectionReconcile(),this.isResuming=!1,this.log.debug("Resumed signal connection"),this.updateSubscriptions(),this.setAndEmitConnectionState(K.Connected)&&this.emit(_.Reconnected),this.emitBufferedEvents()}).on(R.SignalResumed,()=>{(this.state===K.Reconnecting||this.isResuming)&&this.sendSyncState(),this.emitBufferedEvents()}).on(R.Restarting,this.handleRestarting).on(R.Restarted,this.handleRestarted).on(R.SignalRestarted,this.handleSignalRestarted).on(R.Offline,()=>{this.setAndEmitConnectionState(K.Reconnecting)&&this.emit(_.Reconnecting)}).on(R.DCBufferStatusChanged,(e,t)=>{this.emit(_.DCBufferStatusChanged,e,t)}).on(R.LocalTrackSubscribed,e=>{this.handleLocalTrackSubscribed(e)}).on(R.RoomMoved,e=>{this.log.debug("room moved",e),e.room&&this.handleRoomUpdate(e.room),this.remoteParticipants.forEach((t,n)=>{this.handleParticipantDisconnected(n,t)}),this.emit(_.Moved,e.room.name),e.participant?this.handleParticipantUpdates([e.participant,...e.otherParticipants]):this.handleParticipantUpdates(e.otherParticipants)}).on(R.PublishDataTrackResponse,e=>{if(!e.info){this.log.warn("received PublishDataTrackResponse, but event.info was ".concat(e.info,", so skipping."));return}this.outgoingDataTrackManager.receivedSfuPublishResponse(e.info.pubHandle,{type:"ok",data:{sid:e.info.sid,pubHandle:e.info.pubHandle,name:e.info.name,usesE2ee:e.info.encryption!==G.NONE}})}).on(R.UnPublishDataTrackResponse,e=>{if(!e.info){this.log.warn("received UnPublishDataTrackResponse, but event.info was ".concat(e.info,", so skipping."));return}this.outgoingDataTrackManager.receivedSfuUnpublishResponse(e.info.pubHandle)}).on(R.DataTrackSubscriberHandles,e=>{let t=new Map(Object.entries(e.subHandles).map(n=>{let s=L(n,2),r=s[0],a=s[1];return[parseInt(r,10),a.trackSid]}));this.incomingDataTrackManager.receivedSfuSubscriberHandles(t)}).on(R.DataTrackPacketReceived,e=>{try{this.incomingDataTrackManager.packetReceived(e)}catch(t){throw t}}).on(R.Joined,e=>{let t=new Map(e.otherParticipants.map(n=>[n.identity,n.dataTracks.map(s=>Mi.from(s))]));this.incomingDataTrackManager.receiveSfuPublicationUpdates(t)}).on(R.TokenRefreshed,e=>{var t;(t=this.regionUrlProvider)===null||t===void 0||t.updateToken(e)}).on(R.ServerRegionsReported,e=>{var t;(t=this.regionUrlProvider)===null||t===void 0||t.setServerReportedRegions({regionSettings:e,updatedAtInMs:Date.now(),maxAgeInMs:ea})}),this.localParticipant&&this.localParticipant.setupEngine(this.engine),this.e2eeManager&&this.e2eeManager.setupEngine(this.engine),this.outgoingDataStreamManager&&this.outgoingDataStreamManager.setupEngine(this.engine))}createRegionStrategy(){return{getNextUrl:e=>f(this,void 0,void 0,function*(){return this.regionUrlProvider?this.regionUrlProvider.getNextBestRegionUrl(e):null}),resetAttempts:()=>{var e;return(e=this.regionUrlProvider)===null||e===void 0?void 0:e.resetAttempts()}}}static getLocalDevices(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return Me.getInstance().getDevices(e,t)}prepareConnection(e,t){return f(this,void 0,void 0,function*(){if(this.state===K.Disconnected){this.log.debug("prepareConnection to ".concat(e));try{if(cn(new URL(e))&&t){this.regionUrlProvider=new ze(e,t);let n=yield this.regionUrlProvider.getNextBestRegionUrl();n&&this.state===K.Disconnected&&(this.regionUrl=n,yield fetch(jn(n),{method:"HEAD"}),this.log.debug("prepared connection to ".concat(n)))}else yield fetch(jn(e),{method:"HEAD"})}catch(n){this.log.warn("could not prepare connection",{error:n})}}})}getParticipantByIdentity(e){return this.localParticipant.identity===e?this.localParticipant:this.remoteParticipants.get(e)}clearConnectionFutures(){this.connectFuture=void 0}simulateScenario(e,t){return f(this,void 0,void 0,function*(){let n=()=>f(this,void 0,void 0,function*(){}),s;switch(e){case"signal-reconnect":yield this.engine.client.handleOnClose("simulate disconnect");break;case"fail-on-v1-path":this.engine.failNextV1Path();break;case"speaker":s=new Ye({scenario:{case:"speakerUpdate",value:3}});break;case"node-failure":s=new Ye({scenario:{case:"nodeFailure",value:!0}});break;case"server-leave":s=new Ye({scenario:{case:"serverLeave",value:!0}});break;case"migration":s=new Ye({scenario:{case:"migration",value:!0}});break;case"resume-reconnect":this.engine.failNext(),yield this.engine.client.handleOnClose("simulate resume-disconnect");break;case"disconnect-signal-on-resume":n=()=>f(this,void 0,void 0,function*(){yield this.engine.client.handleOnClose("simulate resume-disconnect")}),s=new Ye({scenario:{case:"disconnectSignalOnResume",value:!0}});break;case"disconnect-signal-on-resume-no-messages":n=()=>f(this,void 0,void 0,function*(){yield this.engine.client.handleOnClose("simulate resume-disconnect")}),s=new Ye({scenario:{case:"disconnectSignalOnResumeNoMessages",value:!0}});break;case"full-reconnect":this.engine.fullReconnectOnNext=!0,yield this.engine.client.handleOnClose("simulate full-reconnect");break;case"force-tcp":case"force-tls":s=new Ye({scenario:{case:"switchCandidateProtocol",value:e==="force-tls"?2:1}}),n=()=>f(this,void 0,void 0,function*(){let r=this.engine.client.onLeave;r&&r(new hs({reason:Pe.CLIENT_INITIATED,action:Jt.RECONNECT}))});break;case"subscriber-bandwidth":if(t===void 0||typeof t!="number")throw new Error("subscriber-bandwidth requires a number as argument");s=new Ye({scenario:{case:"subscriberBandwidth",value:He(t)}});break;case"leave-full-reconnect":s=new Ye({scenario:{case:"leaveRequestFullReconnect",value:!0}})}s&&(yield this.engine.client.sendSimulateScenario(s),yield n())})}get canPlaybackAudio(){return this.audioEnabled}get canPlaybackVideo(){return!this.isVideoPlaybackBlocked}getActiveDevice(e){return this.localParticipant.activeDeviceMap.get(e)}switchActiveDevice(e,t){return f(this,arguments,void 0,function(n,s){var r=this;let a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0;return function*(){var o,c,d,l,u,h,m;let g=!0,p=!1,k=a?{exact:s}:s;if(n==="audioinput"){p=r.localParticipant.audioTrackPublications.size===0;let T=(o=r.getActiveDevice(n))!==null&&o!==void 0?o:r.options.audioCaptureDefaults.deviceId;r.options.audioCaptureDefaults.deviceId=k;let S=Array.from(r.localParticipant.audioTrackPublications.values()).filter(b=>b.source===E.Source.Microphone);try{g=(yield Promise.all(S.map(b=>{var y;return(y=b.audioTrack)===null||y===void 0?void 0:y.setDeviceId(k)}))).every(b=>b===!0)}catch(b){throw r.options.audioCaptureDefaults.deviceId=T,b}let I=S.some(b=>{var y,C;return(C=(y=b.track)===null||y===void 0?void 0:y.isMuted)!==null&&C!==void 0?C:!1});g&&I&&(p=!0)}else if(n==="videoinput"){p=r.localParticipant.videoTrackPublications.size===0;let T=(c=r.getActiveDevice(n))!==null&&c!==void 0?c:r.options.videoCaptureDefaults.deviceId;r.options.videoCaptureDefaults.deviceId=k;let S=Array.from(r.localParticipant.videoTrackPublications.values()).filter(b=>b.source===E.Source.Camera);try{g=(yield Promise.all(S.map(b=>{var y;return(y=b.videoTrack)===null||y===void 0?void 0:y.setDeviceId(k)}))).every(b=>b===!0)}catch(b){throw r.options.videoCaptureDefaults.deviceId=T,b}let I=S.some(b=>{var y,C;return(C=(y=b.track)===null||y===void 0?void 0:y.isMuted)!==null&&C!==void 0?C:!1});g&&I&&(p=!0)}else if(n==="audiooutput"){if(p=!0,!Or()&&!r.options.webAudioMix||r.options.webAudioMix&&r.audioContext&&!("setSinkId"in r.audioContext))throw new Error("cannot switch audio output, the current browser does not support it");r.options.webAudioMix&&(s=(d=yield Me.getInstance().normalizeDeviceId("audiooutput",s))!==null&&d!==void 0?d:""),(l=(m=r.options).audioOutput)!==null&&l!==void 0||(m.audioOutput={});let T=(u=r.getActiveDevice(n))!==null&&u!==void 0?u:r.options.audioOutput.deviceId;r.options.audioOutput.deviceId=s;try{r.options.webAudioMix&&((h=r.audioContext)===null||h===void 0||h.setSinkId(s)),yield Promise.all(Array.from(r.remoteParticipants.values()).map(S=>S.setAudioOutput({deviceId:s})))}catch(S){throw r.options.audioOutput.deviceId=T,S}}return p&&(r.localParticipant.activeDeviceMap.set(n,s),r.emit(_.ActiveDeviceChanged,n,s)),g}()})}setupLocalParticipantEvents(){this.localParticipant.on(A.ParticipantMetadataChanged,this.onLocalParticipantMetadataChanged).on(A.ParticipantNameChanged,this.onLocalParticipantNameChanged).on(A.AttributesChanged,this.onLocalAttributesChanged).on(A.TrackMuted,this.onLocalTrackMuted).on(A.TrackUnmuted,this.onLocalTrackUnmuted).on(A.LocalTrackPublished,this.onLocalTrackPublished).on(A.LocalTrackUnpublished,this.onLocalTrackUnpublished).on(A.ConnectionQualityChanged,this.onLocalConnectionQualityChanged).on(A.MediaDevicesError,this.onMediaDevicesError).on(A.AudioStreamAcquired,this.startAudio).on(A.ChatMessage,this.onLocalChatMessageSent).on(A.ParticipantPermissionsChanged,this.onLocalParticipantPermissionsChanged)}recreateEngine(e){let t=this.engine;e&&t&&!t.client.isDisconnected?t.client.sendLeave().finally(()=>t.close()):t?.close(),this.engine=void 0,this.isResuming=!1,this.remoteParticipants.clear(),this.sidToIdentity.clear(),this.bufferedEvents=[],this.maybeCreateEngine()}onTrackAdded(e,t,n){var s,r;if([K.Connecting,K.Reconnecting].includes(this.state)){let m=Xo(e,t);this.log.debug("deferring on track for later",{mediaTrackId:e.id,mediaStreamId:t.id,tracksInStream:t.getTracks().map(k=>k.id)});let g=()=>{p(),this.onTrackAdded(e,t,n)},p=()=>{if(this.off(_.Reconnected,g),this.off(_.Connected,g),this.off(_.Disconnected,p),m){let k=this.pendingTrackAddedCallbacks.get(m);k?.delete(p),k?.size===0&&this.pendingTrackAddedCallbacks.delete(m)}};if(this.once(_.Reconnected,g),this.once(_.Connected,g),this.once(_.Disconnected,p),m){let k=(s=this.pendingTrackAddedCallbacks.get(m))!==null&&s!==void 0?s:new Set;k.add(p),this.pendingTrackAddedCallbacks.set(m,k)}return}if(this.state===K.Disconnected){this.log.warn("skipping incoming track after Room disconnected");return}if(e.readyState==="ended"){this.log.debug("skipping incoming track as it already ended");return}let a=kl(t.id),o=a[0],c=a[1],d=(r=Xo(e,t))!==null&&r!==void 0?r:e.id;if(o===this.localParticipant.sid){this.log.warn("tried to create RemoteParticipant for local participant");return}let l=Array.from(this.remoteParticipants.values()).find(m=>m.sid===o);if(!l){o.startsWith("PA")&&this.log.error("Tried to add a track for a participant, that's not present. Sid: ".concat(o));return}if(!d.startsWith("TR")){let m=this.engine.getTrackIdForReceiver(n);if(!m){this.log.error("Tried to add a track whose 'sid' could not be found for a participant, that's not present. Sid: ".concat(o));return}d=m}d.startsWith("TR")||this.log.warn("Tried to add a track whose 'sid' could not be determined for a participant, that's not present. Sid: ".concat(o,", streamId: ").concat(c,", trackId: ").concat(d),{remoteParticipantID:o,streamId:c,trackId:d});let u;this.options.adaptiveStream&&(typeof this.options.adaptiveStream=="object"?u=this.options.adaptiveStream:u={});let h=l.addSubscribedMediaTrack(e,d,t,n,u);h?.isEncrypted&&!this.e2eeManager&&this.emit(_.EncryptionError,new Error("Encrypted ".concat(h.source," track received from participant ").concat(l.sid,", but room does not have encryption enabled!")))}cancelPendingTrackAdded(e){var t;(t=this.pendingTrackAddedCallbacks.get(e))===null||t===void 0||t.forEach(n=>n())}handleLocalTrackSubscribed(e){let t=()=>this.localParticipant.getTrackPublications().find(c=>c.trackSid===e),n=t();if(n){this.emitLocalTrackSubscribed(n);return}this.log.debug("deferring LocalTrackSubscribed, publication not yet available",{subscribedSid:e});let s=1e4,r,a=c=>{c.trackSid===e&&(o(),this.emitLocalTrackSubscribed(c))},o=()=>{clearTimeout(r),this.localParticipant.off(A.LocalTrackPublished,a),this.off(_.Disconnected,o)};this.localParticipant.on(A.LocalTrackPublished,a),this.once(_.Disconnected,o),r=setTimeout(()=>{o();let c=t();c?this.emitLocalTrackSubscribed(c):this.log.warn("could not find local track publication for LocalTrackSubscribed event after timeout",{subscribedSid:e})},s)}emitLocalTrackSubscribed(e){this.localParticipant.emit(A.LocalTrackSubscribed,e),this.emitWhenConnected(_.LocalTrackSubscribed,e,this.localParticipant)}handleDisconnect(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:!0,t=arguments.length>1?arguments[1]:void 0;var n,s;if(this.clearConnectionReconcile(),this.isResuming=!1,this.bufferedEvents=[],this.transcriptionReceivedTimes.clear(),this.incomingDataStreamManager.clearControllers(),this.incomingDataTrackManager.reset(),this.outgoingDataTrackManager.reset(),this.state!==K.Disconnected){this.regionUrl=void 0,this.regionUrlProvider&&this.regionUrlProvider.notifyDisconnected();try{this.remoteParticipants.forEach(r=>{r.trackPublications.forEach(a=>{r.unpublishTrack(a.trackSid)})}),this.localParticipant.trackPublications.forEach(r=>{var a,o,c;r.track&&this.localParticipant.unpublishTrack(r.track,e),e?((a=r.track)===null||a===void 0||a.detach(),(o=r.track)===null||o===void 0||o.stop()):(c=r.track)===null||c===void 0||c.stopMonitor()}),this.localParticipant.off(A.ParticipantMetadataChanged,this.onLocalParticipantMetadataChanged).off(A.ParticipantNameChanged,this.onLocalParticipantNameChanged).off(A.AttributesChanged,this.onLocalAttributesChanged).off(A.TrackMuted,this.onLocalTrackMuted).off(A.TrackUnmuted,this.onLocalTrackUnmuted).off(A.LocalTrackPublished,this.onLocalTrackPublished).off(A.LocalTrackUnpublished,this.onLocalTrackUnpublished).off(A.ConnectionQualityChanged,this.onLocalConnectionQualityChanged).off(A.MediaDevicesError,this.onMediaDevicesError).off(A.AudioStreamAcquired,this.startAudio).off(A.ChatMessage,this.onLocalChatMessageSent).off(A.ParticipantPermissionsChanged,this.onLocalParticipantPermissionsChanged),this.localParticipant.trackPublications.clear(),this.localParticipant.videoTrackPublications.clear(),this.localParticipant.audioTrackPublications.clear(),this.remoteParticipants.clear(),this.sidToIdentity.clear(),this.activeSpeakers=[],this.audioContext&&typeof this.options.webAudioMix=="boolean"&&(this.audioContext.close(),this.audioContext=void 0),Ce()&&(window.removeEventListener("beforeunload",this.onPageLeave),window.removeEventListener("pagehide",this.onPageLeave),window.removeEventListener("freeze",this.onPageLeave),(s=(n=navigator.mediaDevices)===null||n===void 0?void 0:n.removeEventListener)===null||s===void 0||s.call(n,"devicechange",this.handleDeviceChange))}finally{this.setAndEmitConnectionState(K.Disconnected),this.emit(_.Disconnected,t)}}}handleParticipantDisconnected(e,t,n){this.remoteParticipants.delete(e),t&&(this.incomingDataStreamManager.validateParticipantHasNoActiveDataStreams(e),this.incomingDataTrackManager.handleRemoteParticipantDisconnected(e),t.trackPublications.forEach(s=>{t.unpublishTrack(s.trackSid,!0)}),this.emit(_.ParticipantDisconnected,t,n),t.setDisconnected(),this.rpcClientManager.handleParticipantDisconnected(t.identity))}selectDefaultDevices(){return f(this,void 0,void 0,function*(){var e,t,n;let s=Me.getInstance().previousDevices,r=yield Me.getInstance().getDevices(void 0,!1),a=ye();if(a?.name==="Chrome"&&a.os!=="iOS")for(let c of r){let d=s.find(l=>l.deviceId===c.deviceId);d&&d.label!==""&&d.kind===c.kind&&d.label!==c.label&&this.getActiveDevice(c.kind)==="default"&&this.emit(_.ActiveDeviceChanged,c.kind,c.deviceId)}let o=["audiooutput","audioinput","videoinput"];for(let c of o){let d=Sm(c),l=this.localParticipant.getTrackPublication(d);if(l&&(!((e=l.track)===null||e===void 0)&&e.isUserProvided))continue;let u=r.filter(m=>m.kind===c),h=this.getActiveDevice(c);if(h===((t=s.filter(m=>m.kind===c)[0])===null||t===void 0?void 0:t.deviceId)&&u.length>0&&((n=u[0])===null||n===void 0?void 0:n.deviceId)!==h){yield this.switchActiveDevice(c,u[0].deviceId);continue}c==="audioinput"&&!Bn()||c==="videoinput"||u.length>0&&!u.find(m=>m.deviceId===this.getActiveDevice(c))&&(c!=="audiooutput"||!Bn())&&(yield this.switchActiveDevice(c,u[0].deviceId))}})}acquireAudioContext(){return f(this,void 0,void 0,function*(){var e,t;if(typeof this.options.webAudioMix!="boolean"&&this.options.webAudioMix.audioContext?this.audioContext=this.options.webAudioMix.audioContext:(!this.audioContext||this.audioContext.state==="closed")&&(this.audioContext=(e=bl())!==null&&e!==void 0?e:void 0),this.options.webAudioMix&&this.remoteParticipants.forEach(s=>s.setAudioContext(this.audioContext)),this.localParticipant.setAudioContext(this.audioContext),this.audioContext&&this.audioContext.state==="suspended")try{yield Promise.race([this.audioContext.resume(),ke(200)])}catch(s){this.log.warn("Could not resume audio context",{error:s})}let n=((t=this.audioContext)===null||t===void 0?void 0:t.state)==="running";n!==this.canPlaybackAudio&&(this.audioEnabled=n,this.emit(_.AudioPlaybackStatusChanged,n))})}createParticipant(e,t){var n;let s;return t?s=is.fromParticipantInfo(this.engine.client,t,{loggerContextCb:()=>this.logContext,loggerName:this.options.loggerName},this.incomingDataTrackManager):s=new is(this.engine.client,"",e,void 0,void 0,void 0,{loggerContextCb:()=>this.logContext,loggerName:this.options.loggerName}),this.options.webAudioMix&&s.setAudioContext(this.audioContext),!((n=this.options.audioOutput)===null||n===void 0)&&n.deviceId&&s.setAudioOutput(this.options.audioOutput).catch(r=>this.log.warn("Could not set audio output: ".concat(r.message))),s}getOrCreateParticipant(e,t){if(this.remoteParticipants.has(e)){let s=this.remoteParticipants.get(e);return t&&s.updateInfo(t)&&this.sidToIdentity.set(t.sid,t.identity),s}let n=this.createParticipant(e,t);return this.remoteParticipants.set(e,n),this.sidToIdentity.set(t.sid,t.identity),this.emitWhenConnected(_.ParticipantConnected,n),n.on(A.TrackPublished,s=>{this.emitWhenConnected(_.TrackPublished,s,n)}).on(A.TrackSubscribed,(s,r)=>{s.kind===E.Kind.Audio?(s.on(P.AudioPlaybackStarted,this.handleAudioPlaybackStarted),s.on(P.AudioPlaybackFailed,this.handleAudioPlaybackFailed)):s.kind===E.Kind.Video&&(s.on(P.VideoPlaybackFailed,this.handleVideoPlaybackFailed),s.on(P.VideoPlaybackStarted,this.handleVideoPlaybackStarted)),this.emitWhenConnected(_.TrackSubscribed,s,r,n)}).on(A.TrackUnpublished,s=>{this.cancelPendingTrackAdded(s.trackSid),this.emit(_.TrackUnpublished,s,n)}).on(A.TrackUnsubscribed,(s,r)=>{this.emit(_.TrackUnsubscribed,s,r,n)}).on(A.TrackMuted,s=>{this.emitWhenConnected(_.TrackMuted,s,n)}).on(A.TrackUnmuted,s=>{this.emitWhenConnected(_.TrackUnmuted,s,n)}).on(A.ParticipantMetadataChanged,s=>{this.emitWhenConnected(_.ParticipantMetadataChanged,s,n)}).on(A.ParticipantNameChanged,s=>{this.emitWhenConnected(_.ParticipantNameChanged,s,n)}).on(A.AttributesChanged,s=>{this.emitWhenConnected(_.ParticipantAttributesChanged,s,n)}).on(A.ConnectionQualityChanged,s=>{this.emitWhenConnected(_.ConnectionQualityChanged,s,n)}).on(A.ParticipantPermissionsChanged,s=>{this.emitWhenConnected(_.ParticipantPermissionsChanged,s,n)}).on(A.TrackSubscriptionStatusChanged,(s,r)=>{this.emitWhenConnected(_.TrackSubscriptionStatusChanged,s,r,n)}).on(A.TrackSubscriptionFailed,(s,r)=>{this.emit(_.TrackSubscriptionFailed,s,n,r)}).on(A.TrackSubscriptionPermissionChanged,(s,r)=>{this.emitWhenConnected(_.TrackSubscriptionPermissionChanged,s,r,n)}).on(A.Active,()=>{this.emitWhenConnected(_.ParticipantActive,n),n.kind===Ln.AGENT&&this.localParticipant.setActiveAgent(n)}),t&&n.updateInfo(t),n}sendSyncState(){let e=Array.from(this.remoteParticipants.values()).reduce((s,r)=>(s.push(...r.getTrackPublications()),s),[]),t=this.localParticipant.getTrackPublications(),n=this.outgoingDataTrackManager.queryPublished();this.engine.sendSyncState(e,t,n)}updateSubscriptions(){for(let e of this.remoteParticipants.values())for(let t of e.videoTrackPublications.values())t.isSubscribed&&zm(t)&&t.emitTrackUpdate()}getRemoteParticipantBySid(e){let t=this.sidToIdentity.get(e);if(t)return this.remoteParticipants.get(t)}getClientInfoCapabilities(e){var t;let n=[];return(Vn((t=e.frameMetadata)!==null&&t!==void 0?t:e.packetTrailer)||this.e2eeManager)&&n.push(Nn.CAP_PACKET_TRAILER),kt()&&n.push(Nn.CAP_COMPRESSION_DEFLATE_RAW),n}registerRpcDataStreamHandler(){this.incomingDataStreamManager.registerTextStreamHandler(Kl,(e,t)=>f(this,[e,t],void 0,function(n,s){var r=this;let a=s.identity;return function*(){var o;let c=(o=n.info.attributes)!==null&&o!==void 0?o:{};yield r.rpcServerManager.handleIncomingDataStream(n,a,c)}()})),this.incomingDataStreamManager.registerTextStreamHandler(Gl,(e,t)=>f(this,[e,t],void 0,function(n,s){var r=this;let a=s.identity;return function*(){var o;let c=(o=n.info.attributes)!==null&&o!==void 0?o:{};yield r.rpcClientManager.handleIncomingDataStream(n,a,c)}()}))}setStatsLogging(e){e?this.statsLogInterval||(this.statsLogInterval=z.setInterval(()=>{this.logWebRTCStats()},Kp)):this.statsLogInterval&&(z.clearInterval(this.statsLogInterval),this.statsLogInterval=void 0)}registerConnectionReconcile(){this.clearConnectionReconcile();let e=0;this.connectionReconcileInterval=z.setInterval(()=>{!this.engine||this.engine.isClosed||!this.engine.verifyTransport()?(e++,this.log.warn("detected connection state mismatch",{numFailures:e,engine:this.engine?{closed:this.engine.isClosed,transportsConnectedOrConnecting:this.engine.verifyTransport()}:void 0}),e>=3&&(this.clearConnectionReconcile(),this.engine&&!this.engine.isClosed?(this.log.warn("detected connection state mismatch, attempting full reconnect"),this.engine.reconnect()):(this.recreateEngine(),this.handleDisconnect(this.options.stopLocalTrackOnUnpublish,Pe.STATE_MISMATCH)))):e=0},Hp)}clearConnectionReconcile(){this.connectionReconcileInterval&&z.clearInterval(this.connectionReconcileInterval)}setAndEmitConnectionState(e){return e===this.state?!1:(this.log.info("connection state changed: ".concat(this.state," -> ").concat(e)),this.state=e,this.incomingDataStreamManager.setConnected(e===K.Connected),this.setStatsLogging(e===K.Connected),this.emit(_.ConnectionStateChanged,this.state),!0)}emitBufferedEvents(){this.bufferedEvents.forEach(e=>{let t=L(e,2),n=t[0],s=t[1];this.emit(n,...s)}),this.bufferedEvents=[]}emitWhenConnected(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),s=1;sthis.logContext}),{loggerName:this.options.loggerName,loggerContextCb:()=>this.logContext});this.localParticipant.addTrackPublication(c),this.localParticipant.emit(A.LocalTrackPublished,c)}if(a.audio){let c=new zn(E.Kind.Audio,new Kt({source:de.MICROPHONE,sid:Math.floor(Math.random()*1e4).toString(),type:qe.AUDIO}),new dn(a.useRealTracks&&(!((s=navigator.mediaDevices)===null||s===void 0)&&s.getUserMedia)?(yield navigator.mediaDevices.getUserMedia({audio:!0})).getAudioTracks()[0]:xs(),void 0,!1,this.audioContext,{loggerName:this.options.loggerName,loggerContextCb:()=>this.logContext}),{loggerName:this.options.loggerName,loggerContextCb:()=>this.logContext});this.localParticipant.addTrackPublication(c),this.localParticipant.emit(A.LocalTrackPublished,c)}for(let c=0;c1?t-1:0),s=1;sa!==void 0);(e===_.TrackSubscribed||e===_.TrackUnsubscribed)&&this.log.trace("subscribe trace: ".concat(e),{event:e,args:r}),this.log.debug("room event ".concat(e),{event:e,args:r})}return super.emit(e,...n)}};mn.cleanupRegistry=typeof FinalizationRegistry<"u"&&typeof WeakRef<"u"&&new FinalizationRegistry(i=>{i()});function Jl(i){return i.map(e=>{if(e)return Array.isArray(e)?Jl(e):typeof e=="object"?"logContext"in e?e.logContext:void 0:e})}var Be;(function(i){i[i.IDLE=0]="IDLE",i[i.RUNNING=1]="RUNNING",i[i.SKIPPED=2]="SKIPPED",i[i.SUCCESS=3]="SUCCESS",i[i.FAILED=4]="FAILED"})(Be||(Be={}));var tt=class extends we.EventEmitter{constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};super(),this.status=Be.IDLE,this.logs=[],this.options={},this.url=e,this.token=t,this.name=this.constructor.name,this.room=new mn(n.roomOptions),this.connectOptions=n.connectOptions,this.options=n}run(e){return f(this,void 0,void 0,function*(){if(this.status!==Be.IDLE)throw Error("check is running already");this.setStatus(Be.RUNNING);try{yield this.perform()}catch(t){t instanceof Error&&(this.options.errorsAsWarnings?this.appendWarning(t.message):this.appendError(t.message))}return yield this.disconnect(),yield new Promise(t=>setTimeout(t,500)),this.status!==Be.SKIPPED&&this.setStatus(this.isSuccess()?Be.SUCCESS:Be.FAILED),e&&e(),this.getInfo()})}isSuccess(){return!this.logs.some(e=>e.level==="error")}connect(e){return f(this,void 0,void 0,function*(){return this.room.state===K.Connected?this.room:(e||(e=this.url),yield this.room.connect(e,this.token,this.connectOptions),this.room)})}disconnect(){return f(this,void 0,void 0,function*(){this.room&&this.room.state!==K.Disconnected&&(yield this.room.disconnect(),yield new Promise(e=>setTimeout(e,500)))})}skip(){this.setStatus(Be.SKIPPED)}switchProtocol(e){return f(this,void 0,void 0,function*(){let t=!1,n=!1;if(this.room.on(_.Reconnecting,()=>{t=!0}),this.room.once(_.Reconnected,()=>{n=!0}),this.room.simulateScenario("force-".concat(e)),yield new Promise(r=>setTimeout(r,1e3)),!t)return;let s=Date.now()+1e4;for(;Date.now()(r.duration-a.duration)*.5+(r.rtt-a.rtt)*.5);let s=t[0];this.bestStats=s,this.appendMessage("best Cloud region: ".concat(s.region))})}getInfo(){let e=super.getInfo();return e.data=this.bestStats,e}checkCloudRegion(e){return f(this,void 0,void 0,function*(){var t,n;yield this.connect(e),this.options.protocol==="tcp"&&(yield this.switchProtocol("tcp"));let s=(t=this.room.serverInfo)===null||t===void 0?void 0:t.region;if(!s)throw new Error("Region not found");let r=yield this.room.localParticipant.streamText({topic:"test"}),a=1e3,c=1e6/a,d="A".repeat(a),l=Date.now();for(let g=0;g{g.type==="candidate-pair"&&g.nominated&&(m.rtt=g.currentRoundTripTime*1e3)}),yield this.disconnect(),m})}},er=1e4,ga=class extends tt{get description(){return"Connection via UDP vs TCP"}perform(){return f(this,void 0,void 0,function*(){let e=yield this.checkConnectionProtocol("udp"),t=yield this.checkConnectionProtocol("tcp");this.bestStats=e,e.qualityLimitationDurations.bandwidth-t.qualityLimitationDurations.bandwidth>.5||(e.packetsLost-t.packetsLost)/e.packetsSent>.01?(this.appendMessage("best connection quality via tcp"),this.bestStats=t):this.appendMessage("best connection quality via udp");let n=this.bestStats;this.appendMessage("upstream bitrate: ".concat((n.bitrateTotal/n.count/1e3/1e3).toFixed(2)," mbps")),this.appendMessage("RTT: ".concat((n.rttTotal/n.count*1e3).toFixed(2)," ms")),this.appendMessage("jitter: ".concat((n.jitterTotal/n.count*1e3).toFixed(2)," ms")),n.packetsLost>0&&this.appendWarning("packets lost: ".concat((n.packetsLost/n.packetsSent*100).toFixed(2),"%")),n.qualityLimitationDurations.bandwidth>1&&this.appendWarning("bandwidth limited ".concat((n.qualityLimitationDurations.bandwidth/(er/1e3)*100).toFixed(2),"%")),n.qualityLimitationDurations.cpu>0&&this.appendWarning("cpu limited ".concat((n.qualityLimitationDurations.cpu/(er/1e3)*100).toFixed(2),"%"))})}getInfo(){let e=super.getInfo();return e.data=this.bestStats,e}checkConnectionProtocol(e){return f(this,void 0,void 0,function*(){yield this.connect(),e==="tcp"?yield this.switchProtocol("tcp"):yield this.switchProtocol("udp");let t=document.createElement("canvas");t.width=1280,t.height=720;let n=t.getContext("2d");if(!n)throw new Error("Could not get canvas context");let s=0,r=()=>{s=(s+1)%360,n.fillStyle="hsl(".concat(s,", 100%, 50%)"),n.fillRect(0,0,t.width,t.height),requestAnimationFrame(r)};r();let o=t.captureStream(30).getVideoTracks()[0],d=(yield this.room.localParticipant.publishTrack(o,{simulcast:!1,degradationPreference:"maintain-resolution",videoEncoding:{maxBitrate:2e6}})).track,l={protocol:e,packetsLost:0,packetsSent:0,qualityLimitationDurations:{},rttTotal:0,jitterTotal:0,bitrateTotal:0,count:0},u=setInterval(()=>f(this,void 0,void 0,function*(){let h=yield d.getRTCStatsReport();h?.forEach(m=>{m.type==="outbound-rtp"?(l.packetsSent=m.packetsSent,l.qualityLimitationDurations=m.qualityLimitationDurations,l.bitrateTotal+=m.targetBitrate,l.count++):m.type==="remote-inbound-rtp"&&(l.packetsLost=m.packetsLost,l.rttTotal+=m.roundTripTime,l.jitterTotal+=m.jitter)})}),1e3);return yield new Promise(h=>setTimeout(h,er)),clearInterval(u),o.stop(),t.remove(),yield this.disconnect(),l})}},va=class extends tt{get description(){return"Can publish audio"}perform(){return f(this,void 0,void 0,function*(){var e;let t=yield this.connect(),n=yield qp();if(yield vl(n,1e3))throw new Error("unable to detect audio from microphone");this.appendMessage("detected audio from microphone"),t.localParticipant.publishTrack(n),yield new Promise(o=>setTimeout(o,3e3));let r=yield(e=n.sender)===null||e===void 0?void 0:e.getStats();if(!r)throw new Error("Could not get RTCStats");let a=0;if(r.forEach(o=>{o.type==="outbound-rtp"&&(o.kind==="audio"||!o.kind&&o.mediaType==="audio")&&(a=o.packetsSent)}),a===0)throw new Error("Could not determine packets are sent");this.appendMessage("published ".concat(a," audio packets"))})}},ba=class extends tt{get description(){return"Can publish video"}perform(){return f(this,void 0,void 0,function*(){var e;let t=yield this.connect(),n=yield jp();yield this.checkForVideo(n.mediaStreamTrack),t.localParticipant.publishTrack(n),yield new Promise(a=>setTimeout(a,5e3));let s=yield(e=n.sender)===null||e===void 0?void 0:e.getStats();if(!s)throw new Error("Could not get RTCStats");let r=0;if(s.forEach(a=>{a.type==="outbound-rtp"&&(a.kind==="video"||!a.kind&&a.mediaType==="video")&&(r+=a.packetsSent)}),r===0)throw new Error("Could not determine packets are sent");this.appendMessage("published ".concat(r," video packets"))})}checkForVideo(e){return f(this,void 0,void 0,function*(){let t=new MediaStream;t.addTrack(e.clone());let n=document.createElement("video");n.srcObject=t,n.muted=!0,n.autoplay=!0,n.playsInline=!0,n.setAttribute("playsinline","true"),document.body.appendChild(n),yield new Promise(s=>{n.onplay=()=>{setTimeout(()=>{var r,a,o,c;let d=document.createElement("canvas"),l=e.getSettings(),u=(a=(r=l.width)!==null&&r!==void 0?r:n.videoWidth)!==null&&a!==void 0?a:1280,h=(c=(o=l.height)!==null&&o!==void 0?o:n.videoHeight)!==null&&c!==void 0?c:720;d.width=u,d.height=h;let m=d.getContext("2d");m.drawImage(n,0,0);let p=m.getImageData(0,0,d.width,d.height).data,k=!0;for(let T=0;Ts.stop()),n.remove()})}},ya=class extends tt{get description(){return"Resuming connection after interruption"}perform(){return f(this,void 0,void 0,function*(){var e;let t=yield this.connect(),n=!1,s=!1,r,a=new Promise(d=>{setTimeout(d,5e3),r=d}),o=()=>{n=!0};t.on(_.SignalReconnecting,o).on(_.Reconnecting,o).on(_.Reconnected,()=>{s=!0,r(!0)}),(e=t.engine.client.ws)===null||e===void 0||e.close();let c=t.engine.client.onClose;if(c&&c(""),yield a,n){if(!s||t.state!==K.Connected)throw this.appendWarning("reconnection is only possible in Redis-based configurations"),new Error("Not able to reconnect")}else throw new Error("Did not attempt to reconnect")})}},ka=class extends tt{get description(){return"Can connect via TURN"}perform(){return f(this,void 0,void 0,function*(){var e,t,n;cn(new URL(this.url))&&(this.appendMessage("Using region specific url"),this.url=(e=yield new ze(this.url,this.token).getNextBestRegionUrl())!==null&&e!==void 0?e:this.url);let s=new Kn,r=yield s.join(this.url,this.token,{autoSubscribe:!0,maxRetries:0,e2eeEnabled:!1,websocketTimeout:15e3},void 0,!0),a=!1,o=!1,c=!1;for(let d of r.iceServers)for(let l of d.urls)l.startsWith("turn:")?(o=!0,c=!0):l.startsWith("turns:")&&(o=!0,c=!0,a=!0),l.startsWith("stun:")&&(c=!0);c?o&&!a&&this.appendWarning("TURN is configured server side, but TURN/TLS is unavailable."):this.appendWarning("No STUN servers configured on server side."),yield s.close(),!((n=(t=this.connectOptions)===null||t===void 0?void 0:t.rtcConfig)===null||n===void 0)&&n.iceServers||o?yield this.room.connect(this.url,this.token,{rtcConfig:{iceTransportPolicy:"relay"}}):(this.appendWarning("No TURN servers configured."),this.skip(),yield new Promise(d=>setTimeout(d,0)))})}},Ta=class extends tt{get description(){return"Establishing WebRTC connection"}perform(){return f(this,void 0,void 0,function*(){let e=!1,t=!1;this.room.on(_.SignalConnected,()=>{var n;let s=this.room.engine.client.onTrickle;this.room.engine.client.onTrickle=(r,a)=>{if(r.candidate){let o=new RTCIceCandidate(r),c="".concat(o.protocol," ").concat(o.address,":").concat(o.port," ").concat(o.type);o.address&&(Gp(o.address)?c+=" (private)":o.protocol==="tcp"&&o.tcpType==="passive"?(e=!0,c+=" (passive)"):o.protocol==="udp"&&(t=!0)),this.appendMessage(c)}s&&s(r,a)},!((n=this.room.engine.pcManager)===null||n===void 0)&&n.subscriber&&(this.room.engine.pcManager.subscriber.onIceCandidateError=r=>{r instanceof RTCPeerConnectionIceErrorEvent&&this.appendWarning("error with ICE candidate: ".concat(r.errorCode," ").concat(r.errorText," ").concat(r.url))})});try{yield this.connect(),W.info("now the room is connected")}catch(n){throw this.appendWarning("ports need to be open on firewall in order to connect."),n}e||this.appendWarning("Server is not configured for ICE/TCP"),t||this.appendWarning("No public IPv4 UDP candidates were found. Your server is likely not configured correctly")})}};function Gp(i){let e=i.split(".");if(e.length===4){if(e[0]==="10")return!0;if(e[0]==="192"&&e[1]==="168")return!0;if(e[0]==="172"){let t=parseInt(e[1],10);if(t>=16&&t<=31)return!0}}return!1}var Sa=class extends tt{get description(){return"Connecting to signal connection via WebSocket"}perform(){return f(this,void 0,void 0,function*(){var e,t,n;(this.url.startsWith("ws:")||this.url.startsWith("http:"))&&this.appendWarning("Server is insecure, clients may block connections to it");let s=new Kn,r;try{r=yield s.join(this.url,this.token,{autoSubscribe:!0,maxRetries:0,e2eeEnabled:!1,websocketTimeout:15e3},void 0,!0)}catch(a){if(cn(new URL(this.url))){this.appendMessage("Initial connection failed with error ".concat(a.message,". Retrying with region fallback"));let c=yield new ze(this.url,this.token).getNextBestRegionUrl();c&&(r=yield s.join(c,this.token,{autoSubscribe:!0,maxRetries:0,e2eeEnabled:!1,websocketTimeout:15e3},void 0,!0),this.appendMessage("Fallback to region worked. To avoid initial connections failing, ensure you're calling room.prepareConnection() ahead of time"))}}r?(this.appendMessage("Connected to server, version ".concat(r.serverVersion,".")),((e=r.serverInfo)===null||e===void 0?void 0:e.edition)===fd.Cloud&&(!((t=r.serverInfo)===null||t===void 0)&&t.region)&&this.appendMessage("LiveKit Cloud: ".concat((n=r.serverInfo)===null||n===void 0?void 0:n.region))):this.appendError("Websocket connection could not be established"),yield s.close()})}},xc=class extends we.EventEmitter{constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};super(),this.options={},this.checkResults=new Map,this.url=e,this.token=t,this.options=n}getNextCheckId(){let e=this.checkResults.size;return this.checkResults.set(e,{logs:[],status:Be.IDLE,name:"",description:""}),e}updateCheck(e,t){this.checkResults.set(e,t),this.emit("checkUpdate",e,t)}isSuccess(){return Array.from(this.checkResults.values()).every(e=>e.status!==Be.FAILED)}getResults(){return Array.from(this.checkResults.values())}createAndRunCheck(e){return f(this,void 0,void 0,function*(){let t=this.getNextCheckId(),n=new e(this.url,this.token,this.options),s=a=>{this.updateCheck(t,a)};n.on("update",s);let r=yield n.run();return n.off("update",s),r})}checkWebsocket(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(Sa)})}checkWebRTC(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(Ta)})}checkTURN(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(ka)})}checkReconnect(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(ya)})}checkPublishAudio(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(va)})}checkPublishVideo(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(ba)})}checkConnectionProtocol(){return f(this,void 0,void 0,function*(){let e=yield this.createAndRunCheck(ga);if(e.data&&"protocol"in e.data){let t=e.data;this.options.protocol=t.protocol}return e})}checkCloudRegion(){return f(this,void 0,void 0,function*(){return this.createAndRunCheck(pa)})}};new TextEncoder;var eg=new TextDecoder,tg=new TextDecoder("utf-8",{fatal:!0}),ve=class extends Error{constructor(e,t){var n;super(e,t),x(this,"code","ERR_JOSE_GENERIC"),this.name=this.constructor.name,(n=Error.captureStackTrace)===null||n===void 0||n.call(Error,this,this.constructor)}};x(ve,"code","ERR_JOSE_GENERIC");var Ca=class extends ve{constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:"unspecified",s=arguments.length>3&&arguments[3]!==void 0?arguments[3]:"unspecified";super(e,{cause:{claim:n,reason:s,payload:t}}),x(this,"code","ERR_JWT_CLAIM_VALIDATION_FAILED"),x(this,"claim",void 0),x(this,"reason",void 0),x(this,"payload",void 0),this.claim=n,this.reason=s,this.payload=t}};x(Ca,"code","ERR_JWT_CLAIM_VALIDATION_FAILED");var Ea=class extends ve{constructor(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:"unspecified",s=arguments.length>3&&arguments[3]!==void 0?arguments[3]:"unspecified";super(e,{cause:{claim:n,reason:s,payload:t}}),x(this,"code","ERR_JWT_EXPIRED"),x(this,"claim",void 0),x(this,"reason",void 0),x(this,"payload",void 0),this.claim=n,this.reason=s,this.payload=t}};x(Ea,"code","ERR_JWT_EXPIRED");var wa=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JOSE_ALG_NOT_ALLOWED")}};x(wa,"code","ERR_JOSE_ALG_NOT_ALLOWED");var _a=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JOSE_NOT_SUPPORTED")}};x(_a,"code","ERR_JOSE_NOT_SUPPORTED");var Ra=class extends ve{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"decryption operation failed",t=arguments.length>1?arguments[1]:void 0;super(e,t),x(this,"code","ERR_JWE_DECRYPTION_FAILED")}};x(Ra,"code","ERR_JWE_DECRYPTION_FAILED");var Pa=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JWE_INVALID")}};x(Pa,"code","ERR_JWE_INVALID");var Ia=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JWS_INVALID")}};x(Ia,"code","ERR_JWS_INVALID");var Ma=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JWT_INVALID")}};x(Ma,"code","ERR_JWT_INVALID");var Aa=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JWK_INVALID")}};x(Aa,"code","ERR_JWK_INVALID");var Oa=class extends ve{constructor(){super(...arguments),x(this,"code","ERR_JWKS_INVALID")}};x(Oa,"code","ERR_JWKS_INVALID");var Da=class extends ve{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"no applicable key found in the JSON Web Key Set",t=arguments.length>1?arguments[1]:void 0;super(e,t),x(this,"code","ERR_JWKS_NO_MATCHING_KEY")}};x(Da,"code","ERR_JWKS_NO_MATCHING_KEY");var La=class extends ve{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"multiple matching keys found in the JSON Web Key Set",t=arguments.length>1?arguments[1]:void 0;super(e,t),x(this,Symbol.asyncIterator,Eu(function*(){})),x(this,"code","ERR_JWKS_MULTIPLE_MATCHING_KEYS")}};x(La,"code","ERR_JWKS_MULTIPLE_MATCHING_KEYS");var Na=class extends ve{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"request timed out",t=arguments.length>1?arguments[1]:void 0;super(e,t),x(this,"code","ERR_JWKS_TIMEOUT")}};x(Na,"code","ERR_JWKS_TIMEOUT");var xa=class extends ve{constructor(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"signature verification failed",t=arguments.length>1?arguments[1]:void 0;super(e,t),x(this,"code","ERR_JWS_SIGNATURE_VERIFICATION_FAILED")}};x(xa,"code","ERR_JWS_SIGNATURE_VERIFICATION_FAILED");var zp=1e3,ng=60*zp;var ig=Symbol.for("lk.serializer");var po=(s=>(s[s.DEBUG=0]="DEBUG",s[s.INFO=1]="INFO",s[s.ERROR=2]="ERROR",s[s.CRITICAL=3]="CRITICAL",s))(po||{}),gs=class{constructor(e=1){N(this,"currentLevel");N(this,"destination");N(this,"session_id");this.currentLevel=e,this.destination=null,this.session_id=null}formatMessage(e,t){let n=new Date().toISOString(),s=this.destination??"not_received",r=this.session_id??"not_received";return`SimliClient | ${n} | ${e} | ${s}/${r} | ${t}`}log(e,t,n,...s){if(ethis.logger.debug(n.data)),this.logger=t}async connect(e){this.wsConnection.onopen=e}disconnect(){this.wsConnection.close()}send(e){if(this.wsConnection.readyState!=WebSocket.OPEN)throw`Invalid State, WS Connection ${this.wsConnection.readyState.toString()}`;this.wsConnection.send(e)}sendOffer(e){this.send(JSON.stringify(e))}sendSignal(e){this.send(e)}sendAudioData(e){this.logger.getLevel()===0&&this.logger.debug("Sent Audio of length: "+(e.length/32e3).toString()),this.send(e)}sendAudioDataImmediate(e){this.logger.getLevel()===0&&this.logger.debug("Sent Audio of length for immediate playback: "+(e.length/32e3).toString());let s=new TextEncoder().encode("PLAY_IMMEDIATE"),r=new Uint8Array(s.length+e.length);r.set(s,0),r.set(e,s.length),this.send(r)}};function vs(i,e){let t=JSON.parse(e);i.destination=t.destination,i.session_id=t.session_id}async function bs(i,e){let t=e.data.toUpperCase().split(" ")[0];switch(t){case"START":break;case"ACK":{i.emit("ack");break}case"STOP":{i.disconnect(),i.emit("stop");break}case"CLOSING":case"RATE":case"ERROR":case"ERROR:":i.disconnect(),i.emit("error",e.data);case"SPEAK":{i.emit("speaking");break}case"SILENT":{i.emit("silent");break}default:t.includes("SDP")||t.includes("LIVEKIT")?i.emit("connection_info",e.data):t.includes("VIDEO_METADATA")?i.emit("video_info",e.data):t.includes("ENDFRAME")?(i.emit("stop"),i.disconnect()):t.includes("DESTINATION")?i.emit("destination",e.data):i.emit("unknown",e.data)}}var Jn=class{constructor(e,t,n,s,r,a){N(this,"videoElementAnchor");N(this,"audioElementAnchor");N(this,"signalingConnection");N(this,"session_token");N(this,"pc");N(this,"logger");N(this,"events",new Map);N(this,"websocketPromise");N(this,"websocketReject",null);this.logger=r,this.on("startup_error",a),this.session_token=t;let o=new URL(e+"/compose/webrtc/livekit");o.searchParams.set("session_token",t),this.signalingConnection=new fn(o,this.logger),this.on("destination",d=>vs(this.logger,d)),this.websocketPromise=new Promise((d,l)=>{this.websocketReject=l,this.signalingConnection.connect(()=>{d("success"),this.logger.debug("LK WebSocket Connected")})}),this.signalingConnection.wsConnection.onmessage=d=>{bs(this,d)},this.signalingConnection.wsConnection.onerror=d=>{this.emit("startup_error","Websocket Failed"),this.websocketReject&&(this.websocketReject("Websocket Failed"),this.websocketReject=null)};let c={adaptiveStream:!1,dynacast:!0};this.pc=new mn(c),this.on("connection_info",d=>this.join_lk_room(d)),this.videoElementAnchor=n,this.audioElementAnchor=s}on(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e)?.add(t),this.logger.debug("Registered Callback for Event: "+e)}off(e,t){if(!this.events.has(e))throw"Event Not Regsitered";this.events.get(e)?.delete(t)}emit(e,...t){this.logger.debug("Event: "+e),this.events.get(e)?.forEach(n=>{n(...t)})}async connect(){this.logger.info("Connecting"),this.setupConnectionStateHandler(),await this.websocketPromise}async disconnect(){this.logger.info("Disconnecting");try{this.signalingConnection.sendSignal("DONE")}catch{this.logger.error("FAILED TO SEND FINAL MESSAGE")}try{this.signalingConnection.disconnect()}catch{this.logger.error("SIGNALING ALREADY DISCONNECTED")}try{await this.pc.disconnect()}catch{this.logger.error("LOCAL PEER ALREADY CLOSED")}}async join_lk_room(e){let t=JSON.parse(e);this.logger.debug(t),t.livekit_url&&t.livekit_token?await this.pc.connect(t.livekit_url,t.livekit_token):(this.disconnect(),this.emit("error","Invalid Join Info, Contact Simli For Support"))}setupConnectionStateHandler(){this.pc.on(_.Disconnected,()=>{this.disconnect()}),this.pc.on(_.Connected,()=>{}),this.pc.on(_.TrackSubscribed,(e,t,n)=>{this.logger.debug("Track Received: "+e.kind),e.kind===E.Kind.Video?(e.attach(this.videoElementAnchor),this.videoElementAnchor.requestVideoFrameCallback(()=>{this.emit("start")})):e.kind===E.Kind.Audio&&e.attach(this.audioElementAnchor)})}};var Qn=class{constructor(e,t,n,s,r,a,o,c){N(this,"videoElementAnchor");N(this,"audioElementAnchor");N(this,"signalingConnection");N(this,"session_token");N(this,"pc");N(this,"events",new Map);N(this,"logger");N(this,"iceCandidateCount");N(this,"previousIceCandidateCount");N(this,"iceTimeout",null);N(this,"websocketPromise");N(this,"websocketReject",null);this.logger=o,this.on("startup_error",c),this.session_token=t;let d=new URL(e+"/compose/webrtc/p2p");d.searchParams.set("session_token",t),d.searchParams.set("enableSFU",String(n)),this.on("destination",u=>vs(this.logger,u)),this.signalingConnection=new fn(d,this.logger),this.websocketPromise=new Promise((u,h)=>{this.websocketReject=h,this.signalingConnection.connect(()=>{u("success"),this.logger.debug("P2P WebSocket Connected")})}),this.signalingConnection.wsConnection.onmessage=u=>{bs(this,u)},this.signalingConnection.wsConnection.onerror=u=>{this.emit("startup_error","Websocket Failed"),this.websocketReject&&(this.websocketReject("Websocket Failed"),this.websocketReject=null)},this.on("connection_info",u=>this.registerPeerInfo(u)),this.videoElementAnchor=r,this.audioElementAnchor=a,this.iceCandidateCount=0,this.previousIceCandidateCount=0;let l={sdpSemantics:"unified-plan",iceServers:s};this.pc=new window.RTCPeerConnection(l),this.pc.addTransceiver("audio",{direction:"recvonly"}),this.pc.addTransceiver("video",{direction:"recvonly"})}on(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e)?.add(t)}off(e,t){this.events.get(e)?.delete(t)}emit(e,...t){this.events.get(e)?.forEach(n=>{try{n(...t)}catch{this.logger.error("CALLBACK FAILED: "+n.name)}})}async connect(){let e=await this.pc.createOffer();await this.pc.setLocalDescription(e),await this.waitForIceGathering(),this.setupPeerConnectionListeners(),await this.websocketPromise,this.pc.localDescription&&this.signalingConnection.sendOffer(this.pc.localDescription)}async disconnect(){this.logger.info("Disconnecting");try{this.signalingConnection.sendSignal("DONE")}catch{this.logger.error("FAILED TO SEND FINAL MESSAGE")}try{this.signalingConnection.disconnect()}catch{this.logger.error("SIGNALING ALREADY DISCONNECTED")}try{this.pc.close()}catch{this.logger.error("LOCAL PEER ALREADY CLOSED")}}async registerPeerInfo(e){let t=JSON.parse(e);t.sdp&&t.type=="answer"?await this.pc.setRemoteDescription(new RTCSessionDescription(t)):(this.disconnect(),this.emit("error","Invalid Join Info, Contact Simli For Support"))}async waitForIceGathering(){if(this.iceCandidateCount=0,this.previousIceCandidateCount=0,this.pc.iceGatheringState!=="complete")return new Promise((e,t)=>{this.iceTimeout||(this.iceTimeout=setTimeout(()=>{t(new Error("ICE gathering timeout"))},1e4));let n=()=>{this.pc.iceGatheringState==="complete"||this.iceCandidateCount===this.previousIceCandidateCount?(this.iceTimeout&&clearTimeout(this.iceTimeout),e()):(this.previousIceCandidateCount=this.iceCandidateCount,setTimeout(n,150))};n()})}setupPeerConnectionListeners(){this.pc.addEventListener("track",e=>{e.track.kind==="video"?(this.videoElementAnchor.srcObject=e.streams[0],this.videoElementAnchor.requestVideoFrameCallback(()=>{this.emit("start")})):e.track.kind==="audio"&&this.audioElementAnchor&&(this.audioElementAnchor.srcObject=e.streams[0])}),this.pc.onicecandidate=e=>{e.candidate!==null&&(this.iceCandidateCount+=1)}}};var Jp=i=>{if(i<=0)throw"Invalid Buffer Size, Can't be negative";if(Math.floor(i)-i!=0)throw"Invalid Buffer Size, Can't be a float";return` + class AudioProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.buffer = new Int16Array(${i}); + this.bufferIndex = 0; + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const inputChannel = input[0]; + if (inputChannel) { + for (let i = 0; i < inputChannel.length; i++) { + this.buffer[this.bufferIndex] = Math.max(-32768, Math.min(32767, Math.round(inputChannel[i] * 32767))); + this.bufferIndex++; + + if (this.bufferIndex === this.buffer.length){ + this.port.postMessage({type: 'audioData', data: this.buffer.slice(0, this.bufferIndex)}); + this.bufferIndex = 0; + } + } + } + return true; + } + } + + registerProcessor('audio-processor', AudioProcessor); + `};async function Ng(i,e="https://api.simli.ai"){let t=`${e}/compose/token`,n=await fetch(t,{method:"POST",body:JSON.stringify(i.config),headers:{"Content-Type":"application/json","x-simli-api-key":i.apiKey}});if(!n.ok)throw await n.text();return await n.json()}async function xg(i,e="https://api.simli.ai"){try{let t=`${e}/compose/ice`,n=await fetch(t,{headers:{"Content-Type":"application/json","x-simli-api-key":i},method:"GET"});if(!n.ok)throw new Error(`SIMLI: HTTP error! status: ${n.status}`);let s=await n.json();if(!s||s.length===0)throw new Error("SIMLI: No ICE servers returned");return s}catch{return[{urls:["stun:stun.l.google.com:19302"]}]}}var Ql=class{constructor(e,t,n,s,r=0,a="p2p",o="websockets",c="wss://api.simli.ai",d=3e3){N(this,"session_token");N(this,"transport","livekit");N(this,"signaling","websockets");N(this,"videoElement");N(this,"audioElement");N(this,"audioBufferSize",3e3);N(this,"connection");N(this,"connectionTimeout");N(this,"connectionResolve");N(this,"connectionReject");N(this,"connectionPromise");N(this,"sourceNode",null);N(this,"audioWorklet",null);N(this,"MAX_RETRY_ATTEMPTS",10);N(this,"RETRY_DELAY",2e3);N(this,"CONNECTION_TIMEOUT_MS",15e3);N(this,"retryAttempt",0);N(this,"SimliWSURL","wss://api.simli.ai");N(this,"audioContext",new(window.AudioContext||window.webkitAudioContext)({sampleRate:16e3}));N(this,"logger");N(this,"iceServers");N(this,"persistent_events");N(this,"failReason",null);N(this,"shouldStop",!1);N(this,"ClearBuffer",()=>{this.connection.signalingConnection.sendSignal("SKIP")});if(d<=0)throw"Invalid Buffer Size, Can't be negative";if(Math.floor(d)-d!=0)throw"Invalid Buffer Size, Can't be a float";if(!(c.startsWith("ws://")||c.startsWith("wss://"))||c.endsWith("/"))throw"Invalid Simli WS URL";this.audioBufferSize=d,this.session_token=e,this.transport=a,this.signaling=o,this.SimliWSURL=c,this.videoElement=t,this.audioElement=n,this.iceServers=s,this.logger=new gs(r);let l,u;switch(this.connectionPromise=new Promise((h,m)=>{l=h,u=m}),this.connectionResolve=l,this.connectionReject=u,this.persistent_events=new Map,this.connectionTimeout=setTimeout(()=>this.connectionReject("CONNECTION TIMED OUT"),this.CONNECTION_TIMEOUT_MS),this.transport){case"livekit":this.connection=new Jn(this.SimliWSURL,this.session_token,t,n,this.logger,this.connectionReject);break;case"p2p":if(!s||s.length==0)throw"Ice Servers Required for P2P Mode";this.connection=new Qn(this.SimliWSURL,this.session_token,!0,s,t,n,this.logger,this.connectionReject);break;default:throw new Error("Not Implemented Yet")}this.connection.on("start",()=>{this.connectionResolve(),clearTimeout(this.connectionTimeout)}),this.connection.on("unknown",h=>this.logger.debug("UNKOWN MESSAGE FROM SERVER: "+h)),this.connection.on("error",h=>{this.failReason=h,this.connectionReject(h)})}on(e,t){this.persistent_events.has(e)||this.persistent_events.set(e,new Set),this.persistent_events.get(e)?.add(t),this.logger.debug("Registered Callback for Event: "+e),this.connection.on(e,t)}off(e,t){if(!this.persistent_events.has(e))throw"Event Not Regsitered";this.persistent_events.get(e)?.delete(t),this.connection.off(e,t)}resetConnections(e,t,n){this.failReason=null;let s,r;switch(this.connectionPromise=new Promise((a,o)=>{s=a,r=o}),this.connectionResolve=s,this.connectionReject=r,this.connectionTimeout=setTimeout(()=>this.connectionReject("Connection Timed Out"),this.CONNECTION_TIMEOUT_MS),this.transport){case"livekit":this.connection=new Jn(this.SimliWSURL,this.session_token,e,t,this.logger,this.connectionReject);break;case"p2p":if(!n||n.length==0)throw"Ice Servers Required for P2P Mode";this.connection=new Qn(this.SimliWSURL,this.session_token,!0,n,e,t,this.logger,this.connectionReject);break;default:throw new Error("Not Implemented Yet")}this.connection.on("start",()=>{this.connectionResolve(),clearTimeout(this.connectionTimeout)}),this.connection.on("error",a=>{this.retryAttempt=this.MAX_RETRY_ATTEMPTS,this.connectionReject(a)}),this.connection.on("unknown",a=>this.logger.debug("UNKOWN MESSAGE FROM SERVER: "+a)),this.persistent_events.forEach((a,o)=>{a.forEach(c=>{this.connection.on(o,c)})})}async start(){if(this.shouldStop)throw new Error("Disconnect Already Called, Can't reuse same SimliClient multiple times create a new SimliClient Object");try{await this.connection.connect(),await this.connectionPromise,this.retryAttempt=0}catch(e){if(this.failReason)throw e;if(this.retryAttempt>=this.MAX_RETRY_ATTEMPTS)throw new Error("Too Many Retry Attempts Failed to connect");if(this.shouldStop)throw this.shouldStop=!1,new Error("Called Disconnect Before A Connecction succeeded");this.logger.error("FAILED: "+e),await this.connection.disconnect(),await new Promise(t=>setTimeout(t,this.RETRY_DELAY)),this.retryAttempt+=1,this.retryAttempt>2&&(this.transport="livekit"),this.resetConnections(this.videoElement,this.audioElement,this.iceServers),await this.start()}}async stop(){this.shouldStop=!0,await this.connection.disconnect()}listenToMediastreamTrack(e){let t=this.audioContext.createMediaStreamSource(new MediaStream([e]));this.sourceNode=t,this.attachSourceToWorklet(this.audioContext,t)}listenToAudioElement(e){let t=this.audioContext.createMediaElementSource(e);this.attachSourceToWorklet(this.audioContext,t)}attachSourceToWorklet(e,t){e.audioWorklet.addModule(URL.createObjectURL(new Blob([Jp(this.audioBufferSize)],{type:"application/javascript"}))).then(()=>{if(this.audioWorklet=new AudioWorkletNode(e,"audio-processor"),this.audioWorklet===null)throw new Error("SIMLI: AudioWorklet not initialized");t.connect(this.audioWorklet),this.audioWorklet.port.onmessage=n=>{n.data.type==="audioData"&&this.connection.signalingConnection.sendAudioData(new Uint8Array(n.data.data.buffer))}})}sendAudioData(e){this.connection.signalingConnection.sendAudioData(e)}sendAudioDataImmediate(e){this.connection.signalingConnection.sendAudioDataImmediate(e)}};export{po as LogLevel,gs as Logger,Ql as SimliClient,xg as generateIceServers,Ng as generateSimliSessionToken}; diff --git a/src/agent/cardsFromTools.js b/src/agent/cardsFromTools.js new file mode 100644 index 0000000..38b0a45 --- /dev/null +++ b/src/agent/cardsFromTools.js @@ -0,0 +1,115 @@ +const { buildTicketQrCard } = require('../utils/ticketCards'); +const { buildSessionCards } = require('../utils/sessionCards'); +const { buildSpeakerCards, resolveSpeakerIdsFromModel } = require('../utils/speakerCards'); +const { buildExhibitorCards, resolveExhibitorIdsFromModel } = require('../utils/exhibitorCards'); +const { buildPlaceCards, resolvePlaceIds } = require('../utils/placeCards'); +const { buildAccommodationCards, resolveHotelIds } = require('../utils/accommodationCards'); +const { buildMapFileCard } = require('../utils/faqAnswer'); +const { buildRegistrationCtaCards } = require('../utils/registrationLinks'); + +function sessionIdsFrom(data) { + if (Array.isArray(data?.sessionIds) && data.sessionIds.length) { + return data.sessionIds.map(String); + } + return (Array.isArray(data?.sessions) ? data.sessions : []) + .map((s) => String(s?.id || '')) + .filter(Boolean) + .slice(0, 8); +} + +function faqKeysFrom(data) { + const keys = []; + const faqs = Array.isArray(data?.faqs) ? data.faqs : []; + for (const f of faqs) { + const key = String(f?.key || f?.category || '').trim(); + if (key) keys.push(key); + } + if (Array.isArray(data?.faqKeysUsed)) { + for (const k of data.faqKeysUsed) { + const key = String(k || '').trim(); + if (key) keys.push(key); + } + } + if (Array.isArray(data?.hits)) { + for (const h of data.hits) { + const key = String(h?.key || h?.category || '').trim(); + if (key) keys.push(key); + } + } + return keys; +} + +function cardsFromToolResults(toolResults) { + const cards = []; + const sessionIds = []; + const faqKeysUsed = []; + let forceOrganizerContact = false; + + for (const row of Array.isArray(toolResults) ? toolResults : []) { + const name = row?.name; + const data = row?.data && typeof row.data === 'object' ? row.data : {}; + if (data.status === 'unauthenticated') continue; + + faqKeysUsed.push(...faqKeysFrom(data)); + if (name === 'contact_organizer') forceOrganizerContact = true; + + if (name === 'get_my_tickets' || name === 'email_my_ticket') { + cards.push(...buildTicketQrCard(data)); + } + + if (name === 'get_agenda' || name === 'get_my_agenda' || name === 'save_session' || name === 'remove_saved_session') { + const ids = sessionIdsFrom(data); + sessionIds.push(...ids); + if (Array.isArray(data.cards) && data.cards.length) { + cards.push(...data.cards); + } else if (ids.length) { + cards.push(...buildSessionCards(ids, { sessions: data.sessions || [] }, { + includeResources: Boolean(data.includeResources), + })); + } + } + + if (name === 'get_speakers') { + const ids = resolveSpeakerIdsFromModel([], data); + cards.push(...buildSpeakerCards(ids, data)); + } + + if (name === 'get_exhibitors') { + const ids = resolveExhibitorIdsFromModel([], data); + cards.push(...buildExhibitorCards(ids, data)); + } + + if (name === 'get_venue') { + const mapCard = buildMapFileCard(data.mapMedia); + if (mapCard) cards.push(mapCard); + if (Array.isArray(data.places) && data.places.length) { + const ids = resolvePlaceIds([], data); + cards.push(...buildPlaceCards(ids, data)); + } + } + + if (name === 'get_local') { + if (Array.isArray(data.places) && data.places.length) { + const ids = resolvePlaceIds([], data); + cards.push(...buildPlaceCards(ids, data)); + } + if (Array.isArray(data.hotels) && data.hotels.length) { + const ids = resolveHotelIds([], data); + cards.push(...buildAccommodationCards(ids, data)); + } + } + + if (name === 'search_faqs' && data.registration?.registrationLinks) { + cards.push(...buildRegistrationCtaCards(data.registration.registrationLinks)); + } + } + + return { + cards, + sessionIds: [...new Set(sessionIds)].slice(0, 8), + faqKeysUsed: [...new Set(faqKeysUsed)], + forceOrganizerContact, + }; +} + +module.exports = { cardsFromToolResults }; diff --git a/src/agent/compact.js b/src/agent/compact.js new file mode 100644 index 0000000..dceb142 --- /dev/null +++ b/src/agent/compact.js @@ -0,0 +1,37 @@ +const MAX_STRING = 800; +const MAX_ARRAY = 25; + +function compactValue(value, depth = 0) { + if (value == null) return undefined; + if (typeof value === 'string') { + const s = value.trim(); + if (!s) return undefined; + return s.length > MAX_STRING ? `${s.slice(0, MAX_STRING)}…` : s; + } + if (typeof value === 'number' || typeof value === 'boolean') return value; + if (depth > 6) return undefined; + if (Array.isArray(value)) { + const next = value + .slice(0, MAX_ARRAY) + .map((item) => compactValue(item, depth + 1)) + .filter((item) => item !== undefined); + return next.length ? next : undefined; + } + if (typeof value === 'object') { + const out = {}; + for (const [key, val] of Object.entries(value)) { + if (key.startsWith('_')) continue; + const compact = compactValue(val, depth + 1); + if (compact !== undefined) out[key] = compact; + } + return Object.keys(out).length ? out : undefined; + } + return undefined; +} + +function compactForLlm(data) { + const compact = compactValue(data); + return compact && typeof compact === 'object' ? compact : { result: compact ?? null }; +} + +module.exports = { compactForLlm }; diff --git a/src/agent/eventBrief.js b/src/agent/eventBrief.js new file mode 100644 index 0000000..d8adec0 --- /dev/null +++ b/src/agent/eventBrief.js @@ -0,0 +1,180 @@ +const { faqAnswerText } = require('../utils/faqAnswer'); +const { getTimeZone, formatDateTimeLocal } = require('../utils/eventTime'); +const { loadEventRegistrationLinks } = require('../utils/registrationLinks'); +const Event = require('../models/Event'); +const Session = require('../models/Session'); + +const FAQ_CAP = 24; +const ANSWER_MAX = 600; +const SESSION_CAP = 50; +const DESC_MAX = 800; +const PUBLIC_EVENT_KEYS = [ + 'name', + 'location', + 'locationType', + 'description', + 'about', + 'summary', + 'shortDescription', + 'city', + 'venue', + 'eventType', +]; + +function pickEventDate(event, keys) { + if (!event || typeof event !== 'object') return null; + for (const key of keys) { + if (event[key]) return event[key]; + } + return null; +} + +function trimText(value, max = DESC_MAX) { + const s = String(value || '').trim(); + if (!s) return ''; + return s.length > max ? `${s.slice(0, max)}…` : s; +} + +function pickPublicEventFields(event) { + const out = {}; + if (!event || typeof event !== 'object') return out; + for (const key of PUBLIC_EVENT_KEYS) { + const v = trimText(event[key], key === 'name' || key === 'location' ? 200 : DESC_MAX); + if (v) out[key] = v; + } + return out; +} + +function faqSnapshot(bot) { + const rows = Array.isArray(bot?.faqs) ? bot.faqs : []; + const out = []; + for (const row of rows) { + const a = trimText(faqAnswerText(row), ANSWER_MAX); + if (!a) continue; + const q = String(row?.question || '').trim(); + out.push({ + q: q || String(row?.category || 'FAQ').trim(), + a, + category: String(row?.category || '').trim() || null, + }); + if (out.length >= FAQ_CAP) break; + } + return out; +} + +function speakerNames(doc) { + const rows = Array.isArray(doc?.speakers) ? doc.speakers : []; + return rows + .map((sp) => { + if (!sp || typeof sp !== 'object') return ''; + return [sp.firstName, sp.lastName].filter(Boolean).join(' ').trim(); + }) + .filter(Boolean) + .slice(0, 6); +} + +async function loadProgrammeSnapshot(eventId) { + if (!eventId) return []; + try { + const rows = await Session.find({ + event: eventId, + status: { $ne: 'cancelled' }, + }) + .populate({ path: 'speakers', select: 'firstName lastName' }) + .sort({ startTime: 1 }) + .limit(SESSION_CAP) + .lean(); + return rows + .map((doc) => { + const title = String(doc.title || '').trim(); + if (!title) return null; + const speakers = speakerNames(doc); + return { + title, + start: formatDateTimeLocal(doc.startTime) || null, + end: formatDateTimeLocal(doc.endTime) || null, + ...(speakers.length ? { speakers } : {}), + }; + }) + .filter(Boolean); + } catch (err) { + console.warn('[eventBrief] programme snapshot failed:', err?.message || err); + return []; + } +} + +async function hydrateEvent(event, eventId) { + const id = eventId || event?._id; + if (!id) return event || {}; + try { + const doc = await Event.findById(id).lean(); + if (!doc) return event || {}; + return { ...(event || {}), ...doc }; + } catch (err) { + console.warn('[eventBrief] event hydrate failed:', err?.message || err); + return event || {}; + } +} + +/** + * Public event facts for the system prompt (cached with the instructions). + * No tickets, QR, or personal agenda. + */ +async function buildEventBrief({ event, bot, eventId } = {}) { + const resolvedId = eventId || event?._id; + const fullEvent = await hydrateEvent(event, resolvedId); + const startRaw = pickEventDate(fullEvent, ['startDate', 'start', 'dateFrom', 'startsAt', 'eventStart']); + const endRaw = pickEventDate(fullEvent, ['endDate', 'end', 'dateTo', 'endsAt', 'eventEnd']); + const now = new Date(); + let registrationLinks = []; + try { + const resolved = await loadEventRegistrationLinks(resolvedId); + registrationLinks = (resolved.links || []).slice(0, 8).map((l) => ({ + label: l.label, + url: l.url, + })); + } catch (err) { + console.warn('[eventBrief] registration links failed:', err?.message || err); + } + + const programme = await loadProgrammeSnapshot(resolvedId); + + return { + ...pickPublicEventFields(fullEvent), + name: String(fullEvent?.name || event?.name || '').trim() || null, + location: String(fullEvent?.location || event?.location || '').trim() || null, + locationType: String(fullEvent?.locationType || event?.locationType || '').trim() || null, + start: formatDateTimeLocal(startRaw) || null, + end: formatDateTimeLocal(endRaw) || null, + timeZone: getTimeZone(), + nowLocal: formatDateTimeLocal(now), + registrationLinks, + faqs: faqSnapshot(bot), + programme, + }; +} + +function staticEventBrief(brief) { + if (!brief || typeof brief !== 'object') return {}; + const { nowLocal, ...rest } = brief; + return rest; +} + +function formatCachedEventBlock(brief) { + return `\n${JSON.stringify(staticEventBrief(brief), null, 2)}\n`; +} + +function formatEventBriefMessage(brief, userMessage) { + return [ + `NOW (event timezone): ${brief?.nowLocal || ''}`, + '', + 'CURRENT USER MESSAGE:', + String(userMessage || '').trim(), + ].join('\n'); +} + +module.exports = { + buildEventBrief, + formatEventBriefMessage, + formatCachedEventBlock, +}; diff --git a/src/agent/groundReply.js b/src/agent/groundReply.js new file mode 100644 index 0000000..7048dba --- /dev/null +++ b/src/agent/groundReply.js @@ -0,0 +1,75 @@ +const URL_RE = /\bhttps?:\/\/[^\s)\]>'"]+|\bwww\.[^\s)\]>'"]+/gi; + +function collectUrlsFromValue(value, into = []) { + if (value == null) return into; + if (typeof value === 'string') { + if (/^https?:\/\//i.test(value.trim()) || /^www\./i.test(value.trim())) { + into.push(value.trim()); + } + return into; + } + if (Array.isArray(value)) { + for (const item of value) collectUrlsFromValue(item, into); + return into; + } + if (typeof value === 'object') { + for (const [key, val] of Object.entries(value)) { + if (/(^url$|href|website|link)/i.test(key) && typeof val === 'string' && val.trim()) { + into.push(val.trim()); + } + collectUrlsFromValue(val, into); + } + } + return into; +} + +function hostOf(raw) { + const s = String(raw || '').trim(); + if (!s) return ''; + const withProto = /^https?:\/\//i.test(s) ? s : `https://${s.replace(/^\/\//, '')}`; + try { + return new URL(withProto).hostname.replace(/^www\./i, '').toLowerCase(); + } catch { + return ''; + } +} + +function allowedHostsFrom(sources) { + const hosts = new Set(); + for (const src of sources) { + for (const u of collectUrlsFromValue(src)) { + const host = hostOf(u); + if (host) hosts.add(host); + } + } + return hosts; +} + +function stripMetaPreamble(text) { + return String(text || '') + .replace( + /^(based on (the )?(event details|information|data|faqs?|details) (provided|above|given)[,:]?\s*)/i, + '', + ) + .replace(/\s*let me know if you need[\s\S]*$/i, '') + .replace(/\s*i('m| am) happy to (provide|help)[\s\S]*$/i, '') + .trim(); +} + +function stripUnknownUrls(text, allowedHosts) { + return String(text || '').replace(URL_RE, (match) => { + const host = hostOf(match.replace(/[.,;:]+$/, '')); + if (host && allowedHosts.has(host)) return match; + return ''; + }).replace(/[ \t]{2,}/g, ' ').replace(/ +\n/g, '\n').trim(); +} + +function groundReply(text, { eventBrief, toolResults } = {}) { + const allowedHosts = allowedHostsFrom([ + eventBrief, + ...(Array.isArray(toolResults) ? toolResults.map((r) => r?.data) : []), + ]); + return stripUnknownUrls(stripMetaPreamble(text), allowedHosts); +} + +module.exports = { groundReply, allowedHostsFrom }; diff --git a/src/agent/prompts.js b/src/agent/prompts.js new file mode 100644 index 0000000..3dafb64 --- /dev/null +++ b/src/agent/prompts.js @@ -0,0 +1,64 @@ +const { formatCachedEventBlock } = require('./eventBrief'); + +function buildAgentSystemPrompt({ channel, eventBrief } = {}) { + const spoken = + channel === 'voice' + ? ` +Spoken: +- Keep answers under 3 short sentences. +- No markdown, bullets, tables, or raw URLs. Spell out links only if essential. +` + : ''; + + const eventBlock = formatCachedEventBlock(eventBrief); + + return ` +You are the virtual concierge for this event. Your objective is to assist participants with logistics, the programme, and their registration. + +The system provides an EVENT block containing the name, venue, dates, timezone, registrationLinks, FAQ snapshot, and programme snapshot. The current time (NOW) is in the user message. + + +- **Immediate Answers:** Answer the question directly. No greetings, no self-introductions, no role statements (e.g., never say you are a concierge, Eventbuzz, a virtual assistant, or an AI). +- **Concise & Conversational:** Keep replies to 1–3 short sentences. Use bullet points only if absolutely necessary for readability. Do not end with filler like "Let me know if you need more help." +- **Prohibited Phrases:** NEVER use: "Based on the information provided", "Based on the event details", "According to the data", or "Dear attendee". +- **Contextual Referencing:** Do not repeat the event name unnecessarily. Use "nearby", "the venue", "here", or the host city. Name the event only if directly asked. + + + +Step 1: Evaluate the EVENT block. If the answer is present (e.g., dates, venue address, FAQ snapshot, or determining if the event is for doctors/students), answer directly. DO NOT call a tool to restate information already in the EVENT block. +Step 2: If the user wants to register, use EVENT.registrationLinks. If empty, state you lack a registration link and share the organizer contact from EVENT.faqs. Do not confuse registration with check-in. +Step 3: Only if the EVENT block cannot satisfy the request, invoke the appropriate tool. + + + +[Personalized Data] (Automatically bound to the user's session; NEVER ask for a user ID) +- get_my_tickets: For ticket tier, status, payment, check-in, or QR code. +- get_my_agenda: For saved/bookmarked sessions and schedule conflicts. +- save_session / remove_saved_session: To modify the user's personal agenda. +- email_my_ticket: To email the QR code to the registered address. + +[Event & Logistics] +- get_agenda: For the live programme (now, next, specific date/track, materials). +- get_speakers: For the lineup or a specific speaker's bio. +- get_exhibitors: For booth locations, categories, and demos. (Refuse meeting bookings). +- get_venue: For maps, halls, amenities, parking, travel, meals, or accessibility (if not in EVENT.faqs). +- get_local: For hotels and host-city food/day-plans (strictly limited to this event's city). +- get_tech_help: For Wi-Fi or streaming help (if not in EVENT.faqs). +- search_faqs: For niche policies not covered in the EVENT FAQ snapshot. +- contact_organizer: For human escalation, lost-and-found, or medical/safety. (Provide zero medical advice). + + + +1. **Zero Hallucination:** Use ONLY facts from the EVENT block or tool JSON. If a fact is missing, state that you do not have that information and suggest the on-site help desk. +2. **No Invention:** NEVER invent websites, URLs, domain names, click-paths, rooms, times, ticket numbers, emails, phone numbers, or Wi-Fi passwords. +3. **URL Policy:** Quote a URL only if it exactly matches a string in EVENT.registrationLinks or tool JSON. If there is no URL, state you do not have one. +4. **Unauthorized Actions:** Ticket transfers and upgrades cannot be done in chat. Refuse the request and offer get_my_tickets or contact_organizer. +5. **Authentication Failures:** If a personal tool returns an "unauthenticated" status, do not invent ticket data. +6. **Invisible Tools:** Tool JSON is for your internal context only. Answer as if you inherently knew the information. NEVER mention tools, JSON, the EVENT block, or how you retrieved the data. + +${spoken} + +${eventBlock}`.trim(); +} + +module.exports = { buildAgentSystemPrompt }; diff --git a/src/agent/runner.js b/src/agent/runner.js new file mode 100644 index 0000000..f4a7141 --- /dev/null +++ b/src/agent/runner.js @@ -0,0 +1,263 @@ +const { + createClient, + converse, + responseText, + toolUseBlocks, + assistantMessageFromResponse, + getRegion, + getAgentModelId, +} = require('../utils/bedrock'); +const { isVerified } = require('../services/identity.service'); +const { buildAgentSystemPrompt } = require('./prompts'); +const { conciergeTools, createToolExecutors } = require('./tools'); +const { compactForLlm } = require('./compact'); +const { cardsFromToolResults } = require('./cardsFromTools'); +const { buildEventBrief, formatEventBriefMessage } = require('./eventBrief'); +const { groundReply } = require('./groundReply'); +const { looksLikeHowToRegister, howToRegisterFromBot } = require('../utils/registerIntent'); +const { buildRegistrationCtaCards } = require('../utils/registrationLinks'); + +const HISTORY_TURNS = 8; +const FALLBACK_REPLY = 'Sorry, I could not respond just now. Please try again in a moment.'; +const FIRST_INFERENCE = { temperature: 0.2, topP: 0.8, maxTokens: 350 }; +const SYNTH_INFERENCE = { temperature: 0.2, topP: 0.8, maxTokens: 350 }; + +function sumUsage(a, b) { + return { + inputTokens: Number(a?.inputTokens || 0) + Number(b?.inputTokens || 0), + outputTokens: Number(a?.outputTokens || 0) + Number(b?.outputTokens || 0), + }; +} + +function usageOf(response) { + return { + inputTokens: Number(response?.usage?.inputTokens || 0), + outputTokens: Number(response?.usage?.outputTokens || 0), + }; +} + +function formatHistory(history) { + return (Array.isArray(history) ? history : []) + .filter((m) => m && (m.role === 'user' || m.role === 'assistant') && String(m.text || '').trim()) + .slice(-HISTORY_TURNS) + .map((m) => ({ + role: m.role === 'assistant' ? 'assistant' : 'user', + content: [{ text: String(m.text).trim() }], + })); +} + +function emptyResult(overrides = {}) { + return { + reply: FALLBACK_REPLY, + cards: [], + sessionIds: [], + faqKeysUsed: [], + toolNames: [], + unauthenticated: false, + forceOrganizerContact: false, + modelId: null, + usage: { inputTokens: 0, outputTokens: 0 }, + ...overrides, + }; +} + +function parseToolInput(input) { + if (input && typeof input === 'object' && !Array.isArray(input)) return input; + if (typeof input === 'string' && input.trim()) { + try { + const parsed = JSON.parse(input); + if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed; + } catch { + return {}; + } + } + return {}; +} + +async function executeToolUses(blocks, executors) { + const rows = await Promise.all( + blocks.map(async (block) => { + const { toolUseId, name, input } = block.toolUse; + const fn = executors[name]; + let data; + try { + if (!fn) { + data = { error: `Unknown tool: ${name}` }; + } else { + data = await fn(parseToolInput(input)); + } + } catch (err) { + console.warn('[agent][tool] failed', name, err?.message || err); + data = { error: err?.message || 'tool_failed' }; + } + let text = JSON.stringify(compactForLlm(data)); + if (text.length > 12000) text = `${text.slice(0, 12000)}…`; + return { + name, + data, + content: { + toolResult: { + toolUseId, + content: [{ text }], + }, + }, + }; + }), + ); + + return { + toolResults: rows.map((r) => ({ name: r.name, data: r.data })), + content: rows.map((r) => r.content), + }; +} + +/** + * One concierge turn: optional tool calls, then a natural-language reply. + */ +async function runConciergeTurn({ bot, event, session, message, history, channel = 'text' }) { + const eventName = event?.name || 'the event'; + const ctx = { + userId: isVerified(session) ? session.participant : null, + eventId: bot?.event || session?.eventId, + eventName, + bot, + event, + session, + message, + channel, + }; + + const modelId = getAgentModelId(); + const client = createClient(getRegion()); + const eventBrief = await buildEventBrief({ event, bot, eventId: ctx.eventId }); + const system = buildAgentSystemPrompt({ channel, eventBrief }); + + if (looksLikeHowToRegister(message)) { + const links = Array.isArray(eventBrief.registrationLinks) ? eventBrief.registrationLinks : []; + return emptyResult({ + reply: howToRegisterFromBot(bot, links), + cards: buildRegistrationCtaCards(links), + modelId, + toolNames: links.length ? ['search_faqs'] : [], + }); + } + + const messages = [ + ...formatHistory(history), + { + role: 'user', + content: [{ text: formatEventBriefMessage(eventBrief, message) }], + }, + ]; + + let usage = { inputTokens: 0, outputTokens: 0 }; + let first; + try { + first = await converse(client, modelId, { + system, + messages, + inferenceConfig: FIRST_INFERENCE, + toolConfig: { tools: conciergeTools }, + toolChoice: { auto: {} }, + logLabel: 'agent-first', + }); + } catch (err) { + console.warn('[agent][first] failed:', err?.message || err); + return emptyResult({ modelId }); + } + + usage = sumUsage(usage, usageOf(first)); + const firstText = String(responseText(first) || '').trim(); + const uses = toolUseBlocks(first); + const assistantMsg = assistantMessageFromResponse(first); + + if (!uses.length) { + const reply = groundReply(firstText, { eventBrief }) || FALLBACK_REPLY; + return emptyResult({ + reply, + modelId, + usage, + toolNames: [], + }); + } + + const executors = createToolExecutors(ctx); + const { toolResults, content: toolResultContent } = await executeToolUses(uses, executors); + const toolNames = toolResults.map((r) => r.name); + const unauthenticated = toolResults.some((r) => r.data?.status === 'unauthenticated'); + const extras = cardsFromToolResults(toolResults); + + if (unauthenticated) { + return { + reply: null, + cards: extras.cards, + sessionIds: extras.sessionIds, + faqKeysUsed: extras.faqKeysUsed, + toolNames, + unauthenticated: true, + forceOrganizerContact: extras.forceOrganizerContact, + modelId, + usage, + }; + } + + if (!assistantMsg) { + return emptyResult({ + reply: groundReply(firstText, { eventBrief, toolResults }) || FALLBACK_REPLY, + modelId, + usage, + toolNames, + cards: extras.cards, + sessionIds: extras.sessionIds, + faqKeysUsed: extras.faqKeysUsed, + forceOrganizerContact: extras.forceOrganizerContact, + }); + } + + const followup = [...messages, assistantMsg, { role: 'user', content: toolResultContent }]; + + let second; + try { + second = await converse(client, modelId, { + system, + messages: followup, + inferenceConfig: SYNTH_INFERENCE, + toolConfig: { tools: conciergeTools }, + toolChoice: { auto: {} }, + logLabel: 'agent-synth', + }); + } catch (err) { + console.warn('[agent][synth] failed:', err?.message || err); + return emptyResult({ + reply: groundReply(firstText, { eventBrief, toolResults }) || FALLBACK_REPLY, + modelId, + usage, + toolNames, + cards: extras.cards, + sessionIds: extras.sessionIds, + faqKeysUsed: extras.faqKeysUsed, + forceOrganizerContact: extras.forceOrganizerContact, + }); + } + + usage = sumUsage(usage, usageOf(second)); + const reply = + groundReply(String(responseText(second) || '').trim() || firstText, { + eventBrief, + toolResults, + }) || FALLBACK_REPLY; + + return { + reply, + cards: extras.cards, + sessionIds: extras.sessionIds, + faqKeysUsed: extras.faqKeysUsed, + toolNames, + unauthenticated: false, + forceOrganizerContact: extras.forceOrganizerContact, + modelId, + usage, + }; +} + +module.exports = { runConciergeTurn }; diff --git a/src/agent/tools/actions.tools.js b/src/agent/tools/actions.tools.js new file mode 100644 index 0000000..a7c1822 --- /dev/null +++ b/src/agent/tools/actions.tools.js @@ -0,0 +1,70 @@ +const { addBookmark, removeBookmark } = require('../../services/bookmark.service'); +const { fetchSystemData } = require('../../services/systemData.service'); + +function unauthenticated() { + return { status: 'unauthenticated' }; +} + +function reasonToIntent(reason) { + const r = String(reason || 'help').toLowerCase(); + if (r === 'emergency') return 'emergency_medical'; + if (r === 'lost_and_found') return 'lost_and_found'; + return 'human_agent_escalation'; +} + +function createActionExecutors(ctx) { + const fallbackSessionIds = Array.isArray(ctx.session?.lastSessionIds) + ? ctx.session.lastSessionIds + : []; + + return { + async save_session(args = {}) { + if (!ctx.userId) return unauthenticated(); + const result = await addBookmark({ + eventId: ctx.eventId, + participantId: ctx.userId, + entities: { session_name: args.query || null }, + fallbackSessionIds, + }); + return { + ok: result.ok, + already: result.already || false, + note: result.reply, + sessionIds: result.sessionIds || [], + cards: result.cards || [], + }; + }, + + async remove_saved_session(args = {}) { + if (!ctx.userId) return unauthenticated(); + const result = await removeBookmark({ + eventId: ctx.eventId, + participantId: ctx.userId, + entities: { session_name: args.query || null }, + fallbackSessionIds, + }); + return { + ok: result.ok, + note: result.reply, + sessionIds: result.sessionIds || [], + cards: result.cards || [], + }; + }, + + async contact_organizer(args = {}) { + const intent = reasonToIntent(args.reason); + return fetchSystemData({ + intent, + bot: ctx.bot, + event: ctx.event, + session: ctx.session, + message: ctx.message, + }); + }, + }; +} + +module.exports = { + createActionExecutors, + AUTH_ACTION_TOOLS: ['save_session', 'remove_saved_session'], +}; diff --git a/src/agent/tools/index.js b/src/agent/tools/index.js new file mode 100644 index 0000000..de322ca --- /dev/null +++ b/src/agent/tools/index.js @@ -0,0 +1,139 @@ +const { createPersonalExecutors, AUTH_PERSONAL_TOOLS } = require('./personal.tools'); +const { createProgrammeExecutors } = require('./programme.tools'); +const { createOnsiteExecutors } = require('./onsite.tools'); +const { createActionExecutors, AUTH_ACTION_TOOLS } = require('./actions.tools'); + +const AUTH_REQUIRED_TOOLS = new Set([...AUTH_PERSONAL_TOOLS, ...AUTH_ACTION_TOOLS]); + +function toolSpec(name, description, properties = {}, required = []) { + return { + toolSpec: { + name, + description, + inputSchema: { + json: { + type: 'object', + properties, + required, + }, + }, + }, + }; +} + +const conciergeTools = [ + toolSpec( + 'get_my_tickets', + "This attendee's ticket tier, registration status, payment, check-in, and QR. No arguments. Never invent another person's booking.", + ), + toolSpec( + 'get_my_agenda', + "This attendee's saved/bookmarked sessions and any time conflicts. No arguments.", + ), + toolSpec( + 'email_my_ticket', + 'Email the attendee their ticket QR using the registered email. No arguments.', + ), + toolSpec( + 'get_agenda', + 'Event programme: sessions happening now, next, on a date, by track, speaker, or session name. Include materials when they ask for slides.', + { + when: { + type: 'string', + description: 'now | next | date, or omit for a general lookup', + }, + date: { type: 'string', description: 'YYYY-MM-DD if they named a day' }, + track: { type: 'string', description: 'Track or stage name' }, + speaker: { type: 'string', description: 'Speaker name when asking when they speak' }, + topic: { type: 'string', description: 'Topic or keyword' }, + session_name: { type: 'string', description: 'Specific session title' }, + }, + ), + toolSpec( + 'get_speakers', + 'Speaker lineup or a named speaker bio / public socials / meet-and-greet.', + { + name: { type: 'string', description: 'Speaker name; omit to list the lineup' }, + }, + ), + toolSpec( + 'get_exhibitors', + 'Exhibitor booth, category list, or demo notes. Meeting booking is not available in chat.', + { + company: { type: 'string', description: 'Exhibitor / company name' }, + category: { type: 'string', description: 'Industry or category' }, + }, + ), + toolSpec( + 'get_venue', + 'Venue map, halls, amenities, parking/shuttle, travel from a city, meals policy, accessibility.', + { + query: { type: 'string', description: 'What they asked about the venue' }, + origin: { type: 'string', description: 'Origin city when asking how to reach the venue' }, + }, + ['query'], + ), + toolSpec( + 'get_local', + 'Hotels and nearby tourist places cached for this event city.', + { + kind: { type: 'string', description: 'hotels | places | auto' }, + query: { type: 'string', description: 'What they asked (e.g. temples, nearby stay)' }, + }, + ), + toolSpec( + 'get_tech_help', + 'Wi-Fi network/password from FAQs, streaming/AV tips, or app login help. Never invent credentials.', + { + query: { type: 'string', description: 'Wi-Fi, streaming, login, or app feature' }, + }, + ['query'], + ), + toolSpec( + 'search_faqs', + 'Event policy FAQs: dress code, refunds, how to register, lost-and-found desk, leftover logistics not covered by other tools.', + { + query: { type: 'string', description: 'The policy or logistics question' }, + }, + ['query'], + ), + toolSpec( + 'save_session', + 'Save a programme session to the attendee personal agenda.', + { + query: { type: 'string', description: 'Session title if they named one; omit to use the last shown sessions' }, + }, + ), + toolSpec( + 'remove_saved_session', + 'Remove a saved session from the attendee personal agenda.', + { + query: { type: 'string', description: 'Session title if they named one; omit to use the last shown sessions' }, + }, + ), + toolSpec( + 'contact_organizer', + 'Notify organizers / share public organizer contact. Use for talk-to-human, lost-and-found, or medical/safety emergencies. No medical advice.', + { + reason: { + type: 'string', + description: 'help | lost_and_found | emergency', + }, + }, + ), +]; + +function createToolExecutors(ctx) { + return { + ...createPersonalExecutors(ctx), + ...createProgrammeExecutors(ctx), + ...createOnsiteExecutors(ctx), + ...createActionExecutors(ctx), + }; +} + +module.exports = { + conciergeTools, + createToolExecutors, + AUTH_REQUIRED_TOOLS, +}; diff --git a/src/agent/tools/onsite.tools.js b/src/agent/tools/onsite.tools.js new file mode 100644 index 0000000..049e315 --- /dev/null +++ b/src/agent/tools/onsite.tools.js @@ -0,0 +1,113 @@ +const { fetchVenueData } = require('../../services/venueData.service'); +const { fetchLocalData } = require('../../services/localData.service'); +const { fetchTechData } = require('../../services/techData.service'); +const { fetchTicketData } = require('../../services/ticketData.service'); +const { searchFaqs, faqKeysUsed } = require('../../rag/retriever'); +const { looksLikeHowToRegister } = require('../../utils/registerIntent'); + +function norm(text) { + return String(text || '').toLowerCase().replace(/\s+/g, ' ').trim(); +} + +function venueIntentFromQuery(query, origin) { + const t = norm(query); + if (/\b(map|floor plan|layout)\b/.test(t)) return 'venue_map_request'; + if (/\b(restroom|washroom|toilet|water|charging|info desk)\b/.test(t)) { + return 'venue_nearest_amenity'; + } + if (/\baccessib|wheelchair\b/.test(t)) return 'venue_accessibility'; + if ( + origin + || /\b(travell?ing|traveling|coming|arriving)\s+from\b/.test(t) + || /\bhow (do|can|to) (i |we )?(get to|reach|travel to|come to)\b/.test(t) + || /\b(flights?|trains?)\s+(from|to)\b/.test(t) + ) { + return 'venue_travel_directions'; + } + if (/\b(parking|taxi|uber|ola|shuttle|metro near)\b/.test(t)) { + return 'venue_parking_transport'; + } + if (/\b(food|lunch|meals?|tea|coffee|dietary)\b/.test(t)) { + return 'venue_food_beverage'; + } + return 'venue_wayfinding'; +} + +function localIntentFromArgs(args = {}, userMessage = '') { + const kind = String(args.kind || 'auto').toLowerCase(); + if (kind === 'hotels') return 'accommodation'; + if (kind === 'places') return 'tourist_places'; + const t = norm(`${args.query || ''} ${userMessage}`); + if (/\b(hotel|stay|lodging|accommodation|check-?in)\b/.test(t)) return 'accommodation'; + return 'tourist_places'; +} + +function techIntentFromQuery(query) { + const t = norm(query); + if (/\bwifi|wi-fi|ssid|password\b/.test(t)) return 'wifi_credentials'; + if (/\bstream|video|audio|can'?t hear|black screen\b/.test(t)) return 'app_issue_streaming'; + if (/\blogin|otp|account|password reset\b/.test(t)) return 'app_issue_account'; + return 'app_feature_guidance'; +} + +function createOnsiteExecutors(ctx) { + return { + async get_venue(args = {}) { + const query = String(args.query || ctx.message || ''); + const origin = String(args.origin || '').trim() || null; + const intent = venueIntentFromQuery(query, origin); + return fetchVenueData({ + intent, + eventId: ctx.eventId, + bot: ctx.bot, + entities: { location: origin }, + message: query, + }); + }, + + async get_local(args = {}) { + const intent = localIntentFromArgs(args, ctx.message); + const query = String(args.query || ctx.message || ''); + return fetchLocalData({ + intent, + eventId: ctx.eventId, + message: query, + bot: ctx.bot, + }); + }, + + async get_tech_help(args = {}) { + const query = String(args.query || ctx.message || ''); + return fetchTechData({ + intent: techIntentFromQuery(query), + bot: ctx.bot, + }); + }, + + async search_faqs(args = {}) { + const query = String(args.query || ctx.message || '').trim(); + const hits = searchFaqs({ + bot: ctx.bot, + query, + alreadyTold: ctx.session?.alreadyTold, + limit: 4, + }); + const payload = { + hits, + faqKeysUsed: faqKeysUsed(hits), + }; + if (looksLikeHowToRegister(query) || looksLikeHowToRegister(ctx.message)) { + payload.registration = await fetchTicketData({ + intent: 'ticket_how_to_register', + eventId: ctx.eventId, + eventName: ctx.eventName, + bot: ctx.bot, + event: ctx.event, + }); + } + return payload; + }, + }; +} + +module.exports = { createOnsiteExecutors }; diff --git a/src/agent/tools/personal.tools.js b/src/agent/tools/personal.tools.js new file mode 100644 index 0000000..e9df310 --- /dev/null +++ b/src/agent/tools/personal.tools.js @@ -0,0 +1,107 @@ +const { fetchTicketData } = require('../../services/ticketData.service'); +const { fetchAgendaData } = require('../../services/agendaData.service'); +const { sendTicketPassEmail } = require('../../utils/mail'); + +const TICKET_EMAIL_COOLDOWN_MS = 60 * 1000; + +function unauthenticated() { + return { status: 'unauthenticated' }; +} + +function createPersonalExecutors(ctx) { + return { + async get_my_tickets() { + if (!ctx.userId) return unauthenticated(); + return fetchTicketData({ + intent: 'ticket_status_lookup', + eventId: ctx.eventId, + participantId: ctx.userId, + eventName: ctx.eventName, + bot: ctx.bot, + event: ctx.event, + }); + }, + + async get_my_agenda() { + if (!ctx.userId) return unauthenticated(); + const personal = await fetchAgendaData({ + intent: 'agenda_personal_schedule', + eventId: ctx.eventId, + participantId: ctx.userId, + }); + const conflicts = await fetchAgendaData({ + intent: 'agenda_conflict_check', + eventId: ctx.eventId, + participantId: ctx.userId, + }); + return { + ...personal, + conflicts: conflicts.conflicts || [], + conflictNote: conflicts.note || null, + }; + }, + + async email_my_ticket() { + if (!ctx.userId) return unauthenticated(); + const data = await fetchTicketData({ + intent: 'ticket_email_qr', + eventId: ctx.eventId, + participantId: ctx.userId, + eventName: ctx.eventName, + bot: ctx.bot, + event: ctx.event, + }); + const to = String(data?.attendee?.email || '').trim().toLowerCase(); + if (!to) { + return { + ...data, + emailSent: false, + emailError: 'missing_email', + note: 'No registered email on file for this attendee.', + }; + } + + const last = ctx.session?.lastTicketEmailAt + ? new Date(ctx.session.lastTicketEmailAt).getTime() + : 0; + if (last && Date.now() - last < TICKET_EMAIL_COOLDOWN_MS) { + return { + ...data, + emailSent: false, + emailTo: to, + emailError: 'rate_limited', + note: 'A ticket email was sent recently for this chat session.', + }; + } + + const payment = data.payment; + const paymentLabel = + payment?.status && payment?.amount != null + ? `${payment.status} (${payment.currency || 'INR'} ${payment.amount})` + : payment?.status || null; + + const result = await sendTicketPassEmail({ + to, + eventName: data.eventName, + attendeeName: data.attendee?.name, + ticketName: data.ticket?.name || data.receipt?.ticketName || null, + statusLabel: data.membership?.statusLabel || data.membership?.status || null, + paymentLabel, + qrImageUrl: data.qr?.imageUrl || null, + }); + + if (result.ok && ctx.session) { + ctx.session.lastTicketEmailAt = new Date(); + } + + return { + ...data, + emailSent: Boolean(result.ok), + emailTo: to, + emailError: result.ok ? null : result.error || 'send_failed', + }; + }, + }; +} + +module.exports = { createPersonalExecutors, AUTH_PERSONAL_TOOLS: ['get_my_tickets', 'get_my_agenda', 'email_my_ticket'] }; diff --git a/src/agent/tools/programme.tools.js b/src/agent/tools/programme.tools.js new file mode 100644 index 0000000..5a71a38 --- /dev/null +++ b/src/agent/tools/programme.tools.js @@ -0,0 +1,85 @@ +const { fetchAgendaData } = require('../../services/agendaData.service'); +const { fetchSpeakerData } = require('../../services/speakerData.service'); +const { fetchExhibitorData } = require('../../services/exhibitorData.service'); + +function norm(text) { + return String(text || '').toLowerCase().replace(/\s+/g, ' ').trim(); +} + +function agendaIntentFromArgs(args = {}, userMessage = '') { + const when = String(args.when || '').toLowerCase().trim(); + const t = norm(`${args.query || ''} ${userMessage}`); + if (when === 'now' || /\b(now|currently|happening right now|live now)\b/.test(t)) { + return 'agenda_current_now'; + } + if (when === 'next' || /\b(what'?s next|upcoming|coming up)\b/.test(t)) { + return 'agenda_upcoming_next'; + } + if (args.track || /\btrack|stream\b/.test(t)) return 'agenda_by_track'; + if (args.speaker) return 'agenda_by_speaker'; + if (/\b(slides?|materials?|handouts?|pdfs?|download)\b/.test(t)) { + return 'agenda_download_materials'; + } + return 'agenda_session_details'; +} + +function speakerIntentFromMessage(message = '') { + const t = norm(message); + if (/\b(meet|greeting|networking)\b/.test(t)) return 'speaker_meet_and_greet'; + if (/\b(linkedin|twitter|website|social)\b/.test(t)) return 'speaker_contact_info'; + return 'speaker_bio_lookup'; +} + +function exhibitorIntentFromArgs(args = {}, userMessage = '') { + const t = norm(`${args.query || ''} ${userMessage}`); + if (/\bbook|meeting|appointment\b/.test(t)) return 'exhibitor_book_meeting'; + if (/\bdemo\b/.test(t)) return 'exhibitor_demo_schedule'; + if (args.category && !args.company) return 'exhibitor_by_category'; + return 'exhibitor_location'; +} + +function createProgrammeExecutors(ctx) { + return { + async get_agenda(args = {}) { + const when = String(args.when || '').toLowerCase().trim(); + const intent = agendaIntentFromArgs(args, ctx.message); + const entities = { + speaker_name: args.speaker || null, + session_name: args.session_name || null, + topic: args.track || args.topic || null, + time_reference: + args.date + || (when && when !== 'now' && when !== 'next' && when !== 'date' ? args.when : null) + || null, + }; + return fetchAgendaData({ + intent, + eventId: ctx.eventId, + entities, + participantId: ctx.userId, + }); + }, + + async get_speakers(args = {}) { + return fetchSpeakerData({ + intent: speakerIntentFromMessage(ctx.message), + eventId: ctx.eventId, + entities: { speaker_name: args.name || null }, + }); + }, + + async get_exhibitors(args = {}) { + return fetchExhibitorData({ + intent: exhibitorIntentFromArgs(args, ctx.message), + eventId: ctx.eventId, + bot: ctx.bot, + entities: { + company_name: args.company || null, + topic: args.category || null, + }, + }); + }, + }; +} + +module.exports = { createProgrammeExecutors }; diff --git a/src/app.js b/src/app.js index 03a1861..68a9d21 100644 --- a/src/app.js +++ b/src/app.js @@ -102,7 +102,7 @@ const createServer = async () => { name: 'EventBuzz Concierge', version: '1.0.0', status: 'ok', - stage: 'step1', + stage: 'agent', }), }); @@ -153,6 +153,30 @@ const createServer = async () => { options: { auth: false }, handler: async (request, h) => sendFile(h, 'embed-demo.html', 'text/html'), }, + { + method: 'GET', + path: '/embed/avatar', + options: { auth: false }, + handler: async (request, h) => sendFile(h, 'embed-avatar.html', 'text/html'), + }, + { + method: 'GET', + path: '/embed/avatar-page.js', + options: { auth: false }, + handler: async (request, h) => sendFile(h, 'embed/avatar-page.js', 'application/javascript'), + }, + { + method: 'GET', + path: '/embed/simli-client.js', + options: { auth: false }, + handler: async (request, h) => sendFile(h, 'embed/simli-client.js', 'application/javascript'), + }, + { + method: 'GET', + path: '/embed/avatar-page.css', + options: { auth: false }, + handler: async (request, h) => sendFile(h, 'embed/avatar-page.css', 'text/css'), + }, ]); server.route(publicConciergeRoutes); diff --git a/src/controllers/publicConcierge.controller.js b/src/controllers/publicConcierge.controller.js index 620edec..21e3647 100644 --- a/src/controllers/publicConcierge.controller.js +++ b/src/controllers/publicConcierge.controller.js @@ -13,6 +13,7 @@ const { runPublicChatTurn, } = require('../utils/webChatTurn'); const { getLivekitConfig, createVisitorVoiceToken } = require('../utils/livekitConcierge'); +const { getSimliConfig, createSimliSessionToken } = require('../utils/simli'); const ipBuckets = new Map(); const WINDOW_MS = 60_000; @@ -37,7 +38,9 @@ async function loadBotOr404(botId) { } const bot = await ConciergeBot.findById(botId).lean(); if (!bot) return { err: error('NOT_FOUND', 'Chatbot not found') }; - if (!bot.enabled || bot.status !== 'ACTIVE') { + // Dashboard "Active" is `enabled`. `status: ARCHIVED` is a knowledge-purge + // marker for ended events and must not block an enabled widget. + if (!bot.enabled) { return { err: error('FORBIDDEN', 'Chatbot is not enabled') }; } const event = await Event.findById(bot.event) @@ -66,6 +69,16 @@ const config = async (request, h) => { quickReplies: DEFAULT_QUICK_REPLIES, maxMessageLength: MAX_MESSAGE_LENGTH, voiceEnabled: Boolean(bot.voiceEnabled) && getLivekitConfig().configured, + videoAvatarEnabled: + bot.videoAvatarEnabled !== false + && Boolean(bot.voiceEnabled) + && getLivekitConfig().configured + && getSimliConfig().configured, + avatarEnabled: + bot.videoAvatarEnabled !== false + && Boolean(bot.voiceEnabled) + && getLivekitConfig().configured + && getSimliConfig().configured, }), ); }; @@ -148,6 +161,36 @@ const voiceToken = async (request, h) => { } }; +const simliToken = async (request, h) => { + const ip = request.headers['x-forwarded-for'] || request.info.remoteAddress; + if (!takeIpToken(ip)) { + return h.response(error('RATE_LIMIT', 'Too many requests. Try again in a minute.')).code(429); + } + + const { botId } = request.params; + const { bot, err } = await loadBotOr404(botId); + if (err) return h.response(err).code(err.error?.code === 'NOT_FOUND' ? 404 : 400); + + if (!bot.voiceEnabled) { + return h.response(error('FORBIDDEN', 'Voice is not enabled for this chatbot')).code(403); + } + if (!getLivekitConfig().configured) { + return h.response(error('SERVICE_UNAVAILABLE', 'Voice is not configured')).code(503); + } + if (!getSimliConfig().configured) { + return h.response(error('SERVICE_UNAVAILABLE', 'Avatar is not configured')).code(503); + } + + try { + const sessionToken = await createSimliSessionToken(); + return h.response(success({ sessionToken })); + } catch (e) { + console.error('[publicConcierge] simli token failed:', e); + const status = Number(e.status) === 400 ? 400 : 502; + return h.response(error('INTERNAL_ERROR', e.message || 'Could not start avatar')).code(status); + } +}; + const feedback = async (request, h) => { const { botId } = request.params; const { bot, err } = await loadBotOr404(botId); @@ -253,6 +296,7 @@ module.exports = { config, chat, voiceToken, + simliToken, feedback, end, getSession, diff --git a/src/prompts/call2/agenda/agenda_by_speaker.js b/src/prompts/call2/agenda/agenda_by_speaker.js deleted file mode 100644 index c235546..0000000 --- a/src/prompts/call2/agenda/agenda_by_speaker.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - intent: 'agenda_by_speaker', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_by_speaker -Select sessions featuring the requested speaker from DATA.sessions. -message: natural spoken answer; session_ids: matching ids from DATA (empty if none). -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_by_track.js b/src/prompts/call2/agenda/agenda_by_track.js deleted file mode 100644 index 009eb72..0000000 --- a/src/prompts/call2/agenda/agenda_by_track.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'agenda_by_track', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_by_track -Select sessions for the requested track from DATA.sessions. -If DATA.tracks is listed without matches, ask which track in message and return session_ids []. -message: natural spoken answer; session_ids: matching ids from DATA. -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_conflict_check.js b/src/prompts/call2/agenda/agenda_conflict_check.js deleted file mode 100644 index 7e01712..0000000 --- a/src/prompts/call2/agenda/agenda_conflict_check.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'agenda_conflict_check', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_conflict_check -Use DATA.conflicts and DATA.sessions. -message: clearly say whether there are overlaps and summarize briefly. -session_ids: ids of sessions involved in conflicts (or all saved sessions if helpful); empty if none. -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_current_now.js b/src/prompts/call2/agenda/agenda_current_now.js deleted file mode 100644 index 549de98..0000000 --- a/src/prompts/call2/agenda/agenda_current_now.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'agenda_current_now', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_current_now -Select sessions that are live now from DATA.sessions. -message: brief note about what is live (or that nothing is live). -session_ids: ids of live sessions from DATA (empty if none). -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_download_materials.js b/src/prompts/call2/agenda/agenda_download_materials.js deleted file mode 100644 index 5049529..0000000 --- a/src/prompts/call2/agenda/agenda_download_materials.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'agenda_download_materials', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_download_materials -Select sessions that have materials/resources in DATA.sessions. -message: natural spoken answer about available downloads (or that none exist). -session_ids: ids of sessions with resources from DATA. -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_personal_schedule.js b/src/prompts/call2/agenda/agenda_personal_schedule.js deleted file mode 100644 index 4b2c364..0000000 --- a/src/prompts/call2/agenda/agenda_personal_schedule.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'agenda_personal_schedule', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_personal_schedule -DATA.sessions are the attendee's saved/bookmarked sessions only (not the full event agenda). -- If DATA.sessions is empty (or DATA.note says none are saved): message must say clearly that no sessions are saved yet, and session_ids MUST be []. -- If DATA.sessions has items: answer naturally; session_ids MUST include those session ids from DATA (prefer all, soonest first, max 8). -Never say "here are the sessions" when DATA.sessions is empty. -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_session_details.js b/src/prompts/call2/agenda/agenda_session_details.js deleted file mode 100644 index 35e5ce3..0000000 --- a/src/prompts/call2/agenda/agenda_session_details.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - intent: 'agenda_session_details', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_session_details -DATA.sessions are filtered only by day and/or speaker when those were requested. -Use ENTITIES.topic / ENTITIES.session_name / the user message to choose which sessions to return (titles + topics). -For specialty / "must not miss" / recommendation questions: pick the strongest fits for that topic (prioritize clinical relevance over tea breaks/sponsors unless asked). Prefer at most 8. -If none fit the topic/name, say so and return []. -If the user asked for the full programme ("what are the sessions", "show the agenda") and DATA.sessions is non-empty: return those ids (up to 8) and answer naturally. Do not say none found. Do not use a canned "Here is the programme." -If DATA.sessions is empty / DATA.note says none found, say so in a natural sentence (never "No matching sessions found.") and return []. -message: natural spoken answer (cards show details). Never repeat the event name. -session_ids: matching ids from DATA. -`.trim(), -}; diff --git a/src/prompts/call2/agenda/agenda_upcoming_next.js b/src/prompts/call2/agenda/agenda_upcoming_next.js deleted file mode 100644 index 31f3c58..0000000 --- a/src/prompts/call2/agenda/agenda_upcoming_next.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'agenda_upcoming_next', - usesSessionCards: true, - promptlet: ` -### INTENT TASK: agenda_upcoming_next -Select the next upcoming sessions from DATA.sessions (chronological). -message: natural spoken answer about what's coming up. -session_ids: those session ids from DATA. -`.trim(), -}; diff --git a/src/prompts/call2/agenda/index.js b/src/prompts/call2/agenda/index.js deleted file mode 100644 index d488324..0000000 --- a/src/prompts/call2/agenda/index.js +++ /dev/null @@ -1,43 +0,0 @@ -const agenda_current_now = require('./agenda_current_now'); -const agenda_upcoming_next = require('./agenda_upcoming_next'); -const agenda_by_track = require('./agenda_by_track'); -const agenda_by_speaker = require('./agenda_by_speaker'); -const agenda_session_details = require('./agenda_session_details'); -const agenda_personal_schedule = require('./agenda_personal_schedule'); -const agenda_conflict_check = require('./agenda_conflict_check'); -const agenda_download_materials = require('./agenda_download_materials'); -const { CALL2_MAIN_PROMPT } = require('../main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [ - agenda_current_now, - agenda_upcoming_next, - agenda_by_track, - agenda_by_speaker, - agenda_session_details, - agenda_personal_schedule, - agenda_conflict_check, - agenda_download_materials, -]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); -const SESSION_CARD_INTENTS = new Set( - MODULES.filter((m) => m.usesSessionCards).map((m) => m.intent), -); - -function buildCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_MAIN_PROMPT}\n\n${promptlet}`; -} - -function usesSessionCards(intent) { - return SESSION_CARD_INTENTS.has(String(intent || '')); -} - -module.exports = { - PROMPTLETS, - SESSION_CARD_INTENTS, - buildCall2SystemPrompt, - usesSessionCards, -}; diff --git a/src/prompts/call2/conversation.js b/src/prompts/call2/conversation.js deleted file mode 100644 index a981d0b..0000000 --- a/src/prompts/call2/conversation.js +++ /dev/null @@ -1,17 +0,0 @@ -const CALL2_CONVERSATION_PROMPT = ` -### CONVERSATION (always — first message or follow-up) -You are talking with one attendee in an ongoing chat. Sound like a helpful person at the event desk, not a search engine or FAQ dump. - -1. Answer what they just said. Acknowledge the ask (or the last turn) in natural language, then give the useful facts from DATA. -2. First turn: speak like a person ("A few good options nearby are…" not "Here are some forts." / "Here is the programme."). -3. "message" may be 2–5 short sentences when that helps (compare two options, react to "I've heard of X", give a reason). Do not pad. Do not list every card field in prose — cards still show details. -4. Never reuse the same sentence as the previous Assistant line in HISTORY. Vary the opener. -5. If HISTORY exists: this is the same conversation. Do not reprint the last list or recap unless they asked to see it again. If they name something from HISTORY or DATA, talk about that item. If it is not in DATA, say so honestly. -6. If there is no HISTORY: still speak like a person. -7. DATA.faqs are background for THIS ask only — use a FAQ when it actually answers the current utterance. Do not recap topics listed in DATA.alreadyTold unless they ask again. -8. Never invent URLs, tickets, Wi-Fi passwords, or organizer contacts. Never invent places or sessions unless the adjacent-local exception in the domain prompt allows world knowledge for this city. -9. Do not add organizer email/phone unless they asked who to contact. -10. Never repeat the event name. Never say "I can only assist with [event]". -`.trim(); - -module.exports = { CALL2_CONVERSATION_PROMPT }; diff --git a/src/prompts/call2/exhibitor/exhibitor_book_meeting.js b/src/prompts/call2/exhibitor/exhibitor_book_meeting.js deleted file mode 100644 index a83fc76..0000000 --- a/src/prompts/call2/exhibitor/exhibitor_book_meeting.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - intent: 'exhibitor_book_meeting', - usesExhibitorCards: true, - promptlet: ` -### INTENT TASK: exhibitor_book_meeting -Always refuse booking in chat. Suggest visiting the booth or asking exhibitor staff / help desk. -If DATA.exhibitors has a company, you may share booth/hall so they can find them. Never confirm a meeting or invent a calendar slot. -`.trim(), -}; diff --git a/src/prompts/call2/exhibitor/exhibitor_by_category.js b/src/prompts/call2/exhibitor/exhibitor_by_category.js deleted file mode 100644 index ccd8ef8..0000000 --- a/src/prompts/call2/exhibitor/exhibitor_by_category.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'exhibitor_by_category', - usesExhibitorCards: true, - promptlet: ` -### INTENT TASK: exhibitor_by_category -List matching exhibitors from DATA.exhibitors for DATA.categoryQuery (name, category, booth/hall). -Set exhibitor_ids for the matches (up to 8). -If needsClarification / empty: follow DATA.note — ask for a category or say none matched. Never invent companies. -`.trim(), -}; diff --git a/src/prompts/call2/exhibitor/exhibitor_demo_schedule.js b/src/prompts/call2/exhibitor/exhibitor_demo_schedule.js deleted file mode 100644 index 6bac9ee..0000000 --- a/src/prompts/call2/exhibitor/exhibitor_demo_schedule.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'exhibitor_demo_schedule', - usesExhibitorCards: true, - promptlet: ` -### INTENT TASK: exhibitor_demo_schedule -If DATA.faqs is non-empty: answer demo timing only from those FAQ Q&A pairs. You may also mention booth/hall from DATA.exhibitors. -If DATA.faqs is empty: say no demo schedule is listed; suggest the booth or help desk. Never invent times. -If needsClarification: ask which company; exhibitor_ids []. -`.trim(), -}; diff --git a/src/prompts/call2/exhibitor/exhibitor_location.js b/src/prompts/call2/exhibitor/exhibitor_location.js deleted file mode 100644 index 6572908..0000000 --- a/src/prompts/call2/exhibitor/exhibitor_location.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'exhibitor_location', - usesExhibitorCards: true, - promptlet: ` -### INTENT TASK: exhibitor_location -If DATA.isExhibitorList: briefly list exhibitors from DATA.exhibitors (name + booth/hall when present). Set exhibitor_ids (up to 8). Invite asking about a company for more detail. -If a single exhibitor: share boothNumber, hall, shortDescription/products/website from DATA. -If needsClarification: list only DATA.candidates; exhibitor_ids []. -If booth/hall missing: follow DATA.note — do not invent a location. -`.trim(), -}; diff --git a/src/prompts/call2/exhibitor/index.js b/src/prompts/call2/exhibitor/index.js deleted file mode 100644 index a89a2bf..0000000 --- a/src/prompts/call2/exhibitor/index.js +++ /dev/null @@ -1,34 +0,0 @@ -const exhibitor_location = require('./exhibitor_location'); -const exhibitor_by_category = require('./exhibitor_by_category'); -const exhibitor_demo_schedule = require('./exhibitor_demo_schedule'); -const exhibitor_book_meeting = require('./exhibitor_book_meeting'); -const { CALL2_EXHIBITOR_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [ - exhibitor_location, - exhibitor_by_category, - exhibitor_demo_schedule, - exhibitor_book_meeting, -]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); -const EXHIBITOR_CARD_INTENTS = new Set( - MODULES.filter((m) => m.usesExhibitorCards).map((m) => m.intent), -); - -function buildExhibitorCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_EXHIBITOR_MAIN_PROMPT}\n\n${promptlet}`; -} - -function usesExhibitorCards(intent) { - return EXHIBITOR_CARD_INTENTS.has(String(intent || '')); -} - -module.exports = { - PROMPTLETS, - buildExhibitorCall2SystemPrompt, - usesExhibitorCards, -}; diff --git a/src/prompts/call2/exhibitor/main.js b/src/prompts/call2/exhibitor/main.js deleted file mode 100644 index 2ee50ca..0000000 --- a/src/prompts/call2/exhibitor/main.js +++ /dev/null @@ -1,19 +0,0 @@ -const CALL2_EXHIBITOR_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (exhibitor / booth questions). - -### GUARDRAILS: -1. Use only DATA. Never invent booth numbers, halls, demo times, meeting slots, websites, or exhibitor names. -2. NEVER invent a candidates list. Only mention names in DATA.exhibitors or DATA.candidates. -3. NEVER output email addresses or phone numbers. -4. If DATA.needsClarification and DATA.candidates is non-empty, ask the user to pick a company name from those candidates only. -5. If DATA.refuseBooking is true, refuse booking politely — never confirm a meeting. -6. exhibitor_ids MUST be taken only from DATA.exhibitors[].id. -7. Prefer at most 8 exhibitor_ids. Answer naturally in "message"; cards show details. -8. Output ONLY the JSON keys message, exhibitor_ids — no other keys. - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. Include commas between properties. -- Example: {"message":"Short answer.","exhibitor_ids":["..."]} -`.trim(); - -module.exports = { CALL2_EXHIBITOR_MAIN_PROMPT }; diff --git a/src/prompts/call2/local/accommodation.js b/src/prompts/call2/local/accommodation.js deleted file mode 100644 index bae23bd..0000000 --- a/src/prompts/call2/local/accommodation.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - intent: 'accommodation', - usesPlaceCards: false, - usesHotelCards: true, - promptlet: ` -### INTENT TASK: accommodation -If DATA.faqs is present and answers this ask: use those FAQ lines. Do not invent a different policy. Do not recap DATA.alreadyTold. -If they ask about hotel check-in: say what the FAQ allows. Never say "refer to the agenda". Do not add organizer email/phone unless they asked who to contact. -If DATA.hostManagedAccommodation is true: say the organizer arranges accommodation; contact the event team. hotel_ids [] / place_ids []. -Else suggest stays from DATA.hotels only; set hotel_ids (up to 6). Remind them to verify rates themselves. -If hotels empty and not host-managed: follow DATA.note — do not invent hotels. -`.trim(), -}; diff --git a/src/prompts/call2/local/index.js b/src/prompts/call2/local/index.js deleted file mode 100644 index 55e58e4..0000000 --- a/src/prompts/call2/local/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const tourist_places = require('./tourist_places'); -const accommodation = require('./accommodation'); -const { CALL2_LOCAL_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [tourist_places, accommodation]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); -const PLACE_CARD_INTENTS = new Set(MODULES.filter((m) => m.usesPlaceCards).map((m) => m.intent)); -const HOTEL_CARD_INTENTS = new Set(MODULES.filter((m) => m.usesHotelCards).map((m) => m.intent)); - -function buildLocalCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_LOCAL_MAIN_PROMPT}\n\n${promptlet}`; -} - -function usesLocalPlaceCards(intent) { - return PLACE_CARD_INTENTS.has(String(intent || '')); -} - -function usesLocalHotelCards(intent) { - return HOTEL_CARD_INTENTS.has(String(intent || '')); -} - -module.exports = { - PROMPTLETS, - buildLocalCall2SystemPrompt, - usesLocalPlaceCards, - usesLocalHotelCards, -}; diff --git a/src/prompts/call2/local/main.js b/src/prompts/call2/local/main.js deleted file mode 100644 index a65312b..0000000 --- a/src/prompts/call2/local/main.js +++ /dev/null @@ -1,18 +0,0 @@ -const CALL2_LOCAL_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (nearby places and stays). - -### GUARDRAILS: -1. Use only DATA. Never invent attractions, hotels, addresses, ratings, or prices. -2. Use DATA.faqs only when they answer this utterance (e.g. host-managed stay). Do not recap DATA.alreadyTold. If DATA.hostManagedAccommodation is true, do not suggest booking hotels as if listed — say the organizer arranges stay. Never say "refer to the agenda" for hotel check-in. Do not add organizer email/phone unless they asked who to contact. -3. place_ids MUST come only from DATA.places[].id. hotel_ids only from DATA.hotels[].id. -4. Never substitute a different place category (temples ≠ churches; forts ≠ churches/beaches). -5. Prefer at most 6 ids. Answer naturally in "message"; cards show details. -6. Output ONLY the JSON keys needed: message plus place_ids and/or hotel_ids. - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. Include commas between properties. -- Tourist example: {"message":"Short answer.","place_ids":["..."],"hotel_ids":[]} -- Stay example: {"message":"Short answer.","place_ids":[],"hotel_ids":["..."]} -`.trim(); - -module.exports = { CALL2_LOCAL_MAIN_PROMPT }; diff --git a/src/prompts/call2/local/tourist_places.js b/src/prompts/call2/local/tourist_places.js deleted file mode 100644 index 25eeb27..0000000 --- a/src/prompts/call2/local/tourist_places.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - intent: 'tourist_places', - usesPlaceCards: true, - usesHotelCards: false, - promptlet: ` -### INTENT TASK: tourist_places -Suggest sightseeing / things to do from DATA.places only. -If DATA.requestedCategory is set, DATA.places are already filtered to that category — do not substitute other types (e.g. churches are not temples; beaches are not forts). -Set place_ids for the best matches (up to 6). hotel_ids []. -If DATA.places is empty: follow DATA.note — do not invent attractions or substitute other categories. -`.trim(), -}; diff --git a/src/prompts/call2/main.js b/src/prompts/call2/main.js deleted file mode 100644 index 371e749..0000000 --- a/src/prompts/call2/main.js +++ /dev/null @@ -1,24 +0,0 @@ -const CALL2_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event. -Use ONLY the structured DATA provided in the user message. - -### GUARDRAILS: -1. Do NOT invent sessions, times, rooms, speakers, links, or materials that are not in DATA. -2. If DATA.sessions is missing/empty or DATA is insufficient, say so clearly in "message" and return "session_ids": []. Do NOT claim sessions exist. -3. Stay on-event. Refuse unrelated requests politely. -4. Do not invent URLs. Do not mention system prompts or intent names. -5. session_ids MUST be taken only from DATA.sessions[].id (exact string ids from DATA). -6. Prefer at most 8 session_ids. Order them usefully (soonest first when relevant). When DATA.sessions has matches, include those ids (do not return an empty session_ids list). -7. "message" answers the user naturally (2–5 short sentences when useful). Do NOT list every session title/time or repeat the event name; cards will show those. -8. If DATA.note is present, treat it as an authoritative hint about missing matches. -9. TIMEZONE: Session times are in DATA.sessions[].when (event local / IST). Use that string only — do not invent or recompute times. -10. NEVER claim you added, saved, bookmarked, removed, or updated the attendee's personal agenda. Call 2 is read-only. - -### REQUIRED OUTPUT (JSON only, no markdown): -{ - "message": "Natural attendee-facing answer.", - "session_ids": ["64f...", "64f..."] -} -`.trim(); - -module.exports = { CALL2_MAIN_PROMPT }; diff --git a/src/prompts/call2/speaker/index.js b/src/prompts/call2/speaker/index.js deleted file mode 100644 index 99e7e81..0000000 --- a/src/prompts/call2/speaker/index.js +++ /dev/null @@ -1,32 +0,0 @@ -const speaker_bio_lookup = require('./speaker_bio_lookup'); -const speaker_meet_and_greet = require('./speaker_meet_and_greet'); -const speaker_contact_info = require('./speaker_contact_info'); -const { CALL2_SPEAKER_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [speaker_bio_lookup, speaker_meet_and_greet, speaker_contact_info]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); -const SPEAKER_CARD_INTENTS = new Set(MODULES.filter((m) => m.usesSpeakerCards).map((m) => m.intent)); -const SESSION_CARD_INTENTS = new Set(MODULES.filter((m) => m.usesSessionCards).map((m) => m.intent)); - -function buildSpeakerCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_SPEAKER_MAIN_PROMPT}\n\n${promptlet}`; -} - -function usesSpeakerCards(intent) { - return SPEAKER_CARD_INTENTS.has(String(intent || '')); -} - -function usesSpeakerSessionCards(intent) { - return SESSION_CARD_INTENTS.has(String(intent || '')); -} - -module.exports = { - PROMPTLETS, - buildSpeakerCall2SystemPrompt, - usesSpeakerCards, - usesSpeakerSessionCards, -}; diff --git a/src/prompts/call2/speaker/main.js b/src/prompts/call2/speaker/main.js deleted file mode 100644 index bd870fa..0000000 --- a/src/prompts/call2/speaker/main.js +++ /dev/null @@ -1,19 +0,0 @@ -const CALL2_SPEAKER_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (speaker questions). - -### GUARDRAILS: -1. Use only DATA. Never invent bios, contact details, meet-and-greet times, social URLs, or speaker names. -2. NEVER invent a candidates list. Only mention names that appear in DATA.speakers or DATA.candidates. If both are empty, say you need a name — do not guess. -3. NEVER output email addresses or phone numbers, even if you think you know them. -4. If DATA.needsClarification and DATA.candidates is non-empty, ask the user to pick a full name from those candidates only. -5. If a profile is private (DATA.speakers[].isPublic false / profileVisibility private), say the profile is not publicly listed. -6. speaker_ids MUST be taken only from DATA.speakers[].id. session_ids only from DATA.sessions[].id (or DATA.meetSessions for meet-and-greet). -7. Prefer at most 6 session_ids. Answer naturally in "message"; cards show details. -8. Output ONLY the JSON keys message, speaker_ids, session_ids — no other keys. - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. Include commas between properties. -- Example: {"message":"Short answer.","speaker_ids":["..."],"session_ids":[]} -`.trim(); - -module.exports = { CALL2_SPEAKER_MAIN_PROMPT }; diff --git a/src/prompts/call2/speaker/speaker_bio_lookup.js b/src/prompts/call2/speaker/speaker_bio_lookup.js deleted file mode 100644 index 99b12f4..0000000 --- a/src/prompts/call2/speaker/speaker_bio_lookup.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - intent: 'speaker_bio_lookup', - usesSpeakerCards: true, - usesSessionCards: true, - promptlet: ` -### INTENT TASK: speaker_bio_lookup -If DATA.isSpeakerList is true: briefly introduce the programme speakers from DATA.speakers (names + title/org). Set speaker_ids to those ids (up to 8). session_ids []. Invite asking about one person for a fuller bio. Never add names not in DATA. -If a single speaker in DATA.speakers: summarize name, role, title, organization, bio, expertise. -Include their sessions when DATA.sessions is present — set session_ids accordingly. -If needsClarification: list only DATA.candidates names and ask which person; speaker_ids/session_ids []. -If private / no bio: follow DATA.note. Set speaker_ids when public speaker(s) are present. -`.trim(), -}; diff --git a/src/prompts/call2/speaker/speaker_contact_info.js b/src/prompts/call2/speaker/speaker_contact_info.js deleted file mode 100644 index 51f2da6..0000000 --- a/src/prompts/call2/speaker/speaker_contact_info.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - intent: 'speaker_contact_info', - usesSpeakerCards: true, - usesSessionCards: false, - promptlet: ` -### INTENT TASK: speaker_contact_info -Share only public social links from DATA.speakers[].socialLinks (linkedin, twitter, website). -Never invent links. Never share email or phone. -If private or no socials: follow DATA.note and suggest organizers / help desk. -speaker_ids: the matched speaker when useful. session_ids: []. -`.trim(), -}; diff --git a/src/prompts/call2/speaker/speaker_meet_and_greet.js b/src/prompts/call2/speaker/speaker_meet_and_greet.js deleted file mode 100644 index f578ad5..0000000 --- a/src/prompts/call2/speaker/speaker_meet_and_greet.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'speaker_meet_and_greet', - usesSpeakerCards: true, - usesSessionCards: true, - promptlet: ` -### INTENT TASK: speaker_meet_and_greet -If DATA.meetSessions / DATA.sessions lists meet-and-greet or networking sessions, describe them and set session_ids. -If DATA.note says none are listed: say so honestly, optionally mention their regular sessions from DATA.sessions, suggest help desk — do not invent a meet-and-greet. -speaker_ids: matched speaker when present. -`.trim(), -}; diff --git a/src/prompts/call2/system/chitchat_greeting.js b/src/prompts/call2/system/chitchat_greeting.js deleted file mode 100644 index 34f5ca7..0000000 --- a/src/prompts/call2/system/chitchat_greeting.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - intent: 'chitchat_greeting', - promptlet: ` -### INTENT TASK: chitchat_greeting -A warm, natural hello — vary the wording. Do not list every topic. Suggestion chips are already in the UI. -Do not reuse the same canned line every time. -`.trim(), -}; diff --git a/src/prompts/call2/system/emergency_medical.js b/src/prompts/call2/system/emergency_medical.js deleted file mode 100644 index 0eee614..0000000 --- a/src/prompts/call2/system/emergency_medical.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'emergency_medical', - promptlet: ` -### INTENT TASK: emergency_medical -FIRST sentence must tell them to contact venue security or medical staff immediately. -Then briefly acknowledge. If DATA.emailSent is true, say organizers were notified; otherwise do not claim that. -If DATA.contact / DATA.faqs has Email or Phone: share those exact contact details. -No diagnosis or treatment advice. Follow DATA.note. -`.trim(), -}; diff --git a/src/prompts/call2/system/event_adjacent.js b/src/prompts/call2/system/event_adjacent.js deleted file mode 100644 index 39a71a5..0000000 --- a/src/prompts/call2/system/event_adjacent.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - intent: 'event_adjacent', - promptlet: ` -### INTENT TASK: event_adjacent -The attendee is asking about the world around THIS event city (DATA.location / DATA.city) and DATA.eventDates — food dishes, a 1-day plan after the event, local tips. - -You MAY use general world knowledge for that city and window only. If they ask about a different city, refuse in one short sentence. - -Never invent event programme, session times, tickets, registration URLs, Wi‑Fi passwords, or organizer contacts. Do not recap meal-provided / FAQ policy unless it is in DATA.faqs for this turn. - -If DATA.dish is set, answer that dish (where to try it, what it is) — do not pivot to whether the venue serves lunch. - -Remind them to verify opening hours and maps. Keep it decent. No attendee PII, no medical advice beyond first aid / go to on-site help. - -If DATA.places exist, you may set place_ids from DATA.places[].id (up to 6) when they help; otherwise []. -Prefer not repeating a long event marketing title. Use the city name. -`.trim(), -}; diff --git a/src/prompts/call2/system/human_agent_escalation.js b/src/prompts/call2/system/human_agent_escalation.js deleted file mode 100644 index fe4fb04..0000000 --- a/src/prompts/call2/system/human_agent_escalation.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'human_agent_escalation', - promptlet: ` -### INTENT TASK: human_agent_escalation -Confirm you received their request for a human organizer. -If DATA.contact / DATA.faqs has Email or Phone: share those exact contact details clearly (required). -If DATA.emailSent is true, also say organizers were notified; otherwise do not claim email was sent. -You may still mention the on-site help desk / info desk as an additional option. -Offer to keep helping with agenda, ticket, or venue meanwhile. -`.trim(), -}; diff --git a/src/prompts/call2/system/index.js b/src/prompts/call2/system/index.js deleted file mode 100644 index 848f330..0000000 --- a/src/prompts/call2/system/index.js +++ /dev/null @@ -1,30 +0,0 @@ -const emergency_medical = require('./emergency_medical'); -const lost_and_found = require('./lost_and_found'); -const human_agent_escalation = require('./human_agent_escalation'); -const chitchat_greeting = require('./chitchat_greeting'); -const out_of_scope = require('./out_of_scope'); -const event_adjacent = require('./event_adjacent'); -const { CALL2_SYSTEM_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [ - emergency_medical, - lost_and_found, - human_agent_escalation, - chitchat_greeting, - out_of_scope, - event_adjacent, -]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); - -function buildSystemCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_SYSTEM_MAIN_PROMPT}\n\n${promptlet}`; -} - -module.exports = { - PROMPTLETS, - buildSystemCall2SystemPrompt, -}; diff --git a/src/prompts/call2/system/lost_and_found.js b/src/prompts/call2/system/lost_and_found.js deleted file mode 100644 index 6495549..0000000 --- a/src/prompts/call2/system/lost_and_found.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - intent: 'lost_and_found', - promptlet: ` -### INTENT TASK: lost_and_found -Acknowledge a lost-and-found request. Ask for a short item description if not already clear. -Suggest the help desk / registration. If DATA.emailSent is true, say organizers were notified; otherwise do not claim that. -`.trim(), -}; diff --git a/src/prompts/call2/system/main.js b/src/prompts/call2/system/main.js deleted file mode 100644 index 9918465..0000000 --- a/src/prompts/call2/system/main.js +++ /dev/null @@ -1,19 +0,0 @@ -const CALL2_SYSTEM_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (greetings, safety, lost & found, human help, event-city local tips). - -### GUARDRAILS: -1. Use only DATA unless the event_adjacent task explicitly allows world knowledge for DATA.city. -2. Never invent that organizers were emailed unless DATA.emailSent is true. -3. NEVER give medical advice. For emergencies, urge immediate on-site security/medical staff first. -4. If DATA.contact or DATA.faqs include Email/Phone, you MUST share those exact values with the visitor — they are organizer-configured public contacts, not invented. -5. Do NOT invent other phone numbers, emails, or desk hours beyond DATA. -6. If DATA.contact is present, do NOT say you have no organizer contact information. -7. Answer naturally in "message" unless listing a required contact or safety step. Never repeat the event name. Never say "I can only assist with [event name]". Do not pad with capability lists. Do not recap DATA.alreadyTold. -8. Output ONLY the JSON key message — no other keys (except place_ids when the adjacent task allows it). - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. -- Example: {"message":"Short answer."} -`.trim(); - -module.exports = { CALL2_SYSTEM_MAIN_PROMPT }; diff --git a/src/prompts/call2/system/out_of_scope.js b/src/prompts/call2/system/out_of_scope.js deleted file mode 100644 index 1278cbf..0000000 --- a/src/prompts/call2/system/out_of_scope.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - intent: 'out_of_scope', - promptlet: ` -### INTENT TASK: out_of_scope -One short sentence, like a person. Do not name the event. Do not start with "I'm afraid". Do not list every topic you can help with. -Example: "I can only help with this event — try the agenda, venue, or your ticket." -`.trim(), -}; diff --git a/src/prompts/call2/tech/app_feature_guidance.js b/src/prompts/call2/tech/app_feature_guidance.js deleted file mode 100644 index a7dca70..0000000 --- a/src/prompts/call2/tech/app_feature_guidance.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - intent: 'app_feature_guidance', - promptlet: ` -### INTENT TASK: app_feature_guidance -If DATA.faqs is non-empty: explain using those answers only. -If empty: ask which feature they need help with, or suggest in-app help / organizers. Do not invent a UI walkthrough. -`.trim(), -}; diff --git a/src/prompts/call2/tech/app_issue_account.js b/src/prompts/call2/tech/app_issue_account.js deleted file mode 100644 index 984bbc3..0000000 --- a/src/prompts/call2/tech/app_issue_account.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - intent: 'app_issue_account', - promptlet: ` -### INTENT TASK: app_issue_account -If DATA.faqs is non-empty: answer from those FAQ Q&A pairs. -Else if DATA.staticTips: share those login tips and suggest organizers / help desk. -Never invent reset URLs or email addresses. -`.trim(), -}; diff --git a/src/prompts/call2/tech/app_issue_streaming.js b/src/prompts/call2/tech/app_issue_streaming.js deleted file mode 100644 index e5fffb9..0000000 --- a/src/prompts/call2/tech/app_issue_streaming.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - intent: 'app_issue_streaming', - promptlet: ` -### INTENT TASK: app_issue_streaming -If DATA.faqs is non-empty: answer from those FAQ Q&A pairs. -Else if DATA.staticTips: list those tips clearly and suggest contacting event support if it still fails. -Never invent platform-specific settings not in DATA. -`.trim(), -}; diff --git a/src/prompts/call2/tech/index.js b/src/prompts/call2/tech/index.js deleted file mode 100644 index b9eed7b..0000000 --- a/src/prompts/call2/tech/index.js +++ /dev/null @@ -1,26 +0,0 @@ -const wifi_credentials = require('./wifi_credentials'); -const app_issue_streaming = require('./app_issue_streaming'); -const app_issue_account = require('./app_issue_account'); -const app_feature_guidance = require('./app_feature_guidance'); -const { CALL2_TECH_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [ - wifi_credentials, - app_issue_streaming, - app_issue_account, - app_feature_guidance, -]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); - -function buildTechCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_TECH_MAIN_PROMPT}\n\n${promptlet}`; -} - -module.exports = { - PROMPTLETS, - buildTechCall2SystemPrompt, -}; diff --git a/src/prompts/call2/tech/main.js b/src/prompts/call2/tech/main.js deleted file mode 100644 index a8f5068..0000000 --- a/src/prompts/call2/tech/main.js +++ /dev/null @@ -1,17 +0,0 @@ -const CALL2_TECH_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (Wi‑Fi and app/tech help). - -### GUARDRAILS: -1. Use only DATA. Never invent Wi‑Fi SSIDs, passwords, reset links, or app UI steps. -2. NEVER invent credentials. Use DATA.faqs only if they answer this ask. If DATA.faqs is empty for wifi, say details are not listed and suggest signage / help desk. Do not recap DATA.alreadyTold. -3. If DATA.staticTips is present, use those tips (you may number them). Do not add invented tips beyond DATA. -4. NEVER output personal email addresses or phone numbers. -5. Answer naturally in "message". You may list Wi‑Fi details from DATA when asked. -6. Output ONLY the JSON key message — no other keys. - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. -- Example: {"message":"Short answer."} -`.trim(); - -module.exports = { CALL2_TECH_MAIN_PROMPT }; diff --git a/src/prompts/call2/tech/wifi_credentials.js b/src/prompts/call2/tech/wifi_credentials.js deleted file mode 100644 index 4b38ef0..0000000 --- a/src/prompts/call2/tech/wifi_credentials.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - intent: 'wifi_credentials', - promptlet: ` -### INTENT TASK: wifi_credentials -If DATA.faqs has answers: share the Wi‑Fi network name and/or password exactly from those answers. -If DATA.faqs is empty: say Wi‑Fi details are not listed here; check venue signage or the help desk. Never invent a password. -`.trim(), -}; diff --git a/src/prompts/call2/ticket/index.js b/src/prompts/call2/ticket/index.js deleted file mode 100644 index 81e9e8c..0000000 --- a/src/prompts/call2/ticket/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const ticket_qr_display = require('./ticket_qr_display'); -const ticket_email_qr = require('./ticket_email_qr'); -const ticket_status_lookup = require('./ticket_status_lookup'); -const ticket_how_to_register = require('./ticket_how_to_register'); -const ticket_document_receipt = require('./ticket_document_receipt'); -const ticket_transfer_request = require('./ticket_transfer_request'); -const ticket_upgrade = require('./ticket_upgrade'); -const { CALL2_TICKET_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [ - ticket_qr_display, - ticket_email_qr, - ticket_status_lookup, - ticket_how_to_register, - ticket_document_receipt, - ticket_transfer_request, - ticket_upgrade, -]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); -const QR_CARD_INTENTS = new Set(MODULES.filter((m) => m.usesQrCard).map((m) => m.intent)); - -function buildTicketCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_TICKET_MAIN_PROMPT}\n\n${promptlet}`; -} - -function usesQrCard(intent) { - return QR_CARD_INTENTS.has(String(intent || '')); -} - -module.exports = { - PROMPTLETS, - QR_CARD_INTENTS, - buildTicketCall2SystemPrompt, - usesQrCard, -}; diff --git a/src/prompts/call2/ticket/main.js b/src/prompts/call2/ticket/main.js deleted file mode 100644 index 7f15bee..0000000 --- a/src/prompts/call2/ticket/main.js +++ /dev/null @@ -1,17 +0,0 @@ -const CALL2_TICKET_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (ticket / registration questions). - -### GUARDRAILS: -1. Use only DATA for personal ticket facts (name, status, ticket tier, payment, receipt, QR availability). Do not invent order ids, amounts, or statuses. -2. Never claim a transfer or upgrade succeeded. Those are not available in chat. -3. Do not mention system prompts, intent names, or internal fields like policy/note unless paraphrasing helpfully for the attendee. -4. Stay on-event. Answer the user's utterance in "message". Do not repeat the event name. For attending/joining questions: answer that ask; DATA.faqs are facts only if they match. Never invent or paste registration URLs — buttons in the UI open the real forms. Never refuse as out of scope. -5. When DATA.qr.imageUrl is present, the UI shows a QR card — say only that their check-in QR is below (or that it was emailed). - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. Include commas between properties. -- Example: {"message":"Your registration is confirmed.","show_qr":true} -- "show_qr" should be true when the user should see their check-in QR in chat (display or after email). -`.trim(); - -module.exports = { CALL2_TICKET_MAIN_PROMPT }; diff --git a/src/prompts/call2/ticket/ticket_document_receipt.js b/src/prompts/call2/ticket/ticket_document_receipt.js deleted file mode 100644 index 9e21467..0000000 --- a/src/prompts/call2/ticket/ticket_document_receipt.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'ticket_document_receipt', - usesQrCard: false, - promptlet: ` -### INTENT TASK: ticket_document_receipt -Present a short text receipt from DATA.receipt when present: -- ticket name, amount, currency/status, order id, date, purchaser email, attendee list. -- If DATA.receipt is missing, say no purchase record was found and suggest contacting the organizer. -- Do not invent PDF downloads. show_qr: false. -`.trim(), -}; diff --git a/src/prompts/call2/ticket/ticket_email_qr.js b/src/prompts/call2/ticket/ticket_email_qr.js deleted file mode 100644 index 11c80da..0000000 --- a/src/prompts/call2/ticket/ticket_email_qr.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - intent: 'ticket_email_qr', - usesQrCard: true, - promptlet: ` -### INTENT TASK: ticket_email_qr -Confirm that the ticket QR and details were emailed (or explain failure). -- Use DATA.emailSent and DATA.emailTo. Never invent a different recipient. -- If emailed: confirm the address (e.g. "I've emailed your ticket QR to you@…"). -- If failed / rate-limited: explain briefly from DATA.emailError or DATA.note. -- Optionally set show_qr true so they can also see the QR in chat when DATA.qr.available. -`.trim(), -}; diff --git a/src/prompts/call2/ticket/ticket_how_to_register.js b/src/prompts/call2/ticket/ticket_how_to_register.js deleted file mode 100644 index 4584a2b..0000000 --- a/src/prompts/call2/ticket/ticket_how_to_register.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - intent: 'ticket_how_to_register', - usesQrCard: false, - promptlet: ` -### INTENT TASK: ticket_how_to_register -Answer the user's question about attending / joining (who may come, how to register, eligibility). -DATA.faqs are background — use a FAQ only if it answers THIS ask. Do not paste an unrelated gate, contact, or how-to line as the whole reply. -If DATA does not say whether everyone may attend, say that honestly; do not invent a public/private policy. -If DATA.hasRegistrationButtons is true, mention they can register with the buttons below. Never invent or paste URLs. show_qr: false. -Do not add organizer email/phone unless they asked who to contact. -`.trim(), -}; diff --git a/src/prompts/call2/ticket/ticket_qr_display.js b/src/prompts/call2/ticket/ticket_qr_display.js deleted file mode 100644 index 1196455..0000000 --- a/src/prompts/call2/ticket/ticket_qr_display.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'ticket_qr_display', - usesQrCard: true, - promptlet: ` -### INTENT TASK: ticket_qr_display -Show the attendee their check-in QR. -- If DATA.qr.available: one short line such as "Here's your check-in QR."; set show_qr true. Do not recap name/status unless asked. -- If not available: apologize in one sentence and suggest the help desk. -`.trim(), -}; diff --git a/src/prompts/call2/ticket/ticket_status_lookup.js b/src/prompts/call2/ticket/ticket_status_lookup.js deleted file mode 100644 index d4b06be..0000000 --- a/src/prompts/call2/ticket/ticket_status_lookup.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'ticket_status_lookup', - usesQrCard: false, - promptlet: ` -### INTENT TASK: ticket_status_lookup -Summarize registration / ticket status from DATA. -- Include attendee name, membership statusLabel, ticket name if present, check-in, and payment status/amount when present. -- show_qr: false unless they also clearly need the QR (prefer false for status-only). -`.trim(), -}; diff --git a/src/prompts/call2/ticket/ticket_transfer_request.js b/src/prompts/call2/ticket/ticket_transfer_request.js deleted file mode 100644 index 27c50c7..0000000 --- a/src/prompts/call2/ticket/ticket_transfer_request.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'ticket_transfer_request', - usesQrCard: false, - promptlet: ` -### INTENT TASK: ticket_transfer_request -Politely refuse. Ticket transfers cannot be completed in this chat. -Ask the attendee to contact the event organizer or on-site help desk. -Offer help with showing/emailing their QR, status, or receipt instead. -Do not collect a recipient email. show_qr: false. -`.trim(), -}; diff --git a/src/prompts/call2/ticket/ticket_upgrade.js b/src/prompts/call2/ticket/ticket_upgrade.js deleted file mode 100644 index e75a6a9..0000000 --- a/src/prompts/call2/ticket/ticket_upgrade.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - intent: 'ticket_upgrade', - usesQrCard: false, - promptlet: ` -### INTENT TASK: ticket_upgrade -Politely refuse. Ticket upgrades cannot be completed in this chat. -Ask the attendee to contact the event organizer or on-site help desk. -Offer help with showing/emailing their QR, status, or receipt instead. -Do not list upgrade tiers for purchase. show_qr: false. -`.trim(), -}; diff --git a/src/prompts/call2/venue/index.js b/src/prompts/call2/venue/index.js deleted file mode 100644 index f735b0e..0000000 --- a/src/prompts/call2/venue/index.js +++ /dev/null @@ -1,41 +0,0 @@ -const venue_map_request = require('./venue_map_request'); -const venue_wayfinding = require('./venue_wayfinding'); -const venue_nearest_amenity = require('./venue_nearest_amenity'); -const venue_parking_transport = require('./venue_parking_transport'); -const venue_travel_directions = require('./venue_travel_directions'); -const venue_food_beverage = require('./venue_food_beverage'); -const venue_accessibility = require('./venue_accessibility'); -const { CALL2_VENUE_MAIN_PROMPT } = require('./main'); -const { CALL2_CONVERSATION_PROMPT } = require('../conversation'); - -const MODULES = [ - venue_map_request, - venue_wayfinding, - venue_nearest_amenity, - venue_parking_transport, - venue_travel_directions, - venue_food_beverage, - venue_accessibility, -]; - -const PROMPTLETS = Object.fromEntries(MODULES.map((m) => [m.intent, m.promptlet])); -const PLACE_CARD_INTENTS = new Set( - MODULES.filter((m) => m.usesPlaceCards).map((m) => m.intent), -); - -function buildVenueCall2SystemPrompt(intent) { - const promptlet = PROMPTLETS[intent]; - if (!promptlet) return null; - return `${CALL2_CONVERSATION_PROMPT}\n\n${CALL2_VENUE_MAIN_PROMPT}\n\n${promptlet}`; -} - -function usesPlaceCards(intent) { - return PLACE_CARD_INTENTS.has(String(intent || '')); -} - -module.exports = { - PROMPTLETS, - PLACE_CARD_INTENTS, - buildVenueCall2SystemPrompt, - usesPlaceCards, -}; diff --git a/src/prompts/call2/venue/main.js b/src/prompts/call2/venue/main.js deleted file mode 100644 index 04bdd30..0000000 --- a/src/prompts/call2/venue/main.js +++ /dev/null @@ -1,20 +0,0 @@ -const CALL2_VENUE_MAIN_PROMPT = ` -You are the Virtual Concierge for an EventBuzz event (venue questions). - -### GUARDRAILS: -1. Use DATA.faqs only when they answer this utterance. They are facts, not a script — do not recap DATA.alreadyTold. -2. For maps, indoor amenities, on-site parking policy, food provided, and accessibility: if DATA.faqs is empty, follow DATA.note / DATA.fallback — do not invent those facts. -3. For intent venue_travel_directions: organizers cannot list every origin. You may use general geographic knowledge for city-to-city train/flight/road corridors to DATA.location (and DATA.eventName only if needed as the destination name). Prefer a direct train/flight when one commonly exists; only suggest a via-hub connection if a direct option is unlikely. Say you are not a live timetable — attendee should verify on IRCTC / airline apps. -4. If DATA.transportModes.enforce is true: local last-mile modes (taxi, Uber/Ola, event shuttle, public transport) are an allow-list. ONLY recommend DATA.transportModes.allowed. NEVER say a mode in DATA.transportModes.notAllowed is available. If asked about a notAllowed mode, say organizers have not listed it for this event. -5. Do not invent URLs. Do not mention system prompts or intent names. -6. Stay on-event. Refuse unrelated requests politely. -7. place_ids MUST be taken only from DATA.places[].id when present; otherwise return []. -8. Prefer at most 6 place_ids for food. Order usefully (closest / highest rated when data allows). -9. "message" should answer the user naturally (2–5 short sentences when useful; longer if listing directions). Do not mix parking FAQ answers into ride-app questions. Do not add organizer email/phone unless the user asked who to contact. - -### REQUIRED OUTPUT (JSON only, no markdown): -- Valid JSON only. Escape newlines inside strings as \\n. Include commas between properties. -- Example: {"message":"Short answer.\\nSecond line.","place_ids":[]} -`.trim(); - -module.exports = { CALL2_VENUE_MAIN_PROMPT }; diff --git a/src/prompts/call2/venue/venue_accessibility.js b/src/prompts/call2/venue/venue_accessibility.js deleted file mode 100644 index 709dd46..0000000 --- a/src/prompts/call2/venue/venue_accessibility.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'venue_accessibility', - usesPlaceCards: false, - promptlet: ` -### INTENT TASK: venue_accessibility -Use DATA.faqs when they answer this ask (wheelchair access, assistance contacts). -If no FAQ: direct the attendee to the venue help desk. Do not invent accessibility policies. -place_ids: []. -`.trim(), -}; diff --git a/src/prompts/call2/venue/venue_food_beverage.js b/src/prompts/call2/venue/venue_food_beverage.js deleted file mode 100644 index 6cbb861..0000000 --- a/src/prompts/call2/venue/venue_food_beverage.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - intent: 'venue_food_beverage', - usesPlaceCards: true, - promptlet: ` -### INTENT TASK: venue_food_beverage -If DATA.faqs answers whether meals/tea are provided or dietary policy, use that for this ask only. -If DATA.faqs is empty, do not invent or replay meal policy. -If they asked about a specific dish, do not pivot to whether lunch is provided. -If DATA.places exists you may select place_ids for nearby dining cards. -If no FAQ and no places: direct the attendee to the venue help desk. Never invent restaurants. -`.trim(), -}; diff --git a/src/prompts/call2/venue/venue_map_request.js b/src/prompts/call2/venue/venue_map_request.js deleted file mode 100644 index b967e39..0000000 --- a/src/prompts/call2/venue/venue_map_request.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'venue_map_request', - usesPlaceCards: false, - promptlet: ` -### INTENT TASK: venue_map_request -Use DATA.faqs about maps/floor plans when they answer this ask. -If no FAQ: share DATA.location (venue address). Do not invent a map URL or floor plan. -place_ids: []. -`.trim(), -}; diff --git a/src/prompts/call2/venue/venue_nearest_amenity.js b/src/prompts/call2/venue/venue_nearest_amenity.js deleted file mode 100644 index cf8a60e..0000000 --- a/src/prompts/call2/venue/venue_nearest_amenity.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'venue_nearest_amenity', - usesPlaceCards: false, - promptlet: ` -### INTENT TASK: venue_nearest_amenity -Use DATA.faqs when they answer this ask (restrooms, water, charging, etc.). -If no FAQ: direct the attendee to the venue help desk. Do not invent amenity locations. -place_ids: []. -`.trim(), -}; diff --git a/src/prompts/call2/venue/venue_parking_transport.js b/src/prompts/call2/venue/venue_parking_transport.js deleted file mode 100644 index ad9fb50..0000000 --- a/src/prompts/call2/venue/venue_parking_transport.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - intent: 'venue_parking_transport', - usesPlaceCards: false, - promptlet: ` -### INTENT TASK: venue_parking_transport -Answer on-site parking from DATA.faqs when present. Do not invent parking rules. - -Local last-mile / ride options (taxi, Uber/Ola, shuttle, public transport): -- If DATA.transportModes.enforce is true: treat DATA.transportModes.allowed as the ONLY organizer-confirmed options. -- Never affirm a mode in DATA.transportModes.notAllowed (e.g. if "Ride apps (Uber/Ola)" is notAllowed, do NOT say Uber or Ola is available). -- If the user asks about a notAllowed mode, say organizers have not listed that option for this event and suggest the help desk or an allowed mode instead. -- If allowed is empty, say organizers have not listed local transfer options; suggest the venue help desk. Do not invent Uber/Ola/shuttle availability. - -If no parking FAQ and no transportModes: share DATA.location if useful, then help desk. -City-to-venue train/flight routing is venue_travel_directions — for those, still respect DATA.transportModes for last-mile claims. -place_ids: []. -`.trim(), -}; diff --git a/src/prompts/call2/venue/venue_travel_directions.js b/src/prompts/call2/venue/venue_travel_directions.js deleted file mode 100644 index f842351..0000000 --- a/src/prompts/call2/venue/venue_travel_directions.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - intent: 'venue_travel_directions', - usesPlaceCards: false, - promptlet: ` -### INTENT TASK: venue_travel_directions -Help the attendee reach the event at DATA.location from their origin (ENTITIES.location or the user message), by train / flight / road. -Use DATA.location (city / venue address) as the destination. Do not lead with a long marketing event title. Do not suggest sightseeing, forts, beaches, or hotels unless they asked. -Organizers cannot document every origin city — use general geographic knowledge for long-distance corridors (train/flight/road between cities). - -Routing rules: -1. Prefer DIRECT options first (e.g. Nagpur–Goa trains if commonly available). Do NOT default to a longer via-Mumbai/hub path when a direct corridor is plausible. -2. Mention a via-hub alternative only as a secondary option. -3. For the last mile from the nearest station/airport to DATA.location: if DATA.transportModes.enforce is true, ONLY suggest modes in DATA.transportModes.allowed. Never affirm modes in DATA.transportModes.notAllowed (e.g. do not say Uber/Ola is available if Ride apps is notAllowed). -4. If the user specifically asks whether Uber/Ola/shuttle/public transport is available and that mode is notAllowed, say organizers have not listed that option — do not invent availability. -5. If the user challenges your route (e.g. "aren't there direct trains?"), acknowledge that direct services may exist and advise checking IRCTC / live schedules — do not stubbornly repeat only the hub route. -6. You are not a live timetable. Never invent specific train numbers or exact departure times unless certain; prefer corridor-level guidance + "verify on IRCTC". -7. Do not invent host shuttles unless "Event shuttle" is in DATA.transportModes.allowed (or in DATA.faqs). Do NOT answer help-desk-only when DATA.location is present. -place_ids: []. -`.trim(), -}; \ No newline at end of file diff --git a/src/prompts/call2/venue/venue_wayfinding.js b/src/prompts/call2/venue/venue_wayfinding.js deleted file mode 100644 index 5eedce0..0000000 --- a/src/prompts/call2/venue/venue_wayfinding.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - intent: 'venue_wayfinding', - usesPlaceCards: false, - promptlet: ` -### INTENT TASK: venue_wayfinding -Use DATA.faqs when they answer this ask. -If no FAQ: list halls/rooms from DATA.halls and DATA.rooms if available, and say more details are at the venue help desk. Do not invent turn-by-turn indoor directions. -place_ids: []. -`.trim(), -}; diff --git a/src/prompts/step1.js b/src/prompts/step1.js deleted file mode 100644 index 1a63157..0000000 --- a/src/prompts/step1.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Call 1 — domain + action + entity extraction. - */ - -const { DOMAINS, ACTIONS } = require('../utils/domainAction'); - -const DOMAIN_DESCRIPTIONS = { - agenda: 'This event’s programme: sessions, tracks, times, personal saved agenda, materials.', - speaker: 'People on the programme: bios, lineup, meet-and-greet, public socials. When they speak → agenda (set speaker_name).', - venue: 'The event site: map, halls, amenities, parking/shuttle, meals *policy*, accessibility, and reaching the venue from any origin city. Not city sightseeing.', - local: 'Around the event city: hotels/stay policy, cached landmarks. City world-knowledge (a dish, a day plan) uses action adjacent, not read.', - ticket: 'Attending this event: who may come, how to join/register, eligibility, ticket status, QR, receipt. Emailing QR is action email_qr.', - exhibitor: 'Booths, exhibitor list, category, demos, meeting requests.', - tech: 'Wi-Fi, streaming/AV, app login, how to use app features.', - system: 'Greeting, lost & found, true refuse (oos), emergency, organizer/human contact.', -}; - -const ACTION_DESCRIPTIONS = { - read: 'Default. Fetch facts and answer. Use for almost every in-scope question.', - bookmark_add: 'They asked to save/add a session. domain agenda.', - bookmark_remove: 'They asked to remove a saved session. domain agenda.', - email_qr: 'They asked to email their QR/ticket. domain ticket.', - emergency: 'Medical/safety emergency. domain system.', - escalate_human: 'Talk to organizer / who to contact. domain system.', - adjacent: - 'World knowledge for THIS event city/dates only (a local dish, a day plan after the event). Not event policy, not programme, not tickets, not another city.', - oos: 'ONLY if the ask is clearly not this event or its host city: other products, homework, a different city as the trip, news, abuse.', - greeting: 'Hello / thanks / goodbye with no event question. domain system.', -}; - -function formatList(ids, descriptions) { - return ids.map((id) => ` - "${id}": ${descriptions[id] || ''}`).join('\n'); -} - -const STEP_1_SYSTEM_PROMPT = ` -You are the classification engine for EventBuzz Virtual Concierge. -Output JSON only — no markdown, no extra keys. - -### HARD CONSTRAINTS -- "domain" MUST be one of: ${DOMAINS.map((d) => `"${d}"`).join(', ')} -- "action" MUST be one of: ${ACTIONS.map((a) => `"${a}"`).join(', ')} -- Do not invent domain or action names. Do not output primary_intent. - -### DOMAINS -${formatList(DOMAINS, DOMAIN_DESCRIPTIONS)} - -### ACTIONS -${formatList(ACTIONS, ACTION_DESCRIPTIONS)} - -### HOW TO CHOOSE (coverage, not phrase matching) -An attendee is talking to the event desk. Classify the *kind of ask*, not whether you have seen this wording. - -1. In-scope if it is about THIS event or THIS event’s host city: programme, people, venue, getting here, meals policy, Wi-Fi/app, tickets, who may attend / how to join, exhibitors, nearby food or sights in that city. Pick the closest domain. Action is "read" unless a mutation below applies. -2. oos ONLY if it is clearly not that: other products, homework, a different city as the trip, general news, abuse. Features we do not have (matchmaking, polls, social feed) → oos. If you are unsure between two in-scope domains, pick one and use "read". Never use oos for lack of a matching example. -3. Event policy (who may attend, meals provided, how to join, Wi-Fi listed) is always "read", never adjacent, never oos. -4. adjacent only when they need city world-knowledge beyond programme/FAQ (a specific dish, a day plan after the event) in THIS city. Set dish when they named food. Other city → oos. -5. Mutations only when they clearly asked to do it: bookmark_add / bookmark_remove, email_qr, emergency, escalate_human. -6. Follow-ups: use RECENT CONVERSATION. Set is_followup. Fill entities. Do not treat a named follow-up as a fresh list dump. -7. Entities: speaker_name without salutations (sir, madam, ji, Dr, Mr, Prof, Shri). location = origin city or named place. dish when they named food. -8. Classification only — never claim a bookmark or email succeeded. - -### REQUIRED JSON -{ - "domain": "ticket", - "action": "read", - "confidence": 0.9, - "entities": { - "speaker_name": null, - "session_name": null, - "session_id": null, - "location": null, - "company_name": null, - "time_reference": null, - "topic": null, - "dish": null - }, - "context_resolution": { - "is_followup": false, - "resolved_from_history": false - } -} -`.trim(); - -const DOMAIN_ALIASES = { - agenda_domain: 'agenda', - speakers: 'speaker', - venue_domain: 'venue', - places: 'local', - tickets: 'ticket', - exhibitors: 'exhibitor', - wifi: 'tech', -}; - -const ACTION_ALIASES = { - lookup: 'read', - query: 'read', - itinerary: 'adjacent', - local_tips: 'adjacent', - out_of_scope: 'oos', - chitchat: 'greeting', - hello: 'greeting', - talk_to_human: 'escalate_human', - human_agent: 'escalate_human', - email_ticket: 'email_qr', - save_session: 'bookmark_add', - remove_session: 'bookmark_remove', -}; - -function resolveDomain(raw) { - const d = String(raw || '').trim(); - if (DOMAINS.includes(d)) return d; - return DOMAIN_ALIASES[d] && DOMAINS.includes(DOMAIN_ALIASES[d]) ? DOMAIN_ALIASES[d] : ''; -} - -function resolveAction(raw) { - const a = String(raw || '').trim(); - if (ACTIONS.includes(a)) return a; - return ACTION_ALIASES[a] && ACTIONS.includes(ACTION_ALIASES[a]) ? ACTION_ALIASES[a] : ''; -} - -module.exports = { - STEP_1_SYSTEM_PROMPT, - DOMAINS, - ACTIONS, - resolveDomain, - resolveAction, -}; diff --git a/src/rag/retriever.js b/src/rag/retriever.js new file mode 100644 index 0000000..1a270e4 --- /dev/null +++ b/src/rag/retriever.js @@ -0,0 +1,80 @@ +const { faqAnswerText } = require('../utils/faqAnswer'); + +function faqKeyOf(row) { + return String(row?.key || row?.category || '').trim() || null; +} + +function tokens(text) { + return String(text || '') + .toLowerCase() + .replace(/[^a-z0-9\s]/g, ' ') + .split(/\s+/) + .filter((t) => t.length > 2); +} + +function serializeBotFaqs(bot) { + const rows = Array.isArray(bot?.faqs) ? bot.faqs : []; + return rows + .map((f) => { + const a = faqAnswerText(f); + if (!a) return null; + const q = String(f?.question || '').trim(); + const key = faqKeyOf(f); + return { + q: q || key || 'FAQ', + a, + key, + category: String(f?.category || '').trim() || key, + }; + }) + .filter(Boolean); +} + +function scoreFaq(row, query) { + const hay = `${row?.q || ''} ${row?.key || ''} ${row?.category || ''} ${row?.a || ''}`.toLowerCase(); + let score = 0; + for (const b of tokens(query)) { + if (hay.includes(b)) score += 1; + } + return score; +} + +/** + * Hybrid keyword rank over ConciergeBot FAQs, scoped to this event's bot. + */ +function searchFaqs({ bot, query, alreadyTold = [], limit = 4 } = {}) { + const told = new Set((Array.isArray(alreadyTold) ? alreadyTold : []).map(String)); + const pool = serializeBotFaqs(bot).filter((f) => { + const key = String(f.key || f.category || ''); + if (key && told.has(key)) return false; + return Boolean(f.a); + }); + + const scored = pool + .map((f) => ({ f, score: scoreFaq(f, query) })) + .sort((a, b) => b.score - a.score); + + const picked = (scored[0]?.score > 0 ? scored.filter((s) => s.score > 0) : scored) + .slice(0, limit) + .map((s) => s.f); + + return picked.map((f) => ({ + source: 'faq', + title: f.q, + text: f.a, + key: f.key || f.category || null, + category: f.category || null, + })); +} + +function faqKeysUsed(hits) { + return (Array.isArray(hits) ? hits : []) + .map((h) => String(h.key || h.category || '').trim()) + .filter(Boolean); +} + +module.exports = { + searchFaqs, + faqKeysUsed, + serializeBotFaqs, +}; diff --git a/src/routes/publicConcierge.routes.js b/src/routes/publicConcierge.routes.js index ba48668..c9fd81c 100644 --- a/src/routes/publicConcierge.routes.js +++ b/src/routes/publicConcierge.routes.js @@ -68,6 +68,17 @@ module.exports = [ }, handler: controller.voiceToken, }, + { + method: 'POST', + path: '/api/public/concierge/{botId}/simli/token', + options: { + auth: false, + validate: { + params: Joi.object({ botId: Joi.string().required() }), + }, + }, + handler: controller.simliToken, + }, { method: 'POST', path: '/api/public/concierge/{botId}/feedback', diff --git a/src/services/identity.service.js b/src/services/identity.service.js index 93886d5..f8caaf0 100644 --- a/src/services/identity.service.js +++ b/src/services/identity.service.js @@ -139,10 +139,9 @@ async function sendOtpForEmail(session, { email, eventId, eventName }) { if (isFixedOtpMode()) { console.log(`[concierge OTP — fixed mode] to=${em} code=${otp}`); + await sendConciergeOtpEmail({ to: em, otp, eventName }); return { - reply: - `I found your registration for ${em}. ` - + `This environment uses a fixed verification code — reply with that 6-digit code to continue.`, + reply: `I found your registration for ${em}. Reply with ${otp} to continue.`, verified: false, }; } diff --git a/src/services/orchestrator.service.js b/src/services/orchestrator.service.js index 0ffeaf7..5cd8c11 100644 --- a/src/services/orchestrator.service.js +++ b/src/services/orchestrator.service.js @@ -1,33 +1,4 @@ -const { classifyIntent } = require('./step1.service'); -const { - generateAgendaReply, - generateVenueReply, - generateTicketReply, - generateSpeakerReply, - generateExhibitorReply, - generateTechReply, - generateSystemReply, - generateLocalReply, -} = require('./step2.service'); -const { - isAgendaIntent, - isAuthRequiredAgendaIntent, -} = require('./agendaData.service'); -const { isVenueIntent } = require('./venueData.service'); -const { - isTicketIntent, - isAuthRequiredTicketIntent, -} = require('./ticketData.service'); -const { isSpeakerIntent } = require('./speakerData.service'); -const { isExhibitorIntent } = require('./exhibitorData.service'); -const { isTechIntent } = require('./techData.service'); -const { isSystemIntent } = require('./systemData.service'); -const { isLocalIntent } = require('./localData.service'); -const { - addBookmark, - removeBookmark, - isBookmarkMutationIntent, -} = require('./bookmark.service'); +const { runConciergeTurn } = require('../agent/runner'); const { isVerified, beginEmailAuth, @@ -35,249 +6,72 @@ const { clearPending, clearAuthChallenge, } = require('./identity.service'); -const { sendTicketPassEmail } = require('../utils/mail'); const { withOrganizerContact } = require('../utils/organizerContact'); -const { retrieveForTurn } = require('./retrieveForTurn'); -const { fromLegacyIntent, isBookmarkAction } = require('../utils/domainAction'); const { isUnsafeAdjacentMessage, UNSAFE_REFUSE } = require('../utils/adjacentSafety'); +const { DEFAULT_QUICK_REPLIES } = require('../utils/quickReplies'); -const TICKET_EMAIL_COOLDOWN_MS = 60 * 1000; - -function publicClassification(classification) { - if (!classification) return null; - const { _fallback, _reason, ...rest } = classification; - return rest; -} - -function sumUsage(a, b) { +function emptyTurn(overrides = {}) { return { - inputTokens: Number(a?.inputTokens || 0) + Number(b?.inputTokens || 0), - outputTokens: Number(a?.outputTokens || 0) + Number(b?.outputTokens || 0), - }; -} - -async function maybeSendTicketEmail({ session, data, intent }) { - if (intent !== 'ticket_email_qr') return data; - - const to = String(data?.attendee?.email || '').trim().toLowerCase(); - if (!to) { - return { - ...data, - emailSent: false, - emailError: 'missing_email', - note: 'No registered email on file for this attendee.', - }; - } - - const last = session.lastTicketEmailAt ? new Date(session.lastTicketEmailAt).getTime() : 0; - if (last && Date.now() - last < TICKET_EMAIL_COOLDOWN_MS) { - return { - ...data, - emailSent: false, - emailTo: to, - emailError: 'rate_limited', - note: 'A ticket email was sent recently for this chat session.', - }; - } - - const payment = data.payment; - const paymentLabel = - payment?.status && payment?.amount != null - ? `${payment.status} (${payment.currency || 'INR'} ${payment.amount})` - : payment?.status || null; - - const result = await sendTicketPassEmail({ - to, - eventName: data.eventName, - attendeeName: data.attendee?.name, - ticketName: data.ticket?.name || data.receipt?.ticketName || null, - statusLabel: data.membership?.statusLabel || data.membership?.status || null, - paymentLabel, - qrImageUrl: data.qr?.imageUrl || null, - }); - - if (result.ok) { - session.lastTicketEmailAt = new Date(); - return { - ...data, - emailSent: true, - emailTo: to, - emailError: null, - }; - } - - return { - ...data, - emailSent: false, - emailTo: to, - emailError: result.error || 'send_failed', - }; -} - -async function generateForDomain({ intent, action, message, history, entities, data }) { - if (action === 'adjacent' || intent === 'event_adjacent') { - return generateSystemReply({ - intent: 'event_adjacent', - message, - history, - entities, - data, - }); - } - if (isAgendaIntent(intent)) { - return generateAgendaReply({ intent, message, history, entities, data }); - } - if (isVenueIntent(intent)) { - return generateVenueReply({ intent, message, history, entities, data }); - } - if (isLocalIntent(intent)) { - return generateLocalReply({ intent, message, history, entities, data }); - } - if (isTicketIntent(intent)) { - return generateTicketReply({ intent, message, history, entities, data }); - } - if (isSpeakerIntent(intent)) { - return generateSpeakerReply({ intent, message, history, entities, data }); - } - if (isExhibitorIntent(intent)) { - return generateExhibitorReply({ intent, message, history, entities, data }); - } - if (isTechIntent(intent)) { - return generateTechReply({ intent, message, history, entities, data }); - } - if (isSystemIntent(intent) || action === 'oos' || action === 'greeting' || action === 'emergency' || action === 'escalate_human') { - return generateSystemReply({ intent, message, history, entities, data }); - } - return generateSystemReply({ - intent: 'out_of_scope', - message, - history, - entities, - data, - }); -} - -async function completeReadTurn({ bot, event, session, message, history, classification }) { - const intent = classification.primary_intent; - const action = classification.action; - let data = await retrieveForTurn({ - bot, - event, - session, - classification, - message, - }); - - if (action === 'email_qr' || intent === 'ticket_email_qr') { - data = await maybeSendTicketEmail({ session, data, intent: 'ticket_email_qr' }); - } - - const step2 = await generateForDomain({ - intent, - action, - message, - history, - entities: classification.entities || {}, - data, - }); - - return { - reply: step2.reply, - cards: Array.isArray(step2.cards) ? step2.cards : [], - sessionIds: Array.isArray(step2.sessionIds) ? step2.sessionIds : [], - placeIds: Array.isArray(step2.placeIds) ? step2.placeIds : [], - hotelIds: Array.isArray(step2.hotelIds) ? step2.hotelIds : [], - speakerIds: Array.isArray(step2.speakerIds) ? step2.speakerIds : [], - exhibitorIds: Array.isArray(step2.exhibitorIds) ? step2.exhibitorIds : [], - quickReplies: Array.isArray(step2.quickReplies) ? step2.quickReplies : null, - classification: publicClassification(classification), - intent, - modelId: step2.modelId, - usage: step2.usage, - faqKeysUsed: Array.isArray(data?.faqKeysUsed) ? data.faqKeysUsed : [], - }; -} - -function classificationFromPending(pendingIntent, pendingEntities) { - const mapped = fromLegacyIntent(pendingIntent) || { domain: 'system', action: 'read' }; - const action = - pendingIntent === 'ticket_email_qr' - ? 'email_qr' - : pendingIntent === 'agenda_bookmark_add' - ? 'bookmark_add' - : pendingIntent === 'agenda_bookmark_remove' - ? 'bookmark_remove' - : mapped.action; - return { - primary_intent: pendingIntent, - domain: mapped.domain, - action, - confidence: 1, - action_category: action === 'email_qr' || isBookmarkAction(action) ? 'mutation' : 'read', - entities: pendingEntities, - context_resolution: { is_followup: true, resolved_from_history: true }, - requires_data_fetch: true, - }; -} - -async function runBookmarkMutation({ bot, session, classification }) { - const intent = classification.primary_intent; - const entities = classification.entities || {}; - const fallbackSessionIds = Array.isArray(session.lastSessionIds) ? session.lastSessionIds : []; - const opts = { - eventId: bot.event, - participantId: session.participant || null, - entities, - fallbackSessionIds, - }; - - const result = - intent === 'agenda_bookmark_remove' - ? await removeBookmark(opts) - : await addBookmark(opts); - - return { - reply: result.reply, - cards: Array.isArray(result.cards) ? result.cards : [], - sessionIds: Array.isArray(result.sessionIds) ? result.sessionIds : [], - classification: publicClassification(classification), - intent, + reply: '', + cards: [], + sessionIds: [], + classification: null, + intent: null, modelId: null, usage: { inputTokens: 0, outputTokens: 0 }, faqKeysUsed: [], + quickReplies: null, + ...overrides, }; } -function unsafeRefuseResult(classification, step1) { +function toPublicResult(agent, extras = {}) { + const toolNames = Array.isArray(agent?.toolNames) ? agent.toolNames : []; + const intent = toolNames[0] || extras.intent || null; + const noTools = !toolNames.length && !agent?.unauthenticated; return { - reply: UNSAFE_REFUSE, - cards: [], - sessionIds: [], - classification: publicClassification(classification), - intent: classification?.primary_intent || null, - modelId: step1?.modelId || null, - usage: step1?.usage || { inputTokens: 0, outputTokens: 0 }, - faqKeysUsed: [], + reply: agent?.reply || extras.reply || '', + cards: Array.isArray(agent?.cards) ? agent.cards : [], + sessionIds: Array.isArray(agent?.sessionIds) ? agent.sessionIds : [], + classification: toolNames.length ? { tools: toolNames } : null, + intent, + modelId: agent?.modelId || null, + usage: agent?.usage || { inputTokens: 0, outputTokens: 0 }, + faqKeysUsed: Array.isArray(agent?.faqKeysUsed) ? agent.faqKeysUsed : [], + quickReplies: noTools ? DEFAULT_QUICK_REPLIES : null, + forceOrganizerContact: Boolean(agent?.forceOrganizerContact), + ...extras, }; } -/** - * Full turn orchestration after the user message has been appended to session.history. - */ function finalizeTurn(result, bot, message) { - const intent = String(result?.intent || ''); - const force = intent === 'human_agent_escalation' || intent === 'emergency_medical'; + const force = Boolean(result?.forceOrganizerContact) || result?.intent === 'contact_organizer'; + const { forceOrganizerContact, ...rest } = result || {}; return { - ...result, - reply: withOrganizerContact(result?.reply, bot, { message, force }), + ...rest, + reply: withOrganizerContact(rest?.reply, bot, { message, force }), }; } -async function handleChatTurnCore({ bot, event, session, message }) { +async function runAgent({ bot, event, session, message, history, channel }) { + return runConciergeTurn({ + bot, + event, + session, + message, + history, + channel, + }); +} + +async function handleChatTurnCore({ bot, event, session, message, channel = 'text' }) { const eventName = event?.name || 'the event'; const history = Array.isArray(session.history) ? session.history.slice(0, -1) : []; - // 1) In-progress identity challenge — skip Read Call 1 + if (isUnsafeAdjacentMessage(message)) { + return emptyTurn({ reply: UNSAFE_REFUSE }); + } + if (session.authState === 'awaiting_email' || session.authState === 'awaiting_otp') { const auth = await handleAuthTurn(session, { message, @@ -289,132 +83,60 @@ async function handleChatTurnCore({ bot, event, session, message }) { clearAuthChallenge(session); clearPending(session); if (auth.cancelOnly) { - return { - reply: 'OK. What else can I help with?', - cards: [], - sessionIds: [], - classification: null, - intent: null, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - faqKeysUsed: [], - }; + return emptyTurn({ reply: 'OK. What else can I help with?' }); } // New question while verifying — drop auth and answer it below. } else if (!auth.shouldResume) { - return { + return emptyTurn({ reply: auth.reply, - cards: [], - sessionIds: [], - classification: null, intent: session.pendingIntent || null, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - faqKeysUsed: [], - }; + }); } else { - - // Verified — answer the original pending query - const pendingMessage = session.pendingMessage || message; - const pendingIntent = session.pendingIntent; - const pendingEntities = - session.pendingEntities && typeof session.pendingEntities === 'object' - ? session.pendingEntities - : {}; - clearPending(session); - - if (pendingIntent && isBookmarkMutationIntent(pendingIntent)) { - const classification = classificationFromPending(pendingIntent, pendingEntities); - const result = await runBookmarkMutation({ bot, session, classification }); - result.reply = `You're verified. ${result.reply}`; - return result; - } - - if (pendingIntent && (isAuthRequiredTicketIntent(pendingIntent) || isAgendaIntent(pendingIntent))) { - const classification = classificationFromPending(pendingIntent, pendingEntities); - const result = await completeReadTurn({ + const pendingMessage = session.pendingMessage || message; + clearPending(session); + const agent = await runAgent({ bot, event, session, message: pendingMessage, history, - classification, + channel, }); - result.reply = `You're verified. ${result.reply}`; + const result = toPublicResult(agent); + if (result.reply) { + result.reply = `You're verified. ${result.reply}`; + } else { + result.reply = "You're verified. How can I help with the agenda or your ticket?"; + } return result; } - - return { - reply: "You're verified. How can I help with the agenda or your ticket?", - cards: [], - sessionIds: [], - classification: null, - intent: pendingIntent, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - faqKeysUsed: [], - }; - } } - // 2) Call 1 - const step1 = await classifyIntent({ message, history }); - const classification = step1.classification; - const intent = classification.primary_intent; - const action = classification.action; - - // 3) Safety refuse (abuse / PII / medical-beyond-first-aid) — never the strong model - if (isUnsafeAdjacentMessage(message) && action !== 'emergency') { - return unsafeRefuseResult(classification, step1); - } - - // 4) Auth-required intents (personal agenda + ticket reads/email) - if ( - (isAuthRequiredAgendaIntent(intent) || isAuthRequiredTicketIntent(intent)) - && !isVerified(session) - ) { - const started = await beginEmailAuth(session, { - intent, - message, - eventName, - entities: classification.entities || {}, - }); - return { - reply: started.reply, - cards: [], - sessionIds: [], - classification: publicClassification(classification), - intent, - modelId: step1.modelId, - usage: step1.usage, - faqKeysUsed: [], - }; - } - - // 5) Bookmark mutations — write to SessionBookmark (no Call 2) - if (isBookmarkMutationIntent(intent) || isBookmarkAction(action)) { - const result = await runBookmarkMutation({ bot, session, classification }); - return { - ...result, - usage: sumUsage(step1.usage, result.usage), - modelId: result.modelId || step1.modelId, - }; - } - - // 6) Retrieve this-turn DATA, then Call 2 by domain (email QR / emergency stay code-backed) - const result = await completeReadTurn({ + const agent = await runAgent({ bot, event, session, message, history, - classification, + channel, }); - return { - ...result, - usage: sumUsage(step1.usage, result.usage), - modelId: result.modelId || step1.modelId, - }; + + if (agent.unauthenticated && !isVerified(session)) { + const started = await beginEmailAuth(session, { + intent: (agent.toolNames && agent.toolNames[0]) || 'get_my_tickets', + message, + eventName, + }); + return emptyTurn({ + reply: started.reply, + intent: agent.toolNames?.[0] || null, + classification: agent.toolNames?.length ? { tools: agent.toolNames } : null, + modelId: agent.modelId, + usage: agent.usage, + }); + } + + return toPublicResult(agent); } async function handleChatTurn(opts) { diff --git a/src/services/retrieveForTurn.js b/src/services/retrieveForTurn.js deleted file mode 100644 index bd5245f..0000000 --- a/src/services/retrieveForTurn.js +++ /dev/null @@ -1,301 +0,0 @@ -const { faqAnswerText } = require('../utils/faqAnswer'); -const { - looksLikeFoodPolicy, - looksLikeDishAsk, - looksLikeItinerary, - faqKeyOf, -} = require('../utils/domainAction'); -const { fetchAgendaData, isAgendaIntent } = require('./agendaData.service'); -const { fetchVenueData, isVenueIntent } = require('./venueData.service'); -const { fetchLocalData, isLocalIntent } = require('./localData.service'); -const { fetchTicketData, isTicketIntent } = require('./ticketData.service'); -const { fetchSpeakerData, isSpeakerIntent } = require('./speakerData.service'); -const { fetchExhibitorData, isExhibitorIntent } = require('./exhibitorData.service'); -const { fetchTechData, isTechIntent } = require('./techData.service'); -const { fetchSystemData, isSystemIntent } = require('./systemData.service'); -const Event = require('../models/Event'); - -const FOOD_FAQ_KEYS = new Set(['venue_food', 'venue_food_beverage']); - -function tokens(text) { - return String(text || '') - .toLowerCase() - .replace(/[^a-z0-9\s]/g, ' ') - .split(/\s+/) - .filter((t) => t.length > 2); -} - -function scoreFaq(row, message, entities) { - const q = String(row?.question || row?.q || ''); - const key = String(row?.key || row?.category || ''); - const hay = `${q} ${key}`.toLowerCase(); - const bits = tokens(message); - if (entities?.dish) bits.push(String(entities.dish).toLowerCase()); - if (entities?.topic) bits.push(String(entities.topic).toLowerCase()); - let score = 0; - for (const b of bits) { - if (hay.includes(b)) score += 1; - } - return score; -} - -function alreadyToldSet(session) { - return new Set((Array.isArray(session?.alreadyTold) ? session.alreadyTold : []).map(String)); -} - -function serializeBotFaqs(bot) { - const rows = Array.isArray(bot?.faqs) ? bot.faqs : []; - return rows - .map((f) => { - const a = faqAnswerText(f); - if (!a) return null; - const q = String(f?.question || '').trim(); - const key = faqKeyOf(f); - return { - q: q || key || 'FAQ', - a, - key, - category: String(f?.category || '').trim() || key, - }; - }) - .filter(Boolean); -} - -function shouldSkipFoodFaq({ action, message, entities }) { - if (action === 'adjacent') return true; - if (looksLikeDishAsk(message, entities?.dish) && !looksLikeFoodPolicy(message)) return true; - if (looksLikeItinerary(message)) return true; - return false; -} - -function rankFaqs({ bot, message, entities, action, alreadyTold, existingFaqs }) { - const skipFood = shouldSkipFoodFaq({ action, message, entities }); - const told = alreadyToldSet({ alreadyTold: [...alreadyTold] }); - const fromBot = serializeBotFaqs(bot); - const botByQ = new Map(fromBot.map((f) => [String(f.q || '').toLowerCase(), f])); - const pool = existingFaqs == null ? fromBot : existingFaqs; - - const enriched = (Array.isArray(pool) ? pool : []).map((f) => { - const hit = botByQ.get(String(f.q || '').toLowerCase()); - const key = String(f.key || f.category || hit?.key || hit?.category || '').trim(); - return { - q: f.q, - a: f.a, - key: key || null, - category: String(f.category || hit?.category || key || '').trim() || null, - }; - }); - - const filtered = enriched.filter((f) => { - const key = String(f.key || f.category || ''); - if (key && told.has(key)) return false; - if (skipFood && (FOOD_FAQ_KEYS.has(key) || FOOD_FAQ_KEYS.has(String(f.category || '')))) { - return false; - } - return Boolean(f.a); - }); - - const scored = filtered - .map((f) => ({ f, score: scoreFaq(f, message, entities) })) - .sort((a, b) => b.score - a.score); - - const picked = (scored[0]?.score > 0 ? scored.filter((s) => s.score > 0) : scored) - .slice(0, 3) - .map((s) => s.f); - - return picked.map((f) => ({ - q: f.q, - a: f.a, - key: f.key || f.category || null, - })); -} - -function compactEventDates(event) { - if (!event || typeof event !== 'object') return null; - const start = - event.startDate || event.start || event.dateFrom || event.startsAt || event.eventStart; - const end = event.endDate || event.end || event.dateTo || event.endsAt || event.eventEnd; - const out = {}; - if (start) out.start = start instanceof Date ? start.toISOString() : String(start); - if (end) out.end = end instanceof Date ? end.toISOString() : String(end); - return Object.keys(out).length ? out : null; -} - -function cityHint(location) { - const loc = String(location || '').trim(); - if (!loc) return null; - const parts = loc.split(',').map((p) => p.trim()).filter(Boolean); - return parts[parts.length - 1] || loc; -} - -async function loadEventContext(event, eventId) { - let doc = event; - if (!doc?.location || !compactEventDates(doc)) { - const id = event?._id || eventId; - if (id) { - const extra = await Event.findById(id) - .select('name location startDate endDate start end dateFrom dateTo startsAt endsAt eventStart eventEnd') - .lean(); - if (extra) doc = { ...extra, ...doc }; - } - } - return { - location: String(doc?.location || '').trim() || null, - city: cityHint(doc?.location), - eventDates: compactEventDates(doc), - }; -} - -function faqKeysUsed(faqs) { - return (Array.isArray(faqs) ? faqs : []) - .map((f) => String(f.key || f.category || '').trim()) - .filter(Boolean); -} - -/** - * Retrieve this-turn DATA using existing domain fetchers + ranked FAQs. - */ -async function retrieveForTurn({ - bot, - event, - session, - classification, - message, -}) { - const intent = classification.primary_intent; - const action = classification.action; - const domain = classification.domain; - const entities = classification.entities || {}; - const alreadyTold = Array.isArray(session?.alreadyTold) ? session.alreadyTold : []; - const eventId = bot.event; - const participantId = session?.participant || null; - - let data = { faqs: [], alreadyTold }; - - if (action === 'adjacent' || intent === 'event_adjacent') { - const sys = await fetchSystemData({ - intent: 'event_adjacent', - bot, - event, - session, - message, - }); - const ctx = await loadEventContext(event, eventId); - let places = []; - let hotels = []; - try { - if (looksLikeDishAsk(message, entities.dish)) { - const food = await fetchVenueData({ - intent: 'venue_food_beverage', - eventId, - bot, - entities, - message, - }); - places = Array.isArray(food.places) ? food.places : []; - } else { - const local = await fetchLocalData({ - intent: 'tourist_places', - eventId, - message, - bot, - }); - places = Array.isArray(local.places) ? local.places : []; - hotels = Array.isArray(local.hotels) ? local.hotels : []; - } - } catch (err) { - console.warn('[retrieve] adjacent local cache:', err?.message || err); - } - - const faqs = rankFaqs({ - bot, - message, - entities, - action: 'adjacent', - alreadyTold, - existingFaqs: [], - }); - - data = { - ...sys, - ...ctx, - allowGeneralLocal: true, - faqs, - places, - hotels, - alreadyTold, - dish: entities.dish || null, - }; - data.faqKeysUsed = faqKeysUsed(faqs); - return data; - } - - if (isAgendaIntent(intent)) { - data = await fetchAgendaData({ - intent, - eventId, - entities, - participantId, - }); - } else if (isVenueIntent(intent)) { - data = await fetchVenueData({ - intent, - eventId, - bot, - entities, - message, - }); - } else if (isLocalIntent(intent)) { - data = await fetchLocalData({ intent, eventId, message, bot }); - } else if (isTicketIntent(intent)) { - data = await fetchTicketData({ - intent, - eventId, - participantId, - eventName: event?.name || null, - bot, - event, - }); - } else if (isSpeakerIntent(intent)) { - data = await fetchSpeakerData({ intent, eventId, entities }); - } else if (isExhibitorIntent(intent)) { - data = await fetchExhibitorData({ intent, eventId, bot, entities }); - } else if (isTechIntent(intent)) { - data = await fetchTechData({ intent, bot }); - } else if (isSystemIntent(intent)) { - data = await fetchSystemData({ intent, bot, event, session, message }); - } else { - data = await fetchSystemData({ - intent: 'out_of_scope', - bot, - event, - session, - message, - }); - } - - const ranked = rankFaqs({ - bot, - message, - entities, - action, - alreadyTold, - existingFaqs: Array.isArray(data.faqs) ? data.faqs : [], - }); - - data = { - ...data, - faqs: ranked, - alreadyTold, - domain, - action, - }; - data.faqKeysUsed = faqKeysUsed(ranked); - return data; -} - -module.exports = { - retrieveForTurn, - rankFaqs, - faqKeysUsed, -}; diff --git a/src/services/step1.service.js b/src/services/step1.service.js deleted file mode 100644 index a9ecbb6..0000000 --- a/src/services/step1.service.js +++ /dev/null @@ -1,415 +0,0 @@ -const { STEP_1_SYSTEM_PROMPT, resolveDomain, resolveAction } = require('../prompts/step1'); -const { - createClient, - converse, - responseText, - getRegion, - getRouterModelId, -} = require('../utils/bedrock'); -const { looksLikeTouristPlacesQuestion } = require('../utils/placeCategoryFilter'); -const { looksLikeWhoToContact, looksLikeHotelCheckIn } = require('../utils/organizerContact'); -const { stripNameSalutations } = require('../utils/nameSalutations'); -const { - inferFetchIntent, - fromLegacyIntent, - looksLikeDishAsk, - looksLikeItinerary, - looksLikeFoodPolicy, - looksLikeCityToVenueTravel, - looksLikeEventDeskQuestion, - extractTravelOrigin, - LEGACY_TO_DOMAIN_ACTION, -} = require('../utils/domainAction'); - -const REGISTER_RESCUE_FROM_ACTIONS = new Set(['oos', 'greeting', 'read']); - -function looksLikeHowToRegisterQuestion(message) { - const t = String(message || '') - .toLowerCase() - .replace(/\s+/g, ' ') - .trim(); - if (!t) return false; - - const askingHowToJoin = /\bhow (can|do|to) (i |we )?(register|sign ?up|get (a )?(ticket|pass)|buy (a )?(ticket|pass))\b/.test( - t, - ); - if ( - !askingHowToJoin - && /\b(where|find|locate|direction|way to|desk|counter|booth|hall)\b/.test(t) - && /\bregistration\b/.test(t) - ) { - return false; - } - if (/\b(my (registration|ticket|status)|already registered|check[- ]?in status|payment status)\b/.test(t)) { - return false; - } - - return ( - askingHowToJoin - || /\b(want to|need to|like to|trying to) (register|sign ?up|buy (a )?(ticket|pass))\b/.test(t) - || /\b(buy|purchase) (a |an )?(ticket|pass|registration)\b/.test(t) - || /\bregistration (link|form|page|url|website|online)\b/.test(t) - || /^(how can i register|how do i register|how to register|can i register|register|sign up|sign-up)( for (the |this )?event)?\??$/.test( - t, - ) - ); -} - -function emptyEntities() { - return { - speaker_name: null, - session_name: null, - session_id: null, - location: null, - company_name: null, - time_reference: null, - topic: null, - dish: null, - }; -} - -function fallbackClassification(reason) { - return { - domain: 'system', - action: 'oos', - primary_intent: 'out_of_scope', - confidence: 0, - action_category: 'direct_reply', - entities: emptyEntities(), - context_resolution: { - is_followup: false, - resolved_from_history: false, - }, - requires_data_fetch: false, - _fallback: true, - _reason: reason || 'unknown', - }; -} - -function stripJsonFences(raw) { - let text = String(raw || '').trim(); - if (text.startsWith('```')) { - text = text.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim(); - } - const start = text.indexOf('{'); - const end = text.lastIndexOf('}'); - if (start >= 0 && end > start) { - text = text.slice(start, end + 1); - } - return text; -} - -function normalizeEntities(raw) { - const base = emptyEntities(); - if (!raw || typeof raw !== 'object') return base; - for (const key of Object.keys(base)) { - if (raw[key] == null || raw[key] === '') { - base[key] = null; - } else { - const value = typeof raw[key] === 'string' ? raw[key].trim() : raw[key]; - if (key === 'speaker_name' && typeof value === 'string') { - base[key] = stripNameSalutations(value) || null; - } else { - base[key] = value; - } - } - } - return base; -} - -function actionCategoryFor(action) { - if (action === 'bookmark_add' || action === 'bookmark_remove' || action === 'email_qr') { - return 'mutation'; - } - if (action === 'emergency' || action === 'escalate_human') return 'escalation'; - if (action === 'oos' || action === 'greeting') return 'direct_reply'; - return 'read'; -} - -function attachFetchIntent(base, message) { - const fetchIntent = inferFetchIntent({ - domain: base.domain, - action: base.action, - entities: base.entities, - message, - }); - return { - ...base, - primary_intent: fetchIntent, - action_category: actionCategoryFor(base.action), - requires_data_fetch: !['oos', 'greeting'].includes(base.action) || fetchIntent === 'lost_and_found', - }; -} - -function normalizeClassification(parsed, message = '') { - if (!parsed || typeof parsed !== 'object') { - return fallbackClassification('invalid_json_object'); - } - - let domain = resolveDomain(parsed.domain); - let action = resolveAction(parsed.action); - - // Model still emitting old primary_intent - if ((!domain || !action) && parsed.primary_intent) { - const legacy = fromLegacyIntent(String(parsed.primary_intent).trim()) - || LEGACY_TO_DOMAIN_ACTION[String(parsed.primary_intent).trim()]; - if (legacy) { - domain = domain || legacy.domain; - action = action || legacy.action; - } - } - - if (!domain || !action) { - return fallbackClassification( - `unknown_domain_action:${parsed.domain || ''}:${parsed.action || parsed.primary_intent || ''}`, - ); - } - - let confidence = Number(parsed.confidence); - if (!Number.isFinite(confidence)) confidence = 0; - confidence = Math.max(0, Math.min(1, confidence)); - - const ctx = - parsed.context_resolution && typeof parsed.context_resolution === 'object' - ? parsed.context_resolution - : {}; - - const entities = normalizeEntities(parsed.entities); - - return attachFetchIntent( - { - domain, - action, - confidence, - entities, - context_resolution: { - is_followup: Boolean(ctx.is_followup), - resolved_from_history: Boolean(ctx.resolved_from_history), - }, - }, - message, - ); -} - -function formatHistory(history) { - return (Array.isArray(history) ? history.slice(-6) : []) - .map((m) => { - const role = m?.role === 'assistant' ? 'Assistant' : 'User'; - const text = String(m?.text || '').trim(); - return text ? `${role}: ${text}` : null; - }) - .filter(Boolean) - .join('\n'); -} - -function applyRescues(classification, message) { - let next = { ...classification }; - - // Mutations / code paths that must run even if Call 1 misses the action. - if ( - REGISTER_RESCUE_FROM_ACTIONS.has(next.action) - && looksLikeHowToRegisterQuestion(message) - ) { - next = attachFetchIntent( - { - ...next, - domain: 'ticket', - action: 'read', - _rescued_from: classification.action, - _rescue: 'how_to_register_keyword', - }, - message, - ); - next.primary_intent = 'ticket_how_to_register'; - } - - if (looksLikeWhoToContact(message)) { - next = attachFetchIntent( - { - ...next, - domain: 'system', - action: 'escalate_human', - _rescued_from: classification.action, - _rescue: 'who_to_contact_keyword', - }, - message, - ); - } else if (looksLikeHotelCheckIn(message)) { - next = attachFetchIntent( - { - ...next, - domain: 'local', - action: 'read', - _rescued_from: classification.action, - _rescue: 'hotel_checkin_keyword', - }, - message, - ); - next.primary_intent = 'accommodation'; - } - - if ( - ['read', 'oos', 'greeting', 'adjacent'].includes(next.action) - && looksLikeCityToVenueTravel(message) - && !looksLikeItinerary(message) - && !looksLikeDishAsk(message, next.entities?.dish) - ) { - const origin = extractTravelOrigin(message, next.entities?.location); - next = attachFetchIntent( - { - ...next, - domain: 'venue', - action: 'read', - entities: { - ...next.entities, - location: origin || next.entities?.location || null, - }, - _rescued_from: classification.action, - _rescue: 'city_to_venue', - }, - message, - ); - next.primary_intent = 'venue_travel_directions'; - } - - if ( - ['read', 'oos', 'greeting'].includes(next.action) - && !looksLikeFoodPolicy(message) - && (looksLikeItinerary(message) || looksLikeDishAsk(message, next.entities?.dish)) - ) { - const dishMatch = String(message || '').match( - /\b(puttu|dosa|idli|appam|porotta|parotta|biryani)\b/i, - ); - const entities = { - ...next.entities, - dish: next.entities?.dish || (dishMatch ? dishMatch[1].toLowerCase() : null), - }; - next = attachFetchIntent( - { - ...next, - domain: 'local', - action: 'adjacent', - entities, - _rescued_from: classification.action, - _rescue: 'adjacent_keyword', - }, - message, - ); - } - - // If the router refused but the ask is still an event-desk question, recover in-scope. - if (next.action === 'oos' || next.action === 'greeting') { - if ( - looksLikeTouristPlacesQuestion(message) - && !looksLikeItinerary(message) - && !looksLikeDishAsk(message, next.entities?.dish) - ) { - next = attachFetchIntent( - { - ...next, - domain: 'local', - action: 'read', - _rescued_from: classification.action, - _rescue: 'oos_event_desk', - }, - message, - ); - } else if (looksLikeEventDeskQuestion(message)) { - next = attachFetchIntent( - { - ...next, - domain: 'ticket', - action: 'read', - _rescued_from: classification.action, - _rescue: 'oos_event_desk', - }, - message, - ); - } - } - - return next; -} - -async function classifyIntent({ message, history }) { - const modelId = getRouterModelId(); - const client = createClient(getRegion()); - const recentContext = formatHistory(history); - - const userText = [ - 'RECENT CONVERSATION:', - recentContext || '(none)', - '', - 'CURRENT USER MESSAGE:', - String(message || '').trim(), - '', - 'Respond with JSON only.', - ].join('\n'); - - try { - const response = await converse(client, modelId, { - system: STEP_1_SYSTEM_PROMPT, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: { temperature: 0, topP: 0.1, maxTokens: 400 }, - logLabel: 'call1', - }); - - const raw = responseText(response); - let parsed; - try { - parsed = JSON.parse(stripJsonFences(raw)); - } catch { - parsed = null; - } - - let classification = parsed - ? normalizeClassification(parsed, message) - : fallbackClassification('json_parse_failed'); - - classification = applyRescues(classification, message); - - console.log('[step1][call1][classification]', { - modelId, - parsedOk: Boolean(parsed), - classification, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }); - - return { - classification, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - raw, - }; - } catch (err) { - console.warn('[step1][call1] failed:', err?.message || err); - return { - classification: fallbackClassification(`llm_error:${err?.message || 'unknown'}`), - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - raw: null, - }; - } -} - -function formatClassificationReply(classification) { - const domain = classification?.domain || 'system'; - const action = classification?.action || 'oos'; - const conf = Number(classification?.confidence); - const confLabel = Number.isFinite(conf) ? conf.toFixed(2) : '0.00'; - return `Domain: ${domain} · ${action} · ${confLabel}`; -} - -module.exports = { - classifyIntent, - formatClassificationReply, - normalizeClassification, - fallbackClassification, - applyRescues, -}; diff --git a/src/services/step2.service.js b/src/services/step2.service.js deleted file mode 100644 index 4082113..0000000 --- a/src/services/step2.service.js +++ /dev/null @@ -1,1775 +0,0 @@ -const { buildCall2SystemPrompt, usesSessionCards } = require('../prompts/call2/agenda'); -const { buildVenueCall2SystemPrompt, usesPlaceCards } = require('../prompts/call2/venue'); -const { buildTicketCall2SystemPrompt, usesQrCard } = require('../prompts/call2/ticket'); -const { - buildSpeakerCall2SystemPrompt, - usesSpeakerCards, - usesSpeakerSessionCards, -} = require('../prompts/call2/speaker'); -const { - buildExhibitorCall2SystemPrompt, - usesExhibitorCards, -} = require('../prompts/call2/exhibitor'); -const { buildTechCall2SystemPrompt } = require('../prompts/call2/tech'); -const { buildSystemCall2SystemPrompt } = require('../prompts/call2/system'); -const { - buildLocalCall2SystemPrompt, - usesLocalPlaceCards, - usesLocalHotelCards, -} = require('../prompts/call2/local'); -const { - createClient, - converse, - responseText, - getRegion, - getCall2StrongModelId, -} = require('../utils/bedrock'); -const { buildSessionCards, resolveSessionIds } = require('../utils/sessionCards'); -const { buildPlaceCards, resolvePlaceIds } = require('../utils/placeCards'); -const { buildMapFileCard } = require('../utils/faqAnswer'); -const { - buildAccommodationCards, - resolveHotelIds, -} = require('../utils/accommodationCards'); -const { buildTicketQrCard } = require('../utils/ticketCards'); -const { buildRegistrationCtaCards } = require('../utils/registrationLinks'); -const { buildSpeakerCards, resolveSpeakerIdsFromModel } = require('../utils/speakerCards'); -const { buildExhibitorCards, resolveExhibitorIdsFromModel } = require('../utils/exhibitorCards'); -const { - compactEntities, - slimDataForLlm, - slimVenueDataForLlm, - slimTicketDataForLlm, - slimSpeakerDataForLlm, - slimExhibitorDataForLlm, - slimTechDataForLlm, - slimSystemDataForLlm, - slimLocalDataForLlm, -} = require('../utils/call2Context'); -const { - STREAMING_STATIC_TIPS, - ACCOUNT_STATIC_TIPS, -} = require('./techData.service'); -const { DEFAULT_QUICK_REPLIES } = require('../utils/quickReplies'); - -const DEFAULT_CALL2_MODEL_ID = 'anthropic.claude-3-haiku-20240307-v1:0'; -const CALL2_INFERENCE = { temperature: 0.45, topP: 0.9, maxTokens: 1000 }; -const FALLBACK_REPLY = - "I couldn't load the agenda details just now. Please try again in a moment, or ask about a specific session or speaker."; -const VENUE_FALLBACK_REPLY = - 'I could not find venue details for that. Please ask at the venue help desk.'; -const TICKET_FALLBACK_REPLY = - 'I could not load your ticket details just now. Please try again, or ask at the help desk.'; -const TICKET_REFUSE_REPLY = - 'I can’t transfer or upgrade tickets in this chat. Please contact the event organizer or on-site help desk. I can show or email your QR, check your registration status, or share a receipt.'; -const TICKET_REGISTER_NONE_REPLY = - 'I don’t have a registration link on file. Please check the event website or ask at the help desk.'; -const SPEAKER_FALLBACK_REPLY = - 'I could not find that speaker just now. Try another name spelling, or ask about a session on the agenda.'; -const EXHIBITOR_FALLBACK_REPLY = - 'I could not find that exhibitor just now. Try another company name, or ask by category.'; -const EXHIBITOR_BOOKING_REFUSE_REPLY = - 'I can’t book meetings with exhibitors in this chat. Please visit their booth or ask exhibitor staff / the help desk. I can help you find a booth location.'; -const EXHIBITOR_DEMO_NONE_REPLY = - 'I don’t have a demo schedule listed for that. Please check at the exhibitor booth or ask at the help desk.'; -const TECH_FALLBACK_REPLY = - 'I couldn’t load tech help just now. Please try again, or ask at the venue help desk.'; -const WIFI_NONE_REPLY = - 'Wi‑Fi details aren’t listed here yet. Please check venue signage or ask at the help desk.'; -const FEATURE_NONE_REPLY = - 'I don’t have specific app how‑to notes on file. Which feature do you need help with? You can also check in‑app help or ask the organizers.'; -const SYSTEM_FALLBACK_REPLY = - 'Thanks for your message. Please ask at the venue help desk if you need on-site assistance.'; -const EMERGENCY_SAFETY_LINE = - 'If this is an emergency, contact venue security or medical staff immediately.'; -const LOCAL_FALLBACK_REPLY = - 'I couldn’t load local recommendations just now. Please ask at the venue help desk.'; -const HOST_MANAGED_ACCOMMODATION_REPLY = - 'Your organizer is arranging accommodation for this event. For your hotel, check-in dates, and how to confirm your room, please reach out to the event team.'; - -function formatStaticTipsReply(intro, tips) { - const list = (Array.isArray(tips) ? tips : []).filter(Boolean); - if (!list.length) return intro; - const body = list.map((t, i) => `${i + 1}. ${t}`).join('\n'); - return `${intro}\n${body}\n\nIf it still doesn’t work, contact event support or the help desk.`; -} - -function getCall2ModelId() { - const strong = String(process.env.BEDROCK_CALL2_MODEL_STRONG_ID || '').trim(); - if (strong) return strong; - return process.env.BEDROCK_CALL2_MODEL_ID || DEFAULT_CALL2_MODEL_ID; -} - -function formatHistory(history) { - return (Array.isArray(history) ? history.slice(-8) : []) - .map((m) => { - const role = m?.role === 'assistant' ? 'Assistant' : 'User'; - const text = String(m?.text || '').trim().slice(0, 800); - return text ? `${role}: ${text}` : null; - }) - .filter(Boolean) - .join('\n'); -} - -function stripFences(raw) { - let text = String(raw || '').trim(); - if (text.startsWith('```')) { - text = text.replace(/^```(?:\w+)?\s*/i, '').replace(/\s*```$/i, '').trim(); - } - const start = text.indexOf('{'); - const end = text.lastIndexOf('}'); - if (start >= 0 && end > start) { - text = text.slice(start, end + 1); - } - return text; -} - -/** Escape raw control chars inside JSON strings (Haiku often emits literal newlines). */ -function repairJsonControlChars(text) { - let out = ''; - let inString = false; - let escaped = false; - for (let i = 0; i < text.length; i += 1) { - const ch = text[i]; - if (inString) { - if (escaped) { - out += ch; - escaped = false; - continue; - } - if (ch === '\\') { - out += ch; - escaped = true; - continue; - } - if (ch === '"') { - inString = false; - out += ch; - continue; - } - if (ch === '\n') { - out += '\\n'; - continue; - } - if (ch === '\r') { - out += '\\r'; - continue; - } - if (ch === '\t') { - out += '\\t'; - continue; - } - out += ch; - } else { - if (ch === '"') inString = true; - out += ch; - } - } - return out; -} - -/** Insert missing commas between JSON properties (e.g. `"..."\n "place_ids"`). */ -function repairMissingCommas(text) { - return String(text || '').replace( - /(")\s*\n(\s*")((?:message|reply|session_ids|sessionIds|place_ids|placeIds|speaker_ids|speakerIds|exhibitor_ids|exhibitorIds|show_qr|showQr)\s*")/g, - '$1,\n$2$3', - ); -} - -/** - * Extract a JSON string field even when the blob is not valid JSON - * (literal newlines, missing commas). - */ -function extractJsonStringField(text, field) { - const key = `"${field}"`; - const keyIdx = text.indexOf(key); - if (keyIdx < 0) return null; - let i = text.indexOf(':', keyIdx + key.length); - if (i < 0) return null; - i += 1; - while (i < text.length && /\s/.test(text[i])) i += 1; - if (text[i] !== '"') return null; - i += 1; - let out = ''; - let escaped = false; - for (; i < text.length; i += 1) { - const ch = text[i]; - if (escaped) { - if (ch === 'n') out += '\n'; - else if (ch === 'r') out += '\r'; - else if (ch === 't') out += '\t'; - else out += ch; - escaped = false; - continue; - } - if (ch === '\\') { - escaped = true; - continue; - } - if (ch === '"') { - const after = text.slice(i + 1); - if (/^\s*(,|}|"(?:message|reply|session_ids|sessionIds|place_ids|placeIds|hotel_ids|hotelIds)\b)/.test(after)) { - return out; - } - // quote mid-content — keep it - out += ch; - continue; - } - out += ch; - } - return out || null; -} - -function extractJsonArrayField(text, field) { - const re = new RegExp(`"${field}"\\s*:\\s*(\\[[\\s\\S]*?\\])`); - const m = String(text || '').match(re); - if (!m) return null; - try { - const arr = JSON.parse(m[1]); - return Array.isArray(arr) ? arr : null; - } catch { - return null; - } -} - -function normalizeCall2Parsed(parsed) { - if (!parsed || typeof parsed !== 'object') return null; - const message = String(parsed.message || parsed.reply || '').trim(); - const sessionIds = Array.isArray(parsed.session_ids) - ? parsed.session_ids - : Array.isArray(parsed.sessionIds) - ? parsed.sessionIds - : null; - const placeIds = Array.isArray(parsed.place_ids) - ? parsed.place_ids - : Array.isArray(parsed.placeIds) - ? parsed.placeIds - : null; - const speakerIds = Array.isArray(parsed.speaker_ids) - ? parsed.speaker_ids - : Array.isArray(parsed.speakerIds) - ? parsed.speakerIds - : null; - const exhibitorIds = Array.isArray(parsed.exhibitor_ids) - ? parsed.exhibitor_ids - : Array.isArray(parsed.exhibitorIds) - ? parsed.exhibitorIds - : null; - const hotelIds = Array.isArray(parsed.hotel_ids) - ? parsed.hotel_ids - : Array.isArray(parsed.hotelIds) - ? parsed.hotelIds - : null; - const showQr = - parsed.show_qr === true || - parsed.showQr === true || - parsed.show_qr === 'true' || - parsed.showQr === 'true'; - return { message, sessionIds, placeIds, speakerIds, exhibitorIds, hotelIds, showQr }; -} - -function emptyCall2Fields() { - return { - sessionIds: null, - placeIds: null, - speakerIds: null, - exhibitorIds: null, - hotelIds: null, - showQr: false, - }; -} - -/** When Haiku talks instead of wrapping JSON, keep the prose if it is attendee-facing. */ -function proseAsCall2Message(raw) { - let text = String(raw || '').trim(); - if (!text) return null; - if (text.startsWith('```')) { - text = text.replace(/^```(?:\w+)?\s*/i, '').replace(/\s*```$/i, '').trim(); - } - if (!text || text.startsWith('{')) return null; - if (looksLikeInternalNote(text)) return null; - if (text.length < 12) return null; - return text; -} - -function jsonOnlyHint(schema, extra = '') { - const extraBit = extra ? ` ${extra}` : ''; - return [ - `Reply with JSON only${extraBit}: ${schema}`, - 'Put the full attendee answer inside "message". No markdown. No prose outside the JSON object.', - ].join('\n'); -} - -function parseCall2Json(raw) { - const stripped = stripFences(raw); - const attempts = [ - stripped, - repairJsonControlChars(stripped), - repairMissingCommas(repairJsonControlChars(stripped)), - ]; - for (const candidate of attempts) { - try { - const parsed = normalizeCall2Parsed(JSON.parse(candidate)); - if (parsed?.message) return parsed; - } catch { - // try next - } - } - - // Last resort: pull fields without full JSON parse - const message = - extractJsonStringField(stripped, 'message') || - extractJsonStringField(stripped, 'reply'); - if (message && String(message).trim()) { - const showQrMatch = String(stripped).match(/"show_qr"\s*:\s*(true|false)/i) - || String(stripped).match(/"showQr"\s*:\s*(true|false)/i); - return { - message: String(message).trim(), - sessionIds: - extractJsonArrayField(stripped, 'session_ids') || - extractJsonArrayField(stripped, 'sessionIds'), - placeIds: - extractJsonArrayField(stripped, 'place_ids') || - extractJsonArrayField(stripped, 'placeIds'), - speakerIds: - extractJsonArrayField(stripped, 'speaker_ids') || - extractJsonArrayField(stripped, 'speakerIds'), - exhibitorIds: - extractJsonArrayField(stripped, 'exhibitor_ids') || - extractJsonArrayField(stripped, 'exhibitorIds'), - hotelIds: - extractJsonArrayField(stripped, 'hotel_ids') || - extractJsonArrayField(stripped, 'hotelIds'), - showQr: showQrMatch ? String(showQrMatch[1]).toLowerCase() === 'true' : false, - }; - } - - const prose = proseAsCall2Message(raw); - if (prose) return { message: prose, ...emptyCall2Fields() }; - return null; -} - -/** True when text looks like an internal DATA.note, not an attendee reply. */ -function looksLikeInternalNote(text) { - const s = String(text || ''); - return ( - /DATA\.(origin|location|faqs|note|sessions)/i.test(s) || - /answer with general transport knowledge/i.test(s) || - /direct the attendee/i.test(s) || - /do not invent/i.test(s) || - /do not refuse/i.test(s) || - /no .*faq is configured/i.test(s) || - /share the registration url/i.test(s) || - /do not name the event/i.test(s) - ); -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateAgendaReply({ intent, message, history, entities, data }) { - const system = buildCall2SystemPrompt(intent); - if (!system) { - return { - reply: FALLBACK_REPLY, - cards: [], - sessionIds: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const cardMode = usesSessionCards(intent); - const slimEntities = compactEntities(entities); - const slimData = slimDataForLlm(data, intent); - - const userParts = [ - 'USER:', - String(message || '').trim(), - ]; - if (recent) { - userParts.push('', 'HISTORY:', recent); - } - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - cardMode - ? jsonOnlyHint('{"message":string,"session_ids":string[]}', 'using DATA.sessions[].id') - : jsonOnlyHint('{"message":string,"session_ids":[]}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][call2][context]', { - intent, - sessionsIn: Array.isArray(data?.sessions) ? data.sessions.length : 0, - sessionsOut: Array.isArray(slimData.sessions) ? slimData.sessions.length : 0, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - - let reply = parsed?.message || ''; - let sessionIds = []; - - if (cardMode) { - // Prefer model ids when valid; if empty/invalid, fall back to DATA.sessions order. - // (Models often return session_ids:[] with a "Here are…" intro — cards would then be blank.) - sessionIds = resolveSessionIds(parsed?.sessionIds, data); - if (!parsed && !reply) reply = FALLBACK_REPLY; - } else if (parsed && Array.isArray(parsed.sessionIds)) { - const allowed = new Set( - (Array.isArray(data?.sessions) ? data.sessions : []).map((s) => String(s.id)), - ); - sessionIds = parsed.sessionIds - .map((id) => String(id || '').trim()) - .filter((id) => allowed.has(id)); - } - - if (!sessionIds.length) { - const note = String(data?.note || '').trim(); - const looksLikeListing = /here are|following|your (personal )?schedule|in your schedule/i.test( - reply, - ); - if (note && (!reply || looksLikeListing)) { - reply = - intent === 'agenda_personal_schedule' - ? "You don't have any saved sessions yet. Bookmark sessions in the event agenda to see them here." - : note; - } else if (!reply) { - reply = note || 'I could not find matching sessions for that.'; - } - } else if (!reply) { - reply = 'Here are the relevant sessions:'; - } - if (/no matching sessions found/i.test(reply)) { - reply = "I don't have the programme listed yet. Try asking about a day or speaker."; - } - - const cards = cardMode - ? buildSessionCards(sessionIds, data, { - includeResources: intent === 'agenda_download_materials' || Boolean(data?.includeResources), - }) - : []; - - console.log('[step2][call2][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - sessionIds, - cardCount: cards.length, - }); - - return { - reply: reply.slice(0, 1200), - cards, - sessionIds, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Call 2 failed:', err?.message || err); - const sessionIds = cardMode ? resolveSessionIds(null, data) : []; - const cards = cardMode - ? buildSessionCards(sessionIds, data, { - includeResources: intent === 'agenda_download_materials', - }) - : []; - const emptyNote = - intent === 'agenda_personal_schedule' - ? "You don't have any saved sessions yet. Bookmark sessions in the event agenda to see them here." - : String(data?.note || '').trim(); - return { - reply: cards.length - ? 'Here are the relevant sessions:' - : emptyNote || FALLBACK_REPLY, - cards, - sessionIds, - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateVenueReply({ intent, message, history, entities, data }) { - const system = buildVenueCall2SystemPrompt(intent); - if (!system) { - return { - reply: VENUE_FALLBACK_REPLY, - cards: [], - placeIds: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const placeMode = usesPlaceCards(intent) && Array.isArray(data?.places) && data.places.length; - const slimEntities = compactEntities(entities); - const slimData = slimVenueDataForLlm(data); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - placeMode - ? jsonOnlyHint('{"message":string,"place_ids":string[]}', 'using DATA.places[].id when useful') - : jsonOnlyHint('{"message":string,"place_ids":[]}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][venue][context]', { - intent, - faqCount: Array.isArray(data?.faqs) ? data.faqs.length : 0, - placesIn: Array.isArray(data?.places) ? data.places.length : 0, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2-venue', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - let placeIds = []; - - if (placeMode) { - placeIds = resolvePlaceIds(parsed?.placeIds, data); - } - - if (!reply) { - // Never surface DATA.note to attendees — it is model instruction text. - if (intent === 'venue_map_request' && (data?.location || data?.eventName)) { - const bits = [data.eventName, data.location].filter(Boolean); - reply = bits.length - ? `I don't have a venue map on file. The event venue is: ${bits.join(' — ')}.` - : VENUE_FALLBACK_REPLY; - } else if (intent === 'venue_wayfinding') { - const hallNames = (data?.halls || []).map((h) => h.hall).filter(Boolean); - const roomNames = (data?.rooms || []).map((r) => r.room).filter(Boolean); - const listed = [...new Set([...hallNames, ...roomNames])].slice(0, 12); - reply = listed.length - ? `Here are the halls/rooms I know: ${listed.join(', ')}. For more directions, please ask at the venue help desk.` - : 'For directions inside the venue, please ask at the venue help desk.'; - } else if (intent === 'venue_travel_directions' && (data?.location || data?.eventName)) { - const dest = data.location || 'the event venue'; - const from = data.origin ? ` from ${data.origin}` : ''; - reply = `I couldn't format travel details just now. The venue is ${dest}. Please check train/flight options${from}, then local transport to the venue, and verify live schedules.`; - } else { - reply = VENUE_FALLBACK_REPLY; - } - } - - const cards = placeMode ? buildPlaceCards(placeIds, data) : []; - if (intent === 'venue_map_request' && data?.mapMedia?.url) { - const mapCard = buildMapFileCard(data.mapMedia); - if (mapCard) cards.push(mapCard); - if (!reply || looksLikeInternalNote(reply)) { - reply = 'Here is the venue floor plan / map. You can view or download it below.'; - } - } - - console.log('[step2][venue][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - placeIds, - cardCount: cards.length, - }); - - return { - reply: reply.slice(0, 1600), - cards, - placeIds, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Venue Call 2 failed:', err?.message || err); - return { - reply: VENUE_FALLBACK_REPLY, - cards: [], - placeIds: [], - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -function ticketEmailOutcomeReply(data) { - if (data?.emailSent && data?.emailTo) { - return `I've emailed your ticket QR and details to ${data.emailTo}.`; - } - if (data?.emailError === 'rate_limited') { - return 'I emailed your ticket recently. Please check your inbox (and spam), or ask again in a minute.'; - } - if (data?.emailError === 'smtp_not_configured') { - return 'I can’t send email right now. I can still show your QR in chat if you ask.'; - } - if (data?.emailError === 'missing_email') { - return 'I couldn’t find a registered email on your account. Please contact the organizers.'; - } - return 'I couldn’t send the email just now. Please try again, or ask me to show your QR in chat.'; -} - -function contactReachLine(data) { - const email = String(data?.contact?.email || '').trim(); - const phone = String(data?.contact?.phone || '').trim(); - return [email && `email ${email}`, phone && `call ${phone}`].filter(Boolean).join(' or '); -} - -function ticketHowToRegisterFallback(data) { - const links = Array.isArray(data?.registrationLinks) ? data.registrationLinks : []; - if (links.length > 1) { - return 'You can join using the registration options below.'; - } - if (links.length === 1) { - return 'You can join using the registration button below.'; - } - const reach = contactReachLine(data); - if (reach) return `I don’t have a registration form on file. You can reach the organizers — ${reach}.`; - return TICKET_REGISTER_NONE_REPLY; -} - -function sanitizeOutOfScopeReply(reply, eventName) { - let text = String(reply || '').trim(); - text = text.replace( - /^(i('m| am) afraid[, ]+)?i can only (assist|help) with (the )?[^.!?]{0,100}(event details)?[.!]+\s*/i, - '', - ); - const name = String(eventName || '').trim(); - if (name) { - const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - text = text.replace(new RegExp(escaped, 'gi'), '').replace(/\s{2,}/g, ' ').trim(); - text = text.replace(/\s+,/g, ',').replace(/^[,.\s]+/, '').trim(); - } - if (!text || text.length < 8) { - return systemDeterministicFallback('out_of_scope'); - } - return text; -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateTicketReply({ intent, message, history, entities, data }) { - const registerMode = intent === 'ticket_how_to_register'; - const registerCards = registerMode ? buildRegistrationCtaCards(data?.registrationLinks) : []; - - const system = buildTicketCall2SystemPrompt(intent); - if (!system) { - return { - reply: registerMode ? ticketHowToRegisterFallback(data) : TICKET_FALLBACK_REPLY, - cards: registerCards, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const refuse = data?.policy === 'refuse_contact_organizer' - || intent === 'ticket_transfer_request' - || intent === 'ticket_upgrade'; - - if (refuse) { - return { - reply: TICKET_REFUSE_REPLY, - cards: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const qrMode = usesQrCard(intent) && Boolean(data?.qr?.imageUrl); - - // Email send outcome is authoritative — never let Call 2 invent SMTP failure/success. - if (intent === 'ticket_email_qr') { - const reply = ticketEmailOutcomeReply(data); - const cards = qrMode ? buildTicketQrCard(data) : []; - console.log('[step2][ticket][email]', { - emailSent: data?.emailSent, - emailError: data?.emailError, - emailTo: data?.emailTo, - cardCount: cards.length, - }); - return { - reply, - cards, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const slimEntities = compactEntities(entities); - const slimData = slimTicketDataForLlm(data); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - jsonOnlyHint('{"message":string,"show_qr":boolean}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][ticket][context]', { - intent, - found: data?.found, - emailSent: data?.emailSent, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2-ticket', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - - let showQr = Boolean(parsed?.showQr); - if (intent === 'ticket_qr_display' && qrMode) showQr = true; - if ( - intent === 'ticket_status_lookup' - || intent === 'ticket_document_receipt' - || intent === 'ticket_how_to_register' - ) { - showQr = false; - } - - if (!reply) { - if (intent === 'ticket_document_receipt' && data?.receipt) { - const r = data.receipt; - const bits = [ - r.ticketName ? `Ticket: ${r.ticketName}` : null, - r.amountLabel ? `Amount: ${r.amountLabel}` : null, - r.status ? `Status: ${r.status}` : null, - r.orderId ? `Order: ${r.orderId}` : null, - ].filter(Boolean); - reply = bits.length - ? `Here is your receipt:\n${bits.join('\n')}` - : 'I found a purchase record but could not format the receipt.'; - } else if (intent === 'ticket_status_lookup' && data?.found) { - const status = data.membership?.statusLabel || data.membership?.status || 'registered'; - const ticket = data.ticket?.name ? ` (${data.ticket.name})` : ''; - reply = `${data.attendee?.name || 'Your registration'} is ${status}${ticket}.`; - } else if (intent === 'ticket_qr_display' && qrMode) { - reply = 'Here is your check-in QR code.'; - } else if (registerMode) { - reply = ticketHowToRegisterFallback(data); - } else { - reply = String(data?.note || '').trim() || TICKET_FALLBACK_REPLY; - } - } - - if ( - registerMode - && (/i can only (assist|help) with/i.test(reply) || /i('m| am) afraid/i.test(reply)) - ) { - reply = ticketHowToRegisterFallback(data); - } - - if (registerMode) { - reply = String(reply || '').replace(/https?:\/\/\S+/gi, '').replace(/\s{2,}/g, ' ').trim() - || ticketHowToRegisterFallback(data); - } - - const cards = registerMode - ? registerCards - : showQr && qrMode - ? buildTicketQrCard(data) - : []; - - console.log('[step2][ticket][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - showQr, - cardCount: cards.length, - }); - - return { - reply: reply.slice(0, 1600), - cards, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Ticket Call 2 failed:', err?.message || err); - const cards = registerMode - ? registerCards - : intent === 'ticket_qr_display' && qrMode - ? buildTicketQrCard(data) - : []; - let reply = TICKET_FALLBACK_REPLY; - if (registerMode) { - reply = ticketHowToRegisterFallback(data); - } else if (cards.length) { - reply = 'Here is your check-in QR code.'; - } - return { - reply, - cards, - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateSpeakerReply({ intent, message, history, entities, data }) { - const system = buildSpeakerCall2SystemPrompt(intent); - if (!system) { - return { - reply: SPEAKER_FALLBACK_REPLY, - cards: [], - sessionIds: [], - speakerIds: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - // For session cards, meet-and-greet prefers meetSessions when present - const sessionData = - intent === 'speaker_meet_and_greet' && Array.isArray(data?.meetSessions) && data.meetSessions.length - ? { ...data, sessions: data.meetSessions } - : data; - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const slimEntities = compactEntities(entities); - const slimData = slimSpeakerDataForLlm(data); - const speakerMode = usesSpeakerCards(intent); - const sessionMode = usesSpeakerSessionCards(intent); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - jsonOnlyHint('{"message":string,"speaker_ids":string[],"session_ids":string[]}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][speaker][context]', { - intent, - speakers: Array.isArray(data?.speakers) ? data.speakers.length : 0, - candidates: Array.isArray(data?.candidates) ? data.candidates.length : 0, - sessions: Array.isArray(sessionData?.sessions) ? sessionData.sessions.length : 0, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2-speaker', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - - let speakerIds = speakerMode - ? resolveSpeakerIdsFromModel(parsed?.speakerIds, data) - : []; - let sessionIds = sessionMode && !data?.isSpeakerList - ? resolveSessionIds(parsed?.sessionIds, sessionData) - : []; - - if (data?.isSpeakerList) { - sessionIds = []; - if (!speakerIds.length && Array.isArray(data.speakers)) { - speakerIds = data.speakers.map((s) => String(s.id)).filter(Boolean).slice(0, 8); - } - } - - if (data?.needsClarification) { - sessionIds = []; - speakerIds = []; - const names = (Array.isArray(data.candidates) ? data.candidates : []) - .map((c) => c.name) - .filter(Boolean) - .slice(0, 6); - // Deterministic when no real candidates — models invent names otherwise - if (!names.length) { - reply = 'Which speaker did you mean? Please share their full name.'; - } else if (!reply) { - reply = `I found more than one match: ${names.join(', ')}. Which speaker did you mean?`; - } - } - - if (!reply) { - if (data?.isSpeakerList) { - const names = (Array.isArray(data.speakers) ? data.speakers : []) - .map((s) => s.name) - .filter(Boolean); - reply = names.length - ? `Here are speakers on the programme:\n${names.map((n) => `• ${n}`).join('\n')}\n\nAsk about anyone for a fuller bio.` - : String(data?.note || '').trim() || SPEAKER_FALLBACK_REPLY; - } - } - - if (!reply) { - const sp = Array.isArray(data?.speakers) && data.speakers[0] ? data.speakers[0] : null; - if (intent === 'speaker_contact_info' && sp) { - if (!sp.isPublic) { - reply = - 'That speaker’s profile is not publicly listed. Please ask at the help desk or contact the organizers.'; - } else { - const links = [ - sp.socialLinks?.linkedin ? `LinkedIn: ${sp.socialLinks.linkedin}` : null, - sp.socialLinks?.twitter ? `Twitter/X: ${sp.socialLinks.twitter}` : null, - sp.socialLinks?.website ? `Website: ${sp.socialLinks.website}` : null, - ].filter(Boolean); - reply = links.length - ? `Here’s the public contact info I have for ${sp.name}:\n${links.join('\n')}` - : String(data?.note || '').trim() - || `I don’t have public social links on file for ${sp.name}. Please ask at the help desk.`; - } - } else if (intent === 'speaker_meet_and_greet') { - if (sessionIds.length) { - reply = 'Here are meet-and-greet / networking sessions related to that speaker:'; - } else { - reply = - String(data?.note || '').trim() - || 'I don’t see a dedicated meet-and-greet for that speaker on the schedule. You can catch their sessions on the agenda, or ask at the help desk.'; - } - } else if (sp) { - if (!sp.isPublic) { - reply = - 'That speaker’s profile is not publicly listed. I can still help with their sessions on the agenda if you ask when they speak.'; - } else { - const bits = [ - sp.name, - sp.speakerRole, - [sp.title, sp.organizationName].filter(Boolean).join(', '), - ].filter(Boolean); - reply = sp.bio - ? `${bits.join(' — ')}\n\n${sp.bio}` - : String(data?.note || '').trim() - || `${bits.join(' — ') || 'Speaker'} — no biography text is on file.`; - } - } else { - reply = String(data?.note || '').trim() || SPEAKER_FALLBACK_REPLY; - } - } - - const cards = []; - if (speakerMode) { - cards.push(...buildSpeakerCards(speakerIds, data)); - } - if (sessionMode && sessionIds.length) { - cards.push(...buildSessionCards(sessionIds, sessionData)); - } - - console.log('[step2][speaker][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - speakerIds, - sessionIds, - cardCount: cards.length, - }); - - return { - reply: reply.slice(0, 1800), - cards, - sessionIds, - speakerIds, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Speaker Call 2 failed:', err?.message || err); - const speakerIds = speakerMode ? resolveSpeakerIdsFromModel(null, data) : []; - const sessionIds = - sessionMode && !data?.isSpeakerList ? resolveSessionIds(null, sessionData) : []; - const cards = [ - ...(speakerMode ? buildSpeakerCards(speakerIds, data) : []), - ...(sessionIds.length ? buildSessionCards(sessionIds, sessionData) : []), - ]; - let reply = String(data?.note || '').trim() || SPEAKER_FALLBACK_REPLY; - if (data?.isSpeakerList && Array.isArray(data.speakers) && data.speakers.length) { - const names = data.speakers.map((s) => s.name).filter(Boolean); - reply = names.length - ? `Here are speakers on the programme:\n${names.map((n) => `• ${n}`).join('\n')}\n\nAsk about anyone for a fuller bio.` - : reply; - } else if (cards.length) { - reply = 'Here is what I found about that speaker:'; - } - return { - reply, - cards, - sessionIds, - speakerIds, - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -function boothHintForExhibitor(ex) { - if (!ex) return ''; - const bits = [ex.boothNumber ? `booth ${ex.boothNumber}` : null, ex.hall ? `hall ${ex.hall}` : null] - .filter(Boolean); - return bits.length ? ` (${bits.join(', ')})` : ''; -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateExhibitorReply({ intent, message, history, entities, data }) { - const system = buildExhibitorCall2SystemPrompt(intent); - if (!system) { - return { - reply: EXHIBITOR_FALLBACK_REPLY, - cards: [], - exhibitorIds: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const cardMode = usesExhibitorCards(intent); - const faqs = Array.isArray(data?.faqs) ? data.faqs : []; - const hasFaqs = faqs.length > 0; - - // Deterministic refuses — never invent booking or demo times - if (intent === 'exhibitor_book_meeting' || data?.refuseBooking) { - if (data?.needsClarification && Array.isArray(data.candidates) && data.candidates.length) { - const names = data.candidates.map((c) => c.name).filter(Boolean).slice(0, 6); - return { - reply: names.length - ? `I found more than one match: ${names.join(', ')}. Which exhibitor did you mean?` - : 'Which exhibitor did you mean? Please share the company name.', - cards: [], - exhibitorIds: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - const exhibitorIds = cardMode ? resolveExhibitorIdsFromModel(null, data) : []; - const cards = cardMode ? buildExhibitorCards(exhibitorIds, data) : []; - const ex = Array.isArray(data?.exhibitors) && data.exhibitors[0] ? data.exhibitors[0] : null; - let reply = EXHIBITOR_BOOKING_REFUSE_REPLY; - if (ex) { - reply = `I can’t book meetings in this chat. Please visit ${ex.name}${boothHintForExhibitor(ex)} or ask exhibitor staff / the help desk.`; - } - return { - reply, - cards, - exhibitorIds, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - if (intent === 'exhibitor_demo_schedule' && !hasFaqs && !data?.needsClarification) { - const exhibitorIds = cardMode ? resolveExhibitorIdsFromModel(null, data) : []; - const cards = cardMode ? buildExhibitorCards(exhibitorIds, data) : []; - const ex = Array.isArray(data?.exhibitors) && data.exhibitors[0] ? data.exhibitors[0] : null; - let reply = EXHIBITOR_DEMO_NONE_REPLY; - if (ex) { - reply = `I don’t have a demo schedule listed for ${ex.name}. Please check at their booth${boothHintForExhibitor(ex)} or ask at the help desk.`; - } - return { - reply, - cards, - exhibitorIds, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const slimEntities = compactEntities(entities); - const slimData = slimExhibitorDataForLlm(data); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - jsonOnlyHint('{"message":string,"exhibitor_ids":string[]}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][exhibitor][context]', { - intent, - exhibitors: Array.isArray(data?.exhibitors) ? data.exhibitors.length : 0, - candidates: Array.isArray(data?.candidates) ? data.candidates.length : 0, - faqs: faqs.length, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2-exhibitor', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - - let exhibitorIds = cardMode - ? resolveExhibitorIdsFromModel(parsed?.exhibitorIds, data) - : []; - - if (data?.needsClarification) { - exhibitorIds = []; - const names = (Array.isArray(data.candidates) ? data.candidates : []) - .map((c) => c.name) - .filter(Boolean) - .slice(0, 6); - if (!names.length) { - reply = - intent === 'exhibitor_by_category' - ? 'Which category or industry are you interested in?' - : 'Which exhibitor did you mean? Please share the company name.'; - } else if (!reply) { - reply = `I found more than one match: ${names.join(', ')}. Which exhibitor did you mean?`; - } - } - - if (!reply) { - if (data?.isExhibitorList || (Array.isArray(data?.exhibitors) && data.exhibitors.length > 1)) { - const names = (Array.isArray(data.exhibitors) ? data.exhibitors : []) - .map((e) => { - const loc = [e.boothNumber && `Booth ${e.boothNumber}`, e.hall].filter(Boolean).join(', '); - return loc ? `${e.name} (${loc})` : e.name; - }) - .filter(Boolean); - reply = names.length - ? `Here are exhibitors I found:\n${names.map((n) => `• ${n}`).join('\n')}\n\nAsk about a company for more detail.` - : String(data?.note || '').trim() || EXHIBITOR_FALLBACK_REPLY; - } else if (Array.isArray(data?.exhibitors) && data.exhibitors[0]) { - const ex = data.exhibitors[0]; - const bits = [ - ex.name, - ex.boothNumber ? `Booth ${ex.boothNumber}` : null, - ex.hall ? `Hall ${ex.hall}` : null, - ex.category, - ].filter(Boolean); - reply = ex.shortDescription - ? `${bits.join(' · ')}\n\n${ex.shortDescription}` - : bits.join(' · ') || EXHIBITOR_FALLBACK_REPLY; - } else { - reply = String(data?.note || '').trim() || EXHIBITOR_FALLBACK_REPLY; - } - } - - const cards = cardMode ? buildExhibitorCards(exhibitorIds, data) : []; - - console.log('[step2][exhibitor][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - exhibitorIds, - cardCount: cards.length, - }); - - return { - reply: reply.slice(0, 1800), - cards, - exhibitorIds, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Exhibitor Call 2 failed:', err?.message || err); - const exhibitorIds = cardMode ? resolveExhibitorIdsFromModel(null, data) : []; - const cards = cardMode ? buildExhibitorCards(exhibitorIds, data) : []; - let reply = String(data?.note || '').trim() || EXHIBITOR_FALLBACK_REPLY; - if (Array.isArray(data?.exhibitors) && data.exhibitors.length) { - const names = data.exhibitors.map((e) => e.name).filter(Boolean); - reply = names.length - ? `Here are exhibitors I found:\n${names.map((n) => `• ${n}`).join('\n')}` - : reply; - } - return { - reply, - cards, - exhibitorIds, - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -function techDeterministicFallback(intent, data) { - const faqs = Array.isArray(data?.faqs) ? data.faqs : []; - if (faqs.length) { - return faqs.map((f) => f.a).filter(Boolean).join('\n\n') || TECH_FALLBACK_REPLY; - } - if (intent === 'wifi_credentials') return WIFI_NONE_REPLY; - if (intent === 'app_issue_streaming') { - return formatStaticTipsReply( - "Sorry you're having trouble with streaming. A few quick things to try:", - data?.staticTips || STREAMING_STATIC_TIPS, - ); - } - if (intent === 'app_issue_account') { - return formatStaticTipsReply( - "Sorry you're having trouble signing in. Try these steps:", - data?.staticTips || ACCOUNT_STATIC_TIPS, - ); - } - if (intent === 'app_feature_guidance') return FEATURE_NONE_REPLY; - return TECH_FALLBACK_REPLY; -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateTechReply({ intent, message, history, entities, data }) { - const system = buildTechCall2SystemPrompt(intent); - if (!system) { - return { - reply: TECH_FALLBACK_REPLY, - cards: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const faqs = Array.isArray(data?.faqs) ? data.faqs : []; - const hasFaqs = faqs.length > 0; - - // Wi‑Fi with no FAQs: never call the model (avoids invented passwords) - if (intent === 'wifi_credentials' && !hasFaqs) { - return { - reply: WIFI_NONE_REPLY, - cards: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const slimEntities = compactEntities(entities); - const slimData = slimTechDataForLlm(data); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - jsonOnlyHint('{"message":string}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][tech][context]', { - intent, - faqCount: faqs.length, - hasStaticTips: Array.isArray(data?.staticTips) && data.staticTips.length > 0, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2-tech', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - if (!reply) reply = techDeterministicFallback(intent, data); - - console.log('[step2][tech][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - }); - - return { - reply: reply.slice(0, 1800), - cards: [], - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Tech Call 2 failed:', err?.message || err); - return { - reply: techDeterministicFallback(intent, data), - cards: [], - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -function contactLinesFromSystemData(data) { - const contact = data?.contact && typeof data.contact === 'object' ? data.contact : null; - if (contact?.text) return String(contact.text).trim(); - const bits = []; - if (contact?.email) bits.push(`Email: ${contact.email}`); - if (contact?.phone) bits.push(`Phone: ${contact.phone}`); - if (contact?.detail) bits.push(String(contact.detail)); - if (bits.length) return bits.join('\n'); - const faqs = Array.isArray(data?.faqs) ? data.faqs : []; - return faqs.map((f) => String(f?.a || '').trim()).filter(Boolean).join('\n') || ''; -} - -function appendContactBlock(reply, data) { - const contacts = contactLinesFromSystemData(data); - if (!contacts) return reply; - const text = String(reply || '').trim(); - // Already includes the email or phone from DATA - const email = String(data?.contact?.email || '').trim(); - const phone = String(data?.contact?.phone || '').trim(); - if (email && text.toLowerCase().includes(email.toLowerCase())) return text; - if (phone) { - const digits = phone.replace(/\D/g, ''); - if (digits.length >= 6 && text.replace(/\D/g, '').includes(digits)) return text; - } - return text ? `${text}\n\nOrganizer contact:\n${contacts}` : `Organizer contact:\n${contacts}`; -} - -function systemDeterministicFallback(intent, data) { - const emailed = data?.emailSent === true; - const contacts = contactLinesFromSystemData(data); - - if (intent === 'emergency_medical') { - const base = emailed - ? `${EMERGENCY_SAFETY_LINE} I’ve also notified the organizers. Please get help on site right away.` - : `${EMERGENCY_SAFETY_LINE} Please also ask at the help desk or info desk.`; - return appendContactBlock(base, data); - } - if (intent === 'lost_and_found') { - return emailed - ? 'I’ve notified the organizers about your lost-and-found request. Please share a short description of the item if you haven’t, and check with the help desk / registration.' - : 'For lost and found, please check with the help desk or registration desk and share a short description of the item.'; - } - if (intent === 'human_agent_escalation') { - if (contacts) { - const lead = emailed - ? 'I’ve notified the organizers. You can also reach them here:' - : 'You can reach the organizers here:'; - return `${lead}\n${contacts}\n\nYou can also ask at the info desk, or tell me how else I can help with the agenda, venue, or your ticket.`; - } - return emailed - ? 'I’ve notified the organizers. Meanwhile you can ask at the info desk, or tell me how else I can help with the agenda, venue, or your ticket.' - : 'Please speak with staff at the info desk. I can still help with the agenda, venue, or your ticket while you wait.'; - } - if (intent === 'chitchat_greeting') { - return 'Hi — ask me about the agenda, venue, or your ticket.'; - } - if (intent === 'out_of_scope') { - return 'I can only help with this event — try the agenda, venue, or your ticket.'; - } - if (intent === 'event_adjacent') { - const city = data?.city || data?.location; - if (data?.dish && city) { - return `Around ${city} you can look for ${data.dish} at local spots — check current hours on a maps app before you go.`; - } - if (city) { - return `Happy to suggest a plan around ${city} after the event. Tell me what you like (food, a fort, a relaxed walk) and I’ll keep it to this city.`; - } - return 'I don’t have the event city on file, so I can’t suggest a local itinerary. Ask about the agenda or venue instead.'; - } - return SYSTEM_FALLBACK_REPLY; -} - -function ensureEmergencyLead(reply) { - const text = String(reply || '').trim(); - if (!text) return EMERGENCY_SAFETY_LINE; - if (/security|medical|emergency|ambulance|first aid/i.test(text.slice(0, 220))) { - return text; - } - return `${EMERGENCY_SAFETY_LINE} ${text}`; -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateSystemReply({ intent, message, history, entities, data }) { - const system = buildSystemCall2SystemPrompt(intent); - const greetingChips = intent === 'chitchat_greeting' ? DEFAULT_QUICK_REPLIES : null; - const adjacent = intent === 'event_adjacent'; - const placeMode = adjacent && Array.isArray(data?.places) && data.places.length; - - if (!system) { - return { - reply: systemDeterministicFallback(intent, data), - cards: [], - placeIds: [], - quickReplies: greetingChips, - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const slimEntities = compactEntities(entities); - const slimData = slimSystemDataForLlm(data); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - userParts.push( - '', - 'DATA:', - JSON.stringify(slimData), - '', - placeMode - ? jsonOnlyHint('{"message":string,"place_ids":string[]}', 'using DATA.places[].id when useful') - : jsonOnlyHint('{"message":string}'), - ); - const userText = userParts.join('\n'); - - console.log('[step2][system][context]', { - intent, - adjacent, - strongModel: adjacent, - emailSent: Boolean(data?.emailSent), - rateLimited: Boolean(data?.rateLimited), - hasContact: Boolean(data?.contact?.email || data?.contact?.phone || data?.contact?.text), - faqCount: Array.isArray(data?.faqs) ? data.faqs.length : 0, - placesIn: Array.isArray(data?.places) ? data.places.length : 0, - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: adjacent ? 'call2-adjacent' : 'call2-system', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - if (!reply) reply = systemDeterministicFallback(intent, data); - if (intent === 'out_of_scope') reply = sanitizeOutOfScopeReply(reply, data?.eventName); - if (intent === 'emergency_medical') reply = ensureEmergencyLead(reply); - - let placeIds = []; - if (placeMode) placeIds = resolvePlaceIds(parsed?.placeIds, data); - const cards = placeMode ? buildPlaceCards(placeIds, data) : []; - - // Never let the model invent a successful organizer email - if ( - data?.emailSent !== true - && /notified the organizers|emailed the organizers|sent (an )?email to the organizers/i.test(reply) - ) { - reply = systemDeterministicFallback(intent, data); - if (intent === 'emergency_medical') reply = ensureEmergencyLead(reply); - } - - // Ensure Bot FAQ contacts are always surfaced for human/emergency intents - if (intent === 'human_agent_escalation' || intent === 'emergency_medical') { - reply = appendContactBlock(reply, data); - // If the model claimed "no contact" despite DATA.contact, replace with deterministic reply - if ( - (data?.contact?.email || data?.contact?.phone || data?.contact?.text) - && /don'?t have.*(contact|direct line)|no (organizer )?contact|could not be emailed since we don'?t have/i.test(reply) - ) { - reply = systemDeterministicFallback(intent, data); - if (intent === 'emergency_medical') reply = ensureEmergencyLead(reply); - } - } - - console.log('[step2][system][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - placeIds, - cardCount: cards.length, - quickReplies: greetingChips ? greetingChips.length : 0, - }); - - return { - reply: reply.slice(0, adjacent ? 2200 : 1800), - cards, - placeIds, - quickReplies: greetingChips, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] System Call 2 failed:', err?.message || err); - let reply = systemDeterministicFallback(intent, data); - if (intent === 'emergency_medical') reply = ensureEmergencyLead(reply); - return { - reply, - cards: [], - placeIds: [], - quickReplies: greetingChips, - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -function localDeterministicFallback(intent, data) { - const location = data?.location ? ` near ${data.location}` : ' near the venue'; - if (intent === 'accommodation') { - const faq = Array.isArray(data?.faqs) - ? data.faqs.map((f) => String(f?.a || '').trim()).filter(Boolean).join('\n\n') - : ''; - if (faq) return faq; - if (data?.hostManagedAccommodation) return HOST_MANAGED_ACCOMMODATION_REPLY; - if (Array.isArray(data?.hotels) && data.hotels.length) { - return `Here are some stay options${location}. Please verify rates and availability yourself before booking.`; - } - return `Accommodation isn’t arranged by the organizer, and I don’t have hotel suggestions on file yet. You can book a stay${location}, or ask at the help desk.`; - } - if (intent === 'tourist_places') { - if (data?.categoryNoMatch) return String(data.categoryNoMatch); - if (Array.isArray(data?.places) && data.places.length) { - const label = data?.requestedCategory?.label; - return label - ? `Here are some ${label}${location}.` - : `Here are some places to visit${location}.`; - } - return `I don’t have sightseeing suggestions on file yet. You can explore${location}, or ask at the help desk for local tips.`; - } - return LOCAL_FALLBACK_REPLY; -} - -/** - * @param {{ intent: string, message: string, history?: object[], entities?: object, data?: object }} opts - */ -async function generateLocalReply({ intent, message, history, entities, data }) { - const system = buildLocalCall2SystemPrompt(intent); - if (!system) { - return { - reply: LOCAL_FALLBACK_REPLY, - cards: [], - placeIds: [], - hotelIds: [], - modelId: null, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } - - const placeMode = usesLocalPlaceCards(intent) && Array.isArray(data?.places) && data.places.length; - const hotelMode = - usesLocalHotelCards(intent) - && !data?.hostManagedAccommodation - && Array.isArray(data?.hotels) - && data.hotels.length; - - const modelId = getCall2ModelId(); - const client = createClient(getRegion()); - const recent = formatHistory(history); - const slimEntities = compactEntities(entities); - const slimData = slimLocalDataForLlm(data); - - const userParts = ['USER:', String(message || '').trim()]; - if (recent) userParts.push('', 'HISTORY:', recent); - if (Object.keys(slimEntities).length) { - userParts.push('', 'ENTITIES:', JSON.stringify(slimEntities)); - } - - let jsonHint = jsonOnlyHint('{"message":string,"place_ids":[],"hotel_ids":[]}'); - if (placeMode) { - jsonHint = jsonOnlyHint( - '{"message":string,"place_ids":string[],"hotel_ids":[]}', - 'using DATA.places[].id when useful', - ); - } else if (hotelMode) { - jsonHint = jsonOnlyHint( - '{"message":string,"place_ids":[],"hotel_ids":string[]}', - 'using DATA.hotels[].id when useful', - ); - } - - userParts.push('', 'DATA:', JSON.stringify(slimData), '', jsonHint); - const userText = userParts.join('\n'); - - console.log('[step2][local][context]', { - intent, - placesIn: Array.isArray(data?.places) ? data.places.length : 0, - hotelsIn: Array.isArray(data?.hotels) ? data.hotels.length : 0, - hostManaged: Boolean(data?.hostManagedAccommodation), - contextChars: userText.length, - }); - - try { - const response = await converse(client, modelId, { - system, - messages: [{ role: 'user', content: [{ text: userText }] }], - inferenceConfig: CALL2_INFERENCE, - logLabel: 'call2-local', - }); - - const raw = responseText(response); - const parsed = parseCall2Json(raw); - let reply = parsed?.message || ''; - if (looksLikeInternalNote(reply)) reply = ''; - - let placeIds = []; - let hotelIds = []; - if (placeMode) placeIds = resolvePlaceIds(parsed?.placeIds, data); - if (hotelMode) hotelIds = resolveHotelIds(parsed?.hotelIds, data); - - if (!reply) reply = localDeterministicFallback(intent, data); - - const cards = placeMode - ? buildPlaceCards(placeIds, data) - : hotelMode - ? buildAccommodationCards(hotelIds, data) - : []; - - console.log('[step2][local][parsed]', { - intent, - messagePreview: reply.slice(0, 160), - placeIds, - hotelIds, - cardCount: cards.length, - }); - - return { - reply: reply.slice(0, 1600), - cards, - placeIds, - hotelIds, - modelId, - usage: { - inputTokens: Number(response?.usage?.inputTokens || 0), - outputTokens: Number(response?.usage?.outputTokens || 0), - }, - }; - } catch (err) { - console.warn('[step2] Local Call 2 failed:', err?.message || err); - const reply = localDeterministicFallback(intent, data); - const placeIds = placeMode ? resolvePlaceIds([], data) : []; - const hotelIds = hotelMode ? resolveHotelIds([], data) : []; - const cards = placeMode - ? buildPlaceCards(placeIds, data) - : hotelMode - ? buildAccommodationCards(hotelIds, data) - : []; - return { - reply, - cards, - placeIds, - hotelIds, - modelId, - usage: { inputTokens: 0, outputTokens: 0 }, - }; - } -} - -module.exports = { - generateAgendaReply, - generateVenueReply, - generateTicketReply, - generateSpeakerReply, - generateExhibitorReply, - generateTechReply, - generateSystemReply, - generateLocalReply, - getCall2ModelId, - getCall2StrongModelId, - DEFAULT_CALL2_MODEL_ID, - FALLBACK_REPLY, - VENUE_FALLBACK_REPLY, - TICKET_FALLBACK_REPLY, - SPEAKER_FALLBACK_REPLY, - EXHIBITOR_FALLBACK_REPLY, - TECH_FALLBACK_REPLY, - SYSTEM_FALLBACK_REPLY, - LOCAL_FALLBACK_REPLY, - parseCall2Json, -}; diff --git a/src/services/ticketData.service.js b/src/services/ticketData.service.js index 5951102..56c84d0 100644 --- a/src/services/ticketData.service.js +++ b/src/services/ticketData.service.js @@ -21,12 +21,12 @@ const TICKET_INTENTS = new Set([ ]); const REGISTER_FAQ_CATEGORIES = new Set([ - 'ticket_checkin', 'registration_update', - 'checkin_timing', 'refund_policy', + 'how_to_register', + 'ticket_purchase', ]); -const REGISTER_FAQ_HINT = /\b(register|registration|ticket|sign ?up|pass|enrol|enroll)\b/i; +const REGISTER_FAQ_HINT = /\b(how to register|registration link|sign ?up|enrol|enroll|buy (a )?(ticket|pass))\b/i; const FAQ_ANSWER_MAX = 600; const AUTH_REQUIRED_TICKET_INTENTS = new Set([ diff --git a/src/services/venueData.service.js b/src/services/venueData.service.js index 4651d93..7639524 100644 --- a/src/services/venueData.service.js +++ b/src/services/venueData.service.js @@ -5,7 +5,22 @@ const Session = require('../models/Session'); const { faqAnswerText, mapMediaFromBot } = require('../utils/faqAnswer'); const { transportModesFromBot } = require('../utils/transportModes'); const { looksLikeFrontDesk } = require('../utils/organizerContact'); -const { extractTravelOrigin } = require('../utils/domainAction'); + +const TRAVEL_ORIGIN_STOP = new Set(['the', 'this', 'that', 'there', 'here', 'home', 'work', 'my', 'our']); + +function extractTravelOrigin(message, existing) { + const already = String(existing || '').trim(); + if (already) return already; + const t = String(message || ''); + const m = + t.match(/\b(?:travell?ing|traveling|coming|arriving)\s+from\s+([A-Za-z][A-Za-z.\s-]{1,32}?)(?:\s*[,.?!]|$)/i) + || t.match(/\bfrom\s+([A-Za-z][A-Za-z.]{2,32})\b/i); + if (!m) return null; + const city = String(m[1] || '').replace(/[.,]+$/g, '').trim(); + const first = city.split(/\s+/)[0].toLowerCase(); + if (!city || TRAVEL_ORIGIN_STOP.has(first)) return null; + return city; +} const VENUE_INTENTS = new Set([ 'venue_map_request', diff --git a/src/services/whatsappDrip.service.js b/src/services/whatsappDrip.service.js index 45b9b39..e57561e 100644 --- a/src/services/whatsappDrip.service.js +++ b/src/services/whatsappDrip.service.js @@ -9,10 +9,10 @@ const { sendTemplateMessage, sendMessageWithCtas, } = require('../utils/whatsappSend'); -const { interpolateReception } = require('../utils/receptionCopy'); +const { interpolateReception, interpolateCtas, buildEngagementUrl } = require('../utils/receptionCopy'); const { logWhatsApp, maskPhone } = require('../utils/whatsappLog'); const { listUpcomingSessions } = require('./agendaData.service'); -const { normalizeWhatsAppCtas } = require('../utils/whatsappCtas'); +const { normalizeWhatsAppCtas, isHttpCtaUrl, toPlainCtas } = require('../utils/whatsappCtas'); const Participant = require('../models/Participant'); const WINDOW_MS = 24 * 60 * 60 * 1000; @@ -125,6 +125,7 @@ async function scheduleDripsForSession(session, bot, { phase }) { delayMinutes: Number(drip.delayMinutes) || 0, trigger, contentKind, + ctaCount: contentKind === 'upcoming_agenda' ? 0 : normalizeWhatsAppCtas(drip.ctas).length, agendaCount: contentKind === 'upcoming_agenda' ? clampAgendaCount(drip.agendaCount) : undefined, dueNow: sendAt.getTime() <= now.getTime(), }); @@ -184,11 +185,63 @@ async function sendDueDrips() { const ev = await Event.findById(session.eventId).select('name').lean(); eventName = ev?.name || ''; } - const body = interpolateReception(job.body, { + const interpVars = { profileName: session.waProfileName, firstName, eventName, + eventId: session.eventId + ? String(session.eventId) + : bot.event + ? String(bot.event) + : '', + participantId: participant?._id + ? String(participant._id) + : session.participant + ? String(session.participant) + : '', + }; + let rawCtas = toPlainCtas(job.ctas); + if (!rawCtas.length && job.dripId) { + const drips = Array.isArray(bot.reception?.drips) ? bot.reception.drips : []; + const dripCfg = + drips.find((d) => d && String(d._id) === String(job.dripId)) || + drips.find((d) => dripIdentity(d) === String(job.dripId)); + if (dripCfg && Array.isArray(dripCfg.ctas) && dripCfg.ctas.length) { + rawCtas = toPlainCtas(dripCfg.ctas); + } + } + const needsUniqueUrl = + /\{engagementUrl\}/i.test(String(job.body || '')) || + rawCtas.some((c) => /\{engagementUrl\}|\{participantId\}/i.test(String(c.value || ''))); + if (needsUniqueUrl && !interpVars.participantId) { + logWhatsApp('drip.hold', { + jobId: String(job._id), + reason: 'waiting_for_engagement_url', + to: maskPhone(session.phoneNumber), + }); + continue; + } + const builtUrl = buildEngagementUrl(interpVars.eventId, interpVars.participantId); + const body = interpolateReception(job.body, { + ...interpVars, + engagementUrl: builtUrl, }); + const ctas = interpolateCtas(rawCtas, { ...interpVars, engagementUrl: builtUrl }).map((cta) => { + if (!cta || cta.type !== 'url') return cta; + let value = String(cta.value || '').trim(); + if (!isHttpCtaUrl(value) && builtUrl) value = builtUrl; + return { type: 'url', label: cta.label || 'Open', value }; + }); + const urlCta = ctas.find((c) => c && c.type === 'url'); + if (urlCta && !isHttpCtaUrl(urlCta.value)) { + logWhatsApp('drip.hold', { + jobId: String(job._id), + reason: 'cta_url_not_ready', + to: maskPhone(session.phoneNumber), + url: String(urlCta.value || '').slice(0, 80), + }); + continue; + } const isAgenda = job.contentKind === 'upcoming_agenda'; if (outsideWindow) { @@ -244,12 +297,12 @@ async function sendDueDrips() { body, mediaType: job.mediaType, mediaUrl: job.mediaUrl, - ctas: job.ctas, + ctas, }); } catch (sendErr) { - if (sendErr && sendErr.code === 'empty_body') { + if (sendErr && (sendErr.code === 'empty_body' || sendErr.code === 'cta_url_not_ready')) { job.status = 'skipped'; - job.lastError = 'empty_body'; + job.lastError = sendErr.code; await job.save(); continue; } @@ -259,7 +312,11 @@ async function sendDueDrips() { job.status = 'sent'; job.sentAt = new Date(); await job.save(); - logWhatsApp('drip.sent', { jobId: String(job._id), to: maskPhone(session.phoneNumber) }); + logWhatsApp('drip.sent', { + jobId: String(job._id), + to: maskPhone(session.phoneNumber), + ctaCount: Array.isArray(ctas) ? ctas.length : 0, + }); } catch (err) { console.error('[whatsappDrip]', err.message); job.status = 'failed'; diff --git a/src/services/whatsappFollowup.service.js b/src/services/whatsappFollowup.service.js index 3fd8575..0933978 100644 --- a/src/services/whatsappFollowup.service.js +++ b/src/services/whatsappFollowup.service.js @@ -19,6 +19,16 @@ function inboundText(messageData) { } async function handleFollowup({ bot, event, session, messageData }) { + if (!bot?.enabled || bot.status !== 'ACTIVE') { + logWhatsApp('followup.skip', { + reason: 'chatbot_disabled', + botId: bot?._id ? String(bot._id) : '', + enabled: Boolean(bot?.enabled), + status: bot?.status || '', + }); + return; + } + const type = String(messageData?.type || ''); const creds = await getOrgWhatsAppCredentials(String(bot.organization)); if (!creds?.accessToken || !creds.phoneNumberId) { @@ -53,7 +63,13 @@ async function handleFollowup({ bot, event, session, messageData }) { } session.lastSeenAt = new Date(); - const result = await handleChatTurn({ bot, event, session, message: msg }); + const result = await handleChatTurn({ + bot, + event, + session, + message: msg, + channel: 'whatsapp', + }); const reply = String(result.reply || '').trim().slice(0, 2500) || 'Sorry, I could not respond just now.'; const inTok = result.usage?.inputTokens || 0; const outTok = result.usage?.outputTokens || 0; diff --git a/src/services/whatsappInbound.service.js b/src/services/whatsappInbound.service.js index 7d949ca..0e9e3a4 100644 --- a/src/services/whatsappInbound.service.js +++ b/src/services/whatsappInbound.service.js @@ -134,6 +134,17 @@ async function processInboundMessage(jobData) { return; } + if (!bot.enabled || bot.status !== 'ACTIVE') { + logWhatsApp('followup.skip', { + reason: 'chatbot_disabled', + botId: String(bot._id), + enabled: Boolean(bot.enabled), + status: bot.status || '', + }); + await session.save(); + return; + } + await handleFollowup({ bot, event, session, messageData }); } diff --git a/src/services/whatsappReception.service.js b/src/services/whatsappReception.service.js index ecf354d..5e60b76 100644 --- a/src/services/whatsappReception.service.js +++ b/src/services/whatsappReception.service.js @@ -148,6 +148,8 @@ async function sendWelcomeAndDrips(bot, session, participant, eventName) { firstName, profileName, eventName, + eventId: session.eventId || '', + participantId: participant?._id ? String(participant._id) : '', }); await sendToSession(bot, session, text); session.welcomeSentAt = new Date(); diff --git a/src/utils/bedrock.js b/src/utils/bedrock.js index 954159b..7e48e19 100644 --- a/src/utils/bedrock.js +++ b/src/utils/bedrock.js @@ -4,7 +4,8 @@ const { } = require('@aws-sdk/client-bedrock-runtime'); const DEFAULT_ROUTER_MODEL_ID = 'amazon.nova-lite-v1:0'; -const DEFAULT_CALL2_MODEL_ID = 'anthropic.claude-3-haiku-20240307-v1:0'; +const DEFAULT_CALL2_MODEL_ID = 'global.anthropic.claude-haiku-4-5-20251001-v1:0'; +const DEFAULT_AGENT_MODEL_ID = DEFAULT_CALL2_MODEL_ID; function getRegion() { return ( @@ -23,6 +24,12 @@ function getCall2ModelId() { return process.env.BEDROCK_CALL2_MODEL_ID || DEFAULT_CALL2_MODEL_ID; } +/** Single model for tool routing + synthesis. */ +function getAgentModelId() { + const id = String(process.env.BEDROCK_AGENT_MODEL_ID || '').trim(); + return id || getCall2ModelId(); +} + /** Stronger Call 2 for event-adjacent only. Unset → same as Haiku Call 2. */ function getCall2StrongModelId() { const id = String(process.env.BEDROCK_CALL2_MODEL_STRONG_ID || '').trim(); @@ -36,7 +43,13 @@ function getCall2StrongModelId() { function resolveModelId(modelId, region = getRegion()) { const id = String(modelId || '').trim(); if (!id || id.startsWith('arn:aws:bedrock:')) return id; - if (/^(us|eu|apac)\./.test(id)) return id; + if (/^(us|eu|apac|au|jp|global)\./.test(id)) return id; + + // Haiku 4.5 has no in-region ID in ap-south-1 — use the global inference profile. + if (/^anthropic\.claude-haiku-4-5/.test(id)) { + return `global.${id}`; + } + if (id.startsWith('anthropic.')) return id; const mode = String(process.env.BEDROCK_USE_INFERENCE_PROFILES ?? 'auto').toLowerCase(); @@ -62,31 +75,81 @@ function createClient(region = getRegion()) { return new BedrockRuntimeClient(config); } -function responseText(response) { - return ( - response.output?.message?.content - ?.map((block) => block.text) - .filter(Boolean) - .join('\n') || '' - ); -} - function isLlmLogEnabled() { const v = String(process.env.BEDROCK_LOG_LLM || '').toLowerCase(); return v === 'true' || v === '1' || v === 'yes'; } +function contentSummary(content) { + const blocks = Array.isArray(content) ? content : []; + return { + text: blocks.map((b) => b?.text || '').filter(Boolean).join('\n'), + toolUses: blocks + .filter((b) => b?.toolUse) + .map((b) => ({ name: b.toolUse.name, toolUseId: b.toolUse.toolUseId })), + toolResults: blocks + .filter((b) => b?.toolResult) + .map((b) => b.toolResult.toolUseId), + }; +} + function messageTexts(messages) { if (!Array.isArray(messages)) return []; return messages.map((m) => ({ role: m?.role, - text: (Array.isArray(m?.content) ? m.content : []) - .map((b) => b?.text || '') - .filter(Boolean) - .join('\n'), + ...contentSummary(m?.content), })); } +function responseText(response) { + return contentSummary(response?.output?.message?.content).text; +} + +function toolUseBlocks(response) { + const blocks = Array.isArray(response?.output?.message?.content) + ? response.output.message.content + : []; + return blocks.filter((b) => b?.toolUse?.name && b.toolUse.toolUseId); +} + +function assistantMessageFromResponse(response) { + const msg = response?.output?.message; + if (!msg || msg.role !== 'assistant' || !Array.isArray(msg.content)) return null; + const content = sanitizeAssistantContent(msg.content); + if (!content.length) return null; + return { role: 'assistant', content }; +} + +/** Strip SDK-only fields so the next Converse request is valid. */ +function sanitizeAssistantContent(content) { + const blocks = []; + for (const b of Array.isArray(content) ? content : []) { + if (b?.toolUse?.toolUseId && b.toolUse.name) { + let input = b.toolUse.input; + if (typeof input === 'string') { + try { + input = JSON.parse(input); + } catch { + input = {}; + } + } + if (!input || typeof input !== 'object' || Array.isArray(input)) input = {}; + blocks.push({ + toolUse: { + toolUseId: String(b.toolUse.toolUseId), + name: String(b.toolUse.name), + input, + }, + }); + continue; + } + if (typeof b?.text === 'string' && b.text.length) { + blocks.push({ text: b.text }); + } + } + return blocks; +} + /** Claude 4.x on Bedrock rejects inference configs that set both temperature and topP. */ function inferenceForbidsTempAndTopP(modelId) { const id = String(modelId || '').toLowerCase(); @@ -117,21 +180,54 @@ function supportsPromptCache(modelId) { const id = String(modelId || '').toLowerCase(); if (/claude-3-haiku-20240307/.test(id)) return false; if (id.includes('amazon.nova-')) return true; + if (/claude-haiku-4-5/.test(id)) return true; if (/claude-(haiku|sonnet|opus)-4/.test(id) || /anthropic\.claude-4/.test(id)) return true; if (/claude-3-7/.test(id)) return true; if (/claude-3-5-sonnet/.test(id) || /claude-3-5-haiku/.test(id)) return true; return false; } -function systemBlocksFor(system, modelId) { - if (!system) return undefined; - const blocks = [{ text: system }]; - if (isPromptCacheEnabled() && supportsPromptCache(modelId)) { - blocks.push({ cachePoint: { type: 'default' } }); - } +/** 1-hour TTL is supported on Haiku 4.5 and Claude 4.x; others default to 5 minutes. */ +function cacheTtlFor(modelId) { + const id = String(modelId || '').toLowerCase(); + if (/claude-haiku-4-5/.test(id)) return '1h'; + if (/claude-(sonnet|opus)-4/.test(id) || /anthropic\.claude-4/.test(id)) return '1h'; + return undefined; +} + +function cachePointBlock(modelId) { + if (!isPromptCacheEnabled() || !supportsPromptCache(modelId)) return null; + const ttl = cacheTtlFor(modelId); + return { + cachePoint: { + type: 'default', + ...(ttl ? { ttl } : {}), + }, + }; +} + +function systemBlocksFor(system, modelId, extraSystem = []) { + const texts = [ + system, + ...(Array.isArray(extraSystem) ? extraSystem : [extraSystem]), + ].filter((t) => typeof t === 'string' && t.trim()); + if (!texts.length) return undefined; + const blocks = texts.map((text) => ({ text })); + const point = cachePointBlock(modelId); + if (point) blocks.push(point); return blocks; } +function withToolCache(toolConfig, modelId) { + if (!toolConfig?.tools?.length) return toolConfig; + const point = cachePointBlock(modelId); + if (!point || toolConfig.tools.some((t) => t?.cachePoint)) return toolConfig; + return { + ...toolConfig, + tools: [...toolConfig.tools, point], + }; +} + function usageForLog(usage) { if (!usage) return null; return { @@ -146,19 +242,28 @@ function usageForLog(usage) { /** * @param {import('@aws-sdk/client-bedrock-runtime').BedrockRuntimeClient} client * @param {string} modelId - * @param {{ system?: string, messages: object[], inferenceConfig?: object, logLabel?: string }} opts + * @param {{ system?: string, extraSystem?: string|string[], messages: object[], inferenceConfig?: object, logLabel?: string, toolConfig?: object, toolChoice?: object }} opts */ async function converse(client, modelId, opts) { - const { system, messages, inferenceConfig, logLabel } = opts; + const { system, extraSystem, messages, inferenceConfig, logLabel, toolConfig, toolChoice } = opts; const region = getRegion(); const resolvedModelId = resolveModelId(modelId, region); - const systemBlocks = systemBlocksFor(system, resolvedModelId); + const systemBlocks = systemBlocksFor(system, resolvedModelId, extraSystem); const mergedInferenceConfig = sanitizeInferenceConfig(resolvedModelId, { maxTokens: 400, temperature: 0, topP: 0.1, ...inferenceConfig, }); + const mergedToolConfig = toolConfig + ? withToolCache( + { + ...toolConfig, + ...(toolChoice ? { toolChoice } : {}), + }, + resolvedModelId, + ) + : undefined; const label = logLabel || 'converse'; const shouldLog = Boolean(logLabel) || isLlmLogEnabled(); const startedAt = Date.now(); @@ -169,7 +274,12 @@ async function converse(client, modelId, opts) { modelId: resolvedModelId, requestedModelId: modelId !== resolvedModelId ? modelId : undefined, inferenceConfig: mergedInferenceConfig, - promptCache: Boolean(systemBlocks?.some((b) => b.cachePoint)), + promptCache: Boolean( + systemBlocks?.some((b) => b.cachePoint) + || mergedToolConfig?.tools?.some((t) => t?.cachePoint), + ), + tools: mergedToolConfig?.tools?.map((t) => t?.toolSpec?.name).filter(Boolean), + toolChoice: mergedToolConfig?.toolChoice || null, system, messages: messageTexts(messages), }); @@ -182,6 +292,7 @@ async function converse(client, modelId, opts) { ...(systemBlocks ? { system: systemBlocks } : {}), messages, inferenceConfig: mergedInferenceConfig, + ...(mergedToolConfig ? { toolConfig: mergedToolConfig } : {}), }), ); @@ -192,6 +303,7 @@ async function converse(client, modelId, opts) { usage: usageForLog(response.usage), stopReason: response.stopReason || null, text: responseText(response), + toolUses: toolUseBlocks(response).map((b) => b.toolUse.name), }); } @@ -203,6 +315,7 @@ async function converse(client, modelId, opts) { latencyMs: Date.now() - startedAt, name: err?.name || err?.Code || 'Error', message: err?.message || String(err), + httpStatusCode: err?.$metadata?.httpStatusCode, }); } throw err; @@ -212,12 +325,17 @@ async function converse(client, modelId, opts) { module.exports = { DEFAULT_ROUTER_MODEL_ID, DEFAULT_CALL2_MODEL_ID, + DEFAULT_AGENT_MODEL_ID, getRegion, getRouterModelId, getCall2ModelId, getCall2StrongModelId, + getAgentModelId, resolveModelId, createClient, converse, responseText, + toolUseBlocks, + assistantMessageFromResponse, + sanitizeAssistantContent, }; diff --git a/src/utils/call2Context.js b/src/utils/call2Context.js deleted file mode 100644 index e3c2f4b..0000000 --- a/src/utils/call2Context.js +++ /dev/null @@ -1,730 +0,0 @@ -/** - * Compact Call 2 LLM context: drop nulls/empties/duplicates. - * Full session objects stay in memory for card building. - */ - -const DETAIL_INTENTS = new Set(['agenda_session_details', 'agenda_download_materials']); - -function isEmpty(value) { - if (value == null) return true; - if (typeof value === 'string') return !value.trim(); - if (Array.isArray(value)) return value.length === 0; - if (typeof value === 'object') return Object.keys(value).length === 0; - return false; -} - -function omitEmpty(obj) { - if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj; - const out = {}; - for (const [k, v] of Object.entries(obj)) { - if (isEmpty(v)) continue; - out[k] = v; - } - return out; -} - -function compactEntities(entities) { - return omitEmpty(entities && typeof entities === 'object' ? entities : {}); -} - -function alreadyToldField(src) { - const keys = Array.isArray(src?.alreadyTold) - ? [...new Set(src.alreadyTold.map((k) => String(k || '').trim()).filter(Boolean))].slice(0, 24) - : []; - return keys.length ? keys : undefined; -} - -function slimTrack(track) { - if (!track) return undefined; - if (typeof track === 'string') return track; - const name = String(track.name || '').trim(); - const hall = String(track.hall || '').trim(); - if (!name && !hall) return undefined; - return hall ? `${name} · ${hall}` : name || hall; -} - -function slimSpeakers(speakers) { - if (!Array.isArray(speakers) || !speakers.length) return undefined; - const names = speakers - .map((sp) => (typeof sp === 'string' ? sp : sp?.name)) - .map((n) => String(n || '').trim()) - .filter(Boolean) - .slice(0, 4); - return names.length ? names : undefined; -} - -function formatTopicDuration(duration) { - const raw = String(duration || '').trim(); - if (!raw) return ''; - const match = raw.match(/^(\d+(?:\s*\+\s*\d+)?)\s*(m|min|mins|minutes)?$/i); - if (match) return `${match[1].replace(/\s+/g, '')}m`; - return raw; -} - -function slimTopics(topics) { - if (!Array.isArray(topics) || !topics.length) return undefined; - const lines = topics - .map((t) => { - if (!t) return null; - if (typeof t === 'string') return t.trim() || null; - const title = String(t.title || '').trim(); - if (!title) return null; - const dur = formatTopicDuration(t.duration); - const role = String(t.role || '').trim(); - let line = title; - if (dur) line += ` (${dur})`; - if (role) line += ` · ${role}`; - return line; - }) - .filter(Boolean) - .slice(0, 8); - return lines.length ? lines : undefined; -} - -function slimResources(resources) { - if (!Array.isArray(resources) || !resources.length) return undefined; - const rows = resources - .map((r) => { - const url = String(r?.url || '').trim(); - if (!url) return null; - return omitEmpty({ - name: String(r.name || 'Resource').trim().slice(0, 80), - url: url.slice(0, 300), - }); - }) - .filter(Boolean) - .slice(0, 6); - return rows.length ? rows : undefined; -} - -function slimSessionForLlm(session, intent) { - if (!session) return null; - const detail = DETAIL_INTENTS.has(intent); - - return omitEmpty({ - id: session.id, - title: session.title, - when: session.timeRangeLocal || null, - track: slimTrack(session.track), - type: detail ? session.type || null : null, - speakers: slimSpeakers(session.speakers), - // Always include topics for detail intents so Call 2 can match by topic - topics: detail ? slimTopics(session.topics) : undefined, - resources: intent === 'agenda_download_materials' ? slimResources(session.resources) : undefined, - }); -} - -function slimConflicts(conflicts) { - if (!Array.isArray(conflicts) || !conflicts.length) return undefined; - return conflicts.slice(0, 12).map((c) => - omitEmpty({ - a: omitEmpty({ - id: c?.a?.id, - title: c?.a?.title, - when: c?.a?.timeRangeLocal || null, - }), - b: omitEmpty({ - id: c?.b?.id, - title: c?.b?.title, - when: c?.b?.timeRangeLocal || null, - }), - }), - ); -} - -function slimTracksList(tracks) { - if (!Array.isArray(tracks) || !tracks.length) return undefined; - return tracks - .map((t) => slimTrack(t)) - .filter(Boolean) - .slice(0, 30); -} - -/** - * Build compact DATA for the LLM. Full `data` is still used for cards. - * @param {object} data full fetchAgendaData result - * @param {string} intent - */ -function slimDataForLlm(data, intent) { - const src = data && typeof data === 'object' ? data : {}; - const sessions = Array.isArray(src.sessions) - ? src.sessions.map((s) => slimSessionForLlm(s, intent)).filter(Boolean) - : []; - - // Always keep sessions (even []) so the model cannot miss an empty personal schedule. - return { - ...omitEmpty({ - now: src.nowLocal || src.now || null, - tz: src.timeZone || 'Asia/Kolkata', - note: src.note || null, - day: src.query?.day || null, - trackQuery: src.trackQuery || null, - speakerQuery: src.speakerQuery || null, - query: src.query ? omitEmpty(src.query) : null, - tracks: slimTracksList(src.tracks), - conflicts: slimConflicts(src.conflicts), - alreadyTold: alreadyToldField(src), - }), - sessions, - }; -} - -/** - * Compact venue DATA for Call 2. - * @param {object} data fetchVenueData result - */ -function slimVenueDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const faqs = Array.isArray(src.faqs) - ? src.faqs - .map((f) => omitEmpty({ q: f.q || f.question || null, a: f.a || f.answer || null })) - .filter((f) => f.q && f.a) - .slice(0, 10) - : []; - const places = Array.isArray(src.places) - ? src.places - .map((p) => - omitEmpty({ - id: p.id, - name: p.name || null, - address: p.address || null, - rating: p.rating != null ? p.rating : null, - distance: p.distance != null ? p.distance : null, - }), - ) - .filter((p) => p.id && p.name) - .slice(0, 8) - : []; - - // Keep instructional notes out of the payload when allowGeneralTravel is set — - // models sometimes echo DATA.note as the user-facing message — unless - // keepNoteWithGeneralTravel (transport allow-list must still be visible). - const includeNote = - src.note && (!src.allowGeneralTravel || src.keepNoteWithGeneralTravel); - - const mapMedia = src.mapMedia && src.mapMedia.url - ? omitEmpty({ - name: src.mapMedia.name || 'Venue floor plan', - mediaType: src.mapMedia.mediaType || null, - available: true, - }) - : null; - - const transportModes = - src.transportModes && src.transportModes.enforce - ? { - enforce: true, - allowed: Array.isArray(src.transportModes.allowed) - ? src.transportModes.allowed.slice(0, 8) - : [], - notAllowed: Array.isArray(src.transportModes.notAllowed) - ? src.transportModes.notAllowed.slice(0, 8) - : [], - } - : null; - - return omitEmpty({ - eventName: src.eventName || null, - location: src.location || null, - origin: src.origin || null, - allowGeneralTravel: src.allowGeneralTravel ? true : null, - note: includeNote ? src.note : null, - fallback: src.fallback || null, - faqs: faqs.length ? faqs : undefined, - transportModes, - mapMedia, - halls: Array.isArray(src.halls) && src.halls.length ? src.halls.slice(0, 20) : undefined, - rooms: Array.isArray(src.rooms) && src.rooms.length ? src.rooms.slice(0, 25) : undefined, - places: places.length ? places : undefined, - alreadyTold: alreadyToldField(src), - }); -} - -/** - * Compact ticket DATA for Call 2. - * @param {object} data fetchTicketData result - */ -function slimTicketDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const receipt = src.receipt && typeof src.receipt === 'object' - ? omitEmpty({ - kind: src.receipt.kind || null, - status: src.receipt.status || null, - ticketName: src.receipt.ticketName || null, - amount: src.receipt.amountLabel || null, - orderId: src.receipt.orderId || null, - paymentId: src.receipt.paymentId || null, - date: src.receipt.date || null, - purchaserEmail: src.receipt.purchaserEmail || null, - attendees: Array.isArray(src.receipt.attendees) - ? src.receipt.attendees.slice(0, 8) - : undefined, - quote: src.receipt.quote || null, - }) - : null; - - const faqs = Array.isArray(src.faqs) - ? src.faqs - .map((f) => - omitEmpty({ - q: f.q || null, - a: f.a || null, - }), - ) - .filter((f) => f.a) - .slice(0, 6) - : []; - const contact = src.contact && typeof src.contact === 'object' - ? omitEmpty({ - email: src.contact.email || null, - phone: src.contact.phone || null, - detail: src.contact.detail || null, - text: src.contact.text || null, - }) - : null; - const tickets = Array.isArray(src.tickets) - ? src.tickets - .map((t) => - omitEmpty({ - name: t.name || null, - price: t.price || null, - }), - ) - .filter((t) => t.name) - .slice(0, 8) - : []; - const hideEventName = src.intent === 'ticket_how_to_register'; - - return omitEmpty({ - eventName: hideEventName ? null : src.eventName || null, - found: src.found === false ? false : src.found ? true : null, - policy: src.policy || null, - note: hideEventName ? null : src.note || null, - registrationLinks: hideEventName - ? undefined - : Array.isArray(src.registrationLinks) - ? src.registrationLinks - .map((l) => - omitEmpty({ - label: l.label || null, - url: l.url || null, - }), - ) - .filter((l) => l.url) - .slice(0, 8) - : undefined, - hasRegistrationButtons: - hideEventName && Array.isArray(src.registrationLinks) && src.registrationLinks.some((l) => l.url) - ? true - : null, - registrationOptions: - hideEventName && Array.isArray(src.registrationLinks) - ? src.registrationLinks.map((l) => l.label).filter(Boolean).slice(0, 8) - : undefined, - contact: - hideEventName - ? undefined - : contact && (contact.email || contact.phone || contact.text) - ? contact - : undefined, - faqs: faqs.length ? faqs : undefined, - tickets: tickets.length ? tickets : undefined, - emailSent: src.emailSent === true ? true : src.emailSent === false ? false : null, - emailTo: src.emailTo || null, - emailError: src.emailError || null, - attendee: src.attendee - ? omitEmpty({ - name: src.attendee.name || null, - email: src.attendee.email || null, - }) - : null, - membership: src.membership - ? omitEmpty({ - status: src.membership.statusLabel || src.membership.status || null, - checkedIn: src.membership.checkedIn ? true : null, - registrationDate: src.membership.registrationDate || null, - }) - : null, - ticket: src.ticket - ? omitEmpty({ - name: src.ticket.name || null, - price: src.ticket.basePriceMajor != null - ? `${src.ticket.currency || 'INR'} ${src.ticket.basePriceMajor}` - : null, - }) - : null, - payment: src.payment - ? omitEmpty({ - status: src.payment.status || null, - amount: src.payment.amount != null - ? `${src.payment.currency || 'INR'} ${src.payment.amount}` - : null, - }) - : null, - qr: src.qr - ? omitEmpty({ - available: Boolean(src.qr.available), - }) - : null, - receipt: receipt && Object.keys(receipt).length ? receipt : null, - alreadyTold: alreadyToldField(src), - }); -} - -/** - * Compact speaker DATA for Call 2. - * @param {object} data fetchSpeakerData result - */ -function slimSpeakerDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const isList = Boolean(src.isSpeakerList); - const speakerCap = isList ? 24 : 4; - const bioCap = isList ? 160 : 800; - const speakers = Array.isArray(src.speakers) - ? src.speakers - .map((s) => - omitEmpty({ - id: s.id, - name: s.name || null, - title: s.title || null, - organizationName: s.organizationName || null, - speakerRole: s.speakerRole || null, - bio: s.bio ? String(s.bio).slice(0, bioCap) : null, - expertise: - !isList && Array.isArray(s.expertise) && s.expertise.length - ? s.expertise.slice(0, 8) - : undefined, - socialLinks: - !isList && s.socialLinks - ? omitEmpty({ - linkedin: s.socialLinks.linkedin || null, - twitter: s.socialLinks.twitter || null, - website: s.socialLinks.website || null, - }) - : undefined, - isPublic: s.isPublic === false ? false : true, - profileVisibility: s.profileVisibility || null, - }), - ) - .filter((s) => s.id && s.name) - .slice(0, speakerCap) - : []; - - const candidates = Array.isArray(src.candidates) - ? src.candidates - .map((c) => - omitEmpty({ - id: c.id, - name: c.name || null, - title: c.title || null, - organizationName: c.organizationName || null, - }), - ) - .filter((c) => c.name) - .slice(0, 8) - : []; - - const sessionSource = - src.intent === 'speaker_meet_and_greet' && Array.isArray(src.meetSessions) && src.meetSessions.length - ? src.meetSessions - : src.sessions; - - const sessions = Array.isArray(sessionSource) - ? sessionSource - .map((s) => - omitEmpty({ - id: s.id, - title: s.title || null, - when: s.timeRangeLocal || null, - track: s.track?.name || (typeof s.track === 'string' ? s.track : null), - type: s.type || null, - }), - ) - .filter((s) => s.id && s.title) - .slice(0, 12) - : []; - - return omitEmpty({ - speakerQuery: src.speakerQuery || null, - isSpeakerList: isList ? true : null, - needsClarification: src.needsClarification ? true : null, - note: src.note || null, - speakers: speakers.length ? speakers : undefined, - candidates: candidates.length ? candidates : undefined, - sessions: !isList && sessions.length ? sessions : undefined, - meetSessionCount: - !isList && Array.isArray(src.meetSessions) && src.meetSessions.length - ? src.meetSessions.length - : undefined, - alreadyTold: alreadyToldField(src), - }); -} - -/** - * Compact exhibitor DATA for Call 2. - * @param {object} data fetchExhibitorData result - */ -function slimExhibitorDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const isList = Boolean(src.isExhibitorList); - const exhibitorCap = isList ? 24 : 4; - const descCap = isList ? 120 : 500; - const exhibitors = Array.isArray(src.exhibitors) - ? src.exhibitors - .map((e) => - omitEmpty({ - id: e.id, - name: e.name || null, - boothNumber: e.boothNumber || null, - hall: e.hall || null, - category: e.category || null, - shortDescription: e.shortDescription - ? String(e.shortDescription).slice(0, descCap) - : null, - description: - !isList && e.description ? String(e.description).slice(0, descCap) : null, - products: - Array.isArray(e.products) && e.products.length - ? e.products.slice(0, isList ? 4 : 10) - : undefined, - website: e.website || null, - socialLinks: - !isList && e.socialLinks - ? omitEmpty({ - linkedin: e.socialLinks.linkedin || null, - twitter: e.socialLinks.twitter || null, - instagram: e.socialLinks.instagram || null, - website: e.socialLinks.website || null, - }) - : undefined, - isFeatured: e.isFeatured ? true : null, - }), - ) - .filter((e) => e.id && e.name) - .slice(0, exhibitorCap) - : []; - - const candidates = Array.isArray(src.candidates) - ? src.candidates - .map((c) => - omitEmpty({ - id: c.id, - name: c.name || null, - boothNumber: c.boothNumber || null, - hall: c.hall || null, - category: c.category || null, - }), - ) - .filter((c) => c.name) - .slice(0, 8) - : []; - - const faqs = Array.isArray(src.faqs) - ? src.faqs - .map((f) => - omitEmpty({ - q: f.q || null, - a: f.a || null, - }), - ) - .filter((f) => f.a) - .slice(0, 8) - : []; - - return omitEmpty({ - companyQuery: src.companyQuery || null, - categoryQuery: src.categoryQuery || null, - isExhibitorList: isList ? true : null, - needsClarification: src.needsClarification ? true : null, - refuseBooking: src.refuseBooking ? true : null, - note: src.note || null, - exhibitors: exhibitors.length ? exhibitors : undefined, - candidates: candidates.length ? candidates : undefined, - faqs: faqs.length ? faqs : undefined, - alreadyTold: alreadyToldField(src), - }); -} - -/** - * Compact tech DATA for Call 2. - * @param {object} data fetchTechData result - */ -function slimTechDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const faqs = Array.isArray(src.faqs) - ? src.faqs - .map((f) => - omitEmpty({ - q: f.q || null, - a: f.a || null, - }), - ) - .filter((f) => f.a) - .slice(0, 10) - : []; - const staticTips = Array.isArray(src.staticTips) - ? src.staticTips.map((t) => String(t || '').trim()).filter(Boolean).slice(0, 8) - : []; - - return omitEmpty({ - category: src.category || null, - note: src.note || null, - faqs: faqs.length ? faqs : undefined, - staticTips: staticTips.length ? staticTips : undefined, - alreadyTold: alreadyToldField(src), - }); -} - -/** - * Compact system DATA for Call 2. - * Visitor-facing Bot FAQ contacts (DATA.contact / DATA.faqs) are intentionally included. - * Internal org mailbox used only for escalation email is not exposed separately. - * @param {object} data fetchSystemData result - */ -function slimSystemDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const faqs = Array.isArray(src.faqs) - ? src.faqs - .map((f) => - omitEmpty({ - q: f.q || null, - a: f.a || null, - }), - ) - .filter((f) => f.a) - .slice(0, 4) - : []; - const contact = src.contact && typeof src.contact === 'object' - ? omitEmpty({ - email: src.contact.email || null, - phone: src.contact.phone || null, - detail: src.contact.detail || null, - text: src.contact.text || null, - }) - : null; - const hideEventName = - src.intent === 'out_of_scope' - || src.intent === 'chitchat_greeting' - || src.intent === 'event_adjacent'; - const places = Array.isArray(src.places) - ? src.places - .map((p) => - omitEmpty({ - id: p.id, - name: p.name || null, - address: p.address || null, - rating: p.rating != null ? p.rating : null, - category: p.category || null, - }), - ) - .filter((p) => p.id && p.name) - .slice(0, 8) - : []; - const hotels = Array.isArray(src.hotels) - ? src.hotels - .map((h) => - omitEmpty({ - id: h.id, - name: h.name || null, - address: h.address || null, - }), - ) - .filter((h) => h.id && h.name) - .slice(0, 8) - : []; - return omitEmpty({ - eventName: hideEventName ? null : src.eventName || null, - location: src.location || null, - city: src.city || null, - eventDates: src.eventDates || null, - allowGeneralLocal: src.allowGeneralLocal ? true : null, - dish: src.dish || null, - emailSent: src.emailSent === true ? true : null, - rateLimited: src.rateLimited === true ? true : null, - emailError: src.emailError || null, - note: src.note || null, - contact: contact && (contact.email || contact.phone || contact.text) ? contact : undefined, - faqs: faqs.length ? faqs : undefined, - places: places.length ? places : undefined, - hotels: hotels.length ? hotels : undefined, - alreadyTold: alreadyToldField(src), - }); -} - -/** - * Compact local places / accommodation DATA for Call 2. - * @param {object} data fetchLocalData result - */ -function slimLocalDataForLlm(data) { - const src = data && typeof data === 'object' ? data : {}; - const places = Array.isArray(src.places) - ? src.places - .map((p) => - omitEmpty({ - id: p.id, - name: p.name || null, - address: p.address || null, - rating: p.rating != null ? p.rating : null, - distance: p.distance != null ? p.distance : null, - category: p.category || null, - tags: Array.isArray(p.tags) && p.tags.length ? p.tags.slice(0, 6) : undefined, - }), - ) - .filter((p) => p.id && p.name) - .slice(0, 8) - : []; - const hotels = Array.isArray(src.hotels) - ? src.hotels - .map((h) => - omitEmpty({ - id: h.id, - name: h.name || null, - address: h.address || null, - rating: h.rating != null ? h.rating : null, - distance: h.distance != null ? h.distance : null, - priceLevel: h.priceLevel != null ? h.priceLevel : null, - }), - ) - .filter((h) => h.id && h.name) - .slice(0, 8) - : []; - - const faqs = Array.isArray(src.faqs) - ? src.faqs - .map((f) => - omitEmpty({ - q: f.q || null, - a: f.a || null, - }), - ) - .filter((f) => f.a) - .slice(0, 2) - : []; - - return omitEmpty({ - eventName: src.eventName || null, - location: src.location || null, - hostManagedAccommodation: src.hostManagedAccommodation ? true : null, - faqs: faqs.length ? faqs : undefined, - requestedCategory: src.requestedCategory?.label || null, - categoryNoMatch: src.categoryNoMatch || null, - note: src.note || null, - places: places.length ? places : undefined, - hotels: hotels.length ? hotels : undefined, - alreadyTold: alreadyToldField(src), - }); -} - -module.exports = { - compactEntities, - slimDataForLlm, - slimVenueDataForLlm, - slimTicketDataForLlm, - slimSpeakerDataForLlm, - slimExhibitorDataForLlm, - slimTechDataForLlm, - slimSystemDataForLlm, - slimLocalDataForLlm, - slimSessionForLlm, - omitEmpty, -}; diff --git a/src/utils/domainAction.js b/src/utils/domainAction.js deleted file mode 100644 index a5796ab..0000000 --- a/src/utils/domainAction.js +++ /dev/null @@ -1,334 +0,0 @@ -/** - * Call 1 surface: domain + action. Fetchers still use a derived fetchIntent. - */ - -const DOMAINS = [ - 'agenda', - 'speaker', - 'venue', - 'local', - 'ticket', - 'exhibitor', - 'tech', - 'system', -]; - -const ACTIONS = [ - 'read', - 'bookmark_add', - 'bookmark_remove', - 'email_qr', - 'emergency', - 'escalate_human', - 'adjacent', - 'oos', - 'greeting', -]; - -const DOMAIN_SET = new Set(DOMAINS); -const ACTION_SET = new Set(ACTIONS); - -/** Old Call 1 intent ids → domain/action (model aliases + pendingIntent). */ -const LEGACY_TO_DOMAIN_ACTION = { - agenda_current_now: { domain: 'agenda', action: 'read' }, - agenda_upcoming_next: { domain: 'agenda', action: 'read' }, - agenda_by_track: { domain: 'agenda', action: 'read' }, - agenda_by_speaker: { domain: 'agenda', action: 'read' }, - agenda_session_details: { domain: 'agenda', action: 'read' }, - agenda_personal_schedule: { domain: 'agenda', action: 'read' }, - agenda_conflict_check: { domain: 'agenda', action: 'read' }, - agenda_download_materials: { domain: 'agenda', action: 'read' }, - agenda_bookmark_add: { domain: 'agenda', action: 'bookmark_add' }, - agenda_bookmark_remove: { domain: 'agenda', action: 'bookmark_remove' }, - speaker_bio_lookup: { domain: 'speaker', action: 'read' }, - speaker_meet_and_greet: { domain: 'speaker', action: 'read' }, - speaker_contact_info: { domain: 'speaker', action: 'read' }, - venue_map_request: { domain: 'venue', action: 'read' }, - venue_wayfinding: { domain: 'venue', action: 'read' }, - venue_nearest_amenity: { domain: 'venue', action: 'read' }, - venue_parking_transport: { domain: 'venue', action: 'read' }, - venue_travel_directions: { domain: 'venue', action: 'read' }, - venue_food_beverage: { domain: 'venue', action: 'read' }, - venue_accessibility: { domain: 'venue', action: 'read' }, - tourist_places: { domain: 'local', action: 'read' }, - accommodation: { domain: 'local', action: 'read' }, - event_adjacent: { domain: 'local', action: 'adjacent' }, - ticket_qr_display: { domain: 'ticket', action: 'read' }, - ticket_email_qr: { domain: 'ticket', action: 'email_qr' }, - ticket_status_lookup: { domain: 'ticket', action: 'read' }, - ticket_how_to_register: { domain: 'ticket', action: 'read' }, - ticket_document_receipt: { domain: 'ticket', action: 'read' }, - ticket_transfer_request: { domain: 'ticket', action: 'read' }, - ticket_upgrade: { domain: 'ticket', action: 'read' }, - exhibitor_location: { domain: 'exhibitor', action: 'read' }, - exhibitor_by_category: { domain: 'exhibitor', action: 'read' }, - exhibitor_demo_schedule: { domain: 'exhibitor', action: 'read' }, - exhibitor_book_meeting: { domain: 'exhibitor', action: 'read' }, - wifi_credentials: { domain: 'tech', action: 'read' }, - app_issue_streaming: { domain: 'tech', action: 'read' }, - app_issue_account: { domain: 'tech', action: 'read' }, - app_feature_guidance: { domain: 'tech', action: 'read' }, - emergency_medical: { domain: 'system', action: 'emergency' }, - lost_and_found: { domain: 'system', action: 'read' }, - human_agent_escalation: { domain: 'system', action: 'escalate_human' }, - chitchat_greeting: { domain: 'system', action: 'greeting' }, - out_of_scope: { domain: 'system', action: 'oos' }, -}; - -function normMsg(message) { - return String(message || '').toLowerCase().replace(/\s+/g, ' ').trim(); -} - -function looksLikeFoodPolicy(message) { - const t = normMsg(message); - return ( - /\b(is|are) (food|lunch|dinner|breakfast|meals?|tea|coffee) (provided|included|served)\b/.test(t) - || /\b(food|meals?|lunch|dinner) provided\b/.test(t) - || /\bdietary (options?|needs|requirements?)\b/.test(t) - || /\btea\/coffee\b/.test(t) - || /\bwill (there be|you serve|they serve) (food|lunch|meals?)\b/.test(t) - ); -} - -function looksLikeDishAsk(message, dish) { - if (String(dish || '').trim()) return true; - const t = normMsg(message); - if (looksLikeFoodPolicy(t)) return false; - return ( - /\b(want to|wanted to|wanna|like to|crave|craving) eat\b/.test(t) - || /\b(eat|eating|try|trying) (some )?(puttu|dosa|idli|biryani|appam|porotta|parotta)\b/.test(t) - || /\b(puttu|dosa|idli|appam|porotta|parotta|biryani)\b/.test(t) - ); -} - -function looksLikeItinerary(message) { - const t = normMsg(message); - return ( - /\b(itinerary|day plan|one[- ]day|1[- ]day|tourist plan)\b/.test(t) - || /\bafter (the |this )?event\b/.test(t) - && /\b(plan|roam|visit|sightseeing|tour|explore)\b/.test(t) - || /\bwhat (can|should) i (do|see|visit).{0,40}(after|free time|spare)\b/.test(t) - ); -} - -function inferAgendaFetchIntent(message, entities) { - const t = normMsg(message); - if (/\b(add|save|bookmark|put on my agenda)\b/.test(t)) return 'agenda_bookmark_add'; - if (/\b(remove|unsave|unbookmark)\b/.test(t)) return 'agenda_bookmark_remove'; - if (/\b(my (sessions|schedule|agenda)|saved sessions|bookmarked)\b/.test(t)) { - return 'agenda_personal_schedule'; - } - if (/\bconflict|overlap\b/.test(t)) return 'agenda_conflict_check'; - if (/\b(slides?|materials?|handouts?|pdfs?|download)\b/.test(t)) return 'agenda_download_materials'; - if (entities?.speaker_name && /\b(when|session|speak|talk|lecture)\b/.test(t)) { - return 'agenda_by_speaker'; - } - if (entities?.speaker_name && !/\bwho is|bio|about\b/.test(t)) return 'agenda_by_speaker'; - if (/\b(now|currently|happening right now|live now)\b/.test(t)) return 'agenda_current_now'; - if (/\b(what'?s next|upcoming|coming up)\b/.test(t)) return 'agenda_upcoming_next'; - if (entities?.topic && /\btrack|stream\b/.test(t)) return 'agenda_by_track'; - return 'agenda_session_details'; -} - -function looksLikeEventDeskQuestion(message) { - const t = normMsg(message); - if (!t) return false; - return /\b(event|summit|conference|congress|attend|attending|attendee|register|registration|ticket|session|speaker|venue|agenda|wifi|wi-fi|exhibitor|exhibit)\b/.test( - t, - ); -} - -function looksLikeCityToVenueTravel(message) { - const t = normMsg(message); - if (!t) return false; - const fromOrigin = - /\b(travell?ing|traveling|coming|arriving)\s+from\b/.test(t) - || /\bfrom (?!the\b|this\b|that\b|here\b|there\b|home\b)[a-z]{3,}\b/.test(t); - const sightseeingOnly = - /\b(forts?|beaches?|churches?|sightseeing|places to visit|tourist)\b/.test(t) - && !fromOrigin - && !/\b(flight|train|how to reach|travel options?)\b/.test(t); - if (sightseeingOnly) return false; - return ( - /\b(travell?ing|traveling)\s+from\b/.test(t) - || /\btravel options?\b/.test(t) - || /\bhow (do|can|to) (i |we )?(get to|reach|travel to|come to)\b/.test(t) - || /\b(flights?|trains?)\s+(from|to)\b/.test(t) - || (fromOrigin && /\b(travel|reach|flight|train|options?|how (do|can) i get)\b/.test(t)) - ); -} - -const TRAVEL_ORIGIN_STOP = new Set(['the', 'this', 'that', 'there', 'here', 'home', 'work', 'my', 'our']); - -function extractTravelOrigin(message, existing) { - const already = String(existing || '').trim(); - if (already) return already; - const t = String(message || ''); - const m = - t.match(/\b(?:travell?ing|traveling|coming|arriving)\s+from\s+([A-Za-z][A-Za-z.\s-]{1,32}?)(?:\s*[,.?!]|$)/i) - || t.match(/\bfrom\s+([A-Za-z][A-Za-z.]{2,32})\b/i); - if (!m) return null; - const city = String(m[1] || '').replace(/[.,]+$/g, '').trim(); - const first = city.split(/\s+/)[0].toLowerCase(); - if (!city || TRAVEL_ORIGIN_STOP.has(first)) return null; - return city; -} - -function inferVenueFetchIntent(message, entities) { - const t = normMsg(message); - if (/\b(map|floor plan|layout)\b/.test(t)) return 'venue_map_request'; - if (/\b(restroom|washroom|toilet|water|charging|info desk)\b/.test(t)) { - return 'venue_nearest_amenity'; - } - if (/\baccessib|wheelchair\b/.test(t)) return 'venue_accessibility'; - if (looksLikeCityToVenueTravel(t) || (entities?.location && /\b(train|flight|travel|reach|options?)\b/.test(t))) { - return 'venue_travel_directions'; - } - if (/\b(parking|taxi|uber|ola|shuttle|metro near)\b/.test(t) && !/\bfrom (?!the\b)[a-z]{3,}\b/.test(t)) { - return 'venue_parking_transport'; - } - if (looksLikeFoodPolicy(t) || (/\b(food|lunch|meals?|tea|coffee|dietary)\b/.test(t) && !looksLikeDishAsk(t))) { - return 'venue_food_beverage'; - } - if (/\b(where is|how do i find|hall|room|registration desk|stage)\b/.test(t)) { - return 'venue_wayfinding'; - } - return 'venue_wayfinding'; -} - -function inferLocalFetchIntent(message) { - const t = normMsg(message); - if (/\b(hotel|stay|lodging|accommodation|check-?in)\b/.test(t)) return 'accommodation'; - return 'tourist_places'; -} - -function inferTicketFetchIntent(message, action) { - if (action === 'email_qr') return 'ticket_email_qr'; - const t = normMsg(message); - if (/\b(show|display|see) (my )?(qr|badge)\b/.test(t) || /\bmy qr\b/.test(t)) { - return 'ticket_qr_display'; - } - if (/\b(invoice|receipt)\b/.test(t)) return 'ticket_document_receipt'; - if (/\btransfer\b/.test(t)) return 'ticket_transfer_request'; - if (/\bupgrade\b/.test(t)) return 'ticket_upgrade'; - if (/\b(my (registration|ticket|status)|already registered|check[- ]?in status|payment status)\b/.test(t)) { - return 'ticket_status_lookup'; - } - // Guest attending/joining/eligibility — public FAQs + registration links, not personal status. - return 'ticket_how_to_register'; -} - -function inferSpeakerFetchIntent(message) { - const t = normMsg(message); - if (/\b(meet|greeting|networking)\b/.test(t)) return 'speaker_meet_and_greet'; - if (/\b(linkedin|twitter|website|social)\b/.test(t)) return 'speaker_contact_info'; - return 'speaker_bio_lookup'; -} - -function inferExhibitorFetchIntent(message, entities) { - const t = normMsg(message); - if (/\bbook|meeting|appointment\b/.test(t)) return 'exhibitor_book_meeting'; - if (/\bdemo\b/.test(t)) return 'exhibitor_demo_schedule'; - if (entities?.topic && !entities?.company_name) return 'exhibitor_by_category'; - return 'exhibitor_location'; -} - -function inferTechFetchIntent(message) { - const t = normMsg(message); - if (/\bwifi|wi-fi|ssid|password\b/.test(t)) return 'wifi_credentials'; - if (/\bstream|video|audio|can'?t hear|black screen\b/.test(t)) return 'app_issue_streaming'; - if (/\blogin|otp|account|password reset\b/.test(t)) return 'app_issue_account'; - return 'app_feature_guidance'; -} - -/** - * Derive the fetch/Call-2 intent id used by existing services. - */ -function inferFetchIntent({ domain, action, entities = {}, message = '' } = {}) { - const d = String(domain || ''); - const a = String(action || 'read'); - if (a === 'bookmark_add') return 'agenda_bookmark_add'; - if (a === 'bookmark_remove') return 'agenda_bookmark_remove'; - if (a === 'email_qr') return 'ticket_email_qr'; - if (a === 'emergency') return 'emergency_medical'; - if (a === 'escalate_human') return 'human_agent_escalation'; - if (a === 'adjacent') return 'event_adjacent'; - if (a === 'oos') return 'out_of_scope'; - if (a === 'greeting') return 'chitchat_greeting'; - - if (d === 'agenda') return inferAgendaFetchIntent(message, entities); - if (d === 'speaker') { - const t = normMsg(message); - if (entities?.speaker_name && /\b(when|session|speak|lecture|talk)\b/.test(t)) { - return 'agenda_by_speaker'; - } - return inferSpeakerFetchIntent(message); - } - if (d === 'venue') return inferVenueFetchIntent(message, entities); - if (d === 'local') return inferLocalFetchIntent(message); - if (d === 'ticket') return inferTicketFetchIntent(message, a); - if (d === 'exhibitor') return inferExhibitorFetchIntent(message, entities); - if (d === 'tech') return inferTechFetchIntent(message); - if (d === 'system') { - const t = normMsg(message); - if (/\blost\b/.test(t)) return 'lost_and_found'; - return 'out_of_scope'; - } - return 'out_of_scope'; -} - -function fromLegacyIntent(intent) { - const mapped = LEGACY_TO_DOMAIN_ACTION[String(intent || '')]; - if (!mapped) return null; - return { ...mapped, fetchIntent: String(intent) }; -} - -function isValidDomain(d) { - return DOMAIN_SET.has(String(d || '')); -} - -function isValidAction(a) { - return ACTION_SET.has(String(a || '')); -} - -function faqKeyOf(row) { - return String(row?.key || row?.category || '').trim() || null; -} - -function isBookmarkAction(action) { - return action === 'bookmark_add' || action === 'bookmark_remove'; -} - -function isAuthRequiredFetchIntent(intent) { - return [ - 'agenda_personal_schedule', - 'agenda_conflict_check', - 'agenda_bookmark_add', - 'agenda_bookmark_remove', - 'ticket_qr_display', - 'ticket_email_qr', - 'ticket_status_lookup', - 'ticket_document_receipt', - ].includes(String(intent || '')); -} - -module.exports = { - DOMAINS, - ACTIONS, - DOMAIN_SET, - ACTION_SET, - LEGACY_TO_DOMAIN_ACTION, - looksLikeFoodPolicy, - looksLikeDishAsk, - looksLikeItinerary, - looksLikeCityToVenueTravel, - looksLikeEventDeskQuestion, - extractTravelOrigin, - inferFetchIntent, - fromLegacyIntent, - isValidDomain, - isValidAction, - faqKeyOf, - isBookmarkAction, - isAuthRequiredFetchIntent, -}; diff --git a/src/utils/receptionCopy.js b/src/utils/receptionCopy.js index 385e891..afa7e60 100644 --- a/src/utils/receptionCopy.js +++ b/src/utils/receptionCopy.js @@ -1,16 +1,57 @@ -function interpolateReception(template, { profileName, eventName, firstName } = {}) { - const text = String(template || '').trim(); +const { toPlainCtas } = require('./whatsappCtas'); + +function engagementPublicBase() { + const raw = String(process.env.ENGAGEMENT_PUBLIC_BASE_URL || 'https://engage.eventbuzz.live').trim(); + return raw.replace(/\/+$/, '') || 'https://engage.eventbuzz.live'; +} + +function buildEngagementUrl(eventId, participantId) { + const event = String(eventId || '').trim(); + const participant = String(participantId || '').trim(); + if (!event || !participant) return ''; + return `${engagementPublicBase()}/${encodeURIComponent(event)}/${encodeURIComponent(participant)}`; +} + +function receptionVars({ profileName, eventName, firstName, eventId, participantId, engagementUrl } = {}) { const profile = String(profileName || '').trim() || 'Visitor'; const event = String(eventName || '').trim() || 'Event'; const first = String(firstName || '').trim() || profile.split(/\s+/)[0] || 'there'; - if (!text) return `Hi ${first}! Welcome to ${event}.`; - return text + const eid = String(eventId || '').trim(); + const pid = String(participantId || '').trim(); + const url = String(engagementUrl || '').trim() || buildEngagementUrl(eid, pid); + return { first, profile, event, eid, pid, url }; +} + +function applyReceptionPlaceholders(text, vars) { + const { first, profile, event, eid, pid, url } = receptionVars(vars); + return String(text || '') .replace(/\{firstName\}/gi, first) .replace(/\{profileName\}/gi, profile) - .replace(/\{eventName\}/gi, event); + .replace(/\{eventName\}/gi, event) + .replace(/\{eventId\}/gi, eid) + .replace(/\{participantId\}/gi, pid) + .replace(/\{engagementUrl\}/gi, url); +} + +function interpolateReception(template, vars = {}) { + const text = String(template || '').trim(); + const { first, event } = receptionVars(vars); + if (!text) return `Hi ${first}! Welcome to ${event}.`; + return applyReceptionPlaceholders(text, vars); +} + +function interpolateCtas(ctas, vars = {}) { + return toPlainCtas(ctas).map((cta) => { + if (cta.type !== 'url') return cta; + return { + type: 'url', + label: cta.label || 'Open', + value: applyReceptionPlaceholders(cta.value, vars), + }; + }); } function defaultBadgePrompt() { @@ -27,6 +68,10 @@ function defaultWelcome() { module.exports = { interpolateReception, + interpolateCtas, + applyReceptionPlaceholders, + buildEngagementUrl, + engagementPublicBase, defaultBadgePrompt, defaultStaffHandoff, defaultWelcome, diff --git a/src/utils/registerIntent.js b/src/utils/registerIntent.js new file mode 100644 index 0000000..c74014b --- /dev/null +++ b/src/utils/registerIntent.js @@ -0,0 +1,46 @@ +const { contactPayloadFromBot, contactLines } = require('./organizerContact'); + +function norm(text) { + return String(text || '').toLowerCase().replace(/\s+/g, ' ').trim(); +} + +function looksLikeHowToRegister(query) { + const t = norm(query); + if (!t) return false; + if (/\bcheck[- ]?in\b/.test(t) && !/\bregister\b/.test(t)) return false; + return ( + /\bhow (can|do|to) (i |we )?(register|sign ?up|get (a )?(ticket|pass)|buy (a )?(ticket|pass))\b/.test(t) + || /\b(want to|need to|like to) (register|sign ?up|buy (a )?(ticket|pass))\b/.test(t) + || /\b(register|sign ?up) for (the |this |an )?event\b/.test(t) + || /\bwhere (can|do) (i |we )?(register|sign ?up|buy (a )?(ticket|pass))\b/.test(t) + || /\bregistration (link|form|page|url)\b/.test(t) + ); +} + +function howToRegisterReply({ links, contact } = {}) { + const rows = (Array.isArray(links) ? links : []).filter((l) => l && l.url); + if (rows.length === 1) { + return `You can register here: ${rows[0].url}`; + } + if (rows.length > 1) { + return 'You can register using the links below.'; + } + const block = contactLines(contact); + if (block) { + return `I don't have a registration link on file. You can reach the organizers at:\n${block}`; + } + return "I don't have a registration link on file. Please ask at the on-site help desk."; +} + +function howToRegisterFromBot(bot, links) { + return howToRegisterReply({ + links, + contact: contactPayloadFromBot(bot), + }); +} + +module.exports = { + looksLikeHowToRegister, + howToRegisterReply, + howToRegisterFromBot, +}; diff --git a/src/utils/simli.js b/src/utils/simli.js new file mode 100644 index 0000000..97dd7fb --- /dev/null +++ b/src/utils/simli.js @@ -0,0 +1,51 @@ +function getSimliConfig() { + const apiKey = String(process.env.SIMLI_API_KEY || '').trim(); + const faceId = String(process.env.SIMLI_FACE_ID || '').trim(); + const apiUrl = String(process.env.SIMLI_API_URL || 'https://api.simli.ai').replace(/\/$/, ''); + return { + apiKey, + faceId, + apiUrl, + configured: Boolean(apiKey && faceId), + }; +} + +/** + * Mint a short-lived SIMLI compose session token. API key never leaves the server. + */ +async function createSimliSessionToken() { + const { apiKey, faceId, apiUrl, configured } = getSimliConfig(); + if (!configured) { + throw new Error('SIMLI is not configured'); + } + + const res = await fetch(`${apiUrl}/compose/token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-simli-api-key': apiKey, + }, + body: JSON.stringify({ + faceId, + apiVersion: 'v2', + handleSilence: false, + maxSessionLength: 3600, + maxIdleTime: 60, + }), + }); + + const data = await res.json().catch(() => ({})); + const token = String(data?.session_token || '').trim(); + if (!res.ok || !token || token === 'FAIL TOKEN') { + const detail = data?.detail || data?.message || res.statusText || 'Could not start avatar'; + const err = new Error(typeof detail === 'string' ? detail : 'Could not start avatar'); + err.status = res.status; + throw err; + } + return token; +} + +module.exports = { + getSimliConfig, + createSimliSessionToken, +}; diff --git a/src/utils/webChatTurn.js b/src/utils/webChatTurn.js index 7ea6c3a..c69e8f3 100644 --- a/src/utils/webChatTurn.js +++ b/src/utils/webChatTurn.js @@ -36,7 +36,7 @@ async function ensureWebChatSession(bot, sessionIdHint) { /** * Append the user message, run the concierge pipeline, persist the assistant reply. */ -async function runPublicChatTurn({ bot, event, session, message }) { +async function runPublicChatTurn({ bot, event, session, message, channel = 'text' }) { const msg = String(message || '').trim(); if (!msg) throw new Error('Message is required'); if (msg.length > MAX_MESSAGE_LENGTH) { @@ -56,7 +56,7 @@ async function runPublicChatTurn({ bot, event, session, message }) { } session.lastSeenAt = new Date(); - const result = await handleChatTurn({ bot, event, session, message: msg }); + const result = await handleChatTurn({ bot, event, session, message: msg, channel }); const reply = String(result.reply || '').trim().slice(0, 2500) || 'Sorry, I could not respond just now.'; const inTok = result.usage?.inputTokens || 0; const outTok = result.usage?.outputTokens || 0; diff --git a/src/utils/whatsappCtas.js b/src/utils/whatsappCtas.js index 392f2b6..427bc64 100644 --- a/src/utils/whatsappCtas.js +++ b/src/utils/whatsappCtas.js @@ -6,25 +6,45 @@ * @param {{ strict?: boolean }} [opts] * @returns {{ type: 'reply'|'url', label: string, value: string }[]} */ +function isPlaceholderCtaUrl(value) { + return /\{engagementUrl\}|\{eventId\}|\{participantId\}/i.test(String(value || '')); +} + +function isHttpCtaUrl(value) { + return /^https?:\/\//i.test(String(value || '').trim()); +} + +function toPlainCta(item) { + if (!item || typeof item !== 'object') return null; + const raw = typeof item.toObject === 'function' ? item.toObject() : item; + if (!raw || typeof raw !== 'object') return null; + let type = raw.type === 'url' ? 'url' : raw.type === 'reply' ? 'reply' : null; + const label = String(raw.label || '').trim().slice(0, 20); + const value = String(raw.value || '').trim(); + if (!type && (isHttpCtaUrl(value) || isPlaceholderCtaUrl(value))) type = 'url'; + if (!type) return null; + return { type, label, value }; +} + +function toPlainCtas(raw) { + if (!Array.isArray(raw) || raw.length === 0) return []; + return raw.map(toPlainCta).filter(Boolean); +} + function normalizeWhatsAppCtas(raw, opts = {}) { const strict = Boolean(opts.strict); - if (!Array.isArray(raw) || raw.length === 0) return []; - const rows = []; - for (const item of raw.slice(0, 3)) { - if (!item || typeof item !== 'object') continue; - const type = item.type === 'url' ? 'url' : item.type === 'reply' ? 'reply' : null; - if (!type) continue; - const label = String(item.label || '') - .trim() - .slice(0, 20); - let value = String(item.value || '').trim(); + for (const item of toPlainCtas(raw).slice(0, 3)) { + let { type, label, value } = item; + if (!label && type === 'url' && value) label = 'Open'; if (!label) continue; if (!value && type === 'reply') value = label; if (!value) continue; if (type === 'url') { - if (!/^https?:\/\//i.test(value)) { - if (strict) throw new Error('URL buttons must start with http:// or https://'); + if (!isHttpCtaUrl(value) && !isPlaceholderCtaUrl(value)) { + if (strict) { + throw new Error('URL buttons must start with http:// or https://, or use {engagementUrl}'); + } continue; } value = value.slice(0, 2000); @@ -50,4 +70,10 @@ function normalizeWhatsAppCtas(raw, opts = {}) { return rows; } -module.exports = { normalizeWhatsAppCtas }; +module.exports = { + normalizeWhatsAppCtas, + isHttpCtaUrl, + isPlaceholderCtaUrl, + toPlainCta, + toPlainCtas, +}; diff --git a/src/utils/whatsappFormat.js b/src/utils/whatsappFormat.js index fa776ae..9f4ddcc 100644 --- a/src/utils/whatsappFormat.js +++ b/src/utils/whatsappFormat.js @@ -40,13 +40,18 @@ function imageUrlFromCards(cards) { return ''; } +/** Markdown **bold** → WhatsApp *bold*. */ +function markdownBoldToWhatsApp(text) { + return String(text || '').replace(/\*\*([^*]+)\*\*/g, '*$1*'); +} + /** * Send a concierge orchestrator result over WhatsApp Cloud API. */ async function sendConciergeResultAsWhatsApp(creds, toDigits, result) { const phoneNumberId = creds.phoneNumberId; const token = creds.accessToken; - const reply = String(result?.reply || '').trim(); + const reply = markdownBoldToWhatsApp(String(result?.reply || '').trim()); const cards = Array.isArray(result?.cards) ? result.cards : []; const urlCtas = cards.filter( (c) => c?.type === 'cta' && /^https?:\/\//i.test(String(c?.data?.url || '').trim()), @@ -116,4 +121,5 @@ async function sendConciergeResultAsWhatsApp(creds, toDigits, result) { module.exports = { sendConciergeResultAsWhatsApp, cardLine, + markdownBoldToWhatsApp, }; diff --git a/src/utils/whatsappSend.js b/src/utils/whatsappSend.js index dc2ebfa..c84cc10 100644 --- a/src/utils/whatsappSend.js +++ b/src/utils/whatsappSend.js @@ -65,7 +65,7 @@ async function sendTextMessage(phoneNumberId, accessToken, toDigits, text) { for (const chunk of chunks) { last = await postWhatsAppMessage(phoneNumberId, accessToken, toDigits, { type: 'text', - text: { body: chunk }, + text: { body: chunk, preview_url: /https?:\/\//i.test(chunk) }, }); } return last; @@ -265,12 +265,14 @@ async function sendCtaUrlMessage(phoneNumberId, accessToken, toDigits, bodyText, * With media + CTAs: media first (no caption), then interactive body+CTAs. */ async function sendMessageWithCtas(phoneNumberId, accessToken, toDigits, opts = {}) { - const { normalizeWhatsAppCtas } = require('./whatsappCtas'); + const { normalizeWhatsAppCtas, toPlainCtas, isHttpCtaUrl } = require('./whatsappCtas'); const body = String(opts.body || '').trim(); const mediaType = opts.mediaType === 'image' || opts.mediaType === 'video' ? opts.mediaType : 'none'; const mediaUrl = String(opts.mediaUrl || '').trim(); - const ctas = normalizeWhatsAppCtas(opts.ctas); + const intended = toPlainCtas(opts.ctas); + const ctas = normalizeWhatsAppCtas(intended); const hasMedia = mediaType !== 'none' && Boolean(mediaUrl); + const intendedUrl = intended.some((c) => c.type === 'url'); if (ctas.length && !body) { const err = new Error('empty_body'); @@ -279,6 +281,33 @@ async function sendMessageWithCtas(phoneNumberId, accessToken, toDigits, opts = throw err; } + if (intendedUrl) { + const urlCta = ctas.find((c) => c.type === 'url'); + const url = urlCta ? String(urlCta.value || '').trim() : ''; + if (!urlCta || !isHttpCtaUrl(url)) { + logWhatsApp('send.cta_url_skipped', { + to: maskPhone(toDigits), + reason: 'url_not_ready', + url: url.slice(0, 80), + }); + const err = new Error('cta_url_not_ready'); + err.code = 'cta_url_not_ready'; + err.skip = true; + throw err; + } + if (hasMedia) { + if (mediaType === 'image') { + await sendImageMessage(phoneNumberId, accessToken, toDigits, mediaUrl, ''); + } else { + await sendVideoMessage(phoneNumberId, accessToken, toDigits, mediaUrl, ''); + } + } + return sendCtaUrlMessage(phoneNumberId, accessToken, toDigits, body, { + displayText: urlCta.label || 'Open', + url, + }); + } + if (hasMedia && ctas.length) { if (mediaType === 'image') { await sendImageMessage(phoneNumberId, accessToken, toDigits, mediaUrl, ''); @@ -288,12 +317,6 @@ async function sendMessageWithCtas(phoneNumberId, accessToken, toDigits, opts = } if (ctas.length) { - if (ctas[0].type === 'url') { - return sendCtaUrlMessage(phoneNumberId, accessToken, toDigits, body, { - displayText: ctas[0].label, - url: ctas[0].value, - }); - } return sendInteractiveButtons( phoneNumberId, accessToken, diff --git a/src/workers/livekitVoiceAgent.js b/src/workers/livekitVoiceAgent.js index 645d5ed..c1bd4e5 100644 --- a/src/workers/livekitVoiceAgent.js +++ b/src/workers/livekitVoiceAgent.js @@ -9,6 +9,54 @@ const ConciergeBot = require('../models/ConciergeBot'); const Event = require('../models/Event'); const { parseVoiceRoomName, AGENT_NAME, getLivekitConfig } = require('../utils/livekitConcierge'); const { ensureWebChatSession, runPublicChatTurn } = require('../utils/webChatTurn'); +const { interpolateGreeting } = require('../utils/greeting'); + +function speechText(text) { + return String(text || '') + .replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/\s+/g, ' ') + .trim(); +} + +async function loadSpokenGreeting(botId) { + const bot = await ConciergeBot.findById(botId).lean(); + if (!bot) return 'Hi, welcome. How can I help?'; + const event = await Event.findById(bot.event).select('name').lean(); + return ( + speechText( + interpolateGreeting(bot.greeting?.message || '', { + profileName: undefined, + eventName: event?.name, + }), + ) || 'Hi, welcome. How can I help?' + ); +} + +function waitForAvatarReady(room, timeoutMs) { + return new Promise((resolve) => { + let done = false; + const finish = (fromAvatar) => { + if (done) return; + done = true; + try { + room.off(RoomEvent.DataReceived, onData); + } catch { + /* ignore */ + } + resolve(fromAvatar); + }; + const onData = (payload) => { + try { + const msg = JSON.parse(Buffer.from(payload).toString('utf8')); + if (msg && msg.type === 'avatar-ready') finish(true); + } catch { + /* ignore */ + } + }; + room.on(RoomEvent.DataReceived, onData); + setTimeout(() => finish(false), timeoutMs); + }); +} function lastUserText(chatCtx) { const items = Array.isArray(chatCtx?.items) ? chatCtx.items : []; @@ -49,16 +97,24 @@ async function answerVoiceTurn(botId, sessionId, message) { if (!msg) return 'I missed that. Could you say it again?'; const bot = await ConciergeBot.findById(botId); - if (!bot || !bot.enabled || bot.status !== 'ACTIVE') { + if (!bot || !bot.enabled) { return 'This concierge is not available right now.'; } if (!bot.voiceEnabled) { return 'Voice is not enabled for this chatbot.'; } - const event = await Event.findById(bot.event).select('name location locationType'); + const event = await Event.findById(bot.event).select( + 'name location locationType startDate endDate', + ); const { session } = await ensureWebChatSession(bot, sessionId); - const result = await runPublicChatTurn({ bot, event, session, message: msg }); + const result = await runPublicChatTurn({ + bot, + event, + session, + message: msg, + channel: 'voice', + }); return result.reply; } @@ -67,6 +123,10 @@ class ConciergeVoiceAgent extends voice.Agent { super({ instructions: 'You are the EventBuzz event concierge. Speak briefly, clearly, and stay grounded in event facts.', + allowInterruptions: false, + turnHandling: { + interruption: { enabled: false }, + }, }); this._botId = botId; this._sessionId = sessionId; @@ -158,11 +218,11 @@ const agent = defineAgent({ }); const sttModel = process.env.LIVEKIT_STT_MODEL || 'deepgram/nova-3'; - const sttLanguage = process.env.LIVEKIT_STT_LANGUAGE || 'multi'; - // Cartesia Hindi/Hinglish voice (not US English Jacqueline). + const sttLanguage = process.env.LIVEKIT_STT_LANGUAGE || 'en-IN'; + // Cartesia female Indian English (Indian Customer Support Lady). const ttsModel = process.env.LIVEKIT_TTS_MODEL || 'cartesia/sonic-3'; - const ttsVoice = process.env.LIVEKIT_TTS_VOICE || 'a0e99841-438c-4a64-b679-ae501e7d6091'; - const ttsLanguage = process.env.LIVEKIT_TTS_LANGUAGE || 'hi'; + const ttsVoice = process.env.LIVEKIT_TTS_VOICE || 'ff1bb1a9-c582-4570-9670-5f46169d0fc8'; + const ttsLanguage = process.env.LIVEKIT_TTS_LANGUAGE || 'en'; console.log('[livekit-voice] models', { sttModel, sttLanguage, ttsModel, ttsLanguage }); @@ -173,10 +233,22 @@ const agent = defineAgent({ llm: new ConciergePassthroughLLM(), tts: new inference.TTS({ model: ttsModel, voice: ttsVoice, language: ttsLanguage }), turnHandling: { + interruption: { enabled: false, discardAudioIfUninterruptible: true }, endpointing: { minDelay: 600, maxDelay: 4000 }, }, }); + const publishAgentState = async (state) => { + const participant = ctx.room?.localParticipant; + if (!participant) return; + const payload = Buffer.from(JSON.stringify({ type: 'agent-state', state: String(state || '') })); + try { + await participant.publishData(payload, { reliable: true }); + } catch (err) { + console.warn('[livekit-voice] state publish', err?.message || err); + } + }; + session.on(voice.AgentSessionEventTypes.UserInputTranscribed, (ev) => { console.log('[livekit-voice] transcript', { final: ev.isFinal, @@ -185,12 +257,14 @@ const agent = defineAgent({ }); session.on(voice.AgentSessionEventTypes.AgentStateChanged, (ev) => { console.log('[livekit-voice] agent state', `${ev.oldState} -> ${ev.newState}`); + publishAgentState(ev.newState); }); session.on(voice.AgentSessionEventTypes.Error, (ev) => { console.error('[livekit-voice] session error', ev.error || ev); }); session.on(voice.AgentSessionEventTypes.SpeechCreated, (ev) => { console.log('[livekit-voice] speech created', { source: ev.source }); + publishAgentState('speaking'); }); await session.start({ @@ -207,7 +281,13 @@ const agent = defineAgent({ } try { - session.say('Hi, I am your event concierge. How can I help?'); + const spoken = await loadSpokenGreeting(botId); + const fromAvatar = await waitForAvatarReady(ctx.room, 4000); + if (fromAvatar) { + await new Promise((r) => setTimeout(r, 500)); + } + console.log('[livekit-voice] greeting', { fromAvatar, chars: spoken.length }); + session.say(spoken, { allowInterruptions: false }); } catch (err) { console.error('[livekit-voice] greeting failed', err?.message || err); }