@livekit/agents-plugin-protoface 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +15 -0
- package/dist/api.cjs +153 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.d.cts +42 -0
- package/dist/api.d.ts +42 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +133 -0
- package/dist/api.js.map +1 -0
- package/dist/api.test.cjs +55 -0
- package/dist/api.test.cjs.map +1 -0
- package/dist/api.test.d.cts +2 -0
- package/dist/api.test.d.ts +2 -0
- package/dist/api.test.d.ts.map +1 -0
- package/dist/api.test.js +54 -0
- package/dist/api.test.js.map +1 -0
- package/dist/avatar.cjs +160 -0
- package/dist/avatar.cjs.map +1 -0
- package/dist/avatar.d.cts +54 -0
- package/dist/avatar.d.ts +54 -0
- package/dist/avatar.d.ts.map +1 -0
- package/dist/avatar.js +139 -0
- package/dist/avatar.js.map +1 -0
- package/dist/avatar.test.cjs +107 -0
- package/dist/avatar.test.cjs.map +1 -0
- package/dist/avatar.test.d.cts +2 -0
- package/dist/avatar.test.d.ts +2 -0
- package/dist/avatar.test.d.ts.map +1 -0
- package/dist/avatar.test.js +106 -0
- package/dist/avatar.test.js.map +1 -0
- package/dist/index.cjs +36 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/log.cjs +30 -0
- package/dist/log.cjs.map +1 -0
- package/dist/log.d.cts +3 -0
- package/dist/log.d.ts +3 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +6 -0
- package/dist/log.js.map +1 -0
- package/package.json +51 -0
- package/src/api.test.ts +65 -0
- package/src/api.ts +182 -0
- package/src/avatar.test.ts +132 -0
- package/src/avatar.ts +204 -0
- package/src/index.ts +19 -0
- package/src/log.ts +7 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_agents = require("@livekit/agents");
|
|
3
|
+
var import_rtc_node = require("@livekit/rtc-node");
|
|
4
|
+
var import_node_events = require("node:events");
|
|
5
|
+
var import_vitest = require("vitest");
|
|
6
|
+
var import_api = require("./api.cjs");
|
|
7
|
+
var import_avatar = require("./avatar.cjs");
|
|
8
|
+
function fakeRoom() {
|
|
9
|
+
const emitter = new import_node_events.EventEmitter();
|
|
10
|
+
const remoteParticipant = {
|
|
11
|
+
identity: "protoface-avatar-agent",
|
|
12
|
+
trackPublications: /* @__PURE__ */ new Map([["video", { kind: import_rtc_node.TrackKind.KIND_VIDEO }]])
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
name: "test-room",
|
|
16
|
+
isConnected: true,
|
|
17
|
+
localParticipant: {
|
|
18
|
+
identity: "local-agent",
|
|
19
|
+
registerRpcMethod: import_vitest.vi.fn()
|
|
20
|
+
},
|
|
21
|
+
remoteParticipants: /* @__PURE__ */ new Map([[remoteParticipant.identity, remoteParticipant]]),
|
|
22
|
+
on: import_vitest.vi.fn((event, listener) => {
|
|
23
|
+
emitter.on(event, listener);
|
|
24
|
+
return emitter;
|
|
25
|
+
}),
|
|
26
|
+
off: import_vitest.vi.fn((event, listener) => {
|
|
27
|
+
emitter.off(event, listener);
|
|
28
|
+
return emitter;
|
|
29
|
+
})
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function fakeAgentSession() {
|
|
33
|
+
const emitter = new import_node_events.EventEmitter();
|
|
34
|
+
return {
|
|
35
|
+
_started: true,
|
|
36
|
+
output: { audio: null },
|
|
37
|
+
on: import_vitest.vi.fn((event, listener) => {
|
|
38
|
+
emitter.on(event, listener);
|
|
39
|
+
return emitter;
|
|
40
|
+
}),
|
|
41
|
+
off: import_vitest.vi.fn((event, listener) => {
|
|
42
|
+
emitter.off(event, listener);
|
|
43
|
+
return emitter;
|
|
44
|
+
})
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
(0, import_vitest.describe)("Protoface AvatarSession", () => {
|
|
48
|
+
(0, import_vitest.afterEach)(() => {
|
|
49
|
+
import_vitest.vi.restoreAllMocks();
|
|
50
|
+
import_agents.voice.DataStreamAudioOutput._playbackFinishedRpcRegistered = false;
|
|
51
|
+
import_agents.voice.DataStreamAudioOutput._playbackFinishedHandlers = {};
|
|
52
|
+
import_agents.voice.DataStreamAudioOutput._playbackStartedRpcRegistered = false;
|
|
53
|
+
import_agents.voice.DataStreamAudioOutput._playbackStartedHandlers = {};
|
|
54
|
+
});
|
|
55
|
+
(0, import_vitest.it)("calls base AvatarSession.start first", async () => {
|
|
56
|
+
const sentinel = new Error("super-start-called");
|
|
57
|
+
const superStartSpy = import_vitest.vi.spyOn(import_agents.voice.AvatarSession.prototype, "start").mockRejectedValue(sentinel);
|
|
58
|
+
const avatar = new import_avatar.AvatarSession({ apiKey: "protoface-key" });
|
|
59
|
+
await (0, import_vitest.expect)(avatar.start(fakeAgentSession(), fakeRoom())).rejects.toThrow(
|
|
60
|
+
"super-start-called"
|
|
61
|
+
);
|
|
62
|
+
(0, import_vitest.expect)(superStartSpy).toHaveBeenCalledTimes(1);
|
|
63
|
+
});
|
|
64
|
+
(0, import_vitest.it)("starts a Protoface session and routes agent audio to the avatar", async () => {
|
|
65
|
+
import_vitest.vi.spyOn(import_agents.voice.AvatarSession.prototype, "start").mockResolvedValue(void 0);
|
|
66
|
+
import_vitest.vi.spyOn(import_agents.voice.AvatarSession.prototype, "aclose").mockResolvedValue(void 0);
|
|
67
|
+
const startSessionSpy = import_vitest.vi.spyOn(import_api.ProtofaceAPI.prototype, "startSession").mockResolvedValue({ id: "protoface-session-1" });
|
|
68
|
+
const endSessionSpy = import_vitest.vi.spyOn(import_api.ProtofaceAPI.prototype, "endSession").mockResolvedValue({});
|
|
69
|
+
const avatar = new import_avatar.AvatarSession({
|
|
70
|
+
apiKey: "protoface-key",
|
|
71
|
+
avatarId: "avatar-1",
|
|
72
|
+
maxDurationMs: 12e4
|
|
73
|
+
});
|
|
74
|
+
const agentSession = fakeAgentSession();
|
|
75
|
+
await avatar.start(agentSession, fakeRoom(), {
|
|
76
|
+
livekitUrl: "wss://livekit.example.com",
|
|
77
|
+
livekitApiKey: "livekit-api-key",
|
|
78
|
+
livekitApiSecret: "livekit-api-secret"
|
|
79
|
+
});
|
|
80
|
+
(0, import_vitest.expect)(avatar.sessionId).toBe("protoface-session-1");
|
|
81
|
+
(0, import_vitest.expect)(startSessionSpy).toHaveBeenCalledWith({
|
|
82
|
+
avatarId: "avatar-1",
|
|
83
|
+
transport: import_vitest.expect.objectContaining({
|
|
84
|
+
type: "livekit",
|
|
85
|
+
url: "wss://livekit.example.com",
|
|
86
|
+
room_name: "test-room",
|
|
87
|
+
worker_identity: "protoface-avatar-agent",
|
|
88
|
+
audio_source: "data_stream",
|
|
89
|
+
worker_token: import_vitest.expect.any(String)
|
|
90
|
+
}),
|
|
91
|
+
maxDurationMs: 12e4
|
|
92
|
+
});
|
|
93
|
+
const audioOutput = agentSession.output.audio;
|
|
94
|
+
(0, import_vitest.expect)(audioOutput).toBeInstanceOf(import_agents.voice.DataStreamAudioOutput);
|
|
95
|
+
(0, import_vitest.expect)(audioOutput.destinationIdentity).toBe(
|
|
96
|
+
"protoface-avatar-agent"
|
|
97
|
+
);
|
|
98
|
+
(0, import_vitest.expect)(audioOutput.sampleRate).toBe(16e3);
|
|
99
|
+
(0, import_vitest.expect)(audioOutput.waitRemoteTrack).toBe(
|
|
100
|
+
import_rtc_node.TrackKind.KIND_VIDEO
|
|
101
|
+
);
|
|
102
|
+
await avatar.aclose();
|
|
103
|
+
(0, import_vitest.expect)(endSessionSpy).toHaveBeenCalledWith("protoface-session-1");
|
|
104
|
+
(0, import_vitest.expect)(avatar.sessionId).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=avatar.test.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/avatar.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { voice } from '@livekit/agents';\nimport type { Room } from '@livekit/rtc-node';\nimport { TrackKind } from '@livekit/rtc-node';\nimport { EventEmitter } from 'node:events';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { ProtofaceAPI } from './api.js';\nimport { AvatarSession } from './avatar.js';\n\ntype DataStreamAudioOutputInternals = {\n destinationIdentity: string;\n sampleRate: number;\n waitRemoteTrack?: TrackKind;\n};\n\nfunction fakeRoom(): Room {\n const emitter = new EventEmitter();\n const remoteParticipant = {\n identity: 'protoface-avatar-agent',\n trackPublications: new Map([['video', { kind: TrackKind.KIND_VIDEO }]]),\n };\n\n return {\n name: 'test-room',\n isConnected: true,\n localParticipant: {\n identity: 'local-agent',\n registerRpcMethod: vi.fn(),\n },\n remoteParticipants: new Map([[remoteParticipant.identity, remoteParticipant]]),\n on: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.on(event, listener);\n return emitter;\n }),\n off: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.off(event, listener);\n return emitter;\n }),\n } as unknown as Room;\n}\n\nfunction fakeAgentSession(): voice.AgentSession {\n const emitter = new EventEmitter();\n return {\n _started: true,\n output: { audio: null as voice.AudioOutput | null },\n on: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.on(event, listener);\n return emitter;\n }),\n off: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.off(event, listener);\n return emitter;\n }),\n } as unknown as voice.AgentSession;\n}\n\ndescribe('Protoface AvatarSession', () => {\n afterEach(() => {\n vi.restoreAllMocks();\n voice.DataStreamAudioOutput._playbackFinishedRpcRegistered = false;\n voice.DataStreamAudioOutput._playbackFinishedHandlers = {};\n voice.DataStreamAudioOutput._playbackStartedRpcRegistered = false;\n voice.DataStreamAudioOutput._playbackStartedHandlers = {};\n });\n\n it('calls base AvatarSession.start first', async () => {\n const sentinel = new Error('super-start-called');\n const superStartSpy = vi\n .spyOn(voice.AvatarSession.prototype, 'start')\n .mockRejectedValue(sentinel);\n\n const avatar = new AvatarSession({ apiKey: 'protoface-key' });\n\n await expect(avatar.start(fakeAgentSession(), fakeRoom())).rejects.toThrow(\n 'super-start-called',\n );\n expect(superStartSpy).toHaveBeenCalledTimes(1);\n });\n\n it('starts a Protoface session and routes agent audio to the avatar', async () => {\n vi.spyOn(voice.AvatarSession.prototype, 'start').mockResolvedValue(undefined);\n vi.spyOn(voice.AvatarSession.prototype, 'aclose').mockResolvedValue(undefined);\n const startSessionSpy = vi\n .spyOn(ProtofaceAPI.prototype, 'startSession')\n .mockResolvedValue({ id: 'protoface-session-1' });\n const endSessionSpy = vi.spyOn(ProtofaceAPI.prototype, 'endSession').mockResolvedValue({});\n\n const avatar = new AvatarSession({\n apiKey: 'protoface-key',\n avatarId: 'avatar-1',\n maxDurationMs: 120_000,\n });\n const agentSession = fakeAgentSession();\n\n await avatar.start(agentSession, fakeRoom(), {\n livekitUrl: 'wss://livekit.example.com',\n livekitApiKey: 'livekit-api-key',\n livekitApiSecret: 'livekit-api-secret',\n });\n\n expect(avatar.sessionId).toBe('protoface-session-1');\n expect(startSessionSpy).toHaveBeenCalledWith({\n avatarId: 'avatar-1',\n transport: expect.objectContaining({\n type: 'livekit',\n url: 'wss://livekit.example.com',\n room_name: 'test-room',\n worker_identity: 'protoface-avatar-agent',\n audio_source: 'data_stream',\n worker_token: expect.any(String),\n }),\n maxDurationMs: 120_000,\n });\n\n const audioOutput = agentSession.output.audio;\n expect(audioOutput).toBeInstanceOf(voice.DataStreamAudioOutput);\n expect((audioOutput as unknown as DataStreamAudioOutputInternals).destinationIdentity).toBe(\n 'protoface-avatar-agent',\n );\n expect((audioOutput as unknown as DataStreamAudioOutputInternals).sampleRate).toBe(16000);\n expect((audioOutput as unknown as DataStreamAudioOutputInternals).waitRemoteTrack).toBe(\n TrackKind.KIND_VIDEO,\n );\n\n await avatar.aclose();\n expect(endSessionSpy).toHaveBeenCalledWith('protoface-session-1');\n expect(avatar.sessionId).toBeNull();\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar.test.d.ts","sourceRoot":"","sources":["../src/avatar.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { voice } from "@livekit/agents";
|
|
2
|
+
import { TrackKind } from "@livekit/rtc-node";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { ProtofaceAPI } from "./api.js";
|
|
6
|
+
import { AvatarSession } from "./avatar.js";
|
|
7
|
+
function fakeRoom() {
|
|
8
|
+
const emitter = new EventEmitter();
|
|
9
|
+
const remoteParticipant = {
|
|
10
|
+
identity: "protoface-avatar-agent",
|
|
11
|
+
trackPublications: /* @__PURE__ */ new Map([["video", { kind: TrackKind.KIND_VIDEO }]])
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
name: "test-room",
|
|
15
|
+
isConnected: true,
|
|
16
|
+
localParticipant: {
|
|
17
|
+
identity: "local-agent",
|
|
18
|
+
registerRpcMethod: vi.fn()
|
|
19
|
+
},
|
|
20
|
+
remoteParticipants: /* @__PURE__ */ new Map([[remoteParticipant.identity, remoteParticipant]]),
|
|
21
|
+
on: vi.fn((event, listener) => {
|
|
22
|
+
emitter.on(event, listener);
|
|
23
|
+
return emitter;
|
|
24
|
+
}),
|
|
25
|
+
off: vi.fn((event, listener) => {
|
|
26
|
+
emitter.off(event, listener);
|
|
27
|
+
return emitter;
|
|
28
|
+
})
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function fakeAgentSession() {
|
|
32
|
+
const emitter = new EventEmitter();
|
|
33
|
+
return {
|
|
34
|
+
_started: true,
|
|
35
|
+
output: { audio: null },
|
|
36
|
+
on: vi.fn((event, listener) => {
|
|
37
|
+
emitter.on(event, listener);
|
|
38
|
+
return emitter;
|
|
39
|
+
}),
|
|
40
|
+
off: vi.fn((event, listener) => {
|
|
41
|
+
emitter.off(event, listener);
|
|
42
|
+
return emitter;
|
|
43
|
+
})
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
describe("Protoface AvatarSession", () => {
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
vi.restoreAllMocks();
|
|
49
|
+
voice.DataStreamAudioOutput._playbackFinishedRpcRegistered = false;
|
|
50
|
+
voice.DataStreamAudioOutput._playbackFinishedHandlers = {};
|
|
51
|
+
voice.DataStreamAudioOutput._playbackStartedRpcRegistered = false;
|
|
52
|
+
voice.DataStreamAudioOutput._playbackStartedHandlers = {};
|
|
53
|
+
});
|
|
54
|
+
it("calls base AvatarSession.start first", async () => {
|
|
55
|
+
const sentinel = new Error("super-start-called");
|
|
56
|
+
const superStartSpy = vi.spyOn(voice.AvatarSession.prototype, "start").mockRejectedValue(sentinel);
|
|
57
|
+
const avatar = new AvatarSession({ apiKey: "protoface-key" });
|
|
58
|
+
await expect(avatar.start(fakeAgentSession(), fakeRoom())).rejects.toThrow(
|
|
59
|
+
"super-start-called"
|
|
60
|
+
);
|
|
61
|
+
expect(superStartSpy).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
it("starts a Protoface session and routes agent audio to the avatar", async () => {
|
|
64
|
+
vi.spyOn(voice.AvatarSession.prototype, "start").mockResolvedValue(void 0);
|
|
65
|
+
vi.spyOn(voice.AvatarSession.prototype, "aclose").mockResolvedValue(void 0);
|
|
66
|
+
const startSessionSpy = vi.spyOn(ProtofaceAPI.prototype, "startSession").mockResolvedValue({ id: "protoface-session-1" });
|
|
67
|
+
const endSessionSpy = vi.spyOn(ProtofaceAPI.prototype, "endSession").mockResolvedValue({});
|
|
68
|
+
const avatar = new AvatarSession({
|
|
69
|
+
apiKey: "protoface-key",
|
|
70
|
+
avatarId: "avatar-1",
|
|
71
|
+
maxDurationMs: 12e4
|
|
72
|
+
});
|
|
73
|
+
const agentSession = fakeAgentSession();
|
|
74
|
+
await avatar.start(agentSession, fakeRoom(), {
|
|
75
|
+
livekitUrl: "wss://livekit.example.com",
|
|
76
|
+
livekitApiKey: "livekit-api-key",
|
|
77
|
+
livekitApiSecret: "livekit-api-secret"
|
|
78
|
+
});
|
|
79
|
+
expect(avatar.sessionId).toBe("protoface-session-1");
|
|
80
|
+
expect(startSessionSpy).toHaveBeenCalledWith({
|
|
81
|
+
avatarId: "avatar-1",
|
|
82
|
+
transport: expect.objectContaining({
|
|
83
|
+
type: "livekit",
|
|
84
|
+
url: "wss://livekit.example.com",
|
|
85
|
+
room_name: "test-room",
|
|
86
|
+
worker_identity: "protoface-avatar-agent",
|
|
87
|
+
audio_source: "data_stream",
|
|
88
|
+
worker_token: expect.any(String)
|
|
89
|
+
}),
|
|
90
|
+
maxDurationMs: 12e4
|
|
91
|
+
});
|
|
92
|
+
const audioOutput = agentSession.output.audio;
|
|
93
|
+
expect(audioOutput).toBeInstanceOf(voice.DataStreamAudioOutput);
|
|
94
|
+
expect(audioOutput.destinationIdentity).toBe(
|
|
95
|
+
"protoface-avatar-agent"
|
|
96
|
+
);
|
|
97
|
+
expect(audioOutput.sampleRate).toBe(16e3);
|
|
98
|
+
expect(audioOutput.waitRemoteTrack).toBe(
|
|
99
|
+
TrackKind.KIND_VIDEO
|
|
100
|
+
);
|
|
101
|
+
await avatar.aclose();
|
|
102
|
+
expect(endSessionSpy).toHaveBeenCalledWith("protoface-session-1");
|
|
103
|
+
expect(avatar.sessionId).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=avatar.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/avatar.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { voice } from '@livekit/agents';\nimport type { Room } from '@livekit/rtc-node';\nimport { TrackKind } from '@livekit/rtc-node';\nimport { EventEmitter } from 'node:events';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { ProtofaceAPI } from './api.js';\nimport { AvatarSession } from './avatar.js';\n\ntype DataStreamAudioOutputInternals = {\n destinationIdentity: string;\n sampleRate: number;\n waitRemoteTrack?: TrackKind;\n};\n\nfunction fakeRoom(): Room {\n const emitter = new EventEmitter();\n const remoteParticipant = {\n identity: 'protoface-avatar-agent',\n trackPublications: new Map([['video', { kind: TrackKind.KIND_VIDEO }]]),\n };\n\n return {\n name: 'test-room',\n isConnected: true,\n localParticipant: {\n identity: 'local-agent',\n registerRpcMethod: vi.fn(),\n },\n remoteParticipants: new Map([[remoteParticipant.identity, remoteParticipant]]),\n on: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.on(event, listener);\n return emitter;\n }),\n off: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.off(event, listener);\n return emitter;\n }),\n } as unknown as Room;\n}\n\nfunction fakeAgentSession(): voice.AgentSession {\n const emitter = new EventEmitter();\n return {\n _started: true,\n output: { audio: null as voice.AudioOutput | null },\n on: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.on(event, listener);\n return emitter;\n }),\n off: vi.fn((event: string | symbol, listener: (...args: unknown[]) => void) => {\n emitter.off(event, listener);\n return emitter;\n }),\n } as unknown as voice.AgentSession;\n}\n\ndescribe('Protoface AvatarSession', () => {\n afterEach(() => {\n vi.restoreAllMocks();\n voice.DataStreamAudioOutput._playbackFinishedRpcRegistered = false;\n voice.DataStreamAudioOutput._playbackFinishedHandlers = {};\n voice.DataStreamAudioOutput._playbackStartedRpcRegistered = false;\n voice.DataStreamAudioOutput._playbackStartedHandlers = {};\n });\n\n it('calls base AvatarSession.start first', async () => {\n const sentinel = new Error('super-start-called');\n const superStartSpy = vi\n .spyOn(voice.AvatarSession.prototype, 'start')\n .mockRejectedValue(sentinel);\n\n const avatar = new AvatarSession({ apiKey: 'protoface-key' });\n\n await expect(avatar.start(fakeAgentSession(), fakeRoom())).rejects.toThrow(\n 'super-start-called',\n );\n expect(superStartSpy).toHaveBeenCalledTimes(1);\n });\n\n it('starts a Protoface session and routes agent audio to the avatar', async () => {\n vi.spyOn(voice.AvatarSession.prototype, 'start').mockResolvedValue(undefined);\n vi.spyOn(voice.AvatarSession.prototype, 'aclose').mockResolvedValue(undefined);\n const startSessionSpy = vi\n .spyOn(ProtofaceAPI.prototype, 'startSession')\n .mockResolvedValue({ id: 'protoface-session-1' });\n const endSessionSpy = vi.spyOn(ProtofaceAPI.prototype, 'endSession').mockResolvedValue({});\n\n const avatar = new AvatarSession({\n apiKey: 'protoface-key',\n avatarId: 'avatar-1',\n maxDurationMs: 120_000,\n });\n const agentSession = fakeAgentSession();\n\n await avatar.start(agentSession, fakeRoom(), {\n livekitUrl: 'wss://livekit.example.com',\n livekitApiKey: 'livekit-api-key',\n livekitApiSecret: 'livekit-api-secret',\n });\n\n expect(avatar.sessionId).toBe('protoface-session-1');\n expect(startSessionSpy).toHaveBeenCalledWith({\n avatarId: 'avatar-1',\n transport: expect.objectContaining({\n type: 'livekit',\n url: 'wss://livekit.example.com',\n room_name: 'test-room',\n worker_identity: 'protoface-avatar-agent',\n audio_source: 'data_stream',\n worker_token: expect.any(String),\n }),\n maxDurationMs: 120_000,\n });\n\n const audioOutput = agentSession.output.audio;\n expect(audioOutput).toBeInstanceOf(voice.DataStreamAudioOutput);\n expect((audioOutput as unknown as DataStreamAudioOutputInternals).destinationIdentity).toBe(\n 'protoface-avatar-agent',\n );\n expect((audioOutput as unknown as DataStreamAudioOutputInternals).sampleRate).toBe(16000);\n expect((audioOutput as unknown as DataStreamAudioOutputInternals).waitRemoteTrack).toBe(\n TrackKind.KIND_VIDEO,\n );\n\n await avatar.aclose();\n expect(endSessionSpy).toHaveBeenCalledWith('protoface-session-1');\n expect(avatar.sessionId).toBeNull();\n });\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
15
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
16
|
+
var index_exports = {};
|
|
17
|
+
module.exports = __toCommonJS(index_exports);
|
|
18
|
+
var import_agents = require("@livekit/agents");
|
|
19
|
+
__reExport(index_exports, require("./api.cjs"), module.exports);
|
|
20
|
+
__reExport(index_exports, require("./avatar.cjs"), module.exports);
|
|
21
|
+
class ProtofacePlugin extends import_agents.Plugin {
|
|
22
|
+
constructor() {
|
|
23
|
+
super({
|
|
24
|
+
title: "protoface",
|
|
25
|
+
version: "1.5.3",
|
|
26
|
+
package: "@livekit/agents-plugin-protoface"
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
import_agents.Plugin.registerPlugin(new ProtofacePlugin());
|
|
31
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
32
|
+
0 && (module.exports = {
|
|
33
|
+
...require("./api.cjs"),
|
|
34
|
+
...require("./avatar.cjs")
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Plugin } from '@livekit/agents';\n\nexport * from './api.js';\nexport * from './avatar.js';\n\nclass ProtofacePlugin extends Plugin {\n constructor() {\n super({\n title: 'protoface',\n version: __PACKAGE_VERSION__,\n package: __PACKAGE_NAME__,\n });\n }\n}\n\nPlugin.registerPlugin(new ProtofacePlugin());\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAGA,oBAAuB;AAEvB,0BAAc,qBALd;AAMA,0BAAc,wBANd;AAQA,MAAM,wBAAwB,qBAAO;AAAA,EACnC,cAAc;AACZ,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,qBAAO,eAAe,IAAI,gBAAgB,CAAC;","names":[]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Plugin } from "@livekit/agents";
|
|
2
|
+
export * from "./api.js";
|
|
3
|
+
export * from "./avatar.js";
|
|
4
|
+
class ProtofacePlugin extends Plugin {
|
|
5
|
+
constructor() {
|
|
6
|
+
super({
|
|
7
|
+
title: "protoface",
|
|
8
|
+
version: "1.5.3",
|
|
9
|
+
package: "@livekit/agents-plugin-protoface"
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
Plugin.registerPlugin(new ProtofacePlugin());
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Plugin } from '@livekit/agents';\n\nexport * from './api.js';\nexport * from './avatar.js';\n\nclass ProtofacePlugin extends Plugin {\n constructor() {\n super({\n title: 'protoface',\n version: __PACKAGE_VERSION__,\n package: __PACKAGE_NAME__,\n });\n }\n}\n\nPlugin.registerPlugin(new ProtofacePlugin());\n"],"mappings":"AAGA,SAAS,cAAc;AAEvB,cAAc;AACd,cAAc;AAEd,MAAM,wBAAwB,OAAO;AAAA,EACnC,cAAc;AACZ,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,OAAO,eAAe,IAAI,gBAAgB,CAAC;","names":[]}
|
package/dist/log.cjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var log_exports = {};
|
|
20
|
+
__export(log_exports, {
|
|
21
|
+
log: () => log
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(log_exports);
|
|
24
|
+
var import_agents = require("@livekit/agents");
|
|
25
|
+
const log = () => (0, import_agents.log)().child({ plugin: "protoface" });
|
|
26
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
27
|
+
0 && (module.exports = {
|
|
28
|
+
log
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=log.cjs.map
|
package/dist/log.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/log.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log as agentsLog } from '@livekit/agents';\nimport type { Logger } from 'pino';\n\nexport const log = (): Logger => agentsLog().child({ plugin: 'protoface' });\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAiC;AAG1B,MAAM,MAAM,UAAc,cAAAA,KAAU,EAAE,MAAM,EAAE,QAAQ,YAAY,CAAC;","names":["agentsLog"]}
|
package/dist/log.d.cts
ADDED
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,eAAO,MAAM,GAAG,QAAO,MAAoD,CAAC"}
|
package/dist/log.js
ADDED
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/log.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log as agentsLog } from '@livekit/agents';\nimport type { Logger } from 'pino';\n\nexport const log = (): Logger => agentsLog().child({ plugin: 'protoface' });\n"],"mappings":"AAGA,SAAS,OAAO,iBAAiB;AAG1B,MAAM,MAAM,MAAc,UAAU,EAAE,MAAM,EAAE,QAAQ,YAAY,CAAC;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@livekit/agents-plugin-protoface",
|
|
3
|
+
"version": "1.5.3",
|
|
4
|
+
"description": "Protoface avatar plugin for LiveKit Node Agents",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"require": "dist/index.cjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"require": {
|
|
14
|
+
"types": "./dist/index.d.cts",
|
|
15
|
+
"default": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"author": "LiveKit",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"repository": "git@github.com:livekit/agents-js.git",
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@livekit/rtc-node": "^0.13.31",
|
|
29
|
+
"@microsoft/api-extractor": "^7.35.0",
|
|
30
|
+
"pino": "^8.19.0",
|
|
31
|
+
"tsup": "^8.3.5",
|
|
32
|
+
"typescript": "^5.0.0",
|
|
33
|
+
"@livekit/agents": "1.5.4"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"livekit-server-sdk": "^2.13.3"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@livekit/rtc-node": "^0.13.31",
|
|
40
|
+
"@livekit/agents": "1.5.4"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup --onSuccess \"pnpm build:types\"",
|
|
44
|
+
"build:types": "tsc --declaration --emitDeclarationOnly && node ../../scripts/copyDeclarationOutput.js",
|
|
45
|
+
"clean": "rm -rf dist",
|
|
46
|
+
"clean:build": "pnpm clean && pnpm build",
|
|
47
|
+
"lint": "eslint -f unix \"src/**/*.{ts,js}\"",
|
|
48
|
+
"api:check": "api-extractor run --typescript-compiler-folder ../../node_modules/typescript",
|
|
49
|
+
"api:update": "api-extractor run --local --typescript-compiler-folder ../../node_modules/typescript --verbose"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/api.test.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { ProtofaceAPI, ProtofaceException } from './api.js';
|
|
6
|
+
|
|
7
|
+
describe('ProtofaceAPI', () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.unstubAllEnvs();
|
|
10
|
+
vi.unstubAllGlobals();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('requires an API key', () => {
|
|
14
|
+
vi.stubEnv('PROTOFACE_API_KEY', '');
|
|
15
|
+
|
|
16
|
+
expect(() => new ProtofaceAPI()).toThrow(ProtofaceException);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('uses env configuration and sends the expected start session request', async () => {
|
|
20
|
+
vi.stubEnv('PROTOFACE_API_KEY', 'sk_test');
|
|
21
|
+
vi.stubEnv('PROTOFACE_API_URL', 'https://api.example.test/');
|
|
22
|
+
const fetchMock = vi.fn(async () => new Response('{"id":"sess_test"}', { status: 200 }));
|
|
23
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
24
|
+
|
|
25
|
+
const client = new ProtofaceAPI();
|
|
26
|
+
const session = await client.startSession({
|
|
27
|
+
avatarId: 'av_test',
|
|
28
|
+
transport: { type: 'livekit' },
|
|
29
|
+
maxDurationMs: 120_000,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(session).toEqual({ id: 'sess_test' });
|
|
33
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
35
|
+
'https://api.example.test/v1/sessions',
|
|
36
|
+
expect.objectContaining({
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: expect.objectContaining({
|
|
39
|
+
accept: 'application/json',
|
|
40
|
+
authorization: 'Bearer sk_test',
|
|
41
|
+
'content-type': 'application/json',
|
|
42
|
+
'user-agent': '@livekit/agents-plugin-protoface/0.0.0-test',
|
|
43
|
+
}),
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
avatar_id: 'av_test',
|
|
46
|
+
transport: { type: 'livekit' },
|
|
47
|
+
max_duration_seconds: 120,
|
|
48
|
+
}),
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('ends a session', async () => {
|
|
54
|
+
const fetchMock = vi.fn(async () => new Response('{}', { status: 200 }));
|
|
55
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
56
|
+
|
|
57
|
+
const client = new ProtofaceAPI({ apiKey: 'sk_test', apiUrl: 'https://api.example.test' });
|
|
58
|
+
await expect(client.endSession('sess_test')).resolves.toEqual({});
|
|
59
|
+
|
|
60
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
61
|
+
'https://api.example.test/v1/sessions/sess_test/end',
|
|
62
|
+
expect.objectContaining({ method: 'POST' }),
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|