@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Komaa DigiTech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # @komaa/standin-sdk
2
+
3
+ [StandIn](https://standin.komaa.com) for TypeScript: the SDK core and every
4
+ framework plugin, in one package.
5
+
6
+ StandIn is the hosted bridge that joins a Microsoft Teams call. It owns the
7
+ Microsoft side entirely, the bot registration, Graph, media negotiation, the
8
+ avatar tile, and talks to your worker over one authenticated socket per call.
9
+ This package is that socket's other end.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @komaa/standin-sdk
15
+ ```
16
+
17
+ Node 20 or newer. Nothing else is required to answer a call.
18
+
19
+ ## One package, every entry point
20
+
21
+ | Import | What it is | Needs installed |
22
+ |---|---|---|
23
+ | `@komaa/standin-sdk` | The core: `CallServer`, `CallHandler`, `ChatChannel`, the audio and HMAC helpers | nothing but `ws` |
24
+ | `@komaa/standin-sdk/echo` | The smallest plugin that answers a real call. Copy it to start your own | nothing but `ws` |
25
+ | `@komaa/standin-sdk/elevenlabs` | An ElevenLabs agent takes the call | nothing but `ws` |
26
+ | `@komaa/standin-sdk/deepgram` | A Deepgram Voice Agent takes the call | nothing but `ws` |
27
+ | `@komaa/standin-sdk/cartesia` | A Cartesia Line agent takes the call | nothing but `ws` |
28
+ | `@komaa/standin-sdk/openai` | An OpenAI Realtime model takes the call | nothing but `ws` |
29
+ | `@komaa/standin-sdk/livekit` | A LiveKit agent takes the call | `@livekit/rtc-node`, `livekit-server-sdk` |
30
+ | `@komaa/standin-sdk/openclaw` | The OpenClaw gateway plugin | `openclaw` |
31
+
32
+ The core imports **nothing** from `src/plugins/`, so installing this package
33
+ never drags a framework in behind you, and `import { CallServer } from
34
+ "@komaa/standin-sdk"` works on a machine with no OpenClaw at all. `openclaw` is
35
+ an optional peer dependency: you install it because you run a gateway, not
36
+ because you installed this.
37
+
38
+ One package is what keeps the plugins honest with each other. A new
39
+ capability lands in the core once and every plugin has it, instead of being
40
+ threaded into one package per framework by hand.
41
+
42
+ ## Run the echo agent
43
+
44
+ ```bash
45
+ npm install @komaa/standin-sdk
46
+ STANDIN_SECRET=... npx standin-echo
47
+ ```
48
+
49
+ Call your StandIn number and talk. You should hear yourself. Run this before you
50
+ suspect your own agent: if the echo answers, your secret, your tunnel and your
51
+ StandIn identity are all correct.
52
+
53
+ From there, the [OpenClaw example](../../examples/openclaw-msteams-connector) is a
54
+ complete deployment, and the [echo plugin](src/plugins/echo) is the
55
+ file to copy for a custom one.
56
+
57
+ The [Python SDK](../python) uses the same call handler contract and wire
58
+ protocol. Shared conformance vectors check the behaviour of both SDKs. The
59
+ framework plugins are OpenClaw in TypeScript, Hermes in Python, and LiveKit
60
+ in both.
61
+
62
+ ## What it gives you
63
+
64
+ | | |
65
+ |---|---|
66
+ | `CallServer` | Answers the socket StandIn dials. Owns the HMAC handshake and its replay guard, capacity and draining, the wire protocol, sequence numbers and the audio timeline, and the watchdogs that end a call nobody closed. |
67
+ | `CallHandler` | The five-method seam a plugin implements. Every method optional. |
68
+ | `VideoFrame` | One frame of what the caller is showing, on the vision lane. |
69
+
70
+ ## Writing a plugin
71
+
72
+ ```ts
73
+ import { CallServer, type CallSession } from "@komaa/standin-sdk";
74
+
75
+ class EchoHandler {
76
+ #call!: CallSession;
77
+
78
+ async onStart(session: CallSession) {
79
+ this.#call = session;
80
+ }
81
+
82
+ async onCallerAudio(pcm: Buffer) {
83
+ await this.#call.sendAudio(pcm); // PCM16, 16 kHz, mono
84
+ }
85
+ }
86
+
87
+ const server = new CallServer({ handlerFactory: () => new EchoHandler() });
88
+ await server.start();
89
+ ```
90
+
91
+ That is a working Microsoft Teams agent. `@komaa/standin-sdk/echo` is this file plus a
92
+ signal handler, and `npx standin-echo` runs it.
93
+
94
+ ## Configuration
95
+
96
+ Environment only.
97
+
98
+ | Variable | Default | Meaning |
99
+ |---|---|---|
100
+ | `STANDIN_SECRET` | *(required)* | Connection secret from the StandIn portal. |
101
+ | `STANDIN_PORT` | `9442` | Port the call listener binds. |
102
+ | `STANDIN_HOST` | `0.0.0.0` | Bind address. Use `127.0.0.1` when only a local tunnel should reach it. |
103
+ | `STANDIN_WS_PATH` | `/msteams/calling` | Path StandIn dials. |
104
+
105
+ The listener authenticates WebSocket upgrades with HMAC. Terminate TLS at your
106
+ public ingress so StandIn can reach it over `wss://`.
107
+
108
+ ## Signing control requests
109
+
110
+ Use `signRequest` for HTTP control requests. HMAC v2 binds the method, request
111
+ path and hash of the entire body, including `tenantId`:
112
+
113
+ ```ts
114
+ import { SIGNATURE_V2_HEADER, TIMESTAMP_HEADER, nowMs, signRequest } from "@komaa/standin-sdk";
115
+
116
+ const timestamp = String(nowMs());
117
+ const headers = {
118
+ [TIMESTAMP_HEADER]: timestamp,
119
+ [SIGNATURE_V2_HEADER]: signRequest(secret, timestamp, "POST", "/api/calls", rawBody),
120
+ };
121
+ ```
122
+
123
+ Serialize the body once and send those same `rawBody` bytes. These helpers
124
+ prepare signatures; they do not send HTTP requests. `signBody` / `verifyBody`
125
+ are for chat POST bodies, with a 300-second replay window. WebSocket call and
126
+ chat-channel handshakes keep `signHandshake` / `verifyHandshake` and their
127
+ separate 60-second window.
128
+
129
+ ## Audio
130
+
131
+ PCM16, 16 kHz, mono, little-endian, both directions. The server owns the
132
+ outbound sequence number and timeline, so a handler that swaps or re-publishes
133
+ its audio source cannot make timestamps jump backwards.
134
+
135
+ ## Contributing
136
+
137
+ Building the package from a clone, the layout rules for a new plugin and
138
+ the checks CI runs are in
139
+ [CONTRIBUTING.md](https://github.com/komaa-com/standin/blob/main/CONTRIBUTING.md).
140
+
141
+ [MIT](LICENSE).
@@ -0,0 +1,80 @@
1
+ import { type VideoFrame } from "./vision.js";
2
+ import { VisionBudget } from "./visionTools.js";
3
+ /**
4
+ * How often to look again even when no frame arrived. A screen share can go
5
+ * quiet without ending, and the last thing on it is still what is being
6
+ * discussed.
7
+ */
8
+ export declare const AMBIENT_BACKSTOP_MS = 6000;
9
+ /**
10
+ * Images held while the provider's socket is not up yet. Bounded: a sink that
11
+ * never comes up would otherwise hold the whole call's video.
12
+ */
13
+ export declare const MAX_QUEUED_AMBIENT_IMAGES = 6;
14
+ /**
15
+ * Screen share first. Somebody presenting is nearly always talking about the
16
+ * screen rather than about their face.
17
+ */
18
+ export declare const AMBIENT_SOURCE_ORDER: readonly ["screenshare", "camera"];
19
+ /** The default ceiling when a plugin gives no budget of its own. */
20
+ export declare const DEFAULT_AMBIENT_MAX_PER_MINUTE = 30;
21
+ /** One frame, ready to hand to a provider, with who it came from. */
22
+ export interface AmbientImage {
23
+ readonly source: string;
24
+ readonly mime: string;
25
+ readonly dataBase64: string;
26
+ readonly width: number;
27
+ readonly height: number;
28
+ readonly ts: number;
29
+ /** Who is showing it. Degrades to "the caller" rather than vanishing. */
30
+ readonly owner: string;
31
+ /** A sentence to put beside the image, so the model knows whose screen it is. */
32
+ readonly caption: string;
33
+ }
34
+ /** The `data:` form most vision APIs take directly. */
35
+ export declare function ambientImageDataUrl(image: AmbientImage): string;
36
+ /**
37
+ * Hand one image to the provider.
38
+ *
39
+ * It MUST NOT make the agent reply: ambient vision is context, and an agent that
40
+ * answers every scene change talks over the person presenting. It MUST reject on
41
+ * failure, because a silent failure latches a frame that never arrived and the
42
+ * model never sees that screen again.
43
+ */
44
+ export type AmbientSink = (image: AmbientImage) => Promise<void>;
45
+ /** What a session has to offer for ambient vision to gate on it. */
46
+ export interface AmbientSession {
47
+ readonly recordingActive: boolean;
48
+ }
49
+ /** Options for {@link AmbientVision}. */
50
+ export interface AmbientVisionOptions {
51
+ enabled?: boolean;
52
+ budget?: VisionBudget;
53
+ requireRecording?: boolean;
54
+ sinkReady?: () => boolean;
55
+ changeKey?: (frame: VideoFrame) => string;
56
+ onDelivered?: (image: AmbientImage) => void;
57
+ backstopMs?: number;
58
+ queueMax?: number;
59
+ }
60
+ /**
61
+ * Push what changed on screen into the conversation, between turns.
62
+ *
63
+ * Built by a plugin that knows how to deliver an image without forcing a reply.
64
+ * {@link offer} is synchronous and never blocks: it runs on the receive path of
65
+ * a live call, and the vision work happens off it.
66
+ */
67
+ export declare class AmbientVision {
68
+ #private;
69
+ constructor(session: AmbientSession, deliver: AmbientSink, options?: AmbientVisionOptions);
70
+ /** Images held because the provider was not ready for them. */
71
+ get queued(): number;
72
+ get delivered(): number;
73
+ /** Take one frame. Synchronous, non-blocking, and never throws. */
74
+ offer(frame: VideoFrame): void;
75
+ /** Look now. Idempotent, and safe to call from anywhere. */
76
+ flush(): void;
77
+ /** Stop, and stay stopped. */
78
+ close(): Promise<void>;
79
+ }
80
+ //# sourceMappingURL=ambient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ambient.d.ts","sourceRoot":"","sources":["../src/ambient.ts"],"names":[],"mappings":"AAoCA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,OAAQ,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,oBAAoB,oCAAqC,CAAC;AAEvE,oEAAoE;AACpE,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,qEAAqE;AACrE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE/D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEjE,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,qBAAa,aAAa;;gBAuBtB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,oBAAyB;IA8BpC,+DAA+D;IAC/D,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,mEAAmE;IACnE,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAa9B,4DAA4D;IAC5D,KAAK,IAAI,IAAI;IAeb,8BAA8B;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAqI7B"}
@@ -0,0 +1,281 @@
1
+ // Copyright (c) 2026 Komaa DigiTech
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * Showing the model what the caller is showing, without being asked.
5
+ *
6
+ * `VisionTools` answers when a model reaches for `look`. That is the right shape
7
+ * most of the time, and it has a blind spot: the model has to know there is
8
+ * something to look at. Somebody who shares a deck and says "what do you think?"
9
+ * has told it nothing it can act on.
10
+ *
11
+ * Ambient vision closes that by pushing what changed on screen into the
12
+ * conversation between turns. It is OFF unless a plugin turns it on, because it
13
+ * spends money on every scene change and not every deployment wants that.
14
+ *
15
+ * Three things keep it from being expensive or creepy:
16
+ *
17
+ * **A recording gate**, checked before a frame is even stored. Sending
18
+ * somebody's screen to a model continuously is a different promise from
19
+ * glancing at it once, and the recording is what told them their call is being
20
+ * kept.
21
+ *
22
+ * **Change detection**, so a screen nobody touched costs nothing. The latch is
23
+ * the digest of the last frame actually DELIVERED for that source, not the last
24
+ * one seen: a delivery that failed has to be retried, not skipped.
25
+ *
26
+ * **A reserve**, so the ambient lane cannot spend the whole budget and leave the
27
+ * caller's own "look at this" with nothing left.
28
+ *
29
+ * What to DO with an image stays in the plugin, because only it knows how to
30
+ * hand one to its provider without forcing a reply.
31
+ *
32
+ * Identical in shape to the Python SDK's `standin.ambient`.
33
+ */
34
+ import { logger } from "./log.js";
35
+ import { fallbackOwner, frameCaption, frameDigest, frameOwner, } from "./vision.js";
36
+ import { VisionBudget } from "./visionTools.js";
37
+ /**
38
+ * How often to look again even when no frame arrived. A screen share can go
39
+ * quiet without ending, and the last thing on it is still what is being
40
+ * discussed.
41
+ */
42
+ export const AMBIENT_BACKSTOP_MS = 6_000;
43
+ /**
44
+ * Images held while the provider's socket is not up yet. Bounded: a sink that
45
+ * never comes up would otherwise hold the whole call's video.
46
+ */
47
+ export const MAX_QUEUED_AMBIENT_IMAGES = 6;
48
+ /**
49
+ * Screen share first. Somebody presenting is nearly always talking about the
50
+ * screen rather than about their face.
51
+ */
52
+ export const AMBIENT_SOURCE_ORDER = ["screenshare", "camera"];
53
+ /** The default ceiling when a plugin gives no budget of its own. */
54
+ export const DEFAULT_AMBIENT_MAX_PER_MINUTE = 30;
55
+ /** The `data:` form most vision APIs take directly. */
56
+ export function ambientImageDataUrl(image) {
57
+ return `data:${image.mime};base64,${image.dataBase64}`;
58
+ }
59
+ /**
60
+ * Push what changed on screen into the conversation, between turns.
61
+ *
62
+ * Built by a plugin that knows how to deliver an image without forcing a reply.
63
+ * {@link offer} is synchronous and never blocks: it runs on the receive path of
64
+ * a live call, and the vision work happens off it.
65
+ */
66
+ export class AmbientVision {
67
+ #session;
68
+ #deliver;
69
+ #enabled;
70
+ #requireRecording;
71
+ #sinkReady;
72
+ #changeKey;
73
+ #onDelivered;
74
+ #backstopMs;
75
+ #queueMax;
76
+ #budget;
77
+ #latest = new Map();
78
+ #latched = new Map();
79
+ #queue = [];
80
+ #flushing = false;
81
+ #dirty = false;
82
+ #released = false;
83
+ #delivered = 0;
84
+ #saidHolding = false;
85
+ #backstop;
86
+ constructor(session, deliver, options = {}) {
87
+ this.#session = session;
88
+ this.#deliver = deliver;
89
+ this.#enabled = options.enabled ?? false;
90
+ this.#requireRecording = options.requireRecording ?? true;
91
+ this.#sinkReady = options.sinkReady;
92
+ this.#changeKey =
93
+ options.changeKey ?? ((frame) => frameDigest(frame.dataBase64));
94
+ this.#onDelivered = options.onDelivered;
95
+ this.#backstopMs = Math.max(500, options.backstopMs ?? AMBIENT_BACKSTOP_MS);
96
+ this.#queueMax = Math.max(1, options.queueMax ?? MAX_QUEUED_AMBIENT_IMAGES);
97
+ if (options.budget === undefined) {
98
+ this.#budget = new VisionBudget(DEFAULT_AMBIENT_MAX_PER_MINUTE);
99
+ logger.debug("standin: ambient vision has its own budget, so ambient and explicit looks " +
100
+ "are capped separately");
101
+ }
102
+ else {
103
+ this.#budget = options.budget;
104
+ if (this.#enabled && options.budget.maxPerMinute === 0) {
105
+ logger.warn("standin: ambient vision is on with an uncapped budget; every scene change " +
106
+ "will be charged. Set maxPerMinute, or leave enabled off.");
107
+ }
108
+ }
109
+ }
110
+ /** Images held because the provider was not ready for them. */
111
+ get queued() {
112
+ return this.#queue.length;
113
+ }
114
+ get delivered() {
115
+ return this.#delivered;
116
+ }
117
+ /** Take one frame. Synchronous, non-blocking, and never throws. */
118
+ offer(frame) {
119
+ if (!this.#enabled || this.#released)
120
+ return;
121
+ if (this.#requireRecording && !this.#session.recordingActive) {
122
+ // Gated before it is STORED, not just before it is sent. Keeping a frame
123
+ // captured while the gate was shut would let opening the gate surface
124
+ // something from before the caller was told.
125
+ return;
126
+ }
127
+ this.#latest.set(frame.source, frame);
128
+ this.#armBackstop();
129
+ this.flush();
130
+ }
131
+ /** Look now. Idempotent, and safe to call from anywhere. */
132
+ flush() {
133
+ if (!this.#enabled || this.#released)
134
+ return;
135
+ if (this.#flushing) {
136
+ // One more pass after this one, rather than two at once.
137
+ this.#dirty = true;
138
+ return;
139
+ }
140
+ this.#flushing = true;
141
+ // Scheduled, not started here. Calling an async function runs its body up to
142
+ // the first await SYNCHRONOUSLY, so a pass begun inside offer() would see
143
+ // only the frames offered so far and deliver them out of order. It would
144
+ // also do vision work on the receive path, which offer() promises not to.
145
+ void Promise.resolve().then(() => this.#run());
146
+ }
147
+ /** Stop, and stay stopped. */
148
+ async close() {
149
+ this.#released = true;
150
+ this.#latest.clear();
151
+ this.#latched.clear();
152
+ this.#queue = [];
153
+ clearInterval(this.#backstop);
154
+ this.#backstop = undefined;
155
+ }
156
+ async #run() {
157
+ try {
158
+ for (;;) {
159
+ await this.#pass();
160
+ if (!this.#dirty || this.#released)
161
+ return;
162
+ this.#dirty = false;
163
+ }
164
+ }
165
+ catch (err) {
166
+ logger.error(`standin: the ambient vision pass failed: ${String(err)}`);
167
+ }
168
+ finally {
169
+ this.#flushing = false;
170
+ }
171
+ }
172
+ async #pass() {
173
+ if (this.#released)
174
+ return;
175
+ const ready = this.#sinkReady === undefined || this.#sinkReady();
176
+ if (ready && this.#queue.length > 0) {
177
+ // What was held goes first, oldest first: the model should see the screen
178
+ // change in the order it happened.
179
+ await this.#drain();
180
+ }
181
+ for (const source of AMBIENT_SOURCE_ORDER) {
182
+ if (this.#released)
183
+ return;
184
+ const frame = this.#latest.get(source);
185
+ if (frame === undefined)
186
+ continue;
187
+ const key = this.#changeKey(frame);
188
+ // Nothing changed. A frozen screen costs nothing.
189
+ if (this.#latched.get(source) === key)
190
+ continue;
191
+ const token = this.#budget.tryConsumeAmbient();
192
+ // Out of the ambient allowance. Stop the pass rather than trying the next
193
+ // source, which would spend the same exhausted budget.
194
+ if (token === undefined)
195
+ return;
196
+ const image = this.#image(frame);
197
+ if (!ready) {
198
+ // Charged and latched: it is going to be sent, just not yet. Refunding
199
+ // here would re-send the same screen when the sink comes up.
200
+ this.#latched.set(source, key);
201
+ this.#hold(image);
202
+ continue;
203
+ }
204
+ try {
205
+ await this.#deliver(image);
206
+ }
207
+ catch (err) {
208
+ // The latch is untouched, so the same screen is tried again.
209
+ this.#budget.refund(token);
210
+ logger.debug(`standin: an ambient frame did not reach the model: ${String(err)}`);
211
+ continue;
212
+ }
213
+ if (this.#released)
214
+ return;
215
+ this.#latched.set(source, key);
216
+ this.#delivered += 1;
217
+ this.#note(image);
218
+ }
219
+ }
220
+ async #drain() {
221
+ const held = this.#queue;
222
+ this.#queue = [];
223
+ for (const image of held) {
224
+ if (this.#released)
225
+ return;
226
+ try {
227
+ await this.#deliver(image);
228
+ }
229
+ catch (err) {
230
+ // Already charged and already latched. Dropped rather than re-queued,
231
+ // or a dead sink grows the queue for ever.
232
+ logger.debug(`standin: a held ambient frame was dropped: ${String(err)}`);
233
+ continue;
234
+ }
235
+ this.#delivered += 1;
236
+ this.#note(image);
237
+ }
238
+ }
239
+ #note(image) {
240
+ if (this.#onDelivered === undefined)
241
+ return;
242
+ try {
243
+ this.#onDelivered(image);
244
+ }
245
+ catch {
246
+ // A bookkeeping hook must not fail a delivery that already happened.
247
+ }
248
+ }
249
+ #hold(image) {
250
+ if (!this.#saidHolding) {
251
+ logger.info("standin: holding ambient frames until the provider is ready");
252
+ this.#saidHolding = true;
253
+ }
254
+ this.#queue.push(image);
255
+ // Oldest out: what is on screen NOW is worth more than what was.
256
+ if (this.#queue.length > this.#queueMax) {
257
+ this.#queue = this.#queue.slice(-this.#queueMax);
258
+ }
259
+ }
260
+ #image(frame) {
261
+ const owner = frameOwner(frame) ?? fallbackOwner(frame.source);
262
+ return {
263
+ source: frame.source,
264
+ mime: frame.mime,
265
+ dataBase64: frame.dataBase64,
266
+ width: frame.width,
267
+ height: frame.height,
268
+ ts: frame.ts,
269
+ owner,
270
+ caption: frameCaption(owner),
271
+ };
272
+ }
273
+ /** Armed on the first accepted frame, so a call with no video has no timer. */
274
+ #armBackstop() {
275
+ if (this.#backstop !== undefined || this.#released)
276
+ return;
277
+ this.#backstop = setInterval(() => this.flush(), this.#backstopMs);
278
+ this.#backstop.unref?.();
279
+ }
280
+ }
281
+ //# sourceMappingURL=ambient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ambient.js","sourceRoot":"","sources":["../src/ambient.ts"],"names":[],"mappings":"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"}