@primoia/vocall-vue
v1.0.0
Published
Vocall Vue 3 SDK - WebSocket UI automation platform
Maintainers
Readme
Vocall SDK for Vue
Vue 3 SDK for the Vocall platform -- connect your Vue application to a Vocall engine over WebSocket and let the AI assistant read, fill, and navigate your UI in real time.
Features
- Reactive composables built on Vue 3 Composition API (
ref,inject,watch) - Plugin-based setup with a shared
VocallClientinstance across your application - Two-way field binding: register any
<input>,<select>, or<textarea>so the engine can fill, clear, focus, and highlight it - Action registration: expose buttons and callbacks the engine can trigger remotely
- Pre-built UI components (
VocallChat,VocallFab,VocallStatus) with scoped styles - Streaming chat tokens with automatic message assembly
- Automatic WebSocket reconnection with exponential back-off (up to 10 attempts)
- Full TypeScript support with exported types for every protocol message
Installation
npm install @vocall/vue-sdkPeer dependency: Vue >= 3.4.0
Quick Start
1. Register the plugin
// main.ts
import { createApp } from 'vue';
import { VocallPlugin } from '@vocall/vue-sdk';
import App from './App.vue';
const app = createApp(App);
app.use(VocallPlugin, {
url: 'ws://localhost:12900/connect',
token: 'my-api-token', // optional
visitorId: 'visitor-abc-123', // optional, auto-generated if omitted
});
app.mount('#app');2. Connect and send messages
<script setup lang="ts">
import { onMounted } from 'vue';
import { useVocall, useVocallField, type ManifestMessage } from '@vocall/vue-sdk';
const {
status, messages, connected,
connect, disconnect, sendText, client,
} = useVocall();
// Register a field so the engine can fill it
const { fieldRef, value: nameValue } = useVocallField({
fieldId: 'customer_name',
client,
});
// Build the manifest and connect
onMounted(() => {
const manifest: ManifestMessage = {
type: 'manifest',
app: 'my-app',
screens: {
home: {
id: 'home',
label: 'Home',
fields: [
{ id: 'customer_name', type: 'text', label: 'Customer Name', required: true },
],
},
},
currentScreen: 'home',
};
connect(manifest);
});
</script>
<template>
<p>Status: {{ status }}</p>
<input ref="fieldRef" v-model="nameValue" placeholder="Customer Name" />
<button @click="sendText('Fill the customer name with John Doe')">
Ask Vocall
</button>
</template>Composables API
useVocall(options?)
Main composable for connecting to the Vocall server. If the VocallPlugin was installed, the shared client is injected automatically; otherwise a new client is created from the provided options.
Options (UseVocallOptions):
| Property | Type | Default | Description |
|---|---|---|---|
| serverUrl | string | ws://localhost:12900/connect | WebSocket server URL |
| token | string | -- | Optional auth token |
| visitorId | string | auto-generated | Persistent visitor identifier |
| autoDispose | boolean | true | Dispose the client when the component unmounts |
Returns (UseVocallReturn):
| Property | Type | Description |
|---|---|---|
| status | Ref<VocallStatus> | Current connection/processing status (readonly) |
| messages | Ref<ChatMessage[]> | Reactive chat message list |
| connected | Ref<boolean> | Whether the WebSocket is connected (readonly) |
| sessionId | Ref<string \| null> | Session ID assigned by the server (readonly) |
| pendingConfirmSeq | Ref<number> | Sequence number of a pending confirmation (-1 if none) |
| pendingConfirmMessage | Ref<string \| null> | Text of a pending confirmation dialog |
| sendText(text) | (text: string) => void | Send a chat message to the engine |
| sendConfirm(seq, confirmed) | (seq: number, confirmed: boolean) => void | Respond to a confirmation request |
| connect(manifest) | (manifest: ManifestMessage) => void | Open the WebSocket and send the manifest |
| disconnect() | () => void | Close the connection |
| sendState(screen) | (screen: string) => void | Send current field state to the server |
| clearMessages() | () => void | Clear the local chat history |
| client | VocallClient | Underlying client instance for advanced use |
useVocallField(options)
Registers a form field with the Vocall client so the engine can fill, clear, focus, and highlight it.
Options (UseVocallFieldOptions):
| Property | Type | Description |
|---|---|---|
| fieldId | string | Unique identifier matching a field in the manifest |
| client | VocallClient | Client instance from useVocall().client |
| initialValue | string | Optional initial value |
Returns (UseVocallFieldReturn):
| Property | Type | Description |
|---|---|---|
| fieldRef | Ref<HTMLInputElement \| HTMLSelectElement \| HTMLTextAreaElement \| null> | Template ref to bind to the element |
| value | Ref<string> | Reactive model value (use with v-model) |
Example:
<script setup lang="ts">
import { useVocall, useVocallField } from '@vocall/vue-sdk';
const { client } = useVocall();
const { fieldRef: emailRef, value: emailValue } = useVocallField({
fieldId: 'email',
client,
});
</script>
<template>
<input ref="emailRef" v-model="emailValue" type="email" />
</template>useVocallAction(options)
Registers an action handler with the Vocall client so the engine can trigger it via the click command.
Options (UseVocallActionOptions):
| Property | Type | Description |
|---|---|---|
| actionId | string | Unique identifier matching an action in the manifest |
| client | VocallClient | Client instance from useVocall().client |
| handler | () => void \| Promise<void> | Callback invoked when the engine triggers this action |
Example:
<script setup lang="ts">
import { useVocall, useVocallAction } from '@vocall/vue-sdk';
const { client } = useVocall();
useVocallAction({
actionId: 'submit_form',
client,
handler: async () => {
await saveData();
console.log('Form submitted by Vocall');
},
});
</script>Plugin
VocallPlugin
Install Vocall as a Vue plugin to create a single shared VocallClient instance that is provided to all components via inject.
import { createApp } from 'vue';
import { VocallPlugin } from '@vocall/vue-sdk';
const app = createApp(App);
app.use(VocallPlugin, {
url: 'ws://localhost:12900/connect',
token: 'my-api-token',
visitorId: 'visitor-abc-123',
});
app.mount('#app');Options (VocallPluginOptions):
| Property | Type | Required | Description |
|---|---|---|---|
| url | string | No | WebSocket server URL. Defaults to ws://localhost:12900/connect |
| token | string | No | Optional authentication token |
| visitorId | string | No | Visitor identifier. Auto-generated and persisted in localStorage if omitted |
When the plugin is installed, the client is also available on this.$vocall for Options API components.
Components
VocallChat
A full chat panel with message list, text input, and header showing connection status.
Props:
| Prop | Type | Description |
|---|---|---|
| messages | ChatMessage[] | Array of chat messages to display |
| status | VocallStatus | Current engine status |
| connected | boolean | Whether the client is connected |
| sessionId | string \| null | Optional session identifier |
Events:
| Event | Payload | Description |
|---|---|---|
| send | text: string | Emitted when the user submits a message |
| clear | -- | Emitted when the user clicks the clear button |
| close | -- | Emitted when the user clicks the close button |
<template>
<VocallChat
:messages="messages"
:status="status"
:connected="connected"
@send="sendText"
@clear="clearMessages"
@close="chatOpen = false"
/>
</template>VocallFab
A floating action button (fixed bottom-right) that changes color based on the engine status and shows a spinner during thinking or executing states.
Props:
| Prop | Type | Description |
|---|---|---|
| status | VocallStatus | Current engine status |
| connected | boolean | Whether the client is connected |
Events:
| Event | Description |
|---|---|
| click | Emitted when the FAB is clicked |
<template>
<VocallFab :status="status" :connected="connected" @click="toggleChat" />
</template>VocallStatus
A small status pill that becomes visible when the engine is actively processing (thinking, executing, speaking, listening, or recording). Hidden during idle and disconnected states.
Props:
| Prop | Type | Description |
|---|---|---|
| status | VocallStatus | Current engine status |
| connected | boolean | Whether the client is connected |
<template>
<VocallStatus :status="status" :connected="connected" />
</template>VocallClient API
The VocallClient class manages the WebSocket connection, field/action registry, and command execution. It is typically accessed via useVocall().client.
Properties
| Property | Type | Description |
|---|---|---|
| status | VocallStatus | Current status |
| messages | ChatMessage[] | Chat message history |
| sessionId | string \| null | Server-assigned session ID |
| connected | boolean | Connection state |
| registry | VocallFieldRegistry | Field and action registry |
| events | EventEmitter | Internal event bus |
| serverUrl | string | WebSocket server URL (read/write) |
| token | string \| undefined | Auth token (read/write) |
| visitorId | string | Visitor identifier (read-only) |
Methods
| Method | Description |
|---|---|
| connect(manifest) | Open the WebSocket and send the manifest |
| disconnect() | Close the connection intentionally |
| sendText(text) | Send a chat message |
| sendConfirm(seq, confirmed) | Respond to a confirmation dialog |
| sendResult(seq, results, currentScreen?) | Send command execution results |
| sendState(screen) | Send current field state snapshot |
| dispose() | Disconnect, remove listeners, and clear registries |
Events
Subscribe to events via client.events.on(event, handler):
| Event | Arguments | Description |
|---|---|---|
| change | -- | Fired whenever any reactive state changes |
| confirm | seq: number, message: string | A confirmation dialog was requested |
| toast | message: string, level: string, duration: number | A toast notification was requested |
Manifest Structure
The manifest describes your application's screens, fields, and actions. It is sent to the engine on connection.
import type { ManifestMessage } from '@vocall/vue-sdk';
const manifest: ManifestMessage = {
type: 'manifest',
app: 'invoice-app',
version: '1.0.0',
currentScreen: 'dashboard',
user: {
name: 'Jane Doe',
email: '[email protected]',
org: 'Acme Corp',
role: 'admin',
},
persona: {
name: 'Emma',
role: 'Invoice Assistant',
instructions: 'Help the user issue invoices and manage taxpayers.',
},
context: {
locale: 'en-US',
timezone: 'America/New_York',
},
screens: {
dashboard: {
id: 'dashboard',
label: 'Dashboard',
route: '/dashboard',
fields: [
{ id: 'search', type: 'text', label: 'Search', placeholder: 'Search...' },
],
actions: [
{ id: 'new_invoice', label: 'New Invoice' },
],
},
invoice_form: {
id: 'invoice_form',
label: 'Issue Invoice',
route: '/invoices/new',
fields: [
{ id: 'customer_name', type: 'text', label: 'Customer', required: true },
{ id: 'amount', type: 'currency', label: 'Amount', required: true },
{ id: 'due_date', type: 'date', label: 'Due Date' },
{ id: 'notes', type: 'textarea', label: 'Notes', maxLength: 500 },
{
id: 'status',
type: 'select',
label: 'Status',
options: [
{ value: 'draft', label: 'Draft' },
{ value: 'sent', label: 'Sent' },
],
},
],
actions: [
{ id: 'save_invoice', label: 'Save', requiresConfirmation: true },
{ id: 'cancel', label: 'Cancel', destructive: true },
],
modals: [
{ id: 'customer_search', label: 'Search Customer', searchable: true },
],
},
},
};Field Types
The FieldType enum defines all supported field types:
| Type | Value | Description |
|---|---|---|
| Text | text | Plain text input |
| Number | number | Numeric input |
| Currency | currency | Monetary value input |
| Date | date | Date picker |
| Datetime | datetime | Date and time picker |
| Email | email | Email address input |
| Phone | phone | Phone number input |
| Masked | masked | Input with a custom mask pattern |
| Select | select | Dropdown select |
| Autocomplete | autocomplete | Autocomplete / typeahead |
| Checkbox | checkbox | Boolean checkbox |
| Radio | radio | Radio button group |
| Textarea | textarea | Multi-line text area |
| File | file | File upload |
| Hidden | hidden | Hidden field (not visible to the user) |
UI Actions
The engine can send commands containing one or more of the following 14 UI actions:
| Action | Target | Description |
|---|---|---|
| navigate | screen | Navigate to a different screen |
| fill | field | Fill a field with a value (supports typewriter animation) |
| clear | field | Clear a field's value |
| select | field | Set the value of a select/dropdown field |
| click | action | Trigger a registered action handler |
| highlight | field | Temporarily highlight a field element |
| focus | field | Focus a field element |
| scroll_to | field | Scroll the viewport to a field |
| show_toast | -- | Display a toast notification |
| ask_confirm | -- | Show a confirmation dialog and wait for user response |
| open_modal | modal | Open a modal by ID with an optional search query |
| close_modal | -- | Close the currently open modal |
| enable | field | Enable a disabled field |
| disable | field | Disable a field |
Sequential actions (navigate, click, open_modal, close_modal, ask_confirm, show_toast) are executed one at a time. All other actions run in parallel for optimal performance. Failed actions are retried up to 3 times with a 300 ms delay.
Demo
A demo application is available in the demo/ directory. It demonstrates plugin setup, field registration, action handling, and the chat overlay using all three provided components.
Testing
The SDK includes a comprehensive test suite with ~184 tests covering protocol types, WebSocket client, Vue composables, plugin integration, and voice utilities.
Running Tests
npm test # Run all tests
npm test -- --coverage # Run with coverage reportTest Structure
| File | Tests | What it covers |
|---|---|---|
| src/protocol/__tests__/types.test.ts | 24 | Protocol type enums, message interfaces, field types |
| src/client/__tests__/vocall-client.test.ts | 94 | WebSocket connection, reconnection, command execution, streaming |
| src/composables/__tests__/use-vocall.test.ts | 25 | useVocall composable, reactive state |
| src/composables/__tests__/use-vocall-field.test.ts | 13 | useVocallField composable, template ref binding |
| src/composables/__tests__/use-vocall-action.test.ts | 7 | useVocallAction composable, callback registration |
| src/plugin/__tests__/vocall-plugin.test.ts | 8 | VocallPlugin installation, provide/inject |
| src/voice/__tests__/frame-splitter.test.ts | 13 | Audio frame splitting for voice pipeline |
Test Configuration
- Framework: Vitest with Vue Test Utils
- Environment: jsdom
- Config: vitest.config.ts | src/testing/setup.ts
Development
Building the SDK
npm install
npm run buildRunning the Demo
The demo application is in the demo/ directory:
cd demo
npm install
npm run dev # http://localhost:5173The demo connects to a Vocall server at ws://localhost:12900/connect. See the workspace README for local server setup.
Docker
When running from the workspace, the demo is available as a Docker container:
# From the workspace root
make serve-all # Starts engine + all demos
# Vue demo at http://localhost:21004Related
This SDK is part of the Primoia Vocall Workspace.
| Resource | Link | |---|---| | Workspace (setup, Docker) | primoia-vocall-workspace | | Angular SDK | vocall-sdk-angular | | Next.js SDK | vocall-sdk-nextjs | | Kotlin SDK | vocall-sdk-kotlin | | Swift SDK | vocall-sdk-swift | | Flutter SDK | jarvis-sdk-flutter |
License
Proprietary. All rights reserved.
