@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,578 @@
1
+ // Copyright (c) 2026 Komaa DigiTech
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * What an agent can do about what it can see, and what it shows back.
5
+ *
6
+ * `vision.ts` is the lane: frames in, images out. This is the layer a model
7
+ * actually reaches for, and it exists in the core rather than in one plugin
8
+ * because every provider wants the same things and none of them wants to write
9
+ * the guards again.
10
+ *
11
+ * None of these throw at a model. Every one returns a sentence, because the
12
+ * caller is a tool result being read back to something that will say it out
13
+ * loud, and an exception there is a silent tool and a confused agent.
14
+ *
15
+ * Identical in shape to the Python SDK's `standin.vision_tools`.
16
+ */
17
+ import { randomUUID } from "node:crypto";
18
+ import { assertPublicHttpUrl, fetchPublicImage } from "./fetch.js";
19
+ import { logger } from "./log.js";
20
+ import { DISPLAY_IMAGE_MIME_TYPES, MAX_IMAGE_BYTES, frameDigest, } from "./vision.js";
21
+ /** How long an agent-supplied URL has to produce an image. */
22
+ const FETCH_TIMEOUT_MS = 10_000;
23
+ /**
24
+ * A caption reaches the caller's screen, and a model's strings are as long as
25
+ * whoever is steering it wants them to be.
26
+ */
27
+ const MAX_CAPTION_CHARS = 200;
28
+ /** The only two ways a picture can be put on the tile. */
29
+ export const DISPLAY_MODES = ["fullscreen", "overlay"];
30
+ /** How long each picture of a slideshow stays up before the next one. */
31
+ export const SLIDESHOW_HOLD_MS = 4_000;
32
+ /**
33
+ * How much longer than the gap each picture is held for.
34
+ *
35
+ * Without it the tile blanks for the moment between one picture expiring and
36
+ * the next arriving, and the caller sees a flicker rather than a slideshow.
37
+ */
38
+ export const SLIDESHOW_OVERLAP_MS = 500;
39
+ /**
40
+ * How many pictures one slideshow may hold.
41
+ *
42
+ * A model handed a folder will pass the whole folder. Ten at four seconds is
43
+ * already most of a minute of a live call spent looking at pictures.
44
+ */
45
+ export const MAX_SLIDESHOW_IMAGES = 10;
46
+ const MIN_HOLD_MS = 1_000;
47
+ /** How long a page has to render before the caller is told it did not. */
48
+ export const PAGE_RENDER_TIMEOUT_MS = 45_000;
49
+ /**
50
+ * How long a rendered page stays on the tile. Longer than a chart: a page is
51
+ * read rather than glanced at.
52
+ */
53
+ export const PAGE_DISPLAY_MS = 15_000;
54
+ /** A URL makes a poor caption at any length, and a long one makes a worse one. */
55
+ const MAX_PAGE_CAPTION_CHARS = 80;
56
+ const MAX_HOLD_MS = 30_000;
57
+ /**
58
+ * A filename safe to show somebody: a name, a dot, a short extension, and no
59
+ * separators of any kind.
60
+ */
61
+ const SAFE_NAME = /^[\w.-]{1,80}\.[A-Za-z0-9]{2,5}$/;
62
+ /**
63
+ * The display mode this value means, or the default.
64
+ *
65
+ * One rule, shared by both SDKs and every plugin, because the value comes from
66
+ * a model: `"pip"`, `"inset"`, `"full"` and nothing at all are all things a
67
+ * model will say, and none of them is a mode.
68
+ */
69
+ export function normalizeDisplayMode(value, fallback) {
70
+ if (typeof value === "string") {
71
+ const cleaned = value.trim().toLowerCase();
72
+ if (DISPLAY_MODES.includes(cleaned))
73
+ return cleaned;
74
+ }
75
+ return fallback;
76
+ }
77
+ /**
78
+ * A filename to show beside a picture.
79
+ *
80
+ * Taken from the source when it looks like a filename and nothing else. The
81
+ * string came from a model steered by whoever is on the call, and it is about
82
+ * to be shown to them.
83
+ */
84
+ export function displayImageName(pathOrUrl, mime) {
85
+ const raw = String(pathOrUrl ?? "")
86
+ .split("?")[0]
87
+ .split("#")[0];
88
+ const candidate = raw.replace(/\\/g, "/").split("/").pop().trim();
89
+ if (SAFE_NAME.test(candidate))
90
+ return candidate;
91
+ const subtype = (mime || "").split("/").pop() || "bin";
92
+ return `image.${subtype === "jpeg" ? "jpg" : subtype}`;
93
+ }
94
+ /** The last picture the caller actually saw. */
95
+ export class ShownImage {
96
+ image;
97
+ mime;
98
+ name;
99
+ atMs;
100
+ constructor(image, mime, name, atMs) {
101
+ this.image = image;
102
+ this.mime = mime;
103
+ this.name = name;
104
+ this.atMs = atMs;
105
+ }
106
+ asBase64() {
107
+ return typeof this.image === "string"
108
+ ? this.image
109
+ : this.image.toString("base64");
110
+ }
111
+ }
112
+ /** Told apart from a renderer's own failure, so the sentence can name the cause. */
113
+ const TIMED_OUT = Symbol("standin.pageRenderTimeout");
114
+ async function withTimeout(work, ms) {
115
+ let timer;
116
+ try {
117
+ return await Promise.race([
118
+ work,
119
+ new Promise((_, reject) => {
120
+ timer = setTimeout(() => reject(TIMED_OUT), ms);
121
+ }),
122
+ ]);
123
+ }
124
+ finally {
125
+ // The renderer keeps running; nothing here can cancel a host browser. The
126
+ // timer is what must not keep the process alive after the call has moved on.
127
+ if (timer !== undefined)
128
+ clearTimeout(timer);
129
+ }
130
+ }
131
+ /**
132
+ * A ceiling on how often one call may spend on vision.
133
+ *
134
+ * A model that can look can look in a loop, and each look is a paid inference
135
+ * over somebody's screen. A sliding window rather than a total, so a long call
136
+ * is not punished for having been long.
137
+ *
138
+ * Spending returns a token and refunding takes that token back. Two tool calls
139
+ * can overlap, and a refund that simply dropped "the most recent charge" would
140
+ * refund the wrong one and let the budget drift upward under exactly the load it
141
+ * exists to bound.
142
+ */
143
+ export class VisionBudget {
144
+ maxPerMinute;
145
+ #spent = new Map();
146
+ constructor(maxPerMinute = 6) {
147
+ this.maxPerMinute = Math.max(0, maxPerMinute);
148
+ }
149
+ /** Take one look's worth of budget, or undefined when there is none left. */
150
+ tryConsume() {
151
+ if (this.maxPerMinute === 0)
152
+ return randomUUID();
153
+ const now = Date.now();
154
+ for (const [token, at] of this.#spent) {
155
+ if (at <= now - 60_000)
156
+ this.#spent.delete(token);
157
+ }
158
+ if (this.#spent.size >= this.maxPerMinute)
159
+ return undefined;
160
+ const token = randomUUID();
161
+ this.#spent.set(token, now);
162
+ return token;
163
+ }
164
+ /**
165
+ * How much of the window only an explicit look may spend.
166
+ *
167
+ * Ambient vision spends on every scene change, which is exactly the load that
168
+ * would leave a caller's own "look at this" with nothing left. The reserve is
169
+ * what the ambient lane cannot touch.
170
+ */
171
+ get reserve() {
172
+ if (!this.maxPerMinute)
173
+ return 0;
174
+ return Math.max(2, Math.floor(this.maxPerMinute / 4));
175
+ }
176
+ /**
177
+ * Take one look's worth, from the ambient lane only.
178
+ *
179
+ * Refused once the window is down to the reserve. Refunded through the same
180
+ * {@link refund}, with the same token, so a failed ambient push and a failed
181
+ * explicit look are given back the same way.
182
+ */
183
+ tryConsumeAmbient() {
184
+ if (!this.maxPerMinute)
185
+ return this.tryConsume();
186
+ if (this.spent >= this.maxPerMinute - this.reserve)
187
+ return undefined;
188
+ return this.tryConsume();
189
+ }
190
+ /** Give back a charge whose look never happened. Idempotent. */
191
+ refund(token) {
192
+ this.#spent.delete(token);
193
+ }
194
+ /** Looks charged in the current window. */
195
+ get spent() {
196
+ const cutoff = Date.now() - 60_000;
197
+ return [...this.#spent.values()].filter((at) => at > cutoff).length;
198
+ }
199
+ }
200
+ /**
201
+ * A short history of what the caller showed.
202
+ *
203
+ * The call session keeps the LATEST frame per source, which answers "what am I
204
+ * looking at now". This answers "what was on that slide a moment ago".
205
+ *
206
+ * Bounded, and **gated on the call being recorded**. Keeping a history of
207
+ * somebody's screen is a materially different promise from glancing at it once,
208
+ * and the recording is the thing that told them their call is being kept.
209
+ */
210
+ export class KeyframeStore {
211
+ #capacity;
212
+ #frames = [];
213
+ #last = new Map();
214
+ constructor(capacity = 16) {
215
+ this.#capacity = Math.max(1, capacity);
216
+ }
217
+ /** Keep this frame, if the call is being recorded. Returns whether it was kept. */
218
+ offer(frame, recording) {
219
+ if (!recording)
220
+ return false;
221
+ // Per source, so an alternating camera and screen share each keep their own
222
+ // history. A screen nobody touched would otherwise fill the whole store
223
+ // with one picture.
224
+ const digest = frameDigest(frame.dataBase64);
225
+ if (this.#last.get(frame.source) === digest)
226
+ return false;
227
+ this.#last.set(frame.source, digest);
228
+ this.#frames.push(frame);
229
+ if (this.#frames.length > this.#capacity) {
230
+ this.#frames = this.#frames.slice(-this.#capacity);
231
+ }
232
+ return true;
233
+ }
234
+ /** Frames kept so far, oldest first. */
235
+ recent(source) {
236
+ return source === undefined
237
+ ? [...this.#frames]
238
+ : this.#frames.filter((f) => f.source === source);
239
+ }
240
+ /** Forget everything. Called on teardown. */
241
+ clear() {
242
+ this.#frames = [];
243
+ this.#last.clear();
244
+ }
245
+ get size() {
246
+ return this.#frames.length;
247
+ }
248
+ }
249
+ /**
250
+ * The capabilities, bound to one call.
251
+ *
252
+ * Every method returns a sentence for a model to read out, including when it
253
+ * failed. None of them throw.
254
+ */
255
+ export class VisionTools {
256
+ #session;
257
+ #describer;
258
+ budget;
259
+ keyframes;
260
+ #defaultDisplay;
261
+ #lastShown;
262
+ #slideshow;
263
+ #wake;
264
+ #generation = 0;
265
+ constructor(session, options = {}) {
266
+ this.#session = session;
267
+ this.#describer = options.describer;
268
+ this.budget = options.budget ?? new VisionBudget();
269
+ this.keyframes = options.keyframes ?? new KeyframeStore();
270
+ this.#defaultDisplay = normalizeDisplayMode(options.defaultDisplayMode);
271
+ }
272
+ /**
273
+ * The picture the caller can see, if any.
274
+ *
275
+ * Recorded only after a send actually returned, so "send me that" attaches
276
+ * what they saw rather than what was attempted. One slot, replaced each time:
277
+ * a list would be a growing copy of everything shown on the call.
278
+ */
279
+ get lastShown() {
280
+ return this.#lastShown;
281
+ }
282
+ /** The slideshow now running, if any. Await it to let one finish. */
283
+ get slideshow() {
284
+ return this.#slideshow;
285
+ }
286
+ /** Forget what was shown and stop any slideshow. Call this on teardown. */
287
+ async reset() {
288
+ await this.#stopSlideshow();
289
+ this.#lastShown = undefined;
290
+ this.keyframes.clear();
291
+ }
292
+ /**
293
+ * Answer a question about what the caller is showing.
294
+ *
295
+ * Uses the newest frame, preferring the screen share, because an agent asked
296
+ * to look is nearly always being asked about what is being shown rather than
297
+ * who is showing it.
298
+ */
299
+ async look(question = "", source) {
300
+ if (this.#describer === undefined) {
301
+ return ("looking is not available on this deployment: no vision model is configured " +
302
+ "(set STANDIN_VISION_API_URL and STANDIN_VISION_MODEL)");
303
+ }
304
+ const wanted = source === "camera" || source === "screenshare"
305
+ ? source
306
+ : undefined;
307
+ const frame = this.#session.latestVideoFrame(wanted);
308
+ if (frame === undefined) {
309
+ return "there is nothing to look at: the caller is not sharing their camera or screen";
310
+ }
311
+ const token = this.budget.tryConsume();
312
+ if (token === undefined) {
313
+ return ("this call has reached its limit on looking at the screen; " +
314
+ "ask the caller to describe what they are showing");
315
+ }
316
+ try {
317
+ return await this.#describer.describe(frame, question.trim() || "Describe what is visible.");
318
+ }
319
+ catch (err) {
320
+ // The charge is given back, or a flaky vision endpoint silently burns the
321
+ // budget the caller paid nothing for.
322
+ this.budget.refund(token);
323
+ return `could not look at the screen: ${String(err)}`;
324
+ }
325
+ }
326
+ /**
327
+ * Answer about a frame the caller has already moved past.
328
+ *
329
+ * Only possible when the call is being recorded, because that is the only time
330
+ * frames are kept at all.
331
+ */
332
+ async lookBack(question = "") {
333
+ const frames = this.keyframes.recent();
334
+ if (frames.length === 0) {
335
+ if (!this.#session.recordingActive) {
336
+ return "I can only look back at earlier screens while the call is being recorded, and it is not";
337
+ }
338
+ return "nothing has been shown on this call yet";
339
+ }
340
+ if (this.#describer === undefined) {
341
+ return "looking is not available on this deployment: no vision model is configured";
342
+ }
343
+ const token = this.budget.tryConsume();
344
+ if (token === undefined)
345
+ return "this call has reached its limit on looking at the screen";
346
+ try {
347
+ return await this.#describer.describe(frames[frames.length - 1], question.trim() || "Describe what was visible.");
348
+ }
349
+ catch (err) {
350
+ this.budget.refund(token);
351
+ return `could not look back: ${String(err)}`;
352
+ }
353
+ }
354
+ /** Put an image on the bot's video tile. */
355
+ async show(image, mime = "image/jpeg", caption, durationMs, display, name) {
356
+ if (!DISPLAY_IMAGE_MIME_TYPES.includes(mime)) {
357
+ return `that image is ${mime}; it must be one of ${DISPLAY_IMAGE_MIME_TYPES.join(", ")}`;
358
+ }
359
+ try {
360
+ await this.#session.displayImage(image, {
361
+ mime,
362
+ durationMs,
363
+ // The model's choice of fullscreen or overlay, and anything else falls
364
+ // back to the configured default. When neither is set the field is
365
+ // OMITTED rather than defaulted here, so the service decides.
366
+ mode: normalizeDisplayMode(display, this.#defaultDisplay),
367
+ caption: caption ? caption.slice(0, MAX_CAPTION_CHARS) : undefined,
368
+ });
369
+ }
370
+ catch (err) {
371
+ // The wire has a hard ceiling. A model must be told it in words, not by an
372
+ // exception it cannot see.
373
+ return `could not show that: ${String(err)}`;
374
+ }
375
+ // Only after it actually went. "Now send me that" must attach what the
376
+ // caller saw, not what was attempted.
377
+ this.#lastShown = new ShownImage(image, mime, name ?? displayImageName("", mime), Date.now());
378
+ return "the caller can see it";
379
+ }
380
+ /**
381
+ * Fetch an image the model chose, and show it.
382
+ *
383
+ * The URL comes from a model steered by whoever is on the call, so it goes
384
+ * through the SDK's guard: public hosts only, and the address re-checked at
385
+ * connect time.
386
+ */
387
+ async showUrl(url, caption, display) {
388
+ if (!url.trim())
389
+ return "that needs a public https URL of a jpeg or png";
390
+ let fetched;
391
+ try {
392
+ fetched = await fetchPublicImage(url, MAX_IMAGE_BYTES, FETCH_TIMEOUT_MS);
393
+ }
394
+ catch (err) {
395
+ return `could not fetch that image: ${String(err)}`;
396
+ }
397
+ return await this.show(fetched.bytes, fetched.mime, caption, undefined, display, displayImageName(url, fetched.mime));
398
+ }
399
+ /**
400
+ * Put a web page on the tile, as a picture of it.
401
+ *
402
+ * The core has no browser and must never gain one. `render` is supplied by a
403
+ * plugin whose host already runs one, and it returns bytes rather than a
404
+ * path: reading a file chosen downstream of whoever is on the call is not a
405
+ * primitive this belongs in.
406
+ *
407
+ * The guard runs HERE, before the renderer is reached, and it runs even when
408
+ * that renderer is a browser advertising private-network protection of its
409
+ * own. Such a browser assumes whoever wrote the URL already has a shell on
410
+ * the machine. Here the URL was written by a model being steered by a
411
+ * stranger, which is exactly the case that relaxation lets through.
412
+ */
413
+ async showPage(url, caption, render, timeoutMs = PAGE_RENDER_TIMEOUT_MS) {
414
+ if (!url.trim())
415
+ return "that needs a public https URL of a page";
416
+ if (render === undefined)
417
+ return "showing web pages is not available on this deployment";
418
+ try {
419
+ await assertPublicHttpUrl(url);
420
+ }
421
+ catch (err) {
422
+ return `could not open that page: ${String(err)}`;
423
+ }
424
+ let rendered;
425
+ try {
426
+ rendered = await withTimeout(render(url), timeoutMs);
427
+ }
428
+ catch (err) {
429
+ if (err === TIMED_OUT) {
430
+ // Never "within 0 seconds", and never "1 seconds": this sentence is
431
+ // read out loud to the person waiting for the page.
432
+ const seconds = Math.max(1, Math.round(timeoutMs / 1000));
433
+ return `that page did not finish loading within ${seconds} ${seconds === 1 ? "second" : "seconds"}`;
434
+ }
435
+ return `could not open that page: ${String(err)}`;
436
+ }
437
+ return await this.show(rendered.bytes, rendered.mime, caption ?? url.trim().slice(0, MAX_PAGE_CAPTION_CHARS), PAGE_DISPLAY_MS, undefined, displayImageName(url, rendered.mime));
438
+ }
439
+ /**
440
+ * Show several pictures in turn, without waiting for all of them.
441
+ *
442
+ * The FIRST one goes before this resolves, so the model can say "here it is"
443
+ * and be right. The rest are paced from a detached chain: a model that waits
444
+ * out a ten-picture slideshow before speaking leaves the caller in silence for
445
+ * most of a minute.
446
+ *
447
+ * Never throws. The sentence says what is on screen now and what follows.
448
+ */
449
+ async showMany(items, caption, display, holdMs = SLIDESHOW_HOLD_MS) {
450
+ if (items.length === 0)
451
+ return "there was nothing to show";
452
+ await this.#stopSlideshow();
453
+ const hold = Math.max(MIN_HOLD_MS, Math.min(MAX_HOLD_MS, holdMs));
454
+ const shown = items.slice(0, MAX_SLIDESHOW_IMAGES);
455
+ const dropped = items.length - shown.length;
456
+ const mode = normalizeDisplayMode(display, this.#defaultDisplay);
457
+ const first = await this.#showItem(shown[0], caption, mode, shown.length > 1, hold);
458
+ if (first.startsWith("could not"))
459
+ return first;
460
+ if (shown.length > 1) {
461
+ const generation = ++this.#generation;
462
+ this.#slideshow = this.#pace(shown.slice(1), mode, hold, generation);
463
+ }
464
+ const of = shown.length > 1 ? `the first of ${shown.length}` : "it";
465
+ const rest = shown.length > 1
466
+ ? `; the rest follow every ${Math.floor(hold / 1000)} seconds`
467
+ : "";
468
+ const extra = dropped > 0
469
+ ? ` (showing the first ${shown.length} of ${items.length})`
470
+ : "";
471
+ return `the caller can see ${of}${rest}${extra}`;
472
+ }
473
+ /** One frame of a slideshow, loaded late and sent. */
474
+ async #showItem(item, caption, mode, more, hold) {
475
+ let image = item.image;
476
+ let mime = item.mime ?? "image/jpeg";
477
+ if (typeof image === "string" && /^https?:\/\//i.test(image.trim())) {
478
+ const fetched = await fetchPublicImage(image, MAX_IMAGE_BYTES, FETCH_TIMEOUT_MS);
479
+ image = fetched.bytes;
480
+ mime = fetched.mime;
481
+ }
482
+ return await this.show(image, mime, caption,
483
+ // Held a little past the pacing gap, so the tile never blanks between
484
+ // pictures. The LAST frame omits it, so the service's own default applies
485
+ // to what stays on screen.
486
+ more ? hold + SLIDESHOW_OVERLAP_MS : undefined, mode, item.name);
487
+ }
488
+ /**
489
+ * Send the remaining frames, one hold apart.
490
+ *
491
+ * Each is loaded inside this loop rather than up front, so a slideshow of ten
492
+ * URLs does not fetch all ten before the first appears.
493
+ */
494
+ async #pace(rest, mode, hold, generation) {
495
+ for (const [index, item] of rest.entries()) {
496
+ await this.#hold(hold);
497
+ if (generation !== this.#generation)
498
+ return; // a newer slideshow, or reset()
499
+ try {
500
+ const result = await this.#showItem(item, undefined, mode, index < rest.length - 1, hold);
501
+ if (result.startsWith("could not")) {
502
+ // One bad picture skips that picture, never the rest.
503
+ logger.debug(`standin: a slideshow frame was skipped: ${result}`);
504
+ }
505
+ }
506
+ catch (err) {
507
+ logger.debug(`standin: a slideshow frame was skipped: ${String(err)}`);
508
+ }
509
+ }
510
+ }
511
+ /**
512
+ * The gap between two pictures, woken early when the slideshow is replaced.
513
+ *
514
+ * Without the wake, replacing a slideshow would wait out the old one's gap -
515
+ * up to thirty seconds - before the new first picture went up.
516
+ */
517
+ async #hold(ms) {
518
+ await new Promise((resolve) => {
519
+ const timer = setTimeout(() => {
520
+ this.#wake = undefined;
521
+ resolve();
522
+ }, ms);
523
+ this.#wake = () => {
524
+ clearTimeout(timer);
525
+ this.#wake = undefined;
526
+ resolve();
527
+ };
528
+ });
529
+ }
530
+ async #stopSlideshow() {
531
+ this.#generation += 1;
532
+ const running = this.#slideshow;
533
+ this.#slideshow = undefined;
534
+ // Woken rather than cancelled: the bumped generation is what stops it, and
535
+ // a frame already being sent finishes being sent. Killing one mid-write
536
+ // would leave half a picture on the wire.
537
+ this.#wake?.();
538
+ if (running !== undefined)
539
+ await running.catch(() => undefined);
540
+ }
541
+ /**
542
+ * Say and show several things in order, pausing for each.
543
+ *
544
+ * The pacing is here; the SPEAKING is not. Only the provider knows when a line
545
+ * has finished being said, so `speak` is supplied by the plugin and awaited
546
+ * before the next beat begins. Without that, a walkthrough talks over itself.
547
+ *
548
+ * `interrupted` is checked between beats. A caller who cuts in should stop the
549
+ * tour, and the plugin is the only thing that knows they did.
550
+ */
551
+ async walkthrough(steps, speak, interrupted, display) {
552
+ if (steps.length === 0)
553
+ return "there was nothing to walk through";
554
+ // One tile. A walkthrough and a slideshow running at once would fight over
555
+ // it, and the caller would see neither properly.
556
+ await this.#stopSlideshow();
557
+ let shown = 0;
558
+ for (const [index, step] of steps.entries()) {
559
+ if (interrupted?.()) {
560
+ return `stopped after ${shown} of ${steps.length}: the caller interrupted`;
561
+ }
562
+ try {
563
+ await speak(step.say);
564
+ }
565
+ catch (err) {
566
+ return `stopped at step ${index + 1}: ${String(err)}`;
567
+ }
568
+ if (step.image !== undefined) {
569
+ const result = await this.show(step.image, step.mime ?? "image/jpeg", step.caption, undefined, display);
570
+ if (result.startsWith("could not"))
571
+ return `stopped at step ${index + 1}: ${result}`;
572
+ }
573
+ shown = index + 1;
574
+ }
575
+ return `walked through all ${shown} steps`;
576
+ }
577
+ }
578
+ //# sourceMappingURL=visionTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visionTools.js","sourceRoot":"","sources":["../src/visionTools.ts"],"names":[],"mappings":"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"}