@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.
Files changed (269) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/dist/ambient.d.ts +80 -0
  4. package/dist/ambient.d.ts.map +1 -0
  5. package/dist/ambient.js +281 -0
  6. package/dist/ambient.js.map +1 -0
  7. package/dist/attachments.d.ts +149 -0
  8. package/dist/attachments.d.ts.map +1 -0
  9. package/dist/attachments.js +451 -0
  10. package/dist/attachments.js.map +1 -0
  11. package/dist/audio.d.ts +80 -0
  12. package/dist/audio.d.ts.map +1 -0
  13. package/dist/audio.js +170 -0
  14. package/dist/audio.js.map +1 -0
  15. package/dist/avatar.d.ts +111 -0
  16. package/dist/avatar.d.ts.map +1 -0
  17. package/dist/avatar.js +201 -0
  18. package/dist/avatar.js.map +1 -0
  19. package/dist/callServer.d.ts +151 -0
  20. package/dist/callServer.d.ts.map +1 -0
  21. package/dist/callServer.js +1097 -0
  22. package/dist/callServer.js.map +1 -0
  23. package/dist/callTools.d.ts +154 -0
  24. package/dist/callTools.d.ts.map +1 -0
  25. package/dist/callTools.js +255 -0
  26. package/dist/callTools.js.map +1 -0
  27. package/dist/chat.d.ts +226 -0
  28. package/dist/chat.d.ts.map +1 -0
  29. package/dist/chat.js +574 -0
  30. package/dist/chat.js.map +1 -0
  31. package/dist/config.d.ts +32 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +86 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/consult.d.ts +147 -0
  36. package/dist/consult.d.ts.map +1 -0
  37. package/dist/consult.js +388 -0
  38. package/dist/consult.js.map +1 -0
  39. package/dist/delivery.d.ts +81 -0
  40. package/dist/delivery.d.ts.map +1 -0
  41. package/dist/delivery.js +187 -0
  42. package/dist/delivery.js.map +1 -0
  43. package/dist/echoGuard.d.ts +56 -0
  44. package/dist/echoGuard.d.ts.map +1 -0
  45. package/dist/echoGuard.js +57 -0
  46. package/dist/echoGuard.js.map +1 -0
  47. package/dist/errors.d.ts +5 -0
  48. package/dist/errors.d.ts.map +1 -0
  49. package/dist/errors.js +10 -0
  50. package/dist/errors.js.map +1 -0
  51. package/dist/fetch.d.ts +40 -0
  52. package/dist/fetch.d.ts.map +1 -0
  53. package/dist/fetch.js +240 -0
  54. package/dist/fetch.js.map +1 -0
  55. package/dist/gate.d.ts +80 -0
  56. package/dist/gate.d.ts.map +1 -0
  57. package/dist/gate.js +249 -0
  58. package/dist/gate.js.map +1 -0
  59. package/dist/handler.d.ts +283 -0
  60. package/dist/handler.d.ts.map +1 -0
  61. package/dist/handler.js +4 -0
  62. package/dist/handler.js.map +1 -0
  63. package/dist/hmac.d.ts +32 -0
  64. package/dist/hmac.d.ts.map +1 -0
  65. package/dist/hmac.js +108 -0
  66. package/dist/hmac.js.map +1 -0
  67. package/dist/index.d.ts +59 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +60 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/lane.d.ts +92 -0
  72. package/dist/lane.d.ts.map +1 -0
  73. package/dist/lane.js +269 -0
  74. package/dist/lane.js.map +1 -0
  75. package/dist/lipsync.d.ts +136 -0
  76. package/dist/lipsync.d.ts.map +1 -0
  77. package/dist/lipsync.js +310 -0
  78. package/dist/lipsync.js.map +1 -0
  79. package/dist/log.d.ts +18 -0
  80. package/dist/log.d.ts.map +1 -0
  81. package/dist/log.js +20 -0
  82. package/dist/log.js.map +1 -0
  83. package/dist/media.d.ts +43 -0
  84. package/dist/media.d.ts.map +1 -0
  85. package/dist/media.js +190 -0
  86. package/dist/media.js.map +1 -0
  87. package/dist/minutes.d.ts +398 -0
  88. package/dist/minutes.d.ts.map +1 -0
  89. package/dist/minutes.js +805 -0
  90. package/dist/minutes.js.map +1 -0
  91. package/dist/outbound.d.ts +388 -0
  92. package/dist/outbound.d.ts.map +1 -0
  93. package/dist/outbound.js +933 -0
  94. package/dist/outbound.js.map +1 -0
  95. package/dist/plugins/cartesia/agent.d.ts +58 -0
  96. package/dist/plugins/cartesia/agent.d.ts.map +1 -0
  97. package/dist/plugins/cartesia/agent.js +254 -0
  98. package/dist/plugins/cartesia/agent.js.map +1 -0
  99. package/dist/plugins/cartesia/cli.d.ts +3 -0
  100. package/dist/plugins/cartesia/cli.d.ts.map +1 -0
  101. package/dist/plugins/cartesia/cli.js +10 -0
  102. package/dist/plugins/cartesia/cli.js.map +1 -0
  103. package/dist/plugins/cartesia/config.d.ts +30 -0
  104. package/dist/plugins/cartesia/config.d.ts.map +1 -0
  105. package/dist/plugins/cartesia/config.js +29 -0
  106. package/dist/plugins/cartesia/config.js.map +1 -0
  107. package/dist/plugins/cartesia/handler.d.ts +40 -0
  108. package/dist/plugins/cartesia/handler.d.ts.map +1 -0
  109. package/dist/plugins/cartesia/handler.js +142 -0
  110. package/dist/plugins/cartesia/handler.js.map +1 -0
  111. package/dist/plugins/cartesia/index.d.ts +6 -0
  112. package/dist/plugins/cartesia/index.d.ts.map +1 -0
  113. package/dist/plugins/cartesia/index.js +54 -0
  114. package/dist/plugins/cartesia/index.js.map +1 -0
  115. package/dist/plugins/deepgram/agent.d.ts +60 -0
  116. package/dist/plugins/deepgram/agent.d.ts.map +1 -0
  117. package/dist/plugins/deepgram/agent.js +302 -0
  118. package/dist/plugins/deepgram/agent.js.map +1 -0
  119. package/dist/plugins/deepgram/cli.d.ts +3 -0
  120. package/dist/plugins/deepgram/cli.d.ts.map +1 -0
  121. package/dist/plugins/deepgram/cli.js +10 -0
  122. package/dist/plugins/deepgram/cli.js.map +1 -0
  123. package/dist/plugins/deepgram/config.d.ts +39 -0
  124. package/dist/plugins/deepgram/config.d.ts.map +1 -0
  125. package/dist/plugins/deepgram/config.js +66 -0
  126. package/dist/plugins/deepgram/config.js.map +1 -0
  127. package/dist/plugins/deepgram/handler.d.ts +87 -0
  128. package/dist/plugins/deepgram/handler.d.ts.map +1 -0
  129. package/dist/plugins/deepgram/handler.js +268 -0
  130. package/dist/plugins/deepgram/handler.js.map +1 -0
  131. package/dist/plugins/deepgram/index.d.ts +11 -0
  132. package/dist/plugins/deepgram/index.d.ts.map +1 -0
  133. package/dist/plugins/deepgram/index.js +76 -0
  134. package/dist/plugins/deepgram/index.js.map +1 -0
  135. package/dist/plugins/echo/cli.d.ts +3 -0
  136. package/dist/plugins/echo/cli.d.ts.map +1 -0
  137. package/dist/plugins/echo/cli.js +10 -0
  138. package/dist/plugins/echo/cli.js.map +1 -0
  139. package/dist/plugins/echo/index.d.ts +37 -0
  140. package/dist/plugins/echo/index.d.ts.map +1 -0
  141. package/dist/plugins/echo/index.js +61 -0
  142. package/dist/plugins/echo/index.js.map +1 -0
  143. package/dist/plugins/elevenlabs/agent.d.ts +72 -0
  144. package/dist/plugins/elevenlabs/agent.d.ts.map +1 -0
  145. package/dist/plugins/elevenlabs/agent.js +305 -0
  146. package/dist/plugins/elevenlabs/agent.js.map +1 -0
  147. package/dist/plugins/elevenlabs/cli.d.ts +3 -0
  148. package/dist/plugins/elevenlabs/cli.d.ts.map +1 -0
  149. package/dist/plugins/elevenlabs/cli.js +10 -0
  150. package/dist/plugins/elevenlabs/cli.js.map +1 -0
  151. package/dist/plugins/elevenlabs/config.d.ts +27 -0
  152. package/dist/plugins/elevenlabs/config.d.ts.map +1 -0
  153. package/dist/plugins/elevenlabs/config.js +42 -0
  154. package/dist/plugins/elevenlabs/config.js.map +1 -0
  155. package/dist/plugins/elevenlabs/handler.d.ts +67 -0
  156. package/dist/plugins/elevenlabs/handler.d.ts.map +1 -0
  157. package/dist/plugins/elevenlabs/handler.js +419 -0
  158. package/dist/plugins/elevenlabs/handler.js.map +1 -0
  159. package/dist/plugins/elevenlabs/index.d.ts +11 -0
  160. package/dist/plugins/elevenlabs/index.d.ts.map +1 -0
  161. package/dist/plugins/elevenlabs/index.js +72 -0
  162. package/dist/plugins/elevenlabs/index.js.map +1 -0
  163. package/dist/plugins/livekit/cli.d.ts +3 -0
  164. package/dist/plugins/livekit/cli.d.ts.map +1 -0
  165. package/dist/plugins/livekit/cli.js +10 -0
  166. package/dist/plugins/livekit/cli.js.map +1 -0
  167. package/dist/plugins/livekit/config.d.ts +47 -0
  168. package/dist/plugins/livekit/config.d.ts.map +1 -0
  169. package/dist/plugins/livekit/config.js +46 -0
  170. package/dist/plugins/livekit/config.js.map +1 -0
  171. package/dist/plugins/livekit/handler.d.ts +59 -0
  172. package/dist/plugins/livekit/handler.d.ts.map +1 -0
  173. package/dist/plugins/livekit/handler.js +211 -0
  174. package/dist/plugins/livekit/handler.js.map +1 -0
  175. package/dist/plugins/livekit/index.d.ts +6 -0
  176. package/dist/plugins/livekit/index.d.ts.map +1 -0
  177. package/dist/plugins/livekit/index.js +68 -0
  178. package/dist/plugins/livekit/index.js.map +1 -0
  179. package/dist/plugins/livekit/room.d.ts +117 -0
  180. package/dist/plugins/livekit/room.d.ts.map +1 -0
  181. package/dist/plugins/livekit/room.js +449 -0
  182. package/dist/plugins/livekit/room.js.map +1 -0
  183. package/dist/plugins/openai/agent.d.ts +74 -0
  184. package/dist/plugins/openai/agent.d.ts.map +1 -0
  185. package/dist/plugins/openai/agent.js +264 -0
  186. package/dist/plugins/openai/agent.js.map +1 -0
  187. package/dist/plugins/openai/cli.d.ts +3 -0
  188. package/dist/plugins/openai/cli.d.ts.map +1 -0
  189. package/dist/plugins/openai/cli.js +10 -0
  190. package/dist/plugins/openai/cli.js.map +1 -0
  191. package/dist/plugins/openai/config.d.ts +29 -0
  192. package/dist/plugins/openai/config.d.ts.map +1 -0
  193. package/dist/plugins/openai/config.js +37 -0
  194. package/dist/plugins/openai/config.js.map +1 -0
  195. package/dist/plugins/openai/handler.d.ts +65 -0
  196. package/dist/plugins/openai/handler.d.ts.map +1 -0
  197. package/dist/plugins/openai/handler.js +297 -0
  198. package/dist/plugins/openai/handler.js.map +1 -0
  199. package/dist/plugins/openai/index.d.ts +6 -0
  200. package/dist/plugins/openai/index.d.ts.map +1 -0
  201. package/dist/plugins/openai/index.js +72 -0
  202. package/dist/plugins/openai/index.js.map +1 -0
  203. package/dist/plugins/openclaw/allowlist.d.ts +9 -0
  204. package/dist/plugins/openclaw/allowlist.d.ts.map +1 -0
  205. package/dist/plugins/openclaw/allowlist.js +11 -0
  206. package/dist/plugins/openclaw/allowlist.js.map +1 -0
  207. package/dist/plugins/openclaw/config.d.ts +50 -0
  208. package/dist/plugins/openclaw/config.d.ts.map +1 -0
  209. package/dist/plugins/openclaw/config.js +53 -0
  210. package/dist/plugins/openclaw/config.js.map +1 -0
  211. package/dist/plugins/openclaw/handler.d.ts +90 -0
  212. package/dist/plugins/openclaw/handler.d.ts.map +1 -0
  213. package/dist/plugins/openclaw/handler.js +146 -0
  214. package/dist/plugins/openclaw/handler.js.map +1 -0
  215. package/dist/plugins/openclaw/index.d.ts +55 -0
  216. package/dist/plugins/openclaw/index.d.ts.map +1 -0
  217. package/dist/plugins/openclaw/index.js +89 -0
  218. package/dist/plugins/openclaw/index.js.map +1 -0
  219. package/dist/plugins/openclaw/openclaw.plugin.json +74 -0
  220. package/dist/plugins/openclaw/package.json +14 -0
  221. package/dist/plugins/openclaw/realtime.d.ts +82 -0
  222. package/dist/plugins/openclaw/realtime.d.ts.map +1 -0
  223. package/dist/plugins/openclaw/realtime.js +238 -0
  224. package/dist/plugins/openclaw/realtime.js.map +1 -0
  225. package/dist/plugins/openclaw/runtime.d.ts +19 -0
  226. package/dist/plugins/openclaw/runtime.d.ts.map +1 -0
  227. package/dist/plugins/openclaw/runtime.js +160 -0
  228. package/dist/plugins/openclaw/runtime.js.map +1 -0
  229. package/dist/policy.d.ts +38 -0
  230. package/dist/policy.d.ts.map +1 -0
  231. package/dist/policy.js +95 -0
  232. package/dist/policy.js.map +1 -0
  233. package/dist/protocol.d.ts +44 -0
  234. package/dist/protocol.d.ts.map +1 -0
  235. package/dist/protocol.js +77 -0
  236. package/dist/protocol.js.map +1 -0
  237. package/dist/protocolRuntime.d.ts +19 -0
  238. package/dist/protocolRuntime.d.ts.map +1 -0
  239. package/dist/protocolRuntime.js +78 -0
  240. package/dist/protocolRuntime.js.map +1 -0
  241. package/dist/smoke.d.ts +65 -0
  242. package/dist/smoke.d.ts.map +1 -0
  243. package/dist/smoke.js +293 -0
  244. package/dist/smoke.js.map +1 -0
  245. package/dist/startup.d.ts +74 -0
  246. package/dist/startup.d.ts.map +1 -0
  247. package/dist/startup.js +112 -0
  248. package/dist/startup.js.map +1 -0
  249. package/dist/tile.d.ts +101 -0
  250. package/dist/tile.d.ts.map +1 -0
  251. package/dist/tile.js +195 -0
  252. package/dist/tile.js.map +1 -0
  253. package/dist/version.d.ts +2 -0
  254. package/dist/version.d.ts.map +1 -0
  255. package/dist/version.js +4 -0
  256. package/dist/version.js.map +1 -0
  257. package/dist/vision.d.ts +160 -0
  258. package/dist/vision.d.ts.map +1 -0
  259. package/dist/vision.js +301 -0
  260. package/dist/vision.js.map +1 -0
  261. package/dist/visionTools.d.ts +237 -0
  262. package/dist/visionTools.d.ts.map +1 -0
  263. package/dist/visionTools.js +578 -0
  264. package/dist/visionTools.js.map +1 -0
  265. package/dist/voice.d.ts +143 -0
  266. package/dist/voice.d.ts.map +1 -0
  267. package/dist/voice.js +403 -0
  268. package/dist/voice.js.map +1 -0
  269. package/package.json +129 -0
@@ -0,0 +1,449 @@
1
+ // Copyright (c) 2026 Komaa DigiTech
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * One LiveKit room per call, joined from your worker.
5
+ *
6
+ * The shape is the opposite of the other plugins. The providers are a
7
+ * socket you send audio to; LiveKit is a room you JOIN, publishing the caller's
8
+ * voice as a track and subscribing to the agent's. Your agent does not know it
9
+ * is on a Microsoft Teams call at all: it sees a participant who talks.
10
+ *
11
+ * The framework is imported lazily, inside the function that needs it, which is
12
+ * the rule that lets one package ship every plugin. `import { CallServer }
13
+ * from "@komaa/standin-sdk"` must keep working on a machine with no LiveKit
14
+ * installed, so nothing here is imported at module load.
15
+ *
16
+ * The Python twin is `standin.plugins.livekit.call`, and the two data
17
+ * topics below are deliberately the same strings in both languages: an agent
18
+ * written for one works with the other.
19
+ */
20
+ import { StandInError } from "../../errors.js";
21
+ import { logger } from "../../log.js";
22
+ import { NUM_CHANNELS, SAMPLE_RATE_HZ } from "../../protocol.js";
23
+ /** Non-interrupting call context, published into the room as data. */
24
+ export const TOPIC_CONTEXT = "msteams.context";
25
+ /** The closing line StandIn wants spoken before the call ends. */
26
+ export const TOPIC_GOODBYE = "msteams.goodbye";
27
+ /** LiveKit's own transcription topic. Both sides of the call publish on it. */
28
+ export const TOPIC_TRANSCRIPTION = "lk.transcription";
29
+ /** Which track a transcript stream is OF. Absent on some publishers. */
30
+ export const ATTRIBUTE_TRANSCRIBED_TRACK_ID = "lk.transcribed_track_id";
31
+ /** Marks the closing header of a finished turn. */
32
+ export const ATTRIBUTE_TRANSCRIPTION_FINAL = "lk.transcription_final";
33
+ /** The identity this plugin joins under. */
34
+ export const LOCAL_IDENTITY = "standin-msteams";
35
+ /**
36
+ * Whether this transcript is of the CALLER rather than of the agent.
37
+ *
38
+ * The track id is exact when present. Without it, the sender identity is the
39
+ * fallback: livekit publishes a participant's transcript under that
40
+ * participant's identity, and ours is the caller's audio.
41
+ */
42
+ export function isCallerTranscript(attributes, senderIdentity, route) {
43
+ const trackId = attributes?.[ATTRIBUTE_TRANSCRIBED_TRACK_ID];
44
+ if (trackId !== undefined && trackId !== "") {
45
+ return (route.callerTrackSid !== undefined && trackId === route.callerTrackSid);
46
+ }
47
+ if (senderIdentity === undefined)
48
+ return false;
49
+ if (senderIdentity === route.localIdentity)
50
+ return true;
51
+ // Once the agent is bound, anything that is not the agent is the caller.
52
+ return (route.agentIdentity !== undefined && senderIdentity !== route.agentIdentity);
53
+ }
54
+ /**
55
+ * Read one transcript stream to its end and report the caller's words.
56
+ *
57
+ * Every stream is drained, including one this call ignores: an abandoned reader
58
+ * keeps its subscription for the life of the process, and the agent publishes
59
+ * one stream per turn.
60
+ *
61
+ * Each yield is the cumulative transcript so far, so every yield is delivered as
62
+ * a partial and the last one as final. A partial exists so a wake phrase is
63
+ * noticed as it is said; only a finished turn is a turn.
64
+ */
65
+ export async function readTranscript(reader, senderIdentity, route) {
66
+ const mine = isCallerTranscript(reader.info?.attributes, senderIdentity, {
67
+ callerTrackSid: route.callerTrackSid(),
68
+ localIdentity: route.localIdentity,
69
+ agentIdentity: route.agentIdentity(),
70
+ });
71
+ let latest = "";
72
+ try {
73
+ for await (const chunk of reader) {
74
+ if (route.isClosed())
75
+ return;
76
+ latest = chunk;
77
+ // Drained either way, but only the caller's words are reported.
78
+ if (mine && latest !== "")
79
+ await route.deliver(latest, false);
80
+ }
81
+ }
82
+ catch (err) {
83
+ logger.debug(`standin: a transcript stream ended early: ${String(err)}`);
84
+ return;
85
+ }
86
+ if (mine && latest !== "" && !route.isClosed())
87
+ await route.deliver(latest, true);
88
+ }
89
+ /**
90
+ * What goes on a topic: a JSON object carrying `text`, never the bare string.
91
+ *
92
+ * This is a cross-language contract. The Python SDK's `TeamsCall._on_data`
93
+ * json-decodes the packet and requires a dict with a `text` string, so a bare
94
+ * string is dropped in silence at the far end: every context sentence and the
95
+ * goodbye would reach nothing.
96
+ */
97
+ export function contextPayload(text) {
98
+ return JSON.stringify({ text });
99
+ }
100
+ /** How long a room token lives. Longer than any call, shorter than forever. */
101
+ const TOKEN_TTL = "6h";
102
+ /**
103
+ * Load the framework at USE time, never at module load.
104
+ *
105
+ * A missing framework must name the install that fixes it. A reader who gets
106
+ * `Cannot find module '@livekit/rtc-node'` out of somebody else's package
107
+ * cannot tell a missing optional dependency from a broken install.
108
+ */
109
+ async function loadLiveKit() {
110
+ try {
111
+ const [rtc, server] = await Promise.all([
112
+ import("@livekit/rtc-node"),
113
+ import("livekit-server-sdk"),
114
+ ]);
115
+ return { rtc, server };
116
+ }
117
+ catch (err) {
118
+ logger.debug(`standin: loading LiveKit failed: ${String(err)}`);
119
+ throw new StandInError("the livekit plugin needs @livekit/rtc-node and livekit-server-sdk: " +
120
+ "run `npm install @livekit/rtc-node livekit-server-sdk`");
121
+ }
122
+ }
123
+ /**
124
+ * Join a room for one call, publishing the caller and subscribing to the agent.
125
+ *
126
+ * `metadata` is handed to the dispatched agent as job metadata, which is how an
127
+ * agent learns who is calling without this plugin inventing a side channel.
128
+ */
129
+ /** The control-plane form of a room URL. The websocket one is not an API base. */
130
+ function httpUrl(url) {
131
+ const trimmed = url.trim();
132
+ if (trimmed.startsWith("wss://"))
133
+ return `https://${trimmed.slice(6)}`;
134
+ if (trimmed.startsWith("ws://"))
135
+ return `http://${trimmed.slice(5)}`;
136
+ return trimmed;
137
+ }
138
+ export async function connectRoom(config, callId, metadata, handlers) {
139
+ const { rtc, server } = await loadLiveKit();
140
+ // The call id arrives from a decoded URL segment, so a "%2F" would smuggle a
141
+ // slash into the room name. Keep it to a safe charset and a bounded length.
142
+ const safeCallId = callId.replace(/[^A-Za-z0-9._@:-]/g, "-");
143
+ const roomName = `${config.roomPrefix}${safeCallId}`.slice(0, 100);
144
+ const token = new server.AccessToken(config.apiKey, config.apiSecret, {
145
+ identity: "standin-msteams",
146
+ ttl: TOKEN_TTL,
147
+ });
148
+ token.addGrant({
149
+ roomJoin: true,
150
+ room: roomName,
151
+ canPublish: true,
152
+ canSubscribe: true,
153
+ canPublishData: true,
154
+ });
155
+ if (config.agentName) {
156
+ token.roomConfig = new server.RoomConfiguration({
157
+ agents: [
158
+ new server.RoomAgentDispatch({
159
+ agentName: config.agentName,
160
+ metadata: JSON.stringify(metadata),
161
+ }),
162
+ ],
163
+ });
164
+ }
165
+ const room = new rtc.Room();
166
+ await room.connect(config.url, await token.toJwt(), {
167
+ autoSubscribe: true,
168
+ dynacast: false,
169
+ });
170
+ const local = room.localParticipant;
171
+ if (!local) {
172
+ try {
173
+ await room.disconnect();
174
+ }
175
+ catch {
176
+ // already closing
177
+ }
178
+ throw new StandInError("the room connected without a local participant");
179
+ }
180
+ logger.info(`standin: joined LiveKit room "${roomName}"` +
181
+ (config.agentName ? ` and dispatched "${config.agentName}"` : ""));
182
+ const source = new rtc.AudioSource(SAMPLE_RATE_HZ, NUM_CHANNELS);
183
+ const track = rtc.LocalAudioTrack.createAudioTrack("msteams-caller", source);
184
+ // The publication is KEPT, and its sid is read at use time. The SDK re-issues
185
+ // the sid in place after a reconnect, so a cached string stops matching and
186
+ // every caller transcript is then classified as the agent's own speech, which
187
+ // takes the wake-phrase path silently dead for the rest of the call.
188
+ const callerPublication = await local.publishTrack(track, new rtc.TrackPublishOptions({ source: rtc.TrackSource.SOURCE_MICROPHONE }));
189
+ let closed = false;
190
+ // The identity whose audio we relay is "the agent". Captured on the first
191
+ // audio subscription, and only THAT identity leaving ends the call: a monitor
192
+ // or a second participant dropping out must not hang up on the caller.
193
+ let agentIdentity;
194
+ // Any value other than auto or off names the participant whose video the tile
195
+ // relay should take, rather than letting the room decide.
196
+ const pinnedIdentity = config.tileVideo === "auto" || config.tileVideo === "off"
197
+ ? undefined
198
+ : config.tileVideo;
199
+ // One live pump at a time, reset when the stream ends, so an agent that
200
+ // republishes its audio (an avatar track swap, a mute cycle) gets pumped
201
+ // again instead of going silent for the rest of the call.
202
+ let activePumpSid;
203
+ const startPump = (remote, identity) => {
204
+ if (activePumpSid !== undefined)
205
+ return;
206
+ activePumpSid = remote.sid ?? "unknown";
207
+ void (async () => {
208
+ try {
209
+ // Ask for 16 kHz mono: the framework resamples, so our side stays a copy.
210
+ const stream = new rtc.AudioStream(remote, SAMPLE_RATE_HZ, NUM_CHANNELS);
211
+ for await (const frame of stream) {
212
+ if (closed)
213
+ break;
214
+ const pcm = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.length * 2);
215
+ await handlers.onAgentAudio(Buffer.from(pcm));
216
+ }
217
+ }
218
+ catch (err) {
219
+ if (!closed)
220
+ logger.warn(`standin: the LiveKit audio pump failed: ${String(err)}`);
221
+ }
222
+ finally {
223
+ activePumpSid = undefined;
224
+ logger.debug(`standin: the audio pump for "${identity}" ended`);
225
+ }
226
+ })();
227
+ };
228
+ if (handlers.onCallerTranscript !== undefined) {
229
+ // LiveKit publishes transcripts of BOTH sides on this one topic. Telling
230
+ // them apart matters: an agent greeting that says its own name would
231
+ // otherwise open the follow-up window and make the assistant answer the
232
+ // next turn of a meeting nobody addressed it in.
233
+ try {
234
+ room.registerTextStreamHandler(TOPIC_TRANSCRIPTION, (reader, info) => {
235
+ void readTranscript(reader, info?.identity, {
236
+ callerTrackSid: () => callerPublication?.sid,
237
+ localIdentity: LOCAL_IDENTITY,
238
+ agentIdentity: () => agentIdentity,
239
+ isClosed: () => closed,
240
+ deliver: handlers.onCallerTranscript,
241
+ });
242
+ });
243
+ }
244
+ catch (err) {
245
+ logger.warn(`standin: caller transcripts are unavailable on this room: ${String(err)}`);
246
+ }
247
+ }
248
+ room.on(rtc.RoomEvent.TrackSubscribed, (remote, _publication, participant) => {
249
+ if (remote.kind !== rtc.TrackKind.KIND_AUDIO)
250
+ return;
251
+ if (agentIdentity === undefined) {
252
+ agentIdentity = participant.identity;
253
+ handlers.onAnswered?.();
254
+ }
255
+ startPump(remote, participant.identity);
256
+ });
257
+ room.on(rtc.RoomEvent.TrackUnsubscribed, (remote) => {
258
+ if (remote.sid !== undefined && remote.sid === activePumpSid)
259
+ activePumpSid = undefined;
260
+ });
261
+ room.on(rtc.RoomEvent.ParticipantDisconnected, (participant) => {
262
+ if (agentIdentity !== undefined && participant.identity === agentIdentity) {
263
+ void handlers.onClosed(`the agent ${participant.identity} disconnected`);
264
+ }
265
+ });
266
+ // Disconnected is FINAL: the framework retries transient drops internally
267
+ // before this fires.
268
+ room.on(rtc.RoomEvent.Disconnected, () => {
269
+ void handlers.onClosed("the room disconnected");
270
+ });
271
+ /**
272
+ * Relay the avatar's video onto the bot's tile.
273
+ *
274
+ * The participant is chosen by LiveKit's own publish-on-behalf attribute
275
+ * first, because an avatar worker publishes video as a SEPARATE participant
276
+ * acting for the agent, and only then by the agent's own identity.
277
+ *
278
+ * Tracks are matched by KIND, never by source: an avatar worker publishes its
279
+ * video untagged, so it arrives as an unknown source rather than a camera. A
280
+ * source filter would pick the right participant and then stream nothing,
281
+ * which is the most expensive way to get this wrong.
282
+ */
283
+ const startAvatarRelay = async (sink) => {
284
+ let stopped = false;
285
+ let activeSid;
286
+ let activeReader;
287
+ const cancelActive = () => {
288
+ const reader = activeReader;
289
+ activeReader = undefined;
290
+ activeSid = undefined;
291
+ // Cancel through the LOCK HOLDER. A VideoStream is a ReadableStream, the
292
+ // drain loop's reader locks it, and cancelling the stream itself rejects
293
+ // while it is locked. Cancelling the reader also unblocks a parked read,
294
+ // which a quiet or swapped track would otherwise never wake.
295
+ if (reader)
296
+ void reader.cancel().catch(() => undefined);
297
+ };
298
+ const drain = (track, identity) => {
299
+ cancelActive();
300
+ const sid = track.sid ?? "unknown";
301
+ activeSid = sid;
302
+ logger.info(`standin: relaying avatar video from "${identity}"`);
303
+ void (async () => {
304
+ const stream = new rtc.VideoStream(track);
305
+ const reader = stream.getReader();
306
+ activeReader = reader;
307
+ try {
308
+ for (;;) {
309
+ const { done, value } = await reader.read();
310
+ if (done || stopped)
311
+ break;
312
+ const rgb = value.frame.convert(rtc.VideoBufferType.RGB24);
313
+ sink.offerRgb(Buffer.from(rgb.data.buffer, rgb.data.byteOffset, rgb.data.length), rgb.width, rgb.height);
314
+ }
315
+ }
316
+ catch (err) {
317
+ if (!stopped)
318
+ logger.warn(`standin: the avatar video stream ended: ${String(err)}`);
319
+ }
320
+ finally {
321
+ reader.releaseLock();
322
+ if (activeReader === reader)
323
+ activeReader = undefined;
324
+ if (activeSid === sid)
325
+ activeSid = undefined;
326
+ }
327
+ })();
328
+ };
329
+ const pickParticipant = () => {
330
+ const remotes = [...room.remoteParticipants.values()];
331
+ // A pinned identity wins outright. It is set precisely when a separate
332
+ // worker publishes the avatar and publish-on-behalf is NOT, in which case
333
+ // guessing relays whichever participant published first.
334
+ if (pinnedIdentity !== undefined) {
335
+ return remotes.find((p) => p.identity === pinnedIdentity);
336
+ }
337
+ const behalf = remotes.find((p) => p.attributes?.["lk.publish_on_behalf"] === agentIdentity);
338
+ if (behalf)
339
+ return behalf;
340
+ return remotes.find((p) => p.identity === agentIdentity);
341
+ };
342
+ const startFromExisting = () => {
343
+ if (stopped || activeSid !== undefined)
344
+ return;
345
+ const chosen = pickParticipant();
346
+ if (!chosen)
347
+ return;
348
+ for (const pub of chosen.trackPublications.values()) {
349
+ if (pub.kind === rtc.TrackKind.KIND_VIDEO && pub.track) {
350
+ drain(pub.track, chosen.identity);
351
+ return;
352
+ }
353
+ }
354
+ };
355
+ const onSubscribed = (track, _pub, participant) => {
356
+ if (stopped || activeSid !== undefined)
357
+ return;
358
+ if (track.kind === rtc.TrackKind.KIND_AUDIO) {
359
+ // The audio subscribe is what binds the agent identity. The chosen
360
+ // participant's video may already be subscribed, and that event will
361
+ // not fire again, so re-scan here or the relay never starts.
362
+ startFromExisting();
363
+ return;
364
+ }
365
+ if (track.kind !== rtc.TrackKind.KIND_VIDEO)
366
+ return;
367
+ const chosen = pickParticipant();
368
+ if (!chosen || chosen.identity !== participant.identity)
369
+ return;
370
+ drain(track, participant.identity);
371
+ };
372
+ const onUnsubscribed = (track) => {
373
+ if (track.sid !== undefined && track.sid === activeSid)
374
+ cancelActive();
375
+ };
376
+ room.on(rtc.RoomEvent.TrackSubscribed, onSubscribed);
377
+ room.on(rtc.RoomEvent.TrackUnsubscribed, onUnsubscribed);
378
+ startFromExisting();
379
+ return () => {
380
+ stopped = true;
381
+ room.off(rtc.RoomEvent.TrackSubscribed, onSubscribed);
382
+ room.off(rtc.RoomEvent.TrackUnsubscribed, onUnsubscribed);
383
+ cancelActive();
384
+ };
385
+ };
386
+ return {
387
+ startAvatarRelay,
388
+ get isOpen() {
389
+ return !closed;
390
+ },
391
+ get agentIdentity() {
392
+ return agentIdentity;
393
+ },
394
+ sendCallerAudio(pcm) {
395
+ if (closed || pcm.length === 0)
396
+ return;
397
+ // Copied into a fresh, aligned buffer rather than viewed in place. A
398
+ // Buffer from Node's pool can sit at an ODD byteOffset, and an Int16Array
399
+ // view there does not read wrong, it throws RangeError and kills the
400
+ // call. Predicting the allocator is not worth one memcpy per frame.
401
+ const aligned = new ArrayBuffer(pcm.length);
402
+ Buffer.from(aligned).set(pcm);
403
+ const samples = new Int16Array(aligned);
404
+ const frame = new rtc.AudioFrame(samples, SAMPLE_RATE_HZ, NUM_CHANNELS, samples.length / NUM_CHANNELS);
405
+ void source.captureFrame(frame).catch((err) => {
406
+ if (!closed)
407
+ logger.warn(`standin: publishing caller audio failed: ${String(err)}`);
408
+ });
409
+ },
410
+ async publish(topic, payload) {
411
+ if (closed)
412
+ return;
413
+ try {
414
+ await local.publishData(new TextEncoder().encode(payload), {
415
+ topic,
416
+ reliable: true,
417
+ });
418
+ }
419
+ catch (err) {
420
+ // Context is additive: an agent that never reads it is not a failed call.
421
+ logger.warn(`standin: publishing ${topic} failed: ${String(err)}`);
422
+ }
423
+ },
424
+ async aclose() {
425
+ if (closed)
426
+ return;
427
+ closed = true;
428
+ try {
429
+ await room.disconnect();
430
+ }
431
+ catch {
432
+ // already closing
433
+ }
434
+ if (!config.deleteRoomOnEnd)
435
+ return;
436
+ // Deleted, not just left. A dispatched agent job whose room still exists
437
+ // sits there until LiveKit's own empty-room timeout, which is minutes of
438
+ // a worker slot doing nothing. Deleting it ends the job at once.
439
+ try {
440
+ const client = new server.RoomServiceClient(httpUrl(config.url), config.apiKey, config.apiSecret);
441
+ await client.deleteRoom(roomName);
442
+ }
443
+ catch (err) {
444
+ logger.warn(`standin: could not delete the room, it will idle out: ${String(err)}`);
445
+ }
446
+ },
447
+ };
448
+ }
449
+ //# sourceMappingURL=room.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room.js","sourceRoot":"","sources":["../../../src/plugins/livekit/room.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import type { OpenAIConfig } from "./config.js";
2
+ /** An event from the Realtime API. Only `type` is guaranteed. */
3
+ export type AgentMessage = Record<string, unknown> & {
4
+ type: string;
5
+ };
6
+ /** Caller details folded into the session instructions. */
7
+ export interface CallerContext {
8
+ callerName: string;
9
+ tenantId: string;
10
+ direction: string;
11
+ }
12
+ /**
13
+ * The call capabilities every session gets, declared as tools.
14
+ *
15
+ * The list is the SDK's, in the Realtime API's shape, so every provider offers
16
+ * the same agent and a new capability needs no edit here.
17
+ */
18
+ export declare const BUILT_IN_TOOLS: Array<Record<string, unknown>>;
19
+ /** The names above, for the shadowing check. */
20
+ export declare const BUILT_IN_TOOL_NAMES: ReadonlySet<string>;
21
+ /**
22
+ * Normalise a remote MCP tool entry for the session's tools array.
23
+ *
24
+ * The Realtime API dials the server itself and runs those tools server-side, so
25
+ * nothing here executes at call time. Approval defaults to never because a live
26
+ * voice call has no approval interface, and a pending approval is a caller
27
+ * listening to silence.
28
+ */
29
+ export declare function mcpTool(entry: Record<string, unknown>): Record<string, unknown>;
30
+ /** Compose the effective instructions: your prompt, the caller, and tool guidance. */
31
+ export declare function buildInstructions(base: string, caller: CallerContext): string;
32
+ /** Per-call session configuration, the Realtime equivalent of a conversation init. */
33
+ export declare function buildSessionUpdate(config: OpenAIConfig, instructions: string, extraTools?: Array<Record<string, unknown>>): Record<string, unknown>;
34
+ /** What the relay needs from a session. Tests substitute their own. */
35
+ export interface AgentPort {
36
+ readonly isOpen: boolean;
37
+ sendSessionUpdate(update: Record<string, unknown>): void;
38
+ /** Caller audio at the WIRE rate. This resamples up to the model's rate. */
39
+ sendAudio(pcm: Buffer): void;
40
+ sendContext(text: string): void;
41
+ sendUserTurn(text: string): void;
42
+ cancelResponse(): void;
43
+ sendToolResult(callId: string, output: string): void;
44
+ aclose(): Promise<void>;
45
+ }
46
+ /** The socket to one OpenAI Realtime session. */
47
+ export declare class AgentSocket implements AgentPort {
48
+ #private;
49
+ private constructor();
50
+ /** Open the session, retrying once on a transient failure. */
51
+ static connect(config: OpenAIConfig, onMessage: (message: AgentMessage) => void | Promise<void>,
52
+ /** Agent audio, already resampled DOWN to the wire rate. */
53
+ onAudio: (pcm: Buffer) => void | Promise<void>, onClose: (code: number, reason: string) => void | Promise<void>): Promise<AgentSocket>;
54
+ get isOpen(): boolean;
55
+ sendSessionUpdate(update: Record<string, unknown>): void;
56
+ /** Caller audio at the wire rate, resampled up to the model's rate. */
57
+ sendAudio(pcm: Buffer): void;
58
+ /**
59
+ * Background context, with no response requested, so the model sees it on its
60
+ * next turn rather than interrupting to acknowledge it.
61
+ */
62
+ sendContext(text: string): void;
63
+ /** An interrupting user turn, which is how a goodbye gets spoken. */
64
+ sendUserTurn(text: string): void;
65
+ cancelResponse(): void;
66
+ /**
67
+ * Answer a tool call, then ask for the turn that speaks the answer. Without
68
+ * the second message the model has the result and says nothing about it.
69
+ */
70
+ sendToolResult(callId: string, output: string): void;
71
+ /** Close the session. Safe to call twice. */
72
+ aclose(): Promise<void>;
73
+ }
74
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/plugins/openai/agent.ts"],"names":[],"mappings":"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"}