Below is my code of room token generation.
Can you please check and let me know about any issues?
const enableRecording = async ({ roomName, testInfo }) => {
try {
const { testId, inviteId, email, companyId, candidateName } =
testInfo ?? {};
// 1. Prepare your recording output exactly like before
// NOTE: Depending on your SDK version, "value" requires a "new S3Upload()" wrapper
const output = new EncodedFileOutput({
fileType: EncodedFileType.MP3, // good source format for speech
filepath: `interview-recordings/${roomName}.mp3`,
output: {
case: "s3",
value: new S3Upload({
accessKey: INTERVIEW_BUCKET_AWS_ACCESS_KEY_ID,
secret: INTERVIEW_BUCKET_AWS_SECRET_ACCESS_KEY,
bucket: "taptalent-interview-recordings",
region: region,
forcePathStyle: true,
}),
},
});
// 2. Create Room and Auto-Attach Egress in ONE atomic call
await roomService
.createRoom({
name: roomName,
maxParticipants: 3,
departureTimeout: 60,
// 👇 LiveKit Auto-Egress: Automatically records when the room is active
egress: {
room: {
roomName: roomName,
audioOnly: true,
audioMixing: AudioMixing.DUAL_CHANNEL_AGENT, // agent-left, others-right
fileOutputs:[output], // Attach your S3 Output here
},
},
})
.catch(async (err) => {
console.log(
`LIVEKIT ROOM CREATION/EGRESS ERROR testToken ${inviteId} \n Error : ${JSON.stringify(err)}`
);
const msg = `testId : ${testId} \n testToken : ${inviteId} \n email : ${email} \n companyId : ${companyId} \n candidateName : ${candidateName} \n error : ${JSON.stringify(err)}`;
await slackSendMessage({
message: msg,
notificationHeading: `*LIVEKIT ROOM CREATION ERROR [${environment}]*`,
type: "ERROR",
webhookUrl: slackAIInterviewProviderError,
});
throw err;
});
console.log(`Created Room ${roomName} successfully with Auto-Recording attached.`);
} catch (error) {
console.log(`Failed to enable recording for room ${roomName}: ${error}`);
throw error;
}
};
const handleCreateRoom = async ({
initialPrompt,
uniqueRoomId,
testInfo,
voiceId,
voiceProvider,
language,
duration,
}) => {
const roomName = uniqueRoomId ?? taptalent-${Date.now()};
const agentName = “interview-agent”;
const participantName = testInfo?.candidateName ?? “”;
await enableRecording({ roomName, testInfo });
// 3. EXPLICITLY Dispatch the Agent
// Since the room is already created by the recorder, we must manually summon the agent.
console.log(“Dispatching AI Agent…”);
try {
const agentDispatchClient = new AgentDispatchClient(
LIVEKIT_URL,
LIVEKIT_API_KEY,
LIVEKIT_API_SECRET,
);
await agentDispatchClient.createDispatch(roomName, agentName, {
metadata,
});
} catch (error) {
console.error(“Failed to dispatch agent:”, error);
throw error;
}
const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, {
identity: participantName,
name: participantName,
ttl: duration * 60 + 300, // duration in minutes for token validity + 5 minutes buffer
});
at.addGrant({ roomJoin: true, room: roomName });
const token = await at.toJwt();
return { liveKitUrl: LIVEKIT_URL, token, roomName, agentName };
};
Does this give the answer of these question
- Double check the token you are sending to the client, is it valid? Does it contain the roomJoin permission?
- Is the room specified int he egress creation call the same as the room contained within the token