vcon-js
v0.2.0
Published
JavaScript library for creating and managing vCons (Virtual Conversations)
Maintainers
Readme
vcon-js
A JavaScript/TypeScript library for creating and managing vCons (Virtual Conversations), compliant with IETF draft-ietf-vcon-vcon-core-01.
Installation
npm install vcon-jsUsage
Creating a new vCon
import { Vcon, Party, Dialog } from 'vcon-js';
// Create a new vCon
const vcon = Vcon.buildNew();
// Add parties
const party1 = new Party({
tel: '+1234567890',
name: 'John Doe',
role: 'agent'
});
const party2 = new Party({
tel: '+0987654321',
name: 'Jane Smith',
role: 'customer'
});
vcon.addParty(party1);
vcon.addParty(party2);
// Add a text dialog
const dialog = new Dialog({
type: 'text',
start: new Date(),
parties: [0, 1],
body: 'Hello, this is a conversation!',
mediatype: 'text/plain'
});
vcon.addDialog(dialog);
// Convert to JSON
const json = vcon.toJson();Loading from JSON
import { Vcon } from 'vcon-js';
const jsonString = '...'; // Your vCon JSON string
const vcon = Vcon.buildFromJson(jsonString);Working with Attachments
import { Vcon, Attachment } from 'vcon-js';
const vcon = Vcon.buildNew();
// Add an inline attachment
const attachment = vcon.addAttachment({
type: 'application/pdf',
body: 'base64EncodedContent',
encoding: 'base64url',
filename: 'document.pdf'
});
// Add an external attachment
vcon.addAttachment({
purpose: 'transcript',
url: 'https://example.com/transcript.txt',
content_hash: 'sha512-abc123...',
mediatype: 'text/plain'
});
// Find an attachment by type
const pdfAttachment = vcon.findAttachmentByType('application/pdf');
// Find an attachment by purpose
const transcript = vcon.findAttachmentByPurpose('transcript');Working with Analysis
import { Vcon } from 'vcon-js';
const vcon = Vcon.buildNew();
// Add inline analysis
vcon.addAnalysis({
type: 'sentiment',
dialog: 0,
vendor: 'sentiment-analyzer',
product: 'analyzer-v2',
body: {
score: 0.8,
label: 'positive'
},
encoding: 'json'
});
// Add analysis with external reference
vcon.addAnalysis({
type: 'transcription',
dialog: [0, 1],
vendor: 'whisper',
url: 'https://example.com/transcription.json',
content_hash: 'sha512-xyz789...',
mediatype: 'application/json'
});
// Find analysis by type
const sentimentAnalysis = vcon.findAnalysisByType('sentiment');Working with Dialog Types
vcon-core-01 defines four dialog types: recording, text, transfer, and incomplete.
import { Dialog } from 'vcon-js';
// Text dialog
const textDialog = new Dialog({
type: 'text',
start: new Date(),
parties: [0, 1],
body: 'Hello!',
mediatype: 'text/plain'
});
// Recording dialog with external audio
const recordingDialog = new Dialog({
type: 'recording',
start: new Date(),
parties: [0, 1],
duration: 300
});
recordingDialog.addExternalData(
'https://example.com/audio.wav',
'audio/wav',
{ filename: 'call.wav', content_hash: 'sha512-abc...' }
);
// Incomplete dialog (e.g., no answer)
const incompleteDialog = new Dialog({
type: 'incomplete',
start: new Date(),
parties: [0],
disposition: 'no-answer'
});
// Check dialog types
console.log(textDialog.isText()); // true
console.log(recordingDialog.isRecording()); // true
console.log(incompleteDialog.isIncomplete()); // trueWorking with Extensions (vcon-core-01)
import { Vcon } from 'vcon-js';
const vcon = Vcon.buildNew();
// Add a non-critical extension
vcon.addExtension('contact_center');
// Add a critical extension (must be understood by processors)
vcon.addCriticalExtension('encrypted');
// Check extensions
console.log(vcon.hasExtension('contact_center')); // true
console.log(vcon.isCriticalExtension('encrypted')); // trueWorking with Groups
import { Vcon } from 'vcon-js';
const vcon = Vcon.buildNew();
// Add a group reference (for linking related vCons)
vcon.addGroup({ uuid: 'conversation-thread-uuid', type: 'thread' });Working with Tags
import { Vcon } from 'vcon-js';
const vcon = Vcon.buildNew();
// Add a tag
vcon.addTag('category', 'support');
// Get a tag
const category = vcon.getTag('category');Working with Party History
Track party events within a dialog (join, leave, hold, etc.):
import { Dialog, PartyHistory } from 'vcon-js';
const dialog = new Dialog({
type: 'recording',
start: new Date(),
parties: [0, 1]
});
// Add party history events
dialog.party_history = [
new PartyHistory(0, 'joined', new Date()).toDict(),
new PartyHistory(1, 'joined', new Date(Date.now() + 5000)).toDict(),
new PartyHistory(1, 'hold', new Date(Date.now() + 60000)).toDict(),
new PartyHistory(1, 'resume', new Date(Date.now() + 120000)).toDict(),
new PartyHistory(0, 'left', new Date(Date.now() + 300000)).toDict(),
new PartyHistory(1, 'left', new Date(Date.now() + 300000)).toDict()
];Transfer Dialogs
Represent call transfers between parties:
import { Dialog } from 'vcon-js';
const transferDialog = new Dialog({
type: 'transfer',
start: new Date(),
parties: [0, 1, 2], // Original caller, original agent, new agent
transferor: 1, // Agent initiating transfer
transferee: 0, // Caller being transferred
transfer_target: 2 // New agent receiving transfer
});Party Identifiers
vcon-core-01 supports multiple party identifier types:
import { Party } from 'vcon-js';
const party = new Party({
tel: '+1234567890', // Telephone URL
sip: 'sip:[email protected]', // SIP address
mailto: '[email protected]', // Email address
stir: 'eyJhbGci...', // STIR PASSporT
did: 'did:example:123', // Decentralized Identifier
name: 'John Doe',
timezone: 'America/New_York',
role: 'agent'
});
// Check if party has an identifier
console.log(party.hasIdentifier()); // true
// Get primary identifier
console.log(party.getPrimaryIdentifier()); // '+1234567890'API Reference
Vcon
The main class for working with vCons.
Static Methods
buildNew(): Creates a new vCon with UUID and timestampbuildFromJson(jsonString: string): Creates a vCon from JSON string
Instance Methods
addParty(party: Party): Adds a party to the vConaddDialog(dialog: Dialog): Adds a dialog to the vConaddAttachment(params): Adds an attachment (inline or external)addAnalysis(params): Adds analysis dataaddTag(tagName, tagValue): Adds a tagaddExtension(name): Adds a non-critical extensionaddCriticalExtension(name): Adds a critical extensionaddGroup(group): Adds a group referencefindPartyIndex(by, val): Finds a party index by propertyfindDialog(by, val): Finds a dialog by propertyfindAttachmentByType(type): Finds an attachment by typefindAttachmentByPurpose(purpose): Finds an attachment by purposefindAnalysisByType(type): Finds analysis by typehasExtension(name): Checks if extension is usedisCriticalExtension(name): Checks if extension is criticalgetTag(tagName): Gets a tag valuetoJson(): Converts the vCon to JSON stringtoDict(): Converts the vCon to a plain object
Properties
uuid: Unique identifiervcon: Version stringcreated_at: Creation timestamp (RFC3339)updated_at: Last modification timestampsubject: Conversation subjectparties: Array of partiesdialog: Array of dialogsattachments: Array of attachmentsanalysis: Array of analysis resultstags: Tag dictionaryextensions: Non-critical extensions arraycritical: Critical extensions arraygroup: Group referencesredacted: Redaction referenceamended: Amendment referencemeta: Additional metadata
Party
Class for representing parties in a vCon.
Properties
tel?: string: Telephone URL (TEL format)sip?: string: SIP addressmailto?: string: Email addressstir?: string: STIR PASSporTdid?: string: Decentralized Identifiername?: string: Display nameuuid?: string: Participant identifiervalidation?: string: Identity validation methodgmlpos?: string: GML positioncivicaddress?: CivicAddress: Civic addresstimezone?: string: Location timezonerole?: string: Role in conversationmeta?: Record<string, any>: Additional metadata
Methods
toDict(): Converts to plain objecthasIdentifier(): Checks if party has any identifiergetPrimaryIdentifier(): Gets the primary identifiervalidate(): Validates against vcon-core-01 recommendations
Dialog
Class for representing dialogs in a vCon.
Static Constants
Dialog.DIALOG_TYPES // ['recording', 'text', 'transfer', 'incomplete']
Dialog.DISPOSITIONS // ['no-answer', 'congestion', 'failed', 'busy', 'hung-up', 'voicemail-no-message']
Dialog.VALID_ENCODINGS // ['base64url', 'json', 'none']Properties
type: string: Dialog type (recording,text,transfer,incomplete)start: Date | string: Start time (RFC3339)parties?: number | number[]: Party indicesoriginator?: number: Originator party indexmediatype?: string: MIME typefilename?: string: Original filenamebody?: string: Inline contentencoding?: string: Content encoding (base64url,json,none)url?: string: External URL referencecontent_hash?: string: Content hash for external filesduration?: number: Duration in secondsdisposition?: string: Disposition for incomplete dialogssession_id?: SessionId: Session identifierparty_history?: PartyHistory[]: Party event historyapplication?: string: Application that created the dialog (e.g., 'zoom', 'teams')
Methods
toDict(): Converts to plain objectaddExternalData(url, mediatype, options?): Adds external data referenceaddInlineData(body, mediatype, options?): Adds inline dataisExternalData(): Checks if has external dataisInlineData(): Checks if has inline dataisText(): Checks if text typeisRecording(): Checks if recording typeisTransfer(): Checks if transfer typeisIncomplete(): Checks if incomplete typeisAudio(): Checks if audio contentisVideo(): Checks if video contentisEmail(): Checks if email contentvalidate(): Validates against vcon-core-01
Attachment
Class for representing attachments in a vCon.
Static Constants
Attachment.VALID_ENCODINGS // ['base64url', 'json', 'none']Properties
type?: string: Attachment type (MIME type)purpose?: string: Purpose/categorystart?: Date | string: Reference timeparty?: number: Related party indexdialog?: number | number[]: Related dialog indicesmediatype?: string: Media typefilename?: string: Original filenamebody?: any: Inline contentencoding?: string: Content encodingurl?: string: External URLcontent_hash?: string: Content hash
Methods
toDict(): Converts to plain objectaddExternalData(url, mediatype, options?): Adds external referenceaddInlineData(body, mediatype, options?): Adds inline contentisExternalData(): Checks if has external dataisInlineData(): Checks if has inline datavalidate(): Validates against vcon-core-01
PartyHistory
Class for tracking party events within a dialog.
Constructor
new PartyHistory(party: number, event: string, time: Date | string)Properties
party: number: Party indexevent: string: Event type (e.g., 'joined', 'left', 'hold', 'resume', 'mute', 'unmute')time: Date | string: Event timestamp
Methods
toDict(): Converts to plain object with ISO timestampstatic fromDict(data): Creates PartyHistory from plain object
Constants
import { VCON_VERSION } from 'vcon-js';
console.log(VCON_VERSION); // '0.0.1'Tutorial Examples
The examples/ directory contains three comprehensive tutorials demonstrating real-world usage:
Example 1: Text Chat Conversation
File: examples/01-text-chat.ts
Run: npm run example:chat
A customer support chat conversation demonstrating:
- Creating parties with different identifiers (tel, mailto)
- Building a multi-turn text conversation
- Setting conversation subject and tags
- Serializing and deserializing vCons
// Quick start - text chat
const vcon = Vcon.buildNew();
vcon.addParty(new Party({ tel: '+1-555-123-4567', name: 'Customer', role: 'customer' }));
vcon.addParty(new Party({ mailto: '[email protected]', name: 'Agent', role: 'agent' }));
vcon.addDialog(new Dialog({
type: 'text',
start: new Date().toISOString(),
parties: [0, 1],
originator: 0,
body: 'Hi, I need help with my account.',
mediatype: 'text/plain'
}));Example 2: Phone Call Recording with Analysis
File: examples/02-call-recording.ts
Run: npm run example:call
An insurance claim phone call demonstrating:
- Recording type dialogs with duration
- External media references with content_hash
- Party validation (STIR/SHAKEN)
- Multiple analysis types (transcription, sentiment, topic classification)
- Contact center extensions
- Party history tracking (join, hold, resume, leave events)
// Quick start - call recording
const vcon = Vcon.buildNew();
vcon.addExtension('contact_center');
const recordingDialog = new Dialog({
type: 'recording',
start: new Date().toISOString(),
parties: [0, 1],
duration: 847,
campaign: 'claims-inbound'
});
recordingDialog.addExternalData(
'https://storage.example.com/call.wav',
'audio/wav',
{ content_hash: 'sha512-abc123...' }
);
vcon.addAnalysis({
type: 'transcription',
dialog: 0,
vendor: 'whisper',
product: 'large-v3',
body: { segments: [...] }
});Example 3: Video Conference with Attachments
File: examples/03-video-conference.ts
Run: npm run example:conference
A multi-party product roadmap meeting demonstrating:
- 5+ party conferences
- Video recording dialogs
- Incomplete dialogs (failed join attempts)
- Multiple attachments (presentations, notes, chat transcripts)
- Inline and external content storage
- Group references for meeting series
- Meeting-specific analysis (action items, summaries)
- Validation of dialog objects
// Quick start - video conference
const vcon = Vcon.buildNew();
vcon.subject = 'Q1 Product Roadmap Review';
vcon.addExtension('meeting');
// Add multiple participants
['Host', 'Engineer', 'Designer', 'QA', 'Marketing'].forEach((role, i) => {
vcon.addParty(new Party({ mailto: `${role.toLowerCase()}@company.com`, role: i === 0 ? 'host' : 'participant' }));
});
// Add recording with party history
const videoDialog = new Dialog({
type: 'recording',
start: new Date().toISOString(),
parties: [0, 1, 2, 3, 4],
duration: 3720,
application: 'zoom'
});
// Add attachments
vcon.addAttachment({
purpose: 'presentation',
filename: 'roadmap.pptx',
body: '...',
encoding: 'base64url'
});
// Link to meeting series
vcon.addGroup({ uuid: 'roadmap-series-2025', type: 'meeting-series' });Running All Examples
# Run individual examples
npm run example:chat # Text chat conversation
npm run example:call # Phone call with analysis
npm run example:conference # Video conference
# Run all examples
npm run examplesvcon-core-01 Compliance
This library implements the IETF draft-ietf-vcon-vcon-core-01 specification, including:
- Dialog Types:
recording,text,transfer,incomplete - Dispositions:
no-answer,congestion,failed,busy,hung-up,voicemail-no-message - Encodings:
base64url,json,none - Content Hash: SHA-512 hash format for external references
- Extensions: Support for
extensionsandcriticalarrays - Party Identifiers: tel, sip, mailto, stir, did, uuid
- Date Format: RFC3339 timestamps
Development
npm install # Install dependencies
npm run build # Compile TypeScript
npm test # Run tests (61 tests)
npm run lint # Run ESLint
npm run format # Format with PrettierLicense
MIT
