{"_id":"@aquantinc/acp-web-sdk","_rev":"5-1fa73a0abd94901ca8e4acbfd75afa62","name":"@aquantinc/acp-web-sdk","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"@aquantinc/acp-web-sdk","version":"0.1.0","keywords":["aquant","sdk","chat","voice","websocket","webrtc","communication"],"author":{"name":"Aquant"},"license":"MIT","_id":"@aquantinc/acp-web-sdk@0.1.0","maintainers":[{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"}],"homepage":"https://github.com/aquant/acp-web-sdk#readme","bugs":{"url":"https://github.com/aquant/acp-web-sdk/issues"},"dist":{"shasum":"543fb217185c057aac22d1a15d6d75e3eb0810a3","tarball":"https://registry.npmjs.org/@aquantinc/acp-web-sdk/-/acp-web-sdk-0.1.0.tgz","fileCount":23,"integrity":"sha512-hRsY/Os3yvwetSyvA7uumUlIVSl9d2rFwKvWVtEYfIyM8BU7L4hUprKZUkZP9RiaaC665NKsNMO/TqCPlaM0tQ==","signatures":[{"sig":"MEYCIQCt+pIqIIW6cO63r2Ab9pqJUmecvCbCtLRUrv2oveSjtAIhANie4MlFX4tzVB2LzsbsPBVcd/4P0dQP854xIrum4xme","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":417390},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./chat":{"import":{"types":"./dist/chat/index.d.ts","default":"./dist/chat/index.js"},"require":{"types":"./dist/chat/index.d.cts","default":"./dist/chat/index.cjs"}},"./voice":{"import":{"types":"./dist/voice/index.d.ts","default":"./dist/voice/index.js"},"require":{"types":"./dist/voice/index.d.cts","default":"./dist/voice/index.cjs"}}},"gitHead":"700a799d02b59c4361c7064b2b29d1f89689934e","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","clean":"rm -rf dist","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"},"repository":{"url":"git+https://github.com/aquant/acp-web-sdk.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser SDK for Aquant Communication Platform — chat, voice, and session management","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"@twilio/voice-sdk":"^2.18.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^2.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/acp-web-sdk_0.1.0_1776812367431_0.5238459635817239","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aquantinc/acp-web-sdk","version":"0.1.1","keywords":["aquant","sdk","chat","voice","websocket","webrtc","communication"],"author":{"name":"Aquant"},"license":"MIT","_id":"@aquantinc/acp-web-sdk@0.1.1","maintainers":[{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"}],"homepage":"https://github.com/aquant/acp-web-sdk#readme","bugs":{"url":"https://github.com/aquant/acp-web-sdk/issues"},"dist":{"shasum":"b89a907ed42d72af14e0359ca278f6a10046ad6e","tarball":"https://registry.npmjs.org/@aquantinc/acp-web-sdk/-/acp-web-sdk-0.1.1.tgz","fileCount":26,"integrity":"sha512-LQJdJ2BgXSElakkgL/y/2I0oAxK/UKJ3JiZeNemr2UnlB9d70Arnuokiib9cvIHQ9RbN//3wGwLAng6fEj3hug==","signatures":[{"sig":"MEQCIA3oJEjbu8qJMCjjtgXscQ53nAI2FH4uKIF653ZparH8AiAjQaR9HgR+gz05zK7P8GgJ3lVG5MPKKWrzeXjc5eJ46Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":436148},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./chat":{"import":{"types":"./dist/chat/index.d.ts","default":"./dist/chat/index.js"},"require":{"types":"./dist/chat/index.d.cts","default":"./dist/chat/index.cjs"}},"./voice":{"import":{"types":"./dist/voice/index.d.ts","default":"./dist/voice/index.js"},"require":{"types":"./dist/voice/index.d.cts","default":"./dist/voice/index.cjs"}}},"gitHead":"700a799d02b59c4361c7064b2b29d1f89689934e","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","clean":"rm -rf dist","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"},"repository":{"url":"git+https://github.com/aquant/acp-web-sdk.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser SDK for Aquant Communication Platform — chat, voice, and session management","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"@twilio/voice-sdk":"^2.18.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^2.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/acp-web-sdk_0.1.1_1776812942701_0.07918617718536458","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aquantinc/acp-web-sdk","version":"0.1.2","keywords":["aquant","sdk","chat","voice","websocket","webrtc","communication"],"author":{"name":"Aquant"},"license":"MIT","_id":"@aquantinc/acp-web-sdk@0.1.2","maintainers":[{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"}],"homepage":"https://github.com/aquant/acp-web-sdk#readme","bugs":{"url":"https://github.com/aquant/acp-web-sdk/issues"},"dist":{"shasum":"26f807037800292a88c7c31d36851892a28fb2bc","tarball":"https://registry.npmjs.org/@aquantinc/acp-web-sdk/-/acp-web-sdk-0.1.2.tgz","fileCount":26,"integrity":"sha512-B6K6RNBUOOMW2l5LIMKppRzHsrvN4qV3x8ujGQx3E/V5PliZDzjn7GGHO/dr1xLXHr49lfgKPBDIOogdyDDLLA==","signatures":[{"sig":"MEQCIF4kcSEtDtmsEibRRXqaG7O6YnaIYoBt1ns06y/RGGAkAiA0aeV5YtS2bYa1YDb8ya+B33nDk866GbOJ5AIOtFI4Qg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":436143},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./chat":{"import":{"types":"./dist/chat/index.d.ts","default":"./dist/chat/index.js"},"require":{"types":"./dist/chat/index.d.cts","default":"./dist/chat/index.cjs"}},"./voice":{"import":{"types":"./dist/voice/index.d.ts","default":"./dist/voice/index.js"},"require":{"types":"./dist/voice/index.d.cts","default":"./dist/voice/index.cjs"}}},"gitHead":"bfa3816ab826c5dbeb4702053e7b809245944b56","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","clean":"rm -rf dist","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"},"repository":{"url":"git+https://github.com/aquant/acp-web-sdk.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser SDK for Aquant Conversation Platform — chat, voice, and session management","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","dependencies":{"@twilio/voice-sdk":"^2.18.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^2.0.0","typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/acp-web-sdk_0.1.2_1776813601188_0.24450694861580158","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aquantinc/acp-web-sdk","version":"0.1.3","description":"Browser SDK for Aquant Conversation Platform — chat, voice, and session management","author":{"name":"Aquant"},"license":"MIT","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./chat":{"import":{"types":"./dist/chat/index.d.ts","default":"./dist/chat/index.js"},"require":{"types":"./dist/chat/index.d.cts","default":"./dist/chat/index.cjs"}},"./voice":{"import":{"types":"./dist/voice/index.d.ts","default":"./dist/voice/index.js"},"require":{"types":"./dist/voice/index.d.cts","default":"./dist/voice/index.cjs"}}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","lint":"tsc --noEmit","clean":"rm -rf dist","prepublishOnly":"npm run build"},"devDependencies":{"tsup":"^8.0.0","typescript":"^5.4.0","vitest":"^2.0.0"},"keywords":["aquant","sdk","chat","voice","websocket","webrtc","communication"],"repository":{"type":"git","url":"git+https://github.com/aquant/acp-web-sdk.git"},"engines":{"node":">=18"},"sideEffects":false,"dependencies":{"@twilio/voice-sdk":"^2.18.1"},"gitHead":"43d856337c771ef91b1236b65e910ebe20c47880","bugs":{"url":"https://github.com/aquant/acp-web-sdk/issues"},"homepage":"https://github.com/aquant/acp-web-sdk#readme","_id":"@aquantinc/acp-web-sdk@0.1.3","_nodeVersion":"22.19.0","_npmVersion":"8.19.4","dist":{"integrity":"sha512-bbMMHBW+E+ESkLE/0q6PofEfbatiekplnU337PhtsRFh/rg6CIZnHZeH21RiLCAF2zZOFFmADUsP21cJ1c15xQ==","shasum":"45a1808db7b4d936657378cca56dc66710043300","tarball":"https://registry.npmjs.org/@aquantinc/acp-web-sdk/-/acp-web-sdk-0.1.3.tgz","fileCount":26,"unpackedSize":469646,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIErypRmpcihJPhuz/AH6L1QYhhFygqE2fw2B+/6cuemcAiEAwGkWnxbMKcAWdcQALMTLAk4PCIIF7a+tpzjqZubi7Sc="}]},"_npmUser":{"name":"yaakov.sidikman","email":"yaakov.sidikman@aquant.ai"},"directories":{},"maintainers":[{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"},{"name":"yaakov.sidikman","email":"yaakov.sidikman@aquant.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/acp-web-sdk_0.1.3_1777416617146_0.6716563852675232"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-21T22:59:27.326Z","modified":"2026-04-28T22:50:17.567Z","0.1.0":"2026-04-21T22:59:27.608Z","0.1.1":"2026-04-21T23:09:02.850Z","0.1.2":"2026-04-21T23:20:01.369Z","0.1.3":"2026-04-28T22:50:17.421Z"},"bugs":{"url":"https://github.com/aquant/acp-web-sdk/issues"},"author":{"name":"Aquant"},"license":"MIT","homepage":"https://github.com/aquant/acp-web-sdk#readme","keywords":["aquant","sdk","chat","voice","websocket","webrtc","communication"],"repository":{"type":"git","url":"git+https://github.com/aquant/acp-web-sdk.git"},"description":"Browser SDK for Aquant Conversation Platform — chat, voice, and session management","maintainers":[{"name":"indreshms-aquant","email":"indresh.ms@aquant.ai"},{"name":"yaakov.sidikman","email":"yaakov.sidikman@aquant.ai"}],"readme":"# @aquantinc/acp-web-sdk\n\nBrowser SDK for the Aquant Conversation Platform — voice calls, chat messaging, and session management from a single, framework-agnostic package.\n\nWorks with any JavaScript environment that runs in a browser: plain `<script>` tags, React, Vue, Angular, Svelte, or any bundler.\n\n## Install\n\n**With a package manager (React, Vue, Angular, etc.):**\n\n```bash\nnpm install @aquantinc/acp-web-sdk\n```\n\n**Without a bundler (plain HTML/JS):**\n\nNo install needed. Serve the SDK's `dist/` folder or copy `dist/index.js` into your project and import it directly:\n\n```html\n<script type=\"module\">\n  import { AquantClient } from \"./path-to/dist/index.js\";\n  // ready to use\n</script>\n```\n\n> The SDK handles all voice dependencies automatically at runtime — no extra `<script>` tags or installs required.\n\n## Quick Start\n\n### With a bundler (React, Vue, etc.)\n\n```typescript\nimport { AquantClient } from \"@aquantinc/acp-web-sdk\";\n\nconst client = new AquantClient({\n  apiKey: \"your-api-key\",\n  apiSecret: \"your-api-secret\",\n  agentId: \"your-agent-id\",\n});\n\nawait client.initialize();\n\nconst agent = client.voice.agentInfo;\nconsole.log(`Agent: ${agent?.agentName} (${agent?.agentPhone})`);\n\nawait client.voice.startCall();\n\n// Later…\nawait client.voice.endCall();\nawait client.destroy();\n```\n\n### Without a bundler (plain HTML)\n\n```html\n<!DOCTYPE html>\n<html>\n<body>\n  <button id=\"call\">Start Call</button>\n  <button id=\"mute\" disabled>Mute</button>\n  <button id=\"end\" disabled>End Call</button>\n\n  <script type=\"module\">\n    import { AquantClient } from \"./dist/index.js\";\n\n    const client = new AquantClient({\n      apiKey: \"your-api-key\",\n      apiSecret: \"your-api-secret\",\n      agentId: \"your-agent-id\",\n    });\n\n    document.getElementById(\"call\").onclick = async () => {\n      if (!client.isInitialized) await client.initialize();\n      await client.voice.startCall();\n      document.getElementById(\"mute\").disabled = false;\n      document.getElementById(\"end\").disabled = false;\n    };\n\n    document.getElementById(\"mute\").onclick = () => {\n      if (client.voice.isMuted) client.voice.unmute();\n      else client.voice.mute();\n    };\n\n    document.getElementById(\"end\").onclick = async () => {\n      await client.voice.endCall();\n    };\n  </script>\n</body>\n</html>\n```\n\n> **Browser requirement:** The browser will prompt the user for microphone access when a call starts. The SDK requires a modern browser with WebRTC support (Chrome, Edge, Firefox, Safari 14.1+).\n\n## Configuration\n\n```typescript\nconst client = new AquantClient({\n  apiKey: string;       // API key (required)\n  apiSecret: string;    // API secret (required)\n  agentId: string;      // Agent identifier (required)\n  baseUrl?: string;     // VoiceAI server URL (default: \"https://voiceai-api.aquant.ai\")\n  callerId?: string;    // Caller identifier (default: \"acp-sdk-user\")\n  voiceAdapter?: VoiceAdapter;  // Optional — swap VoIP backend (default: Twilio browser SDK)\n  chatAdapter?: ChatAdapter;    // Optional — swap chat transport (default: VoiceAI POST /vss/web-chat SSE)\n  debug?: boolean;      // Enable debug logging (default: false)\n});\n```\n\n| Property        | Required | Default                              | Description                          |\n|-----------------|----------|--------------------------------------|--------------------------------------|\n| `apiKey`        | Yes      | —                                    | Your API key                         |\n| `apiSecret`     | Yes      | —                                    | Your API secret                      |\n| `agentId`       | Yes      | —                                    | AI agent identifier                  |\n| `baseUrl`       | No       | `https://voiceai-api.aquant.ai`      | Server URL (override for dev/staging)|\n| `callerId`      | No       | `acp-sdk-user`                       | Identifies the caller to the system  |\n| `voiceAdapter`  | No       | Twilio-based adapter                 | Custom `VoiceAdapter` implementation |\n| `chatAdapter`   | No       | SSE web-chat adapter                 | Custom `ChatAdapter` implementation  |\n| `debug`         | No       | `false`                              | Enables verbose console logging      |\n\n### Authentication\n\nThe SDK uses API key / secret authentication. During `client.initialize()`, the SDK exchanges your credentials via `POST /acp/token` to obtain an access token and agent metadata (name, phone number). Tokens are valid for 1 hour — to refresh, call `destroy()` and re-initialize.\n\n### Client Lifecycle\n\n```\nnew AquantClient(config)  →  client.initialize()  →  use voice/chat/sessions  →  client.destroy()\n        ↑                          ↑                                                     ↑\n   Creates the client.     Fetches token, registers      Cleans up voice device,\n   No network calls yet.   voice device; initializes     chat adapter, and internal\n                           chat adapter. Required        state.\n                           before voice/chat APIs.\n```\n\nYou can check `client.isInitialized` and `client.isDestroyed` at any time.\n\n## Usage with React\n\nThe SDK is framework-agnostic, but here's the recommended React pattern:\n\n```tsx\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { AquantClient, type VoiceCallState } from \"@aquantinc/acp-web-sdk\";\n\nexport default function VoiceCall() {\n  const clientRef = useRef<AquantClient | null>(null);\n  const [callState, setCallState] = useState<VoiceCallState | null>(null);\n  const [calling, setCalling] = useState(false);\n\n  // Create client once, destroy on unmount\n  useEffect(() => {\n    const client = new AquantClient({\n      apiKey: \"your-api-key\",\n      apiSecret: \"your-api-secret\",\n      agentId: \"your-agent-id\",\n    });\n    clientRef.current = client;\n\n    client.on(\"voice.call.ended\", () => {\n      setCalling(false);\n      setCallState(null);\n    });\n\n    return () => { client.destroy(); };\n  }, []);\n\n  const handleStartCall = async () => {\n    const client = clientRef.current!;\n    setCalling(true);\n    try {\n      if (!client.isInitialized) await client.initialize();\n      const state = await client.voice.startCall();\n      setCallState(state);\n      client.voice.onStateChange(setCallState);\n    } catch (e: any) {\n      setCalling(false);\n      console.error(e.message);\n    }\n  };\n\n  const handleEndCall = async () => {\n    await clientRef.current!.voice.endCall();\n  };\n\n  const handleToggleMute = () => {\n    const voice = clientRef.current!.voice;\n    if (voice.isMuted) voice.unmute(); else voice.mute();\n    setCallState({ ...voice.callState });\n  };\n\n  return (\n    <div>\n      <button onClick={handleStartCall} disabled={calling}>Start Call</button>\n      <button onClick={handleToggleMute} disabled={!callState}>\n        {callState?.muted ? \"Unmute\" : \"Mute\"}\n      </button>\n      <button onClick={handleEndCall} disabled={!callState}>End Call</button>\n      {callState && <p>Status: {callState.status}</p>}\n    </div>\n  );\n}\n```\n\n**Key React tips:**\n\n- Store the client in a `useRef` — it should not trigger re-renders.\n- Create the client in `useEffect` and call `client.destroy()` in the cleanup function.\n- Use `client.isInitialized` to lazy-initialize on first action (avoids a separate \"Initialize\" button).\n- Use `onStateChange` to push call state updates into React state.\n\n## Imports\n\nThe SDK exposes a main entry point and subpath imports:\n\n```typescript\n// Full SDK\nimport { AquantClient, AquantError } from \"@aquantinc/acp-web-sdk\";\n\n// Chat module only\nimport { ChatClient } from \"@aquantinc/acp-web-sdk/chat\";\n\n// Voice module only\nimport { VoiceClient } from \"@aquantinc/acp-web-sdk/voice\";\n```\n\n## Voice\n\n### How it works\n\n1. `client.initialize()` exchanges your credentials for an access token and retrieves agent metadata (name, phone number).\n2. The SDK sets up the underlying voice device automatically.\n3. `startCall()` connects to the agent with optimized WebRTC audio (echo cancellation, noise suppression, auto gain control).\n4. Audio flows: **Browser ↔ VoiceAI ↔ AI Agent**.\n\n### API\n\n```typescript\n// Initialize (fetches token, registers voice device)\nawait client.initialize();\n\n// Agent info is available after initialization\nconst agent = client.voice.agentInfo;\n// { agentId: string, agentName: string, agentPhone: string }\n\n// Start a call\nconst callState = await client.voice.startCall();\n\n// Mute / unmute\nclient.voice.mute();\nclient.voice.unmute();\nclient.voice.isMuted; // boolean\n\n// Listen for state changes\nclient.voice.onStateChange((state) => {\n  console.log(state.status); // \"idle\" | \"connecting\" | \"ringing\" | \"connected\" | \"disconnected\" | \"failed\"\n  console.log(state.muted);  // boolean\n});\n\n// End the call\nawait client.voice.endCall();\n\n// Full call state at any time\nclient.voice.callState;\n// { status, muted, agentInfo? }\n```\n\n### Call Status Flow\n\n```\nidle → connecting → ringing → connected → disconnected\n                                  ↓\n                               failed\n```\n\n### Advanced: Custom Voice Adapter\n\nThe voice module is provider-agnostic. You can swap the built-in provider for any VoIP backend by implementing the `VoiceAdapter` interface:\n\n```typescript\nimport type { VoiceAdapter } from \"@aquantinc/acp-web-sdk/voice\";\n\nclass MyCustomAdapter implements VoiceAdapter {\n  readonly name = \"my-provider\";\n  // Implement: initialize, startCall, endCall, mute, unmute,\n  //            onStatusChange, destroy, status, isMuted, agentInfo\n}\n\nconst client = new AquantClient({\n  apiKey: \"...\",\n  apiSecret: \"...\",\n  agentId: \"...\",\n  voiceAdapter: new MyCustomAdapter(),\n});\n```\n\n## Sessions\n\n```typescript\nconst session = await client.sessions.create({\n  metadata: { source: \"web-app\" },\n});\n\nconst existing = await client.sessions.get(\"sess_123\");\n\nawait client.sessions.updateMetadata(\"sess_123\", { resolved: true });\n\nawait client.sessions.end(\"sess_123\", { reason: \"resolved\" });\n```\n\n## Chat\n\nChat uses **`POST {baseUrl}/vss/web-chat`** with JSON `{ agent_id, sender_id, message }`. The server streams the assistant reply as **Server-Sent Events** (`text/event-stream`): each `data:` line carries JSON with a text `delta`, then `{ \"done\": true }`. There is **no WebSocket** — the SDK consumes the stream with `fetch` and `ReadableStream`.\n\nAfter `initialize()`, call **`connect({ sessionId })`** once per conversation thread. The `sessionId` is sent as **`sender_id`** (stable id for that user/thread — often the id from `sessions.create()`).\n\n```typescript\nawait client.initialize();\n\nclient.chat.connect({ sessionId: session.id });\n\nconst unsubscribe = client.chat.onMessage((message) => {\n  // User message once; assistant messages update as deltas arrive (same id, growing `text`).\n  console.log(`[${message.role}]: ${message.text}`);\n});\n\nconst reply = await client.chat.sendMessage({\n  sessionId: session.id,\n  text: \"Can you help me?\",\n});\n// `reply` is the final assistant message (full accumulated text).\n\nclient.chat.onConnectionStatusChange((status) => {\n  console.log(\"Chat:\", status); // \"disconnected\" | \"connected\"\n});\n\nclient.chat.disconnect();\n```\n\n### Advanced: Custom Chat Adapter\n\nImplement **`ChatAdapter`** and pass **`chatAdapter`** if you need a non-default backend (the default streams VoiceAI web-chat SSE).\n\n```typescript\nimport type { ChatAdapter } from \"@aquantinc/acp-web-sdk\";\n\nclass MyChatAdapter implements ChatAdapter {\n  readonly name = \"custom\";\n  // initialize, connect, disconnect, streamAssistantReply, destroy, isConnected\n}\n\nconst client = new AquantClient({\n  apiKey: \"...\",\n  apiSecret: \"...\",\n  agentId: \"...\",\n  chatAdapter: new MyChatAdapter(),\n});\n```\n\n## Events\n\nSubscribe to typed events across all modules:\n\n```typescript\n// Voice\nclient.on(\"voice.call.started\",   (state) => { /* ... */ });\nclient.on(\"voice.call.connected\", (state) => { /* ... */ });\nclient.on(\"voice.call.ended\",     (state) => { /* ... */ });\n\n// Chat\nclient.on(\"chat.message.received\", (msg) => { /* ... */ });\nclient.on(\"chat.message.sent\",     (msg) => { /* ... */ });\n\n// Sessions\nclient.on(\"session.created\", (session) => { /* ... */ });\nclient.on(\"session.ended\",   (session) => { /* ... */ });\n\n// Errors\nclient.on(\"error\", (error) => { /* ... */ });\n\n// One-time listener\nclient.once(\"voice.call.connected\", (state) => { /* ... */ });\n\n// Unsubscribe\nconst unsub = client.on(\"error\", handler);\nunsub();\n```\n\n## Error Handling\n\nAll SDK errors extend `AquantError` with a typed error code:\n\n```typescript\nimport { AquantError, isAquantError } from \"@aquantinc/acp-web-sdk\";\n\ntry {\n  await client.initialize();\n} catch (error) {\n  if (isAquantError(error)) {\n    console.error(error.code);    // e.g. \"AUTH_INVALID_CREDENTIALS\"\n    console.error(error.message); // human-readable description\n    console.error(error.details); // optional extra context\n  }\n}\n```\n\n| Error Code                  | When                                              |\n|-----------------------------|---------------------------------------------------|\n| `AUTH_INVALID_CREDENTIALS`  | Invalid API key or secret (HTTP 401)               |\n| `AGENT_NOT_FOUND`           | No agent found for the given `agentId` (HTTP 404)  |\n| `AGENT_NOT_ACTIVE`          | Agent exists but is not active (HTTP 400)          |\n| `VOICE_CONNECTION_FAILED`   | Voice device or call connection failed              |\n| `VOICE_NOT_CONNECTED`       | Action requires an active call but none exists     |\n| `CLIENT_NOT_INITIALIZED`    | Method called before `initialize()`                |\n| `CLIENT_DESTROYED`          | Method called after `destroy()`                    |\n| `NETWORK_UNAVAILABLE`       | Network request failed                             |\n| `SESSION_NOT_FOUND`         | Session ID not found                               |\n| `SESSION_CREATE_FAILED`     | Session creation failed                            |\n| `CHAT_SEND_FAILED`          | Chat request failed (network, HTTP error, or SSE error payload) |\n| `CHAT_CONNECTION_FAILED`    | `sendMessage` without `connect()`, or `sessionId` mismatch        |\n\n## Development\n\n```bash\nnpm install       # install dependencies\nnpm run build     # build ESM + CJS + types\nnpm test          # run tests (Vitest)\nnpm run lint      # type-check with tsc\n```\n\n## Project Structure\n\n```\nsrc/\n  index.ts                # Root entry point & public exports\n  core/\n    client.ts             # AquantClient — main entry point\n    config.ts             # Config validation, defaults, headers\n    events.ts             # Typed EventBus\n    errors.ts             # AquantError + error codes\n    sessions.ts           # SessionManager\n    types.ts              # Shared type definitions\n  chat/\n    index.ts              # Chat subpath entry\n    chatClient.ts         # ChatClient (public API)\n    chatAdapter.ts        # ChatAdapter interface + WebChatAdapter (SSE) + default stub\n    types.ts              # Chat-specific types\n  voice/\n    index.ts              # Voice subpath entry\n    voiceClient.ts        # VoiceClient (public API)\n    voiceAdapter.ts       # VoiceAdapter interface + DefaultVoiceAdapter (stub)\n    twilioVoiceAdapter.ts # Built-in Twilio browser SDK adapter\n    types.ts              # Voice-specific types\n  utils/\n    logger.ts             # Leveled logger\n    guards.ts             # Runtime assertion helpers\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}