@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,297 @@
1
+ // Copyright (c) 2026 Komaa DigiTech
2
+ // SPDX-License-Identifier: MIT
3
+ import { ExpressionCue } from "../../avatar.js";
4
+ import { CallTools } from "../../callTools.js";
5
+ import { fetchPublicImage } from "../../fetch.js";
6
+ import { TurnLipSync } from "../../lipsync.js";
7
+ import { logger } from "../../log.js";
8
+ import { StartupBuffer } from "../../startup.js";
9
+ import { FrameDescriber } from "../../vision.js";
10
+ import { VisionTools } from "../../visionTools.js";
11
+ import { AgentSocket, BUILT_IN_TOOL_NAMES, buildInstructions, buildSessionUpdate, } from "./agent.js";
12
+ import { openAIConfigFromEnv } from "./config.js";
13
+ const MAX_EMOTION_CHARS = 40;
14
+ const MAX_CAPTION_CHARS = 200;
15
+ const IMAGE_FETCH_TIMEOUT_MS = 10_000;
16
+ const MAX_PENDING_AUDIO = 200;
17
+ const MAX_PENDING_CONTEXT = 20;
18
+ /** One Microsoft Teams call answered by one OpenAI Realtime session. */
19
+ export class OpenAIHandler {
20
+ #config;
21
+ #tools;
22
+ #mcpTools;
23
+ #describer;
24
+ #connect;
25
+ #call;
26
+ #vision;
27
+ #callTools;
28
+ #agent;
29
+ #closed = false;
30
+ /** The mouth and the face. Both cosmetic, both cheap, both per call. */
31
+ #lip = new TurnLipSync();
32
+ #cue = new ExpressionCue();
33
+ #replyText = "";
34
+ // Holds BOTH the caller's first words and the first context, so neither
35
+ // is lost while the provider is still connecting.
36
+ #pending = new StartupBuffer();
37
+ constructor(options = {}) {
38
+ this.#config = options.config ?? openAIConfigFromEnv();
39
+ this.#tools = new Map((options.tools ?? []).map((tool) => [tool.name, tool]));
40
+ this.#mcpTools = options.mcpTools ?? [];
41
+ this.#describer = options.describer ?? FrameDescriber.fromEnv();
42
+ this.#connect = options.connect ?? AgentSocket.connect.bind(AgentSocket);
43
+ const collisions = [...this.#tools.keys()].filter((name) => BUILT_IN_TOOL_NAMES.has(name));
44
+ if (collisions.length > 0) {
45
+ throw new Error(`custom tools may not shadow built-in ones: ${collisions.sort().join(", ")}`);
46
+ }
47
+ }
48
+ /**
49
+ * Whether the call is being recorded, straight off the session.
50
+ *
51
+ * The server keeps this current from `session.start` and every later
52
+ * `recording.status`, so there is nothing to re-derive here.
53
+ */
54
+ #recording() {
55
+ return this.#call?.recordingActive ?? false;
56
+ }
57
+ // ---- the SDK seam -------------------------------------------------------
58
+ async onStart(session) {
59
+ this.#call = session;
60
+ this.#vision = new VisionTools(session, { describer: this.#describer });
61
+ this.#callTools = new CallTools(session, { vision: this.#vision });
62
+ const caller = session.start.caller;
63
+ const context = {
64
+ callerName: caller.displayName ?? "the caller",
65
+ tenantId: caller.tenantId ?? "unknown-tenant",
66
+ direction: session.start.direction,
67
+ };
68
+ let agent;
69
+ try {
70
+ agent = await this.#connect(this.#config, (message) => this.#onAgentMessage(message), (pcm) => this.#onAgentAudio(pcm), (code, reason) => this.#onAgentClose(code, reason));
71
+ }
72
+ catch (err) {
73
+ logger.error(`standin: could not open the OpenAI Realtime session: ${String(err)}`);
74
+ await session.end("agent-unavailable");
75
+ return;
76
+ }
77
+ // The call can end during the connect above.
78
+ if (this.#closed) {
79
+ await agent.aclose();
80
+ return;
81
+ }
82
+ this.#agent = agent;
83
+ const extraTools = [
84
+ ...[...this.#tools.values()].map((tool) => ({
85
+ type: "function",
86
+ name: tool.name,
87
+ description: tool.description,
88
+ parameters: tool.parameters ?? {
89
+ type: "object",
90
+ properties: {},
91
+ required: [],
92
+ },
93
+ })),
94
+ ...this.#mcpTools,
95
+ ];
96
+ agent.sendSessionUpdate(buildSessionUpdate(this.#config, buildInstructions(this.#config.instructions, context), extraTools));
97
+ await this.#pending.release((pcm) => agent.sendAudio(pcm), (text) => agent.sendContext(text));
98
+ if (this.#pending.dropped.audio || this.#pending.dropped.context) {
99
+ logger.info("standin: the caller outran the agent starting up; some early input was dropped");
100
+ }
101
+ }
102
+ async onCallerAudio(pcm) {
103
+ const agent = this.#agent;
104
+ if (agent === undefined || !agent.isOpen) {
105
+ this.#pending.audio(pcm);
106
+ return;
107
+ }
108
+ agent.sendAudio(pcm);
109
+ }
110
+ /**
111
+ * Context arrives as a user item with no response requested, so the model
112
+ * reads it on its next turn instead of interrupting to acknowledge it.
113
+ */
114
+ /**
115
+ * Keep a short history, so a question about a slide already gone can still be
116
+ * answered. Only kept while the call is recorded.
117
+ */
118
+ async onVideoFrame(frame) {
119
+ this.#vision?.keyframes.offer(frame, this.#call?.recordingActive ?? false);
120
+ }
121
+ async onContext(text) {
122
+ const agent = this.#agent;
123
+ if (agent === undefined || !agent.isOpen) {
124
+ this.#pending.context(text);
125
+ return;
126
+ }
127
+ agent.sendContext(text);
128
+ }
129
+ /** Cancel whatever is being said, then ask for the goodbye. */
130
+ async onGoodbye(text) {
131
+ const agent = this.#agent;
132
+ if (agent === undefined || !agent.isOpen)
133
+ return;
134
+ agent.cancelResponse();
135
+ agent.sendUserTurn(`[system: the call is ending. Say a brief goodbye now: "${text}"]`);
136
+ }
137
+ async aclose() {
138
+ this.#closed = true;
139
+ const agent = this.#agent;
140
+ this.#agent = undefined;
141
+ if (agent !== undefined) {
142
+ try {
143
+ await agent.aclose();
144
+ }
145
+ catch {
146
+ // Teardown must not throw: the slot is freed either way.
147
+ }
148
+ }
149
+ }
150
+ /**
151
+ * The shared vision tools for this call.
152
+ *
153
+ * They carry the budget, the keyframes and the graceful refusals, so this
154
+ * plugin no longer keeps its own copy of any of them.
155
+ */
156
+ #visionTools() {
157
+ if (this.#vision === undefined)
158
+ throw new Error("the call has not started yet");
159
+ return this.#vision;
160
+ }
161
+ // ---- what OpenAI sends us -----------------------------------------------
162
+ /** Agent audio, already resampled to the wire rate by the socket. */
163
+ async #onAgentAudio(pcm) {
164
+ if (pcm.length === 0)
165
+ return;
166
+ await this.#call?.sendAudio(pcm);
167
+ // The only duration this worker genuinely knows. A realtime model hands
168
+ // back no timings, and a guess from text length drifts further out of step
169
+ // with the voice the longer the turn runs.
170
+ this.#lip.audioSent(pcm);
171
+ }
172
+ /**
173
+ * One viseme timeline per turn, spread over the audio that turn actually sent.
174
+ *
175
+ * Cosmetic, so everything here is swallowed: the worst acceptable outcome is
176
+ * a still mouth over correct audio, and the unacceptable one is a dropped
177
+ * turn because a lip shape threw.
178
+ */
179
+ async #sendVisemes() {
180
+ const text = this.#replyText;
181
+ this.#replyText = "";
182
+ try {
183
+ const marks = this.#lip.finish(text);
184
+ if (marks.length > 0)
185
+ await this.#call?.sendSpeechMarks(marks);
186
+ }
187
+ catch (err) {
188
+ logger.debug(`standin: the viseme timeline was skipped: ${String(err)}`);
189
+ }
190
+ }
191
+ /**
192
+ * A piece of what the model is saying, as it is being said.
193
+ *
194
+ * Held for the viseme timeline, and read for the face. Re-read on every piece
195
+ * rather than at the end, because waiting for the final transcript leaves the
196
+ * face wrong for the whole time the reply is being spoken.
197
+ */
198
+ async #onReplyText(text) {
199
+ if (text === "")
200
+ return;
201
+ this.#replyText += text;
202
+ try {
203
+ const emotion = this.#cue.cue(this.#replyText);
204
+ if (emotion !== null)
205
+ await this.#call?.express(emotion);
206
+ }
207
+ catch (err) {
208
+ logger.debug(`standin: the expression cue was skipped: ${String(err)}`);
209
+ }
210
+ }
211
+ async #onAgentMessage(message) {
212
+ switch (message.type) {
213
+ case "input_audio_buffer.speech_started":
214
+ // The model has already cancelled its own response server-side. This is
215
+ // the other half: flushing what StandIn has buffered, which is what
216
+ // actually stops the bot mid-word.
217
+ await this.#call?.cancelPlayback();
218
+ // The service drops audio the caller never heard. Keeping the count
219
+ // would spread the NEXT turn's words over its own audio plus the audio
220
+ // that was thrown away, and the mouth would run long for the rest of it.
221
+ this.#lip.cancel();
222
+ this.#replyText = "";
223
+ break;
224
+ case "response.output_audio_transcript.delta":
225
+ await this.#onReplyText(typeof message.delta === "string" ? message.delta : "");
226
+ break;
227
+ case "response.done":
228
+ await this.#sendVisemes();
229
+ break;
230
+ case "response.function_call_arguments.done":
231
+ void this.#runTool(message);
232
+ break;
233
+ case "conversation.item.input_audio_transcription.completed":
234
+ case "response.output_audio_transcript.done":
235
+ if (this.#config.logTranscripts && this.#recording()) {
236
+ logger.info(`standin: openai ${message.type}`);
237
+ }
238
+ break;
239
+ case "error":
240
+ logger.warn(`standin: OpenAI error: ${JSON.stringify(message.error ?? message)}`);
241
+ break;
242
+ default:
243
+ break;
244
+ }
245
+ }
246
+ async #onAgentClose(code, reason) {
247
+ logger.info(`standin: the OpenAI Realtime session closed (${code} ${reason})`);
248
+ if (!this.#closed)
249
+ await this.#call?.end("agent-disconnected");
250
+ }
251
+ // ---- the model's tools --------------------------------------------------
252
+ async #runTool(message) {
253
+ const name = message.name;
254
+ const callId = message.call_id;
255
+ if (typeof name !== "string" || typeof callId !== "string")
256
+ return;
257
+ let params = {};
258
+ if (typeof message.arguments === "string") {
259
+ try {
260
+ const parsed = JSON.parse(message.arguments || "{}");
261
+ if (typeof parsed === "object" && parsed !== null) {
262
+ params = parsed;
263
+ }
264
+ }
265
+ catch {
266
+ params = {};
267
+ }
268
+ }
269
+ let output;
270
+ try {
271
+ output = await this.dispatch(name, params);
272
+ }
273
+ catch (err) {
274
+ // Tell the model the truth: a tool that silently "succeeded" leaves it
275
+ // talking about something that never happened.
276
+ output = `${name} failed: ${String(err)}`;
277
+ }
278
+ this.#agent?.sendToolResult(callId, output);
279
+ }
280
+ /** Run one tool and return what the model is told. Exposed for tests. */
281
+ async dispatch(name, params) {
282
+ const call = this.#call;
283
+ if (call === undefined)
284
+ return "the call is no longer active";
285
+ const callTools = this.#callTools;
286
+ if (callTools !== undefined && BUILT_IN_TOOL_NAMES.has(name)) {
287
+ // The SDK owns these, and its dispatch never throws: what comes back is
288
+ // the sentence the model should say.
289
+ return callTools.dispatch(name, params);
290
+ }
291
+ const tool = this.#tools.get(name);
292
+ if (tool === undefined)
293
+ return `"${name}" is not a tool this plugin answers`;
294
+ return String(await tool.handler(params, { call, recording: this.#recording() }));
295
+ }
296
+ }
297
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/plugins/openai/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { AgentSocket, BUILT_IN_TOOLS, buildInstructions, buildSessionUpdate, mcpTool, type AgentMessage, type AgentPort, type CallerContext, } from "./agent.js";
2
+ export { DEFAULT_INSTRUCTIONS, openAIConfigFromEnv, type OpenAIConfig, } from "./config.js";
3
+ export { OpenAIHandler, type Connect, type CustomTool, type OpenAIHandlerOptions, type ToolContext, } from "./handler.js";
4
+ /** Answer Microsoft Teams calls with OpenAI until the process is interrupted. */
5
+ export declare function serve(): Promise<void>;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/openai/index.ts"],"names":[],"mappings":"AAuDA,OAAO,EACL,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AAEtB,iFAAiF;AACjF,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 OpenAI Realtime model.
5
+ *
6
+ * Speech to speech: the model hears the caller's voice rather than a transcript
7
+ * of it, so there is no recognition step adding latency in the middle.
8
+ *
9
+ * Nothing to install beyond the SDK: the Realtime API is reached over an
10
+ * ordinary WebSocket, so this plugin adds no dependency.
11
+ *
12
+ * ```bash
13
+ * npm install @komaa/standin-sdk
14
+ *
15
+ * export STANDIN_SECRET=... # your StandIn connection secret
16
+ * export OPENAI_API_KEY=...
17
+ * npx standin-openai
18
+ * ```
19
+ *
20
+ * The Realtime API speaks PCM at 24 kHz and a Microsoft Teams call speaks 16
21
+ * kHz. This plugin owns that conversion in both directions, using the same
22
+ * resampler the Python SDK uses, driven by the same shared conformance vectors.
23
+ *
24
+ * The model gets four call capabilities without any configuration: `end_call`,
25
+ * `express`, `show_image` and `look`. Looking needs a vision model, because the
26
+ * Realtime model hears but does not see - set `STANDIN_VISION_API_URL` and
27
+ * `STANDIN_VISION_MODEL` to any OpenAI-compatible endpoint that takes images.
28
+ *
29
+ * Add tools of your own, or point the session at a remote MCP server that
30
+ * OpenAI dials itself:
31
+ *
32
+ * ```ts
33
+ * import { CallServer } from "@komaa/standin-sdk";
34
+ * import { OpenAIHandler, mcpTool } from "@komaa/standin-sdk/openai";
35
+ *
36
+ * const server = new CallServer({
37
+ * handlerFactory: () => new OpenAIHandler({
38
+ * tools: [{
39
+ * name: "open_ticket",
40
+ * description: "Open a support ticket for the caller.",
41
+ * parameters: { type: "object", properties: { summary: { type: "string" } }, required: ["summary"] },
42
+ * handler: async (params) => `opened ticket for ${String(params.summary)}`,
43
+ * }],
44
+ * mcpTools: [mcpTool({ server_label: "docs", server_url: "https://mcp.example.com" })],
45
+ * }),
46
+ * });
47
+ * await server.start();
48
+ * ```
49
+ */
50
+ import { CallServer } from "../../index.js";
51
+ import { OpenAIHandler } from "./handler.js";
52
+ import { openAIConfigFromEnv } from "./config.js";
53
+ export { AgentSocket, BUILT_IN_TOOLS, buildInstructions, buildSessionUpdate, mcpTool, } from "./agent.js";
54
+ export { DEFAULT_INSTRUCTIONS, openAIConfigFromEnv, } from "./config.js";
55
+ export { OpenAIHandler, } from "./handler.js";
56
+ /** Answer Microsoft Teams calls with OpenAI until the process is interrupted. */
57
+ export async function serve() {
58
+ const config = openAIConfigFromEnv();
59
+ const server = new CallServer({
60
+ handlerFactory: () => new OpenAIHandler({ 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/openai/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+BAA+B;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,OAAO,GAIR,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,GAKd,MAAM,cAAc,CAAC;AAEtB,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC;QAC5B,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,aAAa,CAAC,EAAE,MAAM,EAAE,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,9 @@
1
+ /**
2
+ * Kept so this plugin's historical import path still resolves.
3
+ *
4
+ * The caller policy moved into the SDK core as `policy.ts`. It was never
5
+ * OpenClaw-specific: it already said so itself, and every plugin that answers a
6
+ * call has to decide whether this caller may be answered.
7
+ */
8
+ export { describeInboundRejection, isAllowlistedCaller, isInboundCallAllowed, normalizePhoneNumber, } from "../../policy.js";
9
+ //# sourceMappingURL=allowlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allowlist.d.ts","sourceRoot":"","sources":["../../../src/plugins/openclaw/allowlist.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,11 @@
1
+ // Copyright (c) 2026 Komaa DigiTech
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * Kept so this plugin's historical import path still resolves.
5
+ *
6
+ * The caller policy moved into the SDK core as `policy.ts`. It was never
7
+ * OpenClaw-specific: it already said so itself, and every plugin that answers a
8
+ * call has to decide whether this caller may be answered.
9
+ */
10
+ export { describeInboundRejection, isAllowlistedCaller, isInboundCallAllowed, normalizePhoneNumber, } from "../../policy.js";
11
+ //# sourceMappingURL=allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allowlist.js","sourceRoot":"","sources":["../../../src/plugins/openclaw/allowlist.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+BAA+B;AAE/B;;;;;;GAMG;AAEH,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `api.pluginConfig` in, a typed settings object out.
3
+ *
4
+ * OpenClaw validates the raw config against `openclaw.plugin.json`'s
5
+ * `configSchema` BEFORE this runs and before it resolves any secret reference,
6
+ * so this is a boundary adapter over input that has already been shape-checked
7
+ * but whose secrets may or may not have resolved. Tolerant casts belong here and
8
+ * nowhere else in the plugin.
9
+ */
10
+ /** Everything the plugin reads, resolved and defaulted. */
11
+ export interface ResolvedPluginConfig {
12
+ /** `enabled: false` turns the plugin off without uninstalling it. */
13
+ enabled: boolean;
14
+ /** The call listener - what {@link CallServer} is constructed with. */
15
+ media: {
16
+ port: number;
17
+ bindAddress: string | undefined;
18
+ path: string;
19
+ /** The StandIn connection secret. Empty means refuse to start. */
20
+ secret: string;
21
+ maxConcurrentCalls: number;
22
+ };
23
+ /** How the agent behaves on a call. */
24
+ voice: {
25
+ inboundPolicy?: "disabled" | "allowlist" | "pairing" | "open";
26
+ allowFrom?: string[];
27
+ /** Spoken on pickup. Omitted means the agent waits for the caller. */
28
+ inboundGreeting?: string;
29
+ /**
30
+ * Hold caller audio back until Microsoft Teams reports recording active.
31
+ *
32
+ * This is a Microsoft Media Access API obligation, not a preference: a bot
33
+ * that processes call media before `updateRecordingStatus` goes active is
34
+ * out of policy. Default OFF because the hosted service can be configured
35
+ * either way, and a gate nobody asked for is a call of silence.
36
+ */
37
+ requireRecordingStatus: boolean;
38
+ realtime: {
39
+ provider?: string;
40
+ providers?: Record<string, Record<string, unknown>>;
41
+ instructions?: string;
42
+ suppressInputDuringPlayback?: boolean;
43
+ echoSuppressionWindowMs?: number;
44
+ echoBargeInRms?: number;
45
+ };
46
+ };
47
+ }
48
+ /** Resolve the host's raw plugin config. Never throws: an unusable config fails closed. */
49
+ export declare function resolvePluginConfig(rawInput: unknown): ResolvedPluginConfig;
50
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/plugins/openclaw/config.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,IAAI,EAAE,MAAM,CAAC;QACb,kEAAkE;QAClE,MAAM,EAAE,MAAM,CAAC;QACf,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,uCAAuC;IACvC,KAAK,EAAE;QACL,aAAa,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;QAC9D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,sEAAsE;QACtE,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB;;;;;;;WAOG;QACH,sBAAsB,EAAE,OAAO,CAAC;QAChC,QAAQ,EAAE;YACR,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YACpD,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,2BAA2B,CAAC,EAAE,OAAO,CAAC;YACtC,uBAAuB,CAAC,EAAE,MAAM,CAAC;YACjC,cAAc,CAAC,EAAE,MAAM,CAAC;SACzB,CAAC;KACH,CAAC;CACH;AAqBD,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,oBAAoB,CAuC3E"}
@@ -0,0 +1,53 @@
1
+ // Copyright (c) 2026 Komaa DigiTech
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * A config value only counts when it is a non-empty STRING.
5
+ *
6
+ * This is the second line of defence on the secret, and it is deliberate. The
7
+ * manifest types `secret` as a string and OpenClaw rejects anything else before
8
+ * this resolver runs - but if an UNRESOLVED secret reference ever did arrive (an
9
+ * env reference whose variable is unset), `String({})` yields the literal
10
+ * "[object Object]": a non-empty, guessable secret that the fail-closed check in
11
+ * the entry point would happily accept. Coercing a non-string to "" makes it fail
12
+ * CLOSED, with the listener refusing to start, instead.
13
+ */
14
+ const str = (v) => (typeof v === "string" ? v : "");
15
+ const asObject = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
16
+ /** Resolve the host's raw plugin config. Never throws: an unusable config fails closed. */
17
+ export function resolvePluginConfig(rawInput) {
18
+ const c = rawInput ?? {};
19
+ const r = asObject(c.realtime) ?? {};
20
+ return {
21
+ enabled: c.enabled !== false,
22
+ media: {
23
+ port: Number(c.callingPort ?? 9442),
24
+ // undefined, not "": CallServer's own default (0.0.0.0) should win when the
25
+ // operator said nothing, and an empty string is not a bind address.
26
+ bindAddress: str(c.bindAddress) || undefined,
27
+ path: str(c.path) || "/msteams/calling",
28
+ secret: str(c.secret),
29
+ maxConcurrentCalls: Number(c.maxConcurrentCalls ?? 4),
30
+ },
31
+ voice: {
32
+ inboundPolicy: c.inboundPolicy,
33
+ allowFrom: Array.isArray(c.allowFrom)
34
+ ? c.allowFrom.map(String)
35
+ : undefined,
36
+ inboundGreeting: str(c.inboundGreeting) || undefined,
37
+ requireRecordingStatus: c.requireRecordingStatus === true,
38
+ realtime: {
39
+ provider: str(r.provider) || undefined,
40
+ providers: asObject(r.providers),
41
+ instructions: str(r.instructions) || undefined,
42
+ suppressInputDuringPlayback: typeof r.suppressInputDuringPlayback === "boolean"
43
+ ? r.suppressInputDuringPlayback
44
+ : undefined,
45
+ echoSuppressionWindowMs: r.echoSuppressionWindowMs === undefined
46
+ ? undefined
47
+ : Number(r.echoSuppressionWindowMs),
48
+ echoBargeInRms: r.echoBargeInRms === undefined ? undefined : Number(r.echoBargeInRms),
49
+ },
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/plugins/openclaw/config.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+BAA+B;AAsD/B;;;;;;;;;;GAUG;AACH,MAAM,GAAG,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAErE,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAmB,EAAE,CAC/C,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAE3E,2FAA2F;AAC3F,MAAM,UAAU,mBAAmB,CAAC,QAAiB;IACnD,MAAM,CAAC,GAAS,QAAgB,IAAI,EAAE,CAAC;IACvC,MAAM,CAAC,GAAQ,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC1C,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK;QAC5B,KAAK,EAAE;YACL,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC;YACnC,4EAA4E;YAC5E,oEAAoE;YACpE,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,SAAS;YAC5C,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,kBAAkB;YACvC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;YACrB,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC;SACtD;QACD,KAAK,EAAE;YACL,aAAa,EAAE,CAAC,CAAC,aAAa;YAC9B,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;gBACnC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;gBACzB,CAAC,CAAC,SAAS;YACb,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,SAAS;YACpD,sBAAsB,EAAE,CAAC,CAAC,sBAAsB,KAAK,IAAI;YACzD,QAAQ,EAAE;gBACR,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,SAAS;gBACtC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CACsB;gBACrD,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,SAAS;gBAC9C,2BAA2B,EACzB,OAAO,CAAC,CAAC,2BAA2B,KAAK,SAAS;oBAChD,CAAC,CAAC,CAAC,CAAC,2BAA2B;oBAC/B,CAAC,CAAC,SAAS;gBACf,uBAAuB,EACrB,CAAC,CAAC,uBAAuB,KAAK,SAAS;oBACrC,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAuB,CAAC;gBACvC,cAAc,EACZ,CAAC,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC;aACxE;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The seam adapter: one {@link CallHandler} per Microsoft Teams call.
3
+ *
4
+ * This is the whole surface between StandIn and OpenClaw, and it is deliberately
5
+ * thin. Everything framework-independent - the socket, the HMAC handshake and its
6
+ * replay guard, capacity, the wire protocol, the outbound sequence number and
7
+ * audio timeline, the pre-start and audio-idle watchdogs, idempotent teardown -
8
+ * belongs to the SDK's `CallServer`. Everything model-shaped belongs to
9
+ * {@link createRealtimeCall}. What is left here is the decision of whether to
10
+ * take the call at all, and the wiring between the two.
11
+ *
12
+ * There are four refusal paths, and each is one line, because `session.end()` is
13
+ * safe to call from inside `onStart`: it returns immediately and the close runs
14
+ * once `onStart` unwinds, so refusing a call needs no dance around a teardown
15
+ * deadlock.
16
+ */
17
+ import { type CallHandler, type CallSession } from "../../index.js";
18
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
19
+ import type { RealtimeVoiceProviderConfig, RealtimeVoiceProviderPlugin } from "openclaw/plugin-sdk/realtime-voice";
20
+ import type { ResolvedPluginConfig } from "./config.js";
21
+ import { type CallLogger, type RealtimeCall } from "./realtime.js";
22
+ /** The host realtime provider, resolved once at startup and shared by every call. */
23
+ export interface ResolvedRealtime {
24
+ provider: RealtimeVoiceProviderPlugin;
25
+ providerConfig: RealtimeVoiceProviderConfig;
26
+ }
27
+ /**
28
+ * Concurrency and shutdown, owned by the runtime.
29
+ *
30
+ * The SDK caps connections too, and that cap is the one that protects the worker.
31
+ * This one is the OPERATOR's number: it is what `maxConcurrentCalls` in the
32
+ * plugin config means, it is enforced after the handshake so a refused call gets
33
+ * a spoken-protocol close rather than a dropped socket, and it is the hook the
34
+ * gateway's shutdown uses to end live calls before the listener goes away.
35
+ */
36
+ export interface CallRegistry {
37
+ /** Reserve a slot. False when the operator's cap is already reached. */
38
+ acquire(callId: string): boolean;
39
+ /** Attach the live call so shutdown can reach it. */
40
+ bind(callId: string, call: RealtimeCall): void;
41
+ /** Free the slot. Idempotent - teardown runs on every path. */
42
+ release(callId: string): void;
43
+ }
44
+ /** Everything one handler needs. Built once by the runtime and closed over. */
45
+ export interface HandlerDeps {
46
+ config: ResolvedPluginConfig;
47
+ /**
48
+ * Undefined when no realtime provider resolved at startup. Not an error at
49
+ * construction time: the gateway may be brought up before its credentials are,
50
+ * and refusing each call with a named reason beats refusing to boot.
51
+ */
52
+ realtime?: ResolvedRealtime;
53
+ /** The gateway's own config, passed through to the host session. */
54
+ cfg?: OpenClawConfig;
55
+ registry: CallRegistry;
56
+ logger?: CallLogger;
57
+ }
58
+ /**
59
+ * One Microsoft Teams call.
60
+ *
61
+ * Implements four of the SDK's five methods. `onCallerAudio` is the hot path and
62
+ * does no work of its own - it hands the frame to the realtime bridge, which owns
63
+ * the recording gate, the echo guard and the resampling.
64
+ */
65
+ export declare class TeamsCallHandler implements CallHandler {
66
+ #private;
67
+ constructor(deps: HandlerDeps);
68
+ onStart(session: CallSession): Promise<void>;
69
+ /** PCM16, 16 kHz, mono, little-endian, already validated by the SDK. */
70
+ onCallerAudio(pcm: Buffer): Promise<void>;
71
+ /**
72
+ * Participant counts, DTMF and recording status, as plain sentences.
73
+ *
74
+ * Recording status is the one that is not just text: it opens and closes the
75
+ * media gate, so it is matched rather than forwarded. Matched against the SDK's
76
+ * own {@link contextSentences} rather than a string literal of our own, so the
77
+ * two cannot drift apart the next time the wording is improved.
78
+ */
79
+ onContext(text: string): Promise<void>;
80
+ /**
81
+ * StandIn is ending the call and wants this line spoken first.
82
+ *
83
+ * Interrupt, then say. A goodbye queued behind a long answer is a goodbye the
84
+ * caller never hears, and teardown follows within seconds.
85
+ */
86
+ onGoodbye(text: string): Promise<void>;
87
+ /** Always called exactly once, on every path, before the slot is freed. */
88
+ aclose(reason: string): Promise<void>;
89
+ }
90
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/plugins/openclaw/handler.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,KAAK,EACV,2BAA2B,EAC3B,2BAA2B,EAC5B,MAAM,oCAAoC,CAAC;AAG5C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAEvB,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,2BAA2B,CAAC;IACtC,cAAc,EAAE,2BAA2B,CAAC;CAC7C;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,qDAAqD;IACrD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/C,+DAA+D;IAC/D,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,oBAAoB,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,oEAAoE;IACpE,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,WAAW;;gBAOtC,IAAI,EAAE,WAAW;IAIvB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkFlD,wEAAwE;IAClE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C;;;;;;;OAOG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C;;;;;OAKG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5C,2EAA2E;IACrE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW5C"}