we have only 1 shared outbound trunk on live kit and:
[dotenv@17.2.3] injecting env (0) from secrets.env -- tip: ⚙️ override existing env vars with { override: true }
🔥 Prewarming LinkAI agent...
✅ VAD model loaded
🚀 Starting LinkAI Voice Agent (LiveKit Cloud)...
👤 Participant joined: sip_+17873825481 (964ms)
🔍 Telephony room detected by name pattern: tel-_+17873825481_5VgqKyotAQ2q
📞 Telephony call detected (empty metadata fallback), fetching config for phone:
+17875925211
🔍 Room name: tel-_+17873825481_5VgqKyotAQ2q
🔍 Participant identity: sip_+17873825481
🔍 SIP participant attributes: {
'sip.callID': 'SCL_uB72RN5RnmHm',
'sip.callTag': '03923036_c3356d0b_69591eb7-5579-4fc5-ae51-e99a1db78860',
'sip.twilio.accountSid': 'AC63991c2db51a41638adaa40c2fa6f205',
'sip.hostname': '
sip.twilio.com',
'sip.trunkPhoneNumber': '
+17875925211',
'sip.ruleID': 'SDR_qz8dwh6mbjgZ',
'sip.callStatus': 'ringing',
'sip.trunkID': 'ST_iq7LukpU7dAf',
'sip.callIDFull': 'ec557d78463c9e0750e0177a6f248a41@0.0.0.0',
'sip.phoneNumber': '
+17873825481',
'sip.twilio.callSid': 'CA5ea054a4da894c7e3f90ad68c2196093'
}
🔍 Fetching agent config for phone number:
+17875925211
✅ Found chatbot: PR Convention for phone:
+17875925211
📚 Loading knowledge profiles for 1 sources...
✅ Loaded 1 knowledge profiles
📞 Loaded call forwarding configuration: { forwardingLinesCount: 1 }
✅ Agent config built successfully for telephony
🔧 Enabled tools: {
webSearch: true,
calendar: false,
email: true,
forms: true,
callForwarding: true,
hasKnowledge: true
}
✅ Agent config loaded for phone: PR Convention
🤖 Chatbot ID: cme7jn41o0001d4q5q4gt76dn
🎵 Voice: Charon
⏱️ Config loaded in 4467ms
✅ Agent configuration loaded: PR Convention
🌍 Language Configuration:
Primary: en
Secondary: es
Bilingual Mode: ENABLED
✅ Voice pipeline configured with bilingual support:
🎤 STT: Deepgram nova-3 (multi (Nova-3 native codeswitching: en + es))
🎵 TTS: ElevenLabs eleven_multilingual_v2 (automatic accent detection)
🔄 Turn Detection: MultilingualModel - Context-aware endpointing
⏱️ STT Endpointing: 100ms (optimized for code-switching per Deepgram docs)
⏱️ Turn Detection Delays: min=200ms, max=600ms (optimized for speed)
🌍 Bilingual Mode: ENABLED
🔍 Tool detection: {
hasKnowledge: true,
hasWebSearch: true,
hasForms: true,
hasEmail: true,
hasTools: true,
webSearchEnabled: true,
unrestrictedWebSearchEnabled: false
}
🔧 Pre-loading unified tools for VoiceAgent...
📡 Fetching unified tools metadata... {
url: '
https://linkai-chat-runtime.fly.dev/tools/voice-format',
source: '
Fly.io'
}
❌ Failed to load unified tools (513ms): Error: Failed to fetch tools: 500 Internal Server Error. {"error":"Failed to format tools for voice","details":"Failed to import tool modules: ToolRegistry not found"}
at loadUnifiedToolsForVoice (file
///Users/kevindiaz/LinkAI/voice server/agent.js179:13)
at process.processTicksAndRejections (node
internal/process/task queues95:5)
at async createAssistant (file
///Users/kevindiaz/LinkAI/voice server/agent.js447:22)
at async Promise.all (index 0)
at async entry (file
///Users/kevindiaz/LinkAI/voice server/agent.js635:38)
at async file:///Users/kevindiaz/LinkAI/voice-server/node_modules/@livekit/agents/dist/telemetry/traces.js
7316
at async DynamicTracer.startActiveSpan (file:///Users/kevindiaz/LinkAI/voice-server/node_modules/@livekit/agents/dist/telemetry/traces.js
7112)
at async file:///Users/kevindiaz/LinkAI/voice-server/node_modules/@livekit/agents/dist/ipc/job_proc_lazy_main.js
725
Voice agent will continue with only native tools
⚠️ No tools loaded, falling back to base agent
🔧 Using base Agent without tools
⏱️ Assistant created in 516ms
✅ Background Voice Cancellation (BVC) enabled (agent-side)
[20
3429.748] INFO (72240): Configuring session recording
enableRecording: false
not authenticated
⏱️ Session started in 18ms
📞 Initializing call termination handlers...
⏰ Setting call timeout: 480 seconds (warning at 470s)
👤 Enabling user presence check: 9 seconds delay
🔇 Enabling silence timeout: 22 seconds
🎧 Setting up unified conversation item listener...
🔍 Session object type: object
🔍 Session has on method: function
🔍 Session methods: [ '_onUserInputTranscribed' ]
🔍 [DEBUG] Available session events: {
hasOn: true,
sessionType: 'object',
sessionKeys: [
'_events',
'_eventsCount',
'_maxListeners',
'vad',
'stt',
'llm',
'tts',
'turnDetection',
'options',
'agent'
]
}
👋 Saying greeting: Hi! Thank you for calling the Puerto Rico Convention Center. How can I help?
🎉 LinkAI Voice Agent operational! (Total startup: 5973ms)
[20
3429.756] INFO (72240): participantValue.trackPublications
participantValue: "sip_+17873825481"
trackPublications: [
{
"info": {
"sid": "TR_AMMtj8An6FXfir",
"name": "sip_+17873825481",
"kind": "KIND_AUDIO",
"source": "SOURCE_MICROPHONE",
"simulcasted": false,
"width": 0,
"height": 0,
"mimeType": "audio/opus",
"muted": false,
speech_id: "speech_cd18716c-c8b"
[20
3437.638] INFO (72240): EOU metrics
endOfUtteranceDelayMs: 574
transcriptionDelayMs: 572
onUserTurnCompletedDelayMs: 0
💾 Saved user message to database: Can you transfer me to the main office?...
[20
3438.263] INFO (72240): LLM metrics
ttftMs: 608
inputTokens: 514
promptCachedTokens: 0
outputTokens: 18
tokensPerSecond: 28.8
✅ Found outbound trunk for user: {
userId: 'cmbcjvgsq0000j5kjb37t1bss',
email: '
hello@getlinkai.com',
trunkId: 'ST_pjA2faq7vmfn'
}
📞 ===== COLD TRANSFER INITIATED =====
Forwarding to: Main Office
Phone:
+7876417722
Extension: None
Trunk ID: ST_pjA2faq7vmfn
⚠️ Phone number appears to be missing country code, adding +1:
+7876417722 ->
+17876417722
Formatted target number:
+17876417722
Found caller participant: { identity: 'sip_+17873825481', name: 'Phone
+17873825481' }
ℹ️ Trunk information:
Inbound trunk (call received through): ST_iq7LukpU7dAf
Outbound trunk (transfer will use): ST_pjA2faq7vmfn
Using outbound trunk for REFER transfer
Sending SIP REFER to transfer call...
Transfer details: {
roomName: 'tel-_+17873825481_5VgqKyotAQ2q',
participantIdentity: 'sip_+17873825481',
targetNumber: '
+17876417722',
outboundTrunkId: 'ST_pjA2faq7vmfn',
inboundTrunkId: 'ST_iq7LukpU7dAf',
trunkToUse: 'ST_pjA2faq7vmfn',
note: 'Transfers require outbound trunk with termination credentials'
}
📤 Final transfer request: {
roomName: 'tel-_+17873825481_5VgqKyotAQ2q',
participantIdentity: 'sip_+17873825481',
transferTo: 'tel:+17876417722',
options: { playDialtone: false }
}
❌ Cold transfer failed: Forbidden: twirp error unknown: sip status: 403: Forbidden
at TwirpRpc.request (file
///Users/kevindiaz/LinkAI/voice server/node modules/livekit server sdk/dist/TwirpRPC.js61:13)
at process.processTicksAndRejections (node
internal/process/task queues95:5)
at async SipClient.transferSipParticipant (file
///Users/kevindiaz/LinkAI/voice server/node modules/livekit server sdk/dist/SipClient.js519:5)
at async initiateColdTransfer (file
///Users/kevindiaz/LinkAI/voice server/src/transfer/coldTransfer.js171:5) {
status: 403,
code: 'permission_denied',
metadata: {
error_details: 'CAcSGnNpcCBzdGF0dXM6IDQwMzogRm9yYmlkZGVuGjcKJXR5cGUuZ29vZ2xlYXBpcy5jb20vbGl2ZWtpdC5TSVBTdGF0dXMSDgiTAxIJRm9yYmlkZGVu',
sip_status: 'Forbidden',
sip_status_code: '403'
}
}
Error details: {
message: 'twirp error unknown: sip status: 403: Forbidden',
sipErrorCode: '403',
sipErrorMessage: 'Forbidden',
stack: 'Forbidden: twirp error unknown: sip status: 403: Forbidden\n' +
' at TwirpRpc.request (file
///Users/kevindiaz/LinkAI/voice server/node modules/livekit server sdk/dist/TwirpRPC.js61:13)\n' +
' at process.processTicksAndRejections (node
internal/process/task queues95:5)\n' +
' at async SipClient.transferSipParticipant (file
///Users/kevindiaz/LinkAI/voice server/node modules/livekit server sdk/dist/SipClient.js519:5)\n' +
' at async initiateColdTransfer (file
///Users/kevindiaz/LinkAI/voice server/src/transfer/coldTransfer.js171:5)',
forwardingLine: {
id: 'line-1767132856231',
name: 'Main Office',
extension: '',
phoneNumber: '
+7876417722',
forwardPrompt: 'When user asks to speak with a human.'
},
trunkId: 'ST_pjA2faq7vmfn'
}
❌ Cold transfer failed: Forbidden: twirp error unknown: sip status: 403: Forbidden
at TwirpRpc.request (file
///Users/kevindiaz/LinkAI/voice server/node modules/livekit server sdk/dist/TwirpRPC.js61:13)
at process.processTicksAndRejections (node
internal/process/task queues95:5)
at async SipClient.transferSipParticipant (file
///Users/kevindiaz/LinkAI/voice server/node modules/livekit server sdk/dist/SipClient.js519:5)
at async initiateColdTransfer (file
///Users/kevindiaz/LinkAI/voice server/src/transfer/coldTransfer.js171:5) {
status: 403,
code: 'permission_denied',
metadata: {
error_details: 'CAcSGnNpcCBzdGF0dXM6IDQwMzogRm9yYmlkZGVuGjcKJXR5cGUuZ29vZ2xlYXBpcy5jb20vbGl2ZWtpdC5TSVBTdGF0dXMSDgiTAxIJRm9yYmlkZGVu',
sip_status: 'Forbidden',
sip_status_code: '403'
}
}
🎙️ Agent speaking - State: CONVERSING
🤖 Agent activity detected - resetting timers
[20
3439.902] INFO (72240): TTS metrics
ttfbMs: 889
audioDurationMs: 4000
🔇 Silence detected - starting timers
⏰ Starting presence timer
⏰ Starting presence timer: 9 seconds (waiting for user response)
⏰ Starting silence timer
⏰ Starting silence timer: 22 seconds (absolute user silence)
🎙️ Agent speaking - State: CONVERSING
🤖 Agent activity detected - resetting timers
🎙️ Agent speaking - State: CONVERSING
🤖 Agent activity detected - resetting timers
🎙️ Agent speaking - State: CONVERSING
🤖 Agent activity detected - resetting timers
🎯 CONVERSATION ITEM EVENT FIRED! {
role: 'assistant',
textContent: 'Sure, I can transfer you to the main office. Pleas',
fullLength: 77,
hasText: true,
timestamp: 1767141283135