@komaa/standin-sdk 0.1.0
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 +21 -0
- package/README.md +141 -0
- package/dist/ambient.d.ts +80 -0
- package/dist/ambient.d.ts.map +1 -0
- package/dist/ambient.js +281 -0
- package/dist/ambient.js.map +1 -0
- package/dist/attachments.d.ts +149 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +451 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audio.d.ts +80 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +170 -0
- package/dist/audio.js.map +1 -0
- package/dist/avatar.d.ts +111 -0
- package/dist/avatar.d.ts.map +1 -0
- package/dist/avatar.js +201 -0
- package/dist/avatar.js.map +1 -0
- package/dist/callServer.d.ts +151 -0
- package/dist/callServer.d.ts.map +1 -0
- package/dist/callServer.js +1097 -0
- package/dist/callServer.js.map +1 -0
- package/dist/callTools.d.ts +154 -0
- package/dist/callTools.d.ts.map +1 -0
- package/dist/callTools.js +255 -0
- package/dist/callTools.js.map +1 -0
- package/dist/chat.d.ts +226 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +574 -0
- package/dist/chat.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/consult.d.ts +147 -0
- package/dist/consult.d.ts.map +1 -0
- package/dist/consult.js +388 -0
- package/dist/consult.js.map +1 -0
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +187 -0
- package/dist/delivery.js.map +1 -0
- package/dist/echoGuard.d.ts +56 -0
- package/dist/echoGuard.d.ts.map +1 -0
- package/dist/echoGuard.js +57 -0
- package/dist/echoGuard.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -0
- package/dist/fetch.d.ts +40 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +240 -0
- package/dist/fetch.js.map +1 -0
- package/dist/gate.d.ts +80 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +249 -0
- package/dist/gate.js.map +1 -0
- package/dist/handler.d.ts +283 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +4 -0
- package/dist/handler.js.map +1 -0
- package/dist/hmac.d.ts +32 -0
- package/dist/hmac.d.ts.map +1 -0
- package/dist/hmac.js +108 -0
- package/dist/hmac.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/lane.d.ts +92 -0
- package/dist/lane.d.ts.map +1 -0
- package/dist/lane.js +269 -0
- package/dist/lane.js.map +1 -0
- package/dist/lipsync.d.ts +136 -0
- package/dist/lipsync.d.ts.map +1 -0
- package/dist/lipsync.js +310 -0
- package/dist/lipsync.js.map +1 -0
- package/dist/log.d.ts +18 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +20 -0
- package/dist/log.js.map +1 -0
- package/dist/media.d.ts +43 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +190 -0
- package/dist/media.js.map +1 -0
- package/dist/minutes.d.ts +398 -0
- package/dist/minutes.d.ts.map +1 -0
- package/dist/minutes.js +805 -0
- package/dist/minutes.js.map +1 -0
- package/dist/outbound.d.ts +388 -0
- package/dist/outbound.d.ts.map +1 -0
- package/dist/outbound.js +933 -0
- package/dist/outbound.js.map +1 -0
- package/dist/plugins/cartesia/agent.d.ts +58 -0
- package/dist/plugins/cartesia/agent.d.ts.map +1 -0
- package/dist/plugins/cartesia/agent.js +254 -0
- package/dist/plugins/cartesia/agent.js.map +1 -0
- package/dist/plugins/cartesia/cli.d.ts +3 -0
- package/dist/plugins/cartesia/cli.d.ts.map +1 -0
- package/dist/plugins/cartesia/cli.js +10 -0
- package/dist/plugins/cartesia/cli.js.map +1 -0
- package/dist/plugins/cartesia/config.d.ts +30 -0
- package/dist/plugins/cartesia/config.d.ts.map +1 -0
- package/dist/plugins/cartesia/config.js +29 -0
- package/dist/plugins/cartesia/config.js.map +1 -0
- package/dist/plugins/cartesia/handler.d.ts +40 -0
- package/dist/plugins/cartesia/handler.d.ts.map +1 -0
- package/dist/plugins/cartesia/handler.js +142 -0
- package/dist/plugins/cartesia/handler.js.map +1 -0
- package/dist/plugins/cartesia/index.d.ts +6 -0
- package/dist/plugins/cartesia/index.d.ts.map +1 -0
- package/dist/plugins/cartesia/index.js +54 -0
- package/dist/plugins/cartesia/index.js.map +1 -0
- package/dist/plugins/deepgram/agent.d.ts +60 -0
- package/dist/plugins/deepgram/agent.d.ts.map +1 -0
- package/dist/plugins/deepgram/agent.js +302 -0
- package/dist/plugins/deepgram/agent.js.map +1 -0
- package/dist/plugins/deepgram/cli.d.ts +3 -0
- package/dist/plugins/deepgram/cli.d.ts.map +1 -0
- package/dist/plugins/deepgram/cli.js +10 -0
- package/dist/plugins/deepgram/cli.js.map +1 -0
- package/dist/plugins/deepgram/config.d.ts +39 -0
- package/dist/plugins/deepgram/config.d.ts.map +1 -0
- package/dist/plugins/deepgram/config.js +66 -0
- package/dist/plugins/deepgram/config.js.map +1 -0
- package/dist/plugins/deepgram/handler.d.ts +87 -0
- package/dist/plugins/deepgram/handler.d.ts.map +1 -0
- package/dist/plugins/deepgram/handler.js +268 -0
- package/dist/plugins/deepgram/handler.js.map +1 -0
- package/dist/plugins/deepgram/index.d.ts +11 -0
- package/dist/plugins/deepgram/index.d.ts.map +1 -0
- package/dist/plugins/deepgram/index.js +76 -0
- package/dist/plugins/deepgram/index.js.map +1 -0
- package/dist/plugins/echo/cli.d.ts +3 -0
- package/dist/plugins/echo/cli.d.ts.map +1 -0
- package/dist/plugins/echo/cli.js +10 -0
- package/dist/plugins/echo/cli.js.map +1 -0
- package/dist/plugins/echo/index.d.ts +37 -0
- package/dist/plugins/echo/index.d.ts.map +1 -0
- package/dist/plugins/echo/index.js +61 -0
- package/dist/plugins/echo/index.js.map +1 -0
- package/dist/plugins/elevenlabs/agent.d.ts +72 -0
- package/dist/plugins/elevenlabs/agent.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/agent.js +305 -0
- package/dist/plugins/elevenlabs/agent.js.map +1 -0
- package/dist/plugins/elevenlabs/cli.d.ts +3 -0
- package/dist/plugins/elevenlabs/cli.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/cli.js +10 -0
- package/dist/plugins/elevenlabs/cli.js.map +1 -0
- package/dist/plugins/elevenlabs/config.d.ts +27 -0
- package/dist/plugins/elevenlabs/config.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/config.js +42 -0
- package/dist/plugins/elevenlabs/config.js.map +1 -0
- package/dist/plugins/elevenlabs/handler.d.ts +67 -0
- package/dist/plugins/elevenlabs/handler.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/handler.js +419 -0
- package/dist/plugins/elevenlabs/handler.js.map +1 -0
- package/dist/plugins/elevenlabs/index.d.ts +11 -0
- package/dist/plugins/elevenlabs/index.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/index.js +72 -0
- package/dist/plugins/elevenlabs/index.js.map +1 -0
- package/dist/plugins/livekit/cli.d.ts +3 -0
- package/dist/plugins/livekit/cli.d.ts.map +1 -0
- package/dist/plugins/livekit/cli.js +10 -0
- package/dist/plugins/livekit/cli.js.map +1 -0
- package/dist/plugins/livekit/config.d.ts +47 -0
- package/dist/plugins/livekit/config.d.ts.map +1 -0
- package/dist/plugins/livekit/config.js +46 -0
- package/dist/plugins/livekit/config.js.map +1 -0
- package/dist/plugins/livekit/handler.d.ts +59 -0
- package/dist/plugins/livekit/handler.d.ts.map +1 -0
- package/dist/plugins/livekit/handler.js +211 -0
- package/dist/plugins/livekit/handler.js.map +1 -0
- package/dist/plugins/livekit/index.d.ts +6 -0
- package/dist/plugins/livekit/index.d.ts.map +1 -0
- package/dist/plugins/livekit/index.js +68 -0
- package/dist/plugins/livekit/index.js.map +1 -0
- package/dist/plugins/livekit/room.d.ts +117 -0
- package/dist/plugins/livekit/room.d.ts.map +1 -0
- package/dist/plugins/livekit/room.js +449 -0
- package/dist/plugins/livekit/room.js.map +1 -0
- package/dist/plugins/openai/agent.d.ts +74 -0
- package/dist/plugins/openai/agent.d.ts.map +1 -0
- package/dist/plugins/openai/agent.js +264 -0
- package/dist/plugins/openai/agent.js.map +1 -0
- package/dist/plugins/openai/cli.d.ts +3 -0
- package/dist/plugins/openai/cli.d.ts.map +1 -0
- package/dist/plugins/openai/cli.js +10 -0
- package/dist/plugins/openai/cli.js.map +1 -0
- package/dist/plugins/openai/config.d.ts +29 -0
- package/dist/plugins/openai/config.d.ts.map +1 -0
- package/dist/plugins/openai/config.js +37 -0
- package/dist/plugins/openai/config.js.map +1 -0
- package/dist/plugins/openai/handler.d.ts +65 -0
- package/dist/plugins/openai/handler.d.ts.map +1 -0
- package/dist/plugins/openai/handler.js +297 -0
- package/dist/plugins/openai/handler.js.map +1 -0
- package/dist/plugins/openai/index.d.ts +6 -0
- package/dist/plugins/openai/index.d.ts.map +1 -0
- package/dist/plugins/openai/index.js +72 -0
- package/dist/plugins/openai/index.js.map +1 -0
- package/dist/plugins/openclaw/allowlist.d.ts +9 -0
- package/dist/plugins/openclaw/allowlist.d.ts.map +1 -0
- package/dist/plugins/openclaw/allowlist.js +11 -0
- package/dist/plugins/openclaw/allowlist.js.map +1 -0
- package/dist/plugins/openclaw/config.d.ts +50 -0
- package/dist/plugins/openclaw/config.d.ts.map +1 -0
- package/dist/plugins/openclaw/config.js +53 -0
- package/dist/plugins/openclaw/config.js.map +1 -0
- package/dist/plugins/openclaw/handler.d.ts +90 -0
- package/dist/plugins/openclaw/handler.d.ts.map +1 -0
- package/dist/plugins/openclaw/handler.js +146 -0
- package/dist/plugins/openclaw/handler.js.map +1 -0
- package/dist/plugins/openclaw/index.d.ts +55 -0
- package/dist/plugins/openclaw/index.d.ts.map +1 -0
- package/dist/plugins/openclaw/index.js +89 -0
- package/dist/plugins/openclaw/index.js.map +1 -0
- package/dist/plugins/openclaw/openclaw.plugin.json +74 -0
- package/dist/plugins/openclaw/package.json +14 -0
- package/dist/plugins/openclaw/realtime.d.ts +82 -0
- package/dist/plugins/openclaw/realtime.d.ts.map +1 -0
- package/dist/plugins/openclaw/realtime.js +238 -0
- package/dist/plugins/openclaw/realtime.js.map +1 -0
- package/dist/plugins/openclaw/runtime.d.ts +19 -0
- package/dist/plugins/openclaw/runtime.d.ts.map +1 -0
- package/dist/plugins/openclaw/runtime.js +160 -0
- package/dist/plugins/openclaw/runtime.js.map +1 -0
- package/dist/policy.d.ts +38 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +95 -0
- package/dist/policy.js.map +1 -0
- package/dist/protocol.d.ts +44 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +77 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolRuntime.d.ts +19 -0
- package/dist/protocolRuntime.d.ts.map +1 -0
- package/dist/protocolRuntime.js +78 -0
- package/dist/protocolRuntime.js.map +1 -0
- package/dist/smoke.d.ts +65 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +293 -0
- package/dist/smoke.js.map +1 -0
- package/dist/startup.d.ts +74 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +112 -0
- package/dist/startup.js.map +1 -0
- package/dist/tile.d.ts +101 -0
- package/dist/tile.d.ts.map +1 -0
- package/dist/tile.js +195 -0
- package/dist/tile.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/dist/vision.d.ts +160 -0
- package/dist/vision.d.ts.map +1 -0
- package/dist/vision.js +301 -0
- package/dist/vision.js.map +1 -0
- package/dist/visionTools.d.ts +237 -0
- package/dist/visionTools.d.ts.map +1 -0
- package/dist/visionTools.js +578 -0
- package/dist/visionTools.js.map +1 -0
- package/dist/voice.d.ts +143 -0
- package/dist/voice.d.ts.map +1 -0
- package/dist/voice.js +403 -0
- package/dist/voice.js.map +1 -0
- package/package.json +129 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { toolSchemas } from "../../callTools.js";
|
|
4
|
+
import { fetchPublicImage } from "../../fetch.js";
|
|
5
|
+
import { logger } from "../../log.js";
|
|
6
|
+
import { StartupBuffer } from "../../startup.js";
|
|
7
|
+
import { DISPLAY_IMAGE_MIME_TYPES, MAX_IMAGE_BYTES, } from "../../vision.js";
|
|
8
|
+
import { KeyframeStore } from "../../visionTools.js";
|
|
9
|
+
import { AgentSocket, buildConversationInit, } from "./agent.js";
|
|
10
|
+
import { elevenLabsConfigFromEnv } from "./config.js";
|
|
11
|
+
/**
|
|
12
|
+
* Bounds on what a model may put in an emotion or a caption. These reach the
|
|
13
|
+
* avatar tile, and an unbounded string from a model is an unbounded string from
|
|
14
|
+
* whoever is steering it.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_EMOTION_CHARS = 40;
|
|
17
|
+
const MAX_CAPTION_CHARS = 200;
|
|
18
|
+
const MAX_MODE_CHARS = 20;
|
|
19
|
+
const IMAGE_FETCH_TIMEOUT_MS = 10_000;
|
|
20
|
+
/**
|
|
21
|
+
* Caller audio and context that arrive while the conversation is still opening.
|
|
22
|
+
* Bounded: on a socket that never opens these would grow for the whole call.
|
|
23
|
+
*/
|
|
24
|
+
const MAX_PENDING_AUDIO = 200;
|
|
25
|
+
const MAX_PENDING_CONTEXT = 20;
|
|
26
|
+
/**
|
|
27
|
+
* The client tools to declare on the ElevenLabs agent.
|
|
28
|
+
*
|
|
29
|
+
* ElevenLabs configures tools on the agent, not over the wire, so there is
|
|
30
|
+
* nothing this plugin can send. What it can do is hand you the declarations
|
|
31
|
+
* verbatim. The names and descriptions are the SDK's, so this agent is told
|
|
32
|
+
* about the same capabilities as every other provider's. `show_image` is
|
|
33
|
+
* widened here because ElevenLabs is the one provider that can take the bytes
|
|
34
|
+
* inline.
|
|
35
|
+
*/
|
|
36
|
+
export function clientTools() {
|
|
37
|
+
return toolSchemas("flat").map((tool) => {
|
|
38
|
+
if (tool.name !== "show_image")
|
|
39
|
+
return tool;
|
|
40
|
+
const parameters = tool.parameters;
|
|
41
|
+
return {
|
|
42
|
+
...tool,
|
|
43
|
+
description: `${tool.description} You can give the image inline instead, ` +
|
|
44
|
+
"as 'dataBase64' with its 'mime'.",
|
|
45
|
+
parameters: {
|
|
46
|
+
...parameters,
|
|
47
|
+
properties: {
|
|
48
|
+
...parameters.properties,
|
|
49
|
+
dataBase64: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "The image itself, base64 encoded. An alternative to 'url'.",
|
|
52
|
+
},
|
|
53
|
+
mime: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: `Required with dataBase64: one of ${DISPLAY_IMAGE_MIME_TYPES.join(", ")}.`,
|
|
56
|
+
},
|
|
57
|
+
durationMs: {
|
|
58
|
+
type: "integer",
|
|
59
|
+
description: "How long to leave it up, in milliseconds.",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
// Either form will do, so neither argument can be required.
|
|
63
|
+
required: [],
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** One Microsoft Teams call answered by one ElevenLabs agent. */
|
|
69
|
+
export class ElevenLabsHandler {
|
|
70
|
+
#config;
|
|
71
|
+
#connect;
|
|
72
|
+
#call;
|
|
73
|
+
// Frames kept so a question about a slide already gone can still be
|
|
74
|
+
// answered. The store keeps nothing unless the call is recorded.
|
|
75
|
+
#keyframes = new KeyframeStore();
|
|
76
|
+
#agent;
|
|
77
|
+
#closed = false;
|
|
78
|
+
// Holds BOTH the caller's first words and the first context, so neither
|
|
79
|
+
// is lost while the provider is still connecting.
|
|
80
|
+
#pending = new StartupBuffer();
|
|
81
|
+
#pendingAudio = [];
|
|
82
|
+
// Audio already in flight when an interruption lands is audio the caller must
|
|
83
|
+
// never hear: the model stopped, and playing the tail would talk over the
|
|
84
|
+
// person who just interrupted.
|
|
85
|
+
#lastAudioEvent = 0;
|
|
86
|
+
#lastInterruptEvent = 0;
|
|
87
|
+
constructor(config, connect = AgentSocket.connect.bind(AgentSocket)) {
|
|
88
|
+
this.#config = config ?? elevenLabsConfigFromEnv();
|
|
89
|
+
this.#connect = connect;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Whether the call is being recorded, straight off the session.
|
|
93
|
+
*
|
|
94
|
+
* The server keeps this current from `session.start` and every later
|
|
95
|
+
* `recording.status`, so there is nothing to re-derive here.
|
|
96
|
+
*/
|
|
97
|
+
#recording() {
|
|
98
|
+
return this.#call?.recordingActive ?? false;
|
|
99
|
+
}
|
|
100
|
+
// ---- the SDK seam -------------------------------------------------------
|
|
101
|
+
async onStart(session) {
|
|
102
|
+
this.#call = session;
|
|
103
|
+
const caller = session.start.caller;
|
|
104
|
+
let agent;
|
|
105
|
+
try {
|
|
106
|
+
agent = await this.#connect(this.#config, (message) => this.#onAgentMessage(message), (code, reason) => this.#onAgentClose(code, reason));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
logger.error(`standin: could not open the ElevenLabs conversation: ${String(err)}`);
|
|
110
|
+
await session.end("agent-unavailable");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// The call can end DURING the connect above. Keeping a socket opened after
|
|
114
|
+
// teardown would leave a live, billed conversation with nothing on the
|
|
115
|
+
// other end of it.
|
|
116
|
+
if (this.#closed) {
|
|
117
|
+
await agent.aclose();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.#agent = agent;
|
|
121
|
+
agent.sendConversationInit(buildConversationInit({
|
|
122
|
+
dynamicVariables: {
|
|
123
|
+
caller_name: caller.displayName ?? "caller",
|
|
124
|
+
tenant_id: caller.tenantId ?? "unknown-tenant",
|
|
125
|
+
call_direction: session.start.direction,
|
|
126
|
+
},
|
|
127
|
+
firstMessage: this.#config.firstMessage,
|
|
128
|
+
environment: this.#config.environment,
|
|
129
|
+
// Per-person memory, and only when the person is actually identified.
|
|
130
|
+
userId: caller.aadId,
|
|
131
|
+
branchId: this.#config.agentBranchId,
|
|
132
|
+
}));
|
|
133
|
+
// Whatever arrived while the socket was opening. The "there are N people
|
|
134
|
+
// here, stay quiet" signal usually lands exactly in this window.
|
|
135
|
+
await this.#pending.release((pcm) => agent.sendAudioChunk(pcm.toString("base64")), (text) => agent.sendContextualUpdate(text));
|
|
136
|
+
if (this.#pending.dropped.audio || this.#pending.dropped.context) {
|
|
137
|
+
logger.info("standin: the caller outran the agent starting up; some early input was dropped");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** The caller's voice, straight through. Both sides are PCM16 at 16 kHz. */
|
|
141
|
+
async onCallerAudio(pcm) {
|
|
142
|
+
const chunk = pcm.toString("base64");
|
|
143
|
+
const agent = this.#agent;
|
|
144
|
+
if (agent === undefined || !agent.isOpen) {
|
|
145
|
+
this.#pending.audio(pcm);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
agent.sendAudioChunk(chunk);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Keep a short history, so `look_back` has something to look at.
|
|
152
|
+
*
|
|
153
|
+
* The store itself refuses to keep anything unless the call is being
|
|
154
|
+
* recorded, which is the same promise `look` makes.
|
|
155
|
+
*/
|
|
156
|
+
async onVideoFrame(frame) {
|
|
157
|
+
this.#keyframes.offer(frame, this.#call?.recordingActive ?? false);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Participant counts, key presses, recording changes. Sent as a contextual
|
|
161
|
+
* update, which ElevenLabs delivers WITHOUT interrupting mid-sentence.
|
|
162
|
+
*/
|
|
163
|
+
async onContext(text) {
|
|
164
|
+
const agent = this.#agent;
|
|
165
|
+
if (agent === undefined || !agent.isOpen) {
|
|
166
|
+
this.#pending.context(text);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
agent.sendContextualUpdate(text);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* StandIn is ending the call and wants this line spoken first. Delivered as a
|
|
173
|
+
* user turn, which interrupts whatever the agent was saying.
|
|
174
|
+
*/
|
|
175
|
+
async onGoodbye(text) {
|
|
176
|
+
const agent = this.#agent;
|
|
177
|
+
if (agent === undefined || !agent.isOpen)
|
|
178
|
+
return;
|
|
179
|
+
this.#lastInterruptEvent = Math.max(this.#lastInterruptEvent, this.#lastAudioEvent);
|
|
180
|
+
agent.sendUserMessage(`[system: the call is ending. Say a brief goodbye now: "${text}"]`);
|
|
181
|
+
}
|
|
182
|
+
/** Close the conversation. Always runs exactly once. */
|
|
183
|
+
async aclose() {
|
|
184
|
+
this.#closed = true;
|
|
185
|
+
const agent = this.#agent;
|
|
186
|
+
this.#agent = undefined;
|
|
187
|
+
if (agent !== undefined) {
|
|
188
|
+
try {
|
|
189
|
+
await agent.aclose();
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Teardown must not throw: the slot is freed either way.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// ---- what ElevenLabs sends us -------------------------------------------
|
|
197
|
+
async #onAgentMessage(message) {
|
|
198
|
+
switch (message.type) {
|
|
199
|
+
case "audio":
|
|
200
|
+
await this.#onAgentAudio(message);
|
|
201
|
+
break;
|
|
202
|
+
case "interruption":
|
|
203
|
+
await this.#onInterruption(message);
|
|
204
|
+
break;
|
|
205
|
+
case "ping": {
|
|
206
|
+
const event = message.ping_event;
|
|
207
|
+
if (typeof event?.event_id === "number")
|
|
208
|
+
this.#agent?.sendPong(event.event_id);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "client_tool_call": {
|
|
212
|
+
const call = message.client_tool_call;
|
|
213
|
+
if (typeof call?.tool_name === "string" &&
|
|
214
|
+
typeof call.tool_call_id === "string") {
|
|
215
|
+
void this.#onToolCall(call);
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case "user_transcript":
|
|
220
|
+
case "agent_response":
|
|
221
|
+
// Gated twice. A transcript in your logs is a recording of the caller,
|
|
222
|
+
// so it takes both an explicit opt-in AND the call being recorded.
|
|
223
|
+
if (this.#config.logTranscripts && this.#recording()) {
|
|
224
|
+
logger.info(`standin: elevenlabs ${message.type}`);
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async #onAgentAudio(message) {
|
|
232
|
+
const event = message.audio_event;
|
|
233
|
+
if (typeof event?.event_id !== "number" ||
|
|
234
|
+
typeof event.audio_base_64 !== "string")
|
|
235
|
+
return;
|
|
236
|
+
this.#lastAudioEvent = Math.max(this.#lastAudioEvent, event.event_id);
|
|
237
|
+
if (event.event_id <= this.#lastInterruptEvent) {
|
|
238
|
+
// Audio the model generated before it was interrupted, arriving after.
|
|
239
|
+
// Playing it is exactly the thing barge-in exists to stop.
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const pcm = Buffer.from(event.audio_base_64, "base64");
|
|
243
|
+
if (pcm.length === 0)
|
|
244
|
+
return;
|
|
245
|
+
await this.#call?.sendAudio(pcm);
|
|
246
|
+
}
|
|
247
|
+
async #onInterruption(message) {
|
|
248
|
+
const event = message.interruption_event;
|
|
249
|
+
if (typeof event?.event_id !== "number")
|
|
250
|
+
return;
|
|
251
|
+
this.#lastInterruptEvent = Math.max(this.#lastInterruptEvent, event.event_id);
|
|
252
|
+
// The only lever that un-sends audio StandIn already has buffered.
|
|
253
|
+
await this.#call?.cancelPlayback();
|
|
254
|
+
}
|
|
255
|
+
async #onAgentClose(code, reason) {
|
|
256
|
+
logger.info(`standin: the ElevenLabs conversation closed (${code} ${reason})`);
|
|
257
|
+
if (!this.#closed)
|
|
258
|
+
await this.#call?.end("agent-disconnected");
|
|
259
|
+
}
|
|
260
|
+
// ---- the agent's client tools -------------------------------------------
|
|
261
|
+
async #onToolCall(call) {
|
|
262
|
+
const name = call.tool_name;
|
|
263
|
+
const toolCallId = call.tool_call_id;
|
|
264
|
+
const params = (typeof call.parameters === "object" && call.parameters !== null
|
|
265
|
+
? call.parameters
|
|
266
|
+
: {});
|
|
267
|
+
switch (name) {
|
|
268
|
+
case "end_call":
|
|
269
|
+
this.#reply(toolCallId, "the call is ending");
|
|
270
|
+
await this.#call?.end("agent-ended-call");
|
|
271
|
+
break;
|
|
272
|
+
case "express":
|
|
273
|
+
await this.#onExpress(toolCallId, params);
|
|
274
|
+
break;
|
|
275
|
+
case "show_image":
|
|
276
|
+
await this.#onShowImage(toolCallId, params);
|
|
277
|
+
break;
|
|
278
|
+
case "look":
|
|
279
|
+
await this.#onLook(toolCallId, params);
|
|
280
|
+
break;
|
|
281
|
+
case "look_back":
|
|
282
|
+
await this.#onLookBack(toolCallId, params);
|
|
283
|
+
break;
|
|
284
|
+
default:
|
|
285
|
+
this.#reply(toolCallId, `"${name}" is not a tool this plugin answers`, true);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async #onExpress(toolCallId, params) {
|
|
289
|
+
const emotion = typeof params.emotion === "string" ? params.emotion.trim() : "";
|
|
290
|
+
if (!emotion) {
|
|
291
|
+
this.#reply(toolCallId, "express needs an 'emotion'", true);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
await this.#call?.express(emotion);
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
// The bound lives where the message is built, so every plugin gets it.
|
|
299
|
+
// Read it back rather than throwing at the model.
|
|
300
|
+
this.#reply(toolCallId, String(err), true);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.#reply(toolCallId, `expressing ${emotion}`);
|
|
304
|
+
}
|
|
305
|
+
/** Put a picture on the bot's tile, from inline bytes or a URL. */
|
|
306
|
+
async #onShowImage(toolCallId, params) {
|
|
307
|
+
const call = this.#call;
|
|
308
|
+
if (call === undefined)
|
|
309
|
+
return;
|
|
310
|
+
try {
|
|
311
|
+
let dataBase64 = typeof params.dataBase64 === "string" ? params.dataBase64 : undefined;
|
|
312
|
+
let mime = typeof params.mime === "string" ? params.mime : undefined;
|
|
313
|
+
const url = typeof params.url === "string" ? params.url : undefined;
|
|
314
|
+
if (dataBase64 === undefined && url) {
|
|
315
|
+
// The URL came from the model, which is steered by whoever is on the
|
|
316
|
+
// call. The guard is what stops a crafted prompt reaching cloud
|
|
317
|
+
// metadata or anything else on your network.
|
|
318
|
+
const fetched = await fetchPublicImage(url, MAX_IMAGE_BYTES, IMAGE_FETCH_TIMEOUT_MS);
|
|
319
|
+
dataBase64 = fetched.bytes.toString("base64");
|
|
320
|
+
mime = fetched.mime;
|
|
321
|
+
}
|
|
322
|
+
if (!dataBase64 ||
|
|
323
|
+
!mime ||
|
|
324
|
+
!DISPLAY_IMAGE_MIME_TYPES.includes(mime)) {
|
|
325
|
+
throw new Error("show_image needs {dataBase64, mime} or {url}, and the image must be one of " +
|
|
326
|
+
DISPLAY_IMAGE_MIME_TYPES.join(", "));
|
|
327
|
+
}
|
|
328
|
+
const duration = params.durationMs;
|
|
329
|
+
await call.displayImage(dataBase64, {
|
|
330
|
+
mime,
|
|
331
|
+
durationMs: typeof duration === "number" ? duration : undefined,
|
|
332
|
+
mode: typeof params.mode === "string"
|
|
333
|
+
? params.mode.slice(0, MAX_MODE_CHARS)
|
|
334
|
+
: undefined,
|
|
335
|
+
caption: typeof params.caption === "string"
|
|
336
|
+
? params.caption.slice(0, MAX_CAPTION_CHARS)
|
|
337
|
+
: undefined,
|
|
338
|
+
});
|
|
339
|
+
this.#reply(toolCallId, "the caller can see the image");
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
// Tell the agent the truth. Claiming success would leave it talking about
|
|
343
|
+
// a picture the caller never saw.
|
|
344
|
+
this.#reply(toolCallId, `show_image failed: ${String(err)}`, true);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Look at what the caller is showing.
|
|
349
|
+
*
|
|
350
|
+
* The frame is uploaded to the conversation, which PERSISTS the caller's
|
|
351
|
+
* screen or face with ElevenLabs. That is why it takes the call being
|
|
352
|
+
* recorded: the caller has been told the call is being kept, and this is part
|
|
353
|
+
* of what is kept.
|
|
354
|
+
*/
|
|
355
|
+
async #onLook(toolCallId, params) {
|
|
356
|
+
const call = this.#call;
|
|
357
|
+
const agent = this.#agent;
|
|
358
|
+
if (call === undefined || agent === undefined)
|
|
359
|
+
return;
|
|
360
|
+
const source = params.source;
|
|
361
|
+
const frame = call.latestVideoFrame(source === "camera" || source === "screenshare"
|
|
362
|
+
? source
|
|
363
|
+
: undefined);
|
|
364
|
+
if (frame === undefined) {
|
|
365
|
+
this.#reply(toolCallId, "there is nothing to look at: the caller is not sharing their camera or screen", true);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (!this.#recording()) {
|
|
369
|
+
this.#reply(toolCallId, "cannot look: the Microsoft Teams call is not being recorded, and looking would " +
|
|
370
|
+
"store the caller's screen with a third party", true);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
await this.#attach(toolCallId, frame, params, "look");
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Look at something the caller has already moved past.
|
|
377
|
+
*
|
|
378
|
+
* Only possible on a recorded call, because that is the only time frames are
|
|
379
|
+
* kept at all.
|
|
380
|
+
*/
|
|
381
|
+
async #onLookBack(toolCallId, params) {
|
|
382
|
+
if (this.#call === undefined || this.#agent === undefined)
|
|
383
|
+
return;
|
|
384
|
+
const frames = this.#keyframes.recent();
|
|
385
|
+
if (frames.length === 0) {
|
|
386
|
+
this.#reply(toolCallId, this.#recording()
|
|
387
|
+
? "nothing has been shown on this call yet"
|
|
388
|
+
: "I can only look back at earlier screens while the call is being recorded, " +
|
|
389
|
+
"and nothing has been kept", true);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
await this.#attach(toolCallId, frames[frames.length - 1], params, "look_back");
|
|
393
|
+
}
|
|
394
|
+
/** Upload one frame into the conversation and tell the agent to read it. */
|
|
395
|
+
async #attach(toolCallId, frame, params, tool) {
|
|
396
|
+
const agent = this.#agent;
|
|
397
|
+
if (agent === undefined)
|
|
398
|
+
return;
|
|
399
|
+
const who = frame.participantName ??
|
|
400
|
+
(frame.source === "screenshare" ? "a participant" : "the caller");
|
|
401
|
+
const seeing = frame.source === "screenshare"
|
|
402
|
+
? `screen shared by ${who}`
|
|
403
|
+
: `camera of ${who}`;
|
|
404
|
+
const question = typeof params.question === "string" && params.question.trim()
|
|
405
|
+
? params.question.trim()
|
|
406
|
+
: "Describe what is visible.";
|
|
407
|
+
try {
|
|
408
|
+
await agent.attachImage(frame.data, frame.mime, `[live call frame: ${seeing}] ${question}`);
|
|
409
|
+
this.#reply(toolCallId, "the frame is attached; answer from what you can see");
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
this.#reply(toolCallId, `${tool} failed: ${String(err)}`, true);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
#reply(toolCallId, result, isError = false) {
|
|
416
|
+
this.#agent?.sendToolResult(toolCallId, result, isError);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/plugins/elevenlabs/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AgentSocket, buildConversationInit, type AgentMessage, type AgentPort, } from "./agent.js";
|
|
2
|
+
export { elevenLabsConfigFromEnv, type ElevenLabsConfig } from "./config.js";
|
|
3
|
+
export { ElevenLabsHandler, clientTools, type Connect } from "./handler.js";
|
|
4
|
+
/**
|
|
5
|
+
* Answer Microsoft Teams calls with ElevenLabs until the process is interrupted.
|
|
6
|
+
*
|
|
7
|
+
* The configuration is read ONCE here rather than per call: a missing API key
|
|
8
|
+
* should stop the worker at startup, not surprise the first caller.
|
|
9
|
+
*/
|
|
10
|
+
export declare function serve(): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/elevenlabs/index.ts"],"names":[],"mappings":"AAkDA,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5E;;;;;GAKG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAgB3C"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* Answer Microsoft Teams calls with an ElevenLabs agent.
|
|
5
|
+
*
|
|
6
|
+
* StandIn answers the Microsoft Teams call and dials this worker. This
|
|
7
|
+
* plugin answers that dial, opens one ElevenLabs agent conversation per
|
|
8
|
+
* call, and relays the audio both ways.
|
|
9
|
+
*
|
|
10
|
+
* Nothing to install beyond the SDK: ElevenLabs is reached over an ordinary
|
|
11
|
+
* WebSocket, so this plugin adds no dependency.
|
|
12
|
+
*
|
|
13
|
+
* ```bash
|
|
14
|
+
* npm install @komaa/standin-sdk
|
|
15
|
+
*
|
|
16
|
+
* export STANDIN_SECRET=... # your StandIn connection secret
|
|
17
|
+
* export ELEVENLABS_API_KEY=...
|
|
18
|
+
* export ELEVENLABS_AGENT_ID=...
|
|
19
|
+
* npx standin-elevenlabs
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Configure the agent for `pcm_16000` audio in BOTH directions. That is exactly
|
|
23
|
+
* what StandIn speaks, so nothing resamples anything and the latency you measure
|
|
24
|
+
* is the model's, not the transport's. An agent set to anything else is refused
|
|
25
|
+
* at the first frame rather than producing a whole call of garbled audio.
|
|
26
|
+
*
|
|
27
|
+
* Give the agent the SDK's client tools and it can use the rest of the call, not
|
|
28
|
+
* just the voice channel: hanging up, the avatar's expression, putting a
|
|
29
|
+
* picture on the bot's tile, and looking at what the caller is sharing.
|
|
30
|
+
* ElevenLabs declares tools on the agent rather than over the wire, so
|
|
31
|
+
* {@link clientTools} returns the exact declarations to paste in.
|
|
32
|
+
*
|
|
33
|
+
* Drive it yourself inside your own worker instead:
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { CallServer } from "@komaa/standin-sdk";
|
|
37
|
+
* import { ElevenLabsHandler } from "@komaa/standin-sdk/elevenlabs";
|
|
38
|
+
*
|
|
39
|
+
* const server = new CallServer({ handlerFactory: () => new ElevenLabsHandler() });
|
|
40
|
+
* await server.start();
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* The Python twin is `standin.plugins.elevenlabs`.
|
|
44
|
+
*/
|
|
45
|
+
import { CallServer } from "../../index.js";
|
|
46
|
+
import { ElevenLabsHandler } from "./handler.js";
|
|
47
|
+
import { elevenLabsConfigFromEnv } from "./config.js";
|
|
48
|
+
export { AgentSocket, buildConversationInit, } from "./agent.js";
|
|
49
|
+
export { elevenLabsConfigFromEnv } from "./config.js";
|
|
50
|
+
export { ElevenLabsHandler, clientTools } from "./handler.js";
|
|
51
|
+
/**
|
|
52
|
+
* Answer Microsoft Teams calls with ElevenLabs until the process is interrupted.
|
|
53
|
+
*
|
|
54
|
+
* The configuration is read ONCE here rather than per call: a missing API key
|
|
55
|
+
* should stop the worker at startup, not surprise the first caller.
|
|
56
|
+
*/
|
|
57
|
+
export async function serve() {
|
|
58
|
+
const config = elevenLabsConfigFromEnv();
|
|
59
|
+
const server = new CallServer({
|
|
60
|
+
handlerFactory: () => new ElevenLabsHandler(config),
|
|
61
|
+
});
|
|
62
|
+
await server.start();
|
|
63
|
+
await new Promise((resolve) => {
|
|
64
|
+
const stop = () => {
|
|
65
|
+
resolve();
|
|
66
|
+
};
|
|
67
|
+
process.once("SIGINT", stop);
|
|
68
|
+
process.once("SIGTERM", stop);
|
|
69
|
+
});
|
|
70
|
+
await server.aclose();
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/elevenlabs/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+BAA+B;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EACL,WAAW,EACX,qBAAqB,GAGtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAyB,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC;QAC5B,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,iBAAiB,CAAC,MAAM,CAAC;KACpD,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,GAAS,EAAE;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/plugins/livekit/cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
/** `npx standin-livekit` */
|
|
5
|
+
import { serve } from "./index.js";
|
|
6
|
+
serve().catch((err) => {
|
|
7
|
+
console.error(String(err));
|
|
8
|
+
process.exitCode = 1;
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/plugins/livekit/cli.ts"],"names":[],"mappings":";AACA,oCAAoC;AACpC,+BAA+B;AAE/B,4BAA4B;AAE5B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC7B,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Everything the LiveKit plugin needs, resolved once per worker. */
|
|
2
|
+
export interface LiveKitConfig {
|
|
3
|
+
/** `LIVEKIT_URL`, the `wss://` project URL. */
|
|
4
|
+
url: string;
|
|
5
|
+
/** `LIVEKIT_API_KEY`. */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** `LIVEKIT_API_SECRET`. Never logged, never sent to StandIn. */
|
|
8
|
+
apiSecret: string;
|
|
9
|
+
/**
|
|
10
|
+
* `LIVEKIT_AGENT_NAME`: the name your worker registered with.
|
|
11
|
+
*
|
|
12
|
+
* Set it and each call explicitly dispatches that agent into its room. Leave
|
|
13
|
+
* it and the room creation itself assigns the job, which is LiveKit's
|
|
14
|
+
* automatic dispatch.
|
|
15
|
+
*/
|
|
16
|
+
agentName?: string;
|
|
17
|
+
/** `LIVEKIT_ROOM_PREFIX`, prepended to the call id to name the room. */
|
|
18
|
+
roomPrefix: string;
|
|
19
|
+
/**
|
|
20
|
+
* `LIVEKIT_TILE_VIDEO`: `auto` (the default) relays whichever participant's
|
|
21
|
+
* video the room offers, `off` leaves StandIn's avatar there, and any other
|
|
22
|
+
* value is a participant IDENTITY to pin the relay to.
|
|
23
|
+
*
|
|
24
|
+
* Naming an identity matters when a separate avatar worker publishes the
|
|
25
|
+
* video and publish-on-behalf is not set: `auto` would relay whichever
|
|
26
|
+
* participant happened to publish first, which on a busy room is the wrong
|
|
27
|
+
* one.
|
|
28
|
+
*
|
|
29
|
+
* On by default because an agent that publishes video almost always means it
|
|
30
|
+
* for the caller to see. It needs the optional `sharp` package to encode
|
|
31
|
+
* frames; without it the relay stays off with one log line and the audio is
|
|
32
|
+
* unaffected.
|
|
33
|
+
*/
|
|
34
|
+
tileVideo: "auto" | "off" | (string & {});
|
|
35
|
+
/**
|
|
36
|
+
* Delete the room at teardown, so the dispatched agent job ends at once.
|
|
37
|
+
*
|
|
38
|
+
* On by default. A job whose room still exists sits there until LiveKit's own
|
|
39
|
+
* empty-room timeout, which is minutes of a worker slot doing nothing.
|
|
40
|
+
*/
|
|
41
|
+
deleteRoomOnEnd: boolean;
|
|
42
|
+
/** `LIVEKIT_TILE_VIDEO_FPS`, clamped by the SDK to a sane ceiling. */
|
|
43
|
+
tileVideoFps: number;
|
|
44
|
+
}
|
|
45
|
+
/** Read the configuration, or throw naming the variable that is missing. */
|
|
46
|
+
export declare function liveKitConfigFromEnv(): LiveKitConfig;
|
|
47
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/plugins/livekit/config.ts"],"names":[],"mappings":"AAYA,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;;;;OAcG;IACH,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAE1C;;;;;OAKG;IACH,eAAe,EAAE,OAAO,CAAC;IAEzB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;CACtB;AA4BD,4EAA4E;AAC5E,wBAAgB,oBAAoB,IAAI,aAAa,CAWpD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* What the LiveKit plugin reads from the environment.
|
|
5
|
+
*
|
|
6
|
+
* The `LIVEKIT_*` names are LiveKit's own, which a worker deployment already
|
|
7
|
+
* has set. Nothing new to learn, and nothing duplicated.
|
|
8
|
+
*/
|
|
9
|
+
import { StandInError } from "../../errors.js";
|
|
10
|
+
function required(name) {
|
|
11
|
+
const value = (process.env[name] ?? "").trim();
|
|
12
|
+
if (!value)
|
|
13
|
+
throw new StandInError(`${name} is required to answer calls with LiveKit`);
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A whole positive number, or an error naming the variable.
|
|
18
|
+
*
|
|
19
|
+
* Fails loud rather than substituting the default. An operator who typed
|
|
20
|
+
* `LIVEKIT_TILE_VIDEO_FPS=twelve` is looking at a setting that is not the one in
|
|
21
|
+
* force, and silence is what makes that take an afternoon to find.
|
|
22
|
+
*/
|
|
23
|
+
function positiveInt(name, fallback) {
|
|
24
|
+
const raw = (process.env[name] ?? "").trim();
|
|
25
|
+
if (raw === "")
|
|
26
|
+
return fallback;
|
|
27
|
+
const value = Number(raw);
|
|
28
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
29
|
+
throw new StandInError(`${name} must be a whole number above zero, not ${JSON.stringify(raw)}`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
/** Read the configuration, or throw naming the variable that is missing. */
|
|
34
|
+
export function liveKitConfigFromEnv() {
|
|
35
|
+
return {
|
|
36
|
+
url: required("LIVEKIT_URL"),
|
|
37
|
+
apiKey: required("LIVEKIT_API_KEY"),
|
|
38
|
+
apiSecret: required("LIVEKIT_API_SECRET"),
|
|
39
|
+
agentName: (process.env.LIVEKIT_AGENT_NAME ?? "").trim() || undefined,
|
|
40
|
+
roomPrefix: (process.env.LIVEKIT_ROOM_PREFIX ?? "").trim() || "msteams-",
|
|
41
|
+
deleteRoomOnEnd: (process.env.LIVEKIT_DELETE_ROOM ?? "").trim() !== "off",
|
|
42
|
+
tileVideo: (process.env.LIVEKIT_TILE_VIDEO ?? "").trim() || "auto",
|
|
43
|
+
tileVideoFps: positiveInt("LIVEKIT_TILE_VIDEO_FPS", 12),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/plugins/livekit/config.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+BAA+B;AAE/B;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAkD/C,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,YAAY,CAAC,GAAG,IAAI,2CAA2C,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,IAAY,EAAE,QAAgB;IACjD,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,YAAY,CACpB,GAAG,IAAI,2CAA2C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB;IAClC,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC,aAAa,CAAC;QAC5B,MAAM,EAAE,QAAQ,CAAC,iBAAiB,CAAC;QACnC,SAAS,EAAE,QAAQ,CAAC,oBAAoB,CAAC;QACzC,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS;QACrE,UAAU,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,UAAU;QACxE,eAAe,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK;QACzE,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,MAAM;QAClE,YAAY,EAAE,WAAW,CAAC,wBAAwB,EAAE,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC"}
|