@elizaos/plugin-facewear 2.0.3-beta.5 → 2.0.3-beta.7

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 (191) hide show
  1. package/dist/actions/display-text.d.ts +4 -0
  2. package/dist/actions/display-text.d.ts.map +1 -0
  3. package/dist/actions/display-text.js +90 -0
  4. package/dist/actions/display-text.js.map +1 -0
  5. package/dist/actions/facewear-connect.d.ts +3 -0
  6. package/dist/actions/facewear-connect.d.ts.map +1 -0
  7. package/dist/actions/facewear-connect.js +70 -0
  8. package/dist/actions/facewear-connect.js.map +1 -0
  9. package/dist/actions/facewear-control.d.ts +4 -0
  10. package/dist/actions/facewear-control.d.ts.map +1 -0
  11. package/dist/actions/facewear-control.js +627 -0
  12. package/dist/actions/facewear-control.js.map +1 -0
  13. package/dist/actions/facewear-debug.d.ts +3 -0
  14. package/dist/actions/facewear-debug.d.ts.map +1 -0
  15. package/dist/actions/facewear-debug.js +62 -0
  16. package/dist/actions/facewear-debug.js.map +1 -0
  17. package/dist/actions/facewear-status.d.ts +4 -0
  18. package/dist/actions/facewear-status.d.ts.map +1 -0
  19. package/dist/actions/facewear-status.js +66 -0
  20. package/dist/actions/facewear-status.js.map +1 -0
  21. package/dist/actions/microphone.d.ts +4 -0
  22. package/dist/actions/microphone.d.ts.map +1 -0
  23. package/dist/actions/microphone.js +63 -0
  24. package/dist/actions/microphone.js.map +1 -0
  25. package/dist/actions/view-actions.d.ts +23 -0
  26. package/dist/actions/view-actions.d.ts.map +1 -0
  27. package/dist/actions/view-actions.js +314 -0
  28. package/dist/actions/view-actions.js.map +1 -0
  29. package/dist/actions/vision-query.d.ts +4 -0
  30. package/dist/actions/vision-query.d.ts.map +1 -0
  31. package/dist/actions/vision-query.js +41 -0
  32. package/dist/actions/vision-query.js.map +1 -0
  33. package/dist/actions/xr-view-actions.d.ts +14 -0
  34. package/dist/actions/xr-view-actions.d.ts.map +1 -0
  35. package/dist/actions/xr-view-actions.js +29 -0
  36. package/dist/actions/xr-view-actions.js.map +1 -0
  37. package/dist/components/FacewearSpatialView.d.ts +50 -0
  38. package/dist/components/FacewearSpatialView.d.ts.map +1 -0
  39. package/dist/components/FacewearSpatialView.js +129 -0
  40. package/dist/components/FacewearSpatialView.js.map +1 -0
  41. package/dist/components/FacewearView.d.ts +17 -0
  42. package/dist/components/FacewearView.d.ts.map +1 -0
  43. package/dist/components/FacewearView.js +88 -0
  44. package/dist/components/FacewearView.js.map +1 -0
  45. package/dist/components/SmartglassesPanelView.d.ts +22 -0
  46. package/dist/components/SmartglassesPanelView.d.ts.map +1 -0
  47. package/dist/components/SmartglassesPanelView.js +140 -0
  48. package/dist/components/SmartglassesPanelView.js.map +1 -0
  49. package/dist/components/SmartglassesSpatialView.d.ts +46 -0
  50. package/dist/components/SmartglassesSpatialView.d.ts.map +1 -0
  51. package/dist/components/SmartglassesSpatialView.js +240 -0
  52. package/dist/components/SmartglassesSpatialView.js.map +1 -0
  53. package/dist/components/facewear-profiles.d.ts +27 -0
  54. package/dist/components/facewear-profiles.d.ts.map +1 -0
  55. package/dist/components/facewear-profiles.js +40 -0
  56. package/dist/components/facewear-profiles.js.map +1 -0
  57. package/dist/devices/apple-vision-pro.d.ts +7 -0
  58. package/dist/devices/apple-vision-pro.d.ts.map +1 -0
  59. package/dist/devices/apple-vision-pro.js +21 -0
  60. package/dist/devices/apple-vision-pro.js.map +1 -0
  61. package/dist/devices/even-realities.d.ts +7 -0
  62. package/dist/devices/even-realities.d.ts.map +1 -0
  63. package/dist/devices/even-realities.js +13 -0
  64. package/dist/devices/even-realities.js.map +1 -0
  65. package/dist/devices/meta-quest.d.ts +5 -0
  66. package/dist/devices/meta-quest.d.ts.map +1 -0
  67. package/dist/devices/meta-quest.js +21 -0
  68. package/dist/devices/meta-quest.js.map +1 -0
  69. package/dist/devices/registry.d.ts +19 -0
  70. package/dist/devices/registry.d.ts.map +1 -0
  71. package/dist/devices/registry.js +96 -0
  72. package/dist/devices/registry.js.map +1 -0
  73. package/dist/devices/xreal.d.ts +7 -0
  74. package/dist/devices/xreal.d.ts.map +1 -0
  75. package/dist/devices/xreal.js +19 -0
  76. package/dist/devices/xreal.js.map +1 -0
  77. package/dist/index.d.ts +28 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +260 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/protocol/smartglasses.d.ts +306 -0
  82. package/dist/protocol/smartglasses.d.ts.map +1 -0
  83. package/dist/protocol/smartglasses.js +1485 -0
  84. package/dist/protocol/smartglasses.js.map +1 -0
  85. package/dist/protocol/xr.d.ts +137 -0
  86. package/dist/protocol/xr.d.ts.map +1 -0
  87. package/dist/protocol/xr.js +18 -0
  88. package/dist/protocol/xr.js.map +1 -0
  89. package/dist/providers/facewear-context.d.ts +3 -0
  90. package/dist/providers/facewear-context.d.ts.map +1 -0
  91. package/dist/providers/facewear-context.js +59 -0
  92. package/dist/providers/facewear-context.js.map +1 -0
  93. package/dist/providers/smartglasses-status.d.ts +3 -0
  94. package/dist/providers/smartglasses-status.d.ts.map +1 -0
  95. package/dist/providers/smartglasses-status.js +33 -0
  96. package/dist/providers/smartglasses-status.js.map +1 -0
  97. package/dist/register-terminal-view.d.ts +21 -0
  98. package/dist/register-terminal-view.d.ts.map +1 -0
  99. package/dist/register-terminal-view.js +70 -0
  100. package/dist/register-terminal-view.js.map +1 -0
  101. package/dist/register.d.ts +8 -0
  102. package/dist/register.d.ts.map +1 -0
  103. package/dist/register.js +116 -0
  104. package/dist/register.js.map +1 -0
  105. package/dist/routes/connect.d.ts +3 -0
  106. package/dist/routes/connect.d.ts.map +1 -0
  107. package/dist/routes/connect.js +86 -0
  108. package/dist/routes/connect.js.map +1 -0
  109. package/dist/routes/device-config.d.ts +5 -0
  110. package/dist/routes/device-config.d.ts.map +1 -0
  111. package/dist/routes/device-config.js +56 -0
  112. package/dist/routes/device-config.js.map +1 -0
  113. package/dist/routes/simulator-route.d.ts +8 -0
  114. package/dist/routes/simulator-route.d.ts.map +1 -0
  115. package/dist/routes/simulator-route.js +32 -0
  116. package/dist/routes/simulator-route.js.map +1 -0
  117. package/dist/routes/status.d.ts +3 -0
  118. package/dist/routes/status.d.ts.map +1 -0
  119. package/dist/routes/status.js +34 -0
  120. package/dist/routes/status.js.map +1 -0
  121. package/dist/routes/view-host.d.ts +24 -0
  122. package/dist/routes/view-host.d.ts.map +1 -0
  123. package/dist/routes/view-host.js +339 -0
  124. package/dist/routes/view-host.js.map +1 -0
  125. package/dist/routes/views.d.ts +8 -0
  126. package/dist/routes/views.d.ts.map +1 -0
  127. package/dist/routes/views.js +31 -0
  128. package/dist/routes/views.js.map +1 -0
  129. package/dist/services/audio-pipeline.d.ts +20 -0
  130. package/dist/services/audio-pipeline.d.ts.map +1 -0
  131. package/dist/services/audio-pipeline.js +87 -0
  132. package/dist/services/audio-pipeline.js.map +1 -0
  133. package/dist/services/facewear-service.d.ts +26 -0
  134. package/dist/services/facewear-service.d.ts.map +1 -0
  135. package/dist/services/facewear-service.js +45 -0
  136. package/dist/services/facewear-service.js.map +1 -0
  137. package/dist/services/smartglasses-service.d.ts +244 -0
  138. package/dist/services/smartglasses-service.d.ts.map +1 -0
  139. package/dist/services/smartglasses-service.js +821 -0
  140. package/dist/services/smartglasses-service.js.map +1 -0
  141. package/dist/services/vision-pipeline.d.ts +16 -0
  142. package/dist/services/vision-pipeline.d.ts.map +1 -0
  143. package/dist/services/vision-pipeline.js +39 -0
  144. package/dist/services/vision-pipeline.js.map +1 -0
  145. package/dist/services/xr-session-service.d.ts +54 -0
  146. package/dist/services/xr-session-service.d.ts.map +1 -0
  147. package/dist/services/xr-session-service.js +345 -0
  148. package/dist/services/xr-session-service.js.map +1 -0
  149. package/dist/status-format.d.ts +15 -0
  150. package/dist/status-format.d.ts.map +1 -0
  151. package/dist/status-format.js +89 -0
  152. package/dist/status-format.js.map +1 -0
  153. package/dist/transport/even-bridge.d.ts +69 -0
  154. package/dist/transport/even-bridge.d.ts.map +1 -0
  155. package/dist/transport/even-bridge.js +510 -0
  156. package/dist/transport/even-bridge.js.map +1 -0
  157. package/dist/transport/mock.d.ts +42 -0
  158. package/dist/transport/mock.d.ts.map +1 -0
  159. package/dist/transport/mock.js +124 -0
  160. package/dist/transport/mock.js.map +1 -0
  161. package/dist/transport/noble.d.ts +62 -0
  162. package/dist/transport/noble.d.ts.map +1 -0
  163. package/dist/transport/noble.js +256 -0
  164. package/dist/transport/noble.js.map +1 -0
  165. package/dist/transport/types.d.ts +36 -0
  166. package/dist/transport/types.d.ts.map +1 -0
  167. package/dist/transport/types.js +1 -0
  168. package/dist/transport/types.js.map +1 -0
  169. package/dist/transport/web-bluetooth.d.ts +58 -0
  170. package/dist/transport/web-bluetooth.d.ts.map +1 -0
  171. package/dist/transport/web-bluetooth.js +164 -0
  172. package/dist/transport/web-bluetooth.js.map +1 -0
  173. package/dist/ui/FacewearAppView.d.ts +4 -0
  174. package/dist/ui/FacewearAppView.d.ts.map +1 -0
  175. package/dist/ui/FacewearAppView.js +257 -0
  176. package/dist/ui/FacewearAppView.js.map +1 -0
  177. package/dist/ui/SmartglassesView.d.ts +10 -0
  178. package/dist/ui/SmartglassesView.d.ts.map +1 -0
  179. package/dist/ui/SmartglassesView.helpers.d.ts +104 -0
  180. package/dist/ui/SmartglassesView.helpers.d.ts.map +1 -0
  181. package/dist/ui/SmartglassesView.helpers.js +261 -0
  182. package/dist/ui/SmartglassesView.helpers.js.map +1 -0
  183. package/dist/ui/SmartglassesView.js +1189 -0
  184. package/dist/ui/SmartglassesView.js.map +1 -0
  185. package/dist/ui/facewear-view-bundle.d.ts +5 -0
  186. package/dist/ui/facewear-view-bundle.d.ts.map +1 -0
  187. package/dist/ui/facewear-view-bundle.js +17 -0
  188. package/dist/ui/facewear-view-bundle.js.map +1 -0
  189. package/dist/views/bundle.js +2950 -0
  190. package/dist/views/bundle.js.map +1 -0
  191. package/package.json +5 -5
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/xr-session-service.ts"],"sourcesContent":["import type {\n Content,\n HandlerCallback,\n IAgentRuntime,\n Memory,\n UUID,\n} from \"@elizaos/core\";\nimport {\n ChannelType,\n createMessageMemory,\n ModelType,\n Service,\n} from \"@elizaos/core\";\nimport { type WebSocket, WebSocketServer } from \"ws\";\nimport type {\n G1Event,\n SmartglassesAudioEncoding,\n} from \"../protocol/smartglasses.js\";\nimport {\n decodeBinaryFrame,\n encodeBinaryFrame,\n type XRClientControl,\n type XRDeviceType,\n type XRPanelConfig,\n type XRServerControl,\n type XRTTSAudioHeader,\n} from \"../protocol/xr.js\";\nimport { AudioPipeline } from \"./audio-pipeline.js\";\nimport { getSmartglassesService } from \"./smartglasses-service.js\";\nimport { VisionPipeline } from \"./vision-pipeline.js\";\n\nexport const XR_SERVICE_TYPE = \"xr-session\";\nexport const XR_WS_PORT_DEFAULT = 31338;\nexport const XR_WS_PORT_ENV = \"XR_WS_PORT\";\n\nexport interface XRConnection {\n id: string;\n ws: WebSocket;\n deviceType: XRDeviceType;\n entityId: UUID;\n roomId: UUID;\n connectedAt: Date;\n}\n\nexport class XRSessionService extends Service {\n static override serviceType = XR_SERVICE_TYPE;\n\n readonly capabilityDescription =\n \"Streams audio and camera from XR headsets (Quest 3, XReal) to the agent and returns voice responses.\";\n\n private wss!: WebSocketServer;\n private connections = new Map<string, XRConnection>();\n private audioPipeline!: AudioPipeline;\n private visionPipeline!: VisionPipeline;\n\n static override async start(runtime: IAgentRuntime): Promise<Service> {\n const svc = new XRSessionService(runtime);\n await svc.initialize(runtime);\n return svc;\n }\n\n private async initialize(runtime: IAgentRuntime): Promise<void> {\n this.visionPipeline = new VisionPipeline();\n this.audioPipeline = new AudioPipeline(\n runtime,\n (connectionId, transcript) =>\n this.handleTranscript(connectionId, transcript),\n );\n\n const port = Number(\n runtime.getSetting(XR_WS_PORT_ENV) ?? XR_WS_PORT_DEFAULT,\n );\n this.wss = new WebSocketServer({ port });\n\n this.wss.on(\"connection\", (ws) => this.onConnect(runtime, ws));\n this.wss.on(\"error\", (err) =>\n console.error(\"[plugin-facewear/xr] WebSocket server error:\", err),\n );\n\n console.info(\n `[plugin-facewear/xr] WebSocket server listening on ws://localhost:${port}`,\n );\n }\n\n override async stop(): Promise<void> {\n for (const conn of this.connections.values()) {\n this.audioPipeline.clear(conn.id);\n this.visionPipeline.clear(conn.id);\n conn.ws.close();\n }\n this.connections.clear();\n await new Promise<void>((resolve) => this.wss.close(() => resolve()));\n }\n\n // ── Public accessors used by provider / action ──────────────────────────\n\n getConnections(): XRConnection[] {\n return [...this.connections.values()];\n }\n\n getWebSocketPort(): number | null {\n const address = this.wss.address();\n return typeof address === \"object\" && address ? address.port : null;\n }\n\n hasActiveConnections(): boolean {\n return this.connections.size > 0;\n }\n\n getVisionPipeline(): VisionPipeline {\n return this.visionPipeline;\n }\n\n sendText(connectionId: string, text: string): void {\n const conn = this.connections.get(connectionId);\n if (!conn || conn.ws.readyState !== conn.ws.OPEN) return;\n conn.ws.send(JSON.stringify({ type: \"agent_text\", text }));\n }\n\n sendControl(connectionId: string, msg: XRServerControl): void {\n const conn = this.connections.get(connectionId);\n if (!conn || conn.ws.readyState !== conn.ws.OPEN) return;\n conn.ws.send(JSON.stringify(msg));\n }\n\n broadcastControl(msg: XRServerControl): void {\n for (const conn of this.connections.values()) {\n if (conn.ws.readyState === conn.ws.OPEN) {\n conn.ws.send(JSON.stringify(msg));\n }\n }\n }\n\n openView(\n connectionId: string,\n viewId: string,\n agentBaseUrl: string,\n config?: XRPanelConfig,\n ): void {\n this.sendControl(connectionId, {\n type: \"view_open\",\n viewId,\n agentBaseUrl,\n config,\n });\n }\n\n closeView(connectionId: string, viewId: string): void {\n this.sendControl(connectionId, { type: \"view_close\", viewId });\n }\n\n switchView(connectionId: string, viewId: string): void {\n this.sendControl(connectionId, { type: \"view_switch\", viewId });\n }\n\n resizeView(\n connectionId: string,\n viewId: string,\n config: XRPanelConfig,\n ): void {\n this.sendControl(connectionId, { type: \"view_resize\", viewId, config });\n }\n\n sendViewsCatalog(\n connectionId: string,\n views: Array<{\n id: string;\n label: string;\n icon?: string;\n description?: string;\n }>,\n ): void {\n this.sendControl(connectionId, { type: \"views_catalog\", views });\n }\n\n sendAudio(connectionId: string, audio: Buffer, sampleRate = 24000): void {\n const conn = this.connections.get(connectionId);\n if (!conn || conn.ws.readyState !== conn.ws.OPEN) return;\n const header: XRTTSAudioHeader = {\n type: \"tts_audio\",\n sampleRate,\n channels: 1,\n encoding: \"mp3\",\n };\n conn.ws.send(encodeBinaryFrame(header, audio), { binary: true });\n }\n\n // ── WebSocket connection lifecycle ──────────────────────────────────────\n\n private onConnect(runtime: IAgentRuntime, ws: WebSocket): void {\n const connId = crypto.randomUUID();\n\n ws.on(\"message\", (data, isBinary) => {\n try {\n if (isBinary) {\n this.handleBinaryMessage(connId, data as Buffer);\n } else {\n this.handleTextMessage(runtime, connId, ws, data.toString(\"utf8\"));\n }\n } catch (err) {\n console.error(\"[plugin-facewear/xr] message handler error:\", err);\n }\n });\n\n ws.on(\"close\", () => {\n this.audioPipeline.flush(connId);\n this.audioPipeline.clear(connId);\n this.visionPipeline.clear(connId);\n this.connections.delete(connId);\n console.info(`[plugin-facewear/xr] device disconnected: ${connId}`);\n });\n\n ws.on(\"error\", (err) =>\n console.error(`[plugin-facewear/xr] ws error on ${connId}:`, err),\n );\n }\n\n private handleTextMessage(\n runtime: IAgentRuntime,\n connId: string,\n ws: WebSocket,\n raw: string,\n ): void {\n const msg = JSON.parse(raw) as XRClientControl;\n\n if (msg.type === \"hello\") {\n const entityId = crypto.randomUUID() as UUID;\n const roomId = crypto.randomUUID() as UUID;\n const conn: XRConnection = {\n id: connId,\n ws,\n deviceType: msg.deviceType,\n entityId,\n roomId,\n connectedAt: new Date(),\n };\n this.connections.set(connId, conn);\n ws.send(JSON.stringify({ type: \"ready\", sessionId: connId }));\n void this.ensureEntities(runtime, conn);\n console.info(\n `[plugin-facewear/xr] ${msg.deviceType} connected: ${connId}`,\n );\n return;\n }\n\n if (msg.type === \"ping\") {\n ws.send(JSON.stringify({ type: \"pong\" }));\n return;\n }\n\n if (msg.type === \"g1_raw\") {\n void this.handleSmartglassesRaw(runtime, connId, msg);\n return;\n }\n\n if (msg.type === \"mic_lc3\" || msg.type === \"mic_pcm\") {\n void this.handleSmartglassesAudio(runtime, msg);\n return;\n }\n\n if (msg.type === \"view_ready\") {\n console.info(\n `[plugin-facewear/xr] view ready on ${connId}: ${msg.viewId}`,\n );\n return;\n }\n\n if (msg.type === \"view_closed\") {\n console.info(\n `[plugin-facewear/xr] view closed on ${connId}: ${msg.viewId}`,\n );\n return;\n }\n\n if (msg.type === \"view_event\") {\n console.info(\n `[plugin-facewear/xr] view event on ${connId}:`,\n msg.viewId,\n msg.event,\n );\n return;\n }\n }\n\n private async handleSmartglassesRaw(\n runtime: IAgentRuntime,\n connId: string,\n msg: {\n type: \"g1_raw\";\n side?: \"left\" | \"right\";\n data?: number[];\n base64?: string;\n },\n ): Promise<void> {\n const service = getSmartglassesService(runtime);\n if (!service) return;\n const data = bytesFromMessagePayload(msg.data, msg.base64);\n if (!data) return;\n const side = msg.side ?? \"right\";\n const event = await service.receiveExternalRawEvent(side, data, {\n applyControls: false,\n });\n this.sendSmartglassesControl(connId, event);\n }\n\n private async handleSmartglassesAudio(\n runtime: IAgentRuntime,\n msg: {\n type: \"mic_lc3\" | \"mic_pcm\";\n side?: \"left\" | \"right\";\n sampleRate?: number;\n sequence?: number;\n lc3?: number[];\n pcm?: number[];\n base64?: string;\n },\n ): Promise<void> {\n const service = getSmartglassesService(runtime);\n if (!service) return;\n const data = bytesFromMessagePayload(\n msg.type === \"mic_pcm\" ? msg.pcm : msg.lc3,\n msg.base64,\n );\n if (!data) return;\n await service.receiveExternalAudioChunk(data, {\n sampleRate: msg.sampleRate,\n side: msg.side ?? \"right\",\n encoding: smartglassesEncodingForMessage(msg.type),\n sequence: msg.sequence,\n });\n }\n\n private sendSmartglassesControl(connId: string, event: G1Event): void {\n const conn = this.connections.get(connId);\n if (!conn || conn.ws.readyState !== conn.ws.OPEN) return;\n if (conn.deviceType !== \"even-realities\") return;\n if (event.label === \"single_tap\" || event.label === \"long_press\") {\n conn.ws.send(JSON.stringify({ type: \"mic_control\", enabled: true }));\n }\n if (event.label === \"double_tap\" || event.label === \"stop_ai_recording\") {\n conn.ws.send(JSON.stringify({ type: \"mic_control\", enabled: false }));\n }\n }\n\n private handleBinaryMessage(connId: string, data: Buffer): void {\n const conn = this.connections.get(connId);\n if (!conn) return;\n\n const { header, payload } = decodeBinaryFrame(data);\n\n if (header.type === \"audio\") {\n this.audioPipeline.push(connId, header, payload);\n return;\n }\n\n if (header.type === \"frame\") {\n this.visionPipeline.storeFrame(connId, header, payload);\n return;\n }\n }\n\n // ── Message routing ─────────────────────────────────────────────────────\n\n private async handleTranscript(\n connectionId: string,\n transcript: string,\n ): Promise<void> {\n const conn = this.connections.get(connectionId);\n if (!conn) return;\n const runtime = this.runtime;\n\n // Echo transcript back so the UI can show it\n conn.ws.send(\n JSON.stringify({ type: \"transcript\", text: transcript, final: true }),\n );\n\n // Attach latest camera frame as image attachment if available\n const latestFrame = this.visionPipeline.getLatestFrame(connectionId);\n const attachments: Content[\"attachments\"] = latestFrame\n ? [\n {\n id: crypto.randomUUID(),\n url: `data:image/${latestFrame.header.format};base64,${latestFrame.data.toString(\"base64\")}`,\n title: \"XR camera frame\",\n contentType: \"image\",\n source: \"xr-camera\",\n },\n ]\n : [];\n\n const memory = createMessageMemory({\n entityId: conn.entityId,\n agentId: runtime.agentId,\n roomId: conn.roomId,\n content: {\n text: transcript,\n source: `xr-${conn.deviceType}`,\n attachments,\n },\n });\n\n await runtime.createMemory(memory, \"messages\");\n\n const callback: HandlerCallback = async (\n response: Content,\n ): Promise<Memory[]> => {\n const text = response.text?.trim() ?? \"\";\n if (text.length === 0) return [];\n\n // Send text response immediately\n conn.ws.send(JSON.stringify({ type: \"agent_text\", text }));\n\n // Generate TTS and send audio\n try {\n const audio = await runtime.useModel(ModelType.TEXT_TO_SPEECH, text);\n const audioBuf = Buffer.isBuffer(audio)\n ? audio\n : Buffer.from(audio as ArrayBuffer);\n this.sendAudio(connectionId, audioBuf);\n } catch (err) {\n console.error(\"[plugin-facewear/xr] TTS error:\", err);\n }\n\n // Persist agent response as memory\n const responseMemory = createMessageMemory({\n entityId: runtime.agentId,\n agentId: runtime.agentId,\n roomId: conn.roomId,\n content: { text, source: `xr-agent`, inReplyTo: memory.id },\n });\n await runtime.createMemory(responseMemory, \"messages\");\n return [responseMemory];\n };\n\n if (runtime.messageService) {\n await runtime.messageService.handleMessage(runtime, memory, callback);\n }\n }\n\n // ── Entity / room bootstrap ─────────────────────────────────────────────\n\n private async ensureEntities(\n runtime: IAgentRuntime,\n conn: XRConnection,\n ): Promise<void> {\n try {\n await runtime.createEntity({\n id: conn.entityId,\n agentId: runtime.agentId,\n names: [`XR-${conn.deviceType}`],\n metadata: { source: `xr-${conn.deviceType}` },\n });\n await runtime.createRoom({\n id: conn.roomId,\n name: `XR session (${conn.deviceType} ${conn.id.slice(0, 8)})`,\n source: \"xr\",\n type: ChannelType.GROUP,\n channelId: undefined,\n messageServerId: undefined,\n });\n await runtime.addParticipant(conn.entityId, conn.roomId);\n await runtime.addParticipant(runtime.agentId, conn.roomId);\n } catch (err) {\n console.error(\"[plugin-facewear/xr] entity setup error:\", err);\n }\n }\n}\n\nfunction bytesFromMessagePayload(\n bytes?: number[],\n base64?: string,\n): Uint8Array | null {\n if (Array.isArray(bytes) && bytes.every((value) => Number.isInteger(value))) {\n return Uint8Array.from(bytes.map((value) => value & 0xff));\n }\n if (typeof base64 === \"string\" && base64.trim()) {\n return Uint8Array.from(Buffer.from(base64, \"base64\"));\n }\n return null;\n}\n\nfunction smartglassesEncodingForMessage(\n type: \"mic_lc3\" | \"mic_pcm\",\n): SmartglassesAudioEncoding {\n return type === \"mic_pcm\" ? \"pcm16\" : \"lc3\";\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ import type { SmartglassesStatus } from "./services/smartglasses-service.js";
2
+ import type { SmartglassesConnectedLenses } from "./transport/types.js";
3
+ export type SmartglassesPhysicalBlocker = "disconnected" | "headset_not_found" | "partial_headset" | "in_charging_base" | "wearing_state_missing" | null;
4
+ export interface SmartglassesSetupSummary {
5
+ setupHint: string | null;
6
+ wholeHeadsetConnected: boolean;
7
+ wearingReady: boolean;
8
+ physicalBlocker: SmartglassesPhysicalBlocker;
9
+ }
10
+ export declare function formatConnectedLensesForAction(lenses: SmartglassesConnectedLenses): string;
11
+ export declare function formatConnectedLensesForProvider(lenses: SmartglassesConnectedLenses): string;
12
+ export declare function setupSummaryForStatus(status: Pick<SmartglassesStatus, "connected" | "connectedLenses" | "physicalState" | "batteryState">): SmartglassesSetupSummary;
13
+ export declare function setupHintForStatus(status: Pick<SmartglassesStatus, "connected" | "connectedLenses" | "physicalState" | "batteryState">): string | null;
14
+ export declare function isCradleOrChargingState(physicalState: string | null, batteryState: string | null): boolean;
15
+ //# sourceMappingURL=status-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-format.d.ts","sourceRoot":"","sources":["../src/status-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,MAAM,2BAA2B,GACnC,cAAc,GACd,mBAAmB,GACnB,iBAAiB,GACjB,kBAAkB,GAClB,uBAAuB,GACvB,IAAI,CAAC;AAET,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,2BAA2B,CAAC;CAC9C;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,2BAA2B,GAClC,MAAM,CAIR;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,2BAA2B,GAClC,MAAM,CAIR;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,IAAI,CACV,kBAAkB,EAClB,WAAW,GAAG,iBAAiB,GAAG,eAAe,GAAG,cAAc,CACnE,GACA,wBAAwB,CAgE1B;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,IAAI,CACV,kBAAkB,EAClB,WAAW,GAAG,iBAAiB,GAAG,eAAe,GAAG,cAAc,CACnE,GACA,MAAM,GAAG,IAAI,CAEf;AAED,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,OAAO,CAST"}
@@ -0,0 +1,89 @@
1
+ function formatConnectedLensesForAction(lenses) {
2
+ return `left=${formatLensForAction(lenses.left)}, right=${formatLensForAction(
3
+ lenses.right
4
+ )}`;
5
+ }
6
+ function formatConnectedLensesForProvider(lenses) {
7
+ return `left:${formatLensForProvider(lenses.left)} right:${formatLensForProvider(
8
+ lenses.right
9
+ )}`;
10
+ }
11
+ function setupSummaryForStatus(status) {
12
+ const wholeHeadsetConnected = Boolean(
13
+ status.connected && status.connectedLenses.left?.connected && status.connectedLenses.right?.connected
14
+ );
15
+ const anyLensConnected = Boolean(
16
+ status.connectedLenses.left?.connected || status.connectedLenses.right?.connected
17
+ );
18
+ const wearingReady = wholeHeadsetConnected && status.physicalState === "wearing";
19
+ if (!status.connected) {
20
+ return {
21
+ setupHint: "Connect the whole headset before display, tap, or microphone validation.",
22
+ wholeHeadsetConnected,
23
+ wearingReady,
24
+ physicalBlocker: "disconnected"
25
+ };
26
+ }
27
+ if (!anyLensConnected) {
28
+ return {
29
+ setupHint: "No G1 lenses were found. Remove both lenses from the charging base, keep them near this device, and rerun headset pairing.",
30
+ wholeHeadsetConnected,
31
+ wearingReady,
32
+ physicalBlocker: "headset_not_found"
33
+ };
34
+ }
35
+ if (!wholeHeadsetConnected) {
36
+ return {
37
+ setupHint: "Connect both left and right lenses before display, tap, or microphone validation.",
38
+ wholeHeadsetConnected,
39
+ wearingReady,
40
+ physicalBlocker: "partial_headset"
41
+ };
42
+ }
43
+ if (wearingReady) {
44
+ return {
45
+ setupHint: null,
46
+ wholeHeadsetConnected,
47
+ wearingReady,
48
+ physicalBlocker: null
49
+ };
50
+ }
51
+ const stateText = [status.physicalState, status.batteryState].filter(Boolean).join(" / ") || "no wearing state observed";
52
+ if (isCradleOrChargingState(status.physicalState, status.batteryState)) {
53
+ return {
54
+ setupHint: `Glasses are reporting ${stateText}; remove them from the charging base and wear them before tap or microphone validation.`,
55
+ wholeHeadsetConnected,
56
+ wearingReady,
57
+ physicalBlocker: "in_charging_base"
58
+ };
59
+ }
60
+ return {
61
+ setupHint: `Tap and microphone validation requires the glasses to report wearing; current state is ${stateText}.`,
62
+ wholeHeadsetConnected,
63
+ wearingReady,
64
+ physicalBlocker: "wearing_state_missing"
65
+ };
66
+ }
67
+ function setupHintForStatus(status) {
68
+ return setupSummaryForStatus(status).setupHint;
69
+ }
70
+ function isCradleOrChargingState(physicalState, batteryState) {
71
+ return physicalState === "cradle_open" || physicalState === "cradle_closed" || physicalState === "charged_in_cradle" || batteryState === "glasses_fully_charged" || batteryState === "cradle_charging_cable_changed" || batteryState === "cradle_fully_charged";
72
+ }
73
+ function formatLensForAction(lens) {
74
+ if (!lens) return "missing";
75
+ const state = lens.connected ? "connected" : "disconnected";
76
+ return lens.name ? `${state} (${lens.name})` : state;
77
+ }
78
+ function formatLensForProvider(lens) {
79
+ if (!lens) return "missing";
80
+ return lens.connected ? "connected" : "disconnected";
81
+ }
82
+ export {
83
+ formatConnectedLensesForAction,
84
+ formatConnectedLensesForProvider,
85
+ isCradleOrChargingState,
86
+ setupHintForStatus,
87
+ setupSummaryForStatus
88
+ };
89
+ //# sourceMappingURL=status-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/status-format.ts"],"sourcesContent":["import type { SmartglassesStatus } from \"./services/smartglasses-service.js\";\nimport type { SmartglassesConnectedLenses } from \"./transport/types.js\";\n\nexport type SmartglassesPhysicalBlocker =\n | \"disconnected\"\n | \"headset_not_found\"\n | \"partial_headset\"\n | \"in_charging_base\"\n | \"wearing_state_missing\"\n | null;\n\nexport interface SmartglassesSetupSummary {\n setupHint: string | null;\n wholeHeadsetConnected: boolean;\n wearingReady: boolean;\n physicalBlocker: SmartglassesPhysicalBlocker;\n}\n\nexport function formatConnectedLensesForAction(\n lenses: SmartglassesConnectedLenses,\n): string {\n return `left=${formatLensForAction(lenses.left)}, right=${formatLensForAction(\n lenses.right,\n )}`;\n}\n\nexport function formatConnectedLensesForProvider(\n lenses: SmartglassesConnectedLenses,\n): string {\n return `left:${formatLensForProvider(lenses.left)} right:${formatLensForProvider(\n lenses.right,\n )}`;\n}\n\nexport function setupSummaryForStatus(\n status: Pick<\n SmartglassesStatus,\n \"connected\" | \"connectedLenses\" | \"physicalState\" | \"batteryState\"\n >,\n): SmartglassesSetupSummary {\n const wholeHeadsetConnected = Boolean(\n status.connected &&\n status.connectedLenses.left?.connected &&\n status.connectedLenses.right?.connected,\n );\n const anyLensConnected = Boolean(\n status.connectedLenses.left?.connected ||\n status.connectedLenses.right?.connected,\n );\n const wearingReady =\n wholeHeadsetConnected && status.physicalState === \"wearing\";\n if (!status.connected) {\n return {\n setupHint:\n \"Connect the whole headset before display, tap, or microphone validation.\",\n wholeHeadsetConnected,\n wearingReady,\n physicalBlocker: \"disconnected\",\n };\n }\n if (!anyLensConnected) {\n return {\n setupHint:\n \"No G1 lenses were found. Remove both lenses from the charging base, keep them near this device, and rerun headset pairing.\",\n wholeHeadsetConnected,\n wearingReady,\n physicalBlocker: \"headset_not_found\",\n };\n }\n if (!wholeHeadsetConnected) {\n return {\n setupHint:\n \"Connect both left and right lenses before display, tap, or microphone validation.\",\n wholeHeadsetConnected,\n wearingReady,\n physicalBlocker: \"partial_headset\",\n };\n }\n if (wearingReady) {\n return {\n setupHint: null,\n wholeHeadsetConnected,\n wearingReady,\n physicalBlocker: null,\n };\n }\n const stateText =\n [status.physicalState, status.batteryState].filter(Boolean).join(\" / \") ||\n \"no wearing state observed\";\n if (isCradleOrChargingState(status.physicalState, status.batteryState)) {\n return {\n setupHint: `Glasses are reporting ${stateText}; remove them from the charging base and wear them before tap or microphone validation.`,\n wholeHeadsetConnected,\n wearingReady,\n physicalBlocker: \"in_charging_base\",\n };\n }\n return {\n setupHint: `Tap and microphone validation requires the glasses to report wearing; current state is ${stateText}.`,\n wholeHeadsetConnected,\n wearingReady,\n physicalBlocker: \"wearing_state_missing\",\n };\n}\n\nexport function setupHintForStatus(\n status: Pick<\n SmartglassesStatus,\n \"connected\" | \"connectedLenses\" | \"physicalState\" | \"batteryState\"\n >,\n): string | null {\n return setupSummaryForStatus(status).setupHint;\n}\n\nexport function isCradleOrChargingState(\n physicalState: string | null,\n batteryState: string | null,\n): boolean {\n return (\n physicalState === \"cradle_open\" ||\n physicalState === \"cradle_closed\" ||\n physicalState === \"charged_in_cradle\" ||\n batteryState === \"glasses_fully_charged\" ||\n batteryState === \"cradle_charging_cable_changed\" ||\n batteryState === \"cradle_fully_charged\"\n );\n}\n\nfunction formatLensForAction(\n lens: SmartglassesConnectedLenses[keyof SmartglassesConnectedLenses],\n): string {\n if (!lens) return \"missing\";\n const state = lens.connected ? \"connected\" : \"disconnected\";\n return lens.name ? `${state} (${lens.name})` : state;\n}\n\nfunction formatLensForProvider(\n lens: SmartglassesConnectedLenses[keyof SmartglassesConnectedLenses],\n): string {\n if (!lens) return \"missing\";\n return lens.connected ? \"connected\" : \"disconnected\";\n}\n"],"mappings":"AAkBO,SAAS,+BACd,QACQ;AACR,SAAO,QAAQ,oBAAoB,OAAO,IAAI,CAAC,WAAW;AAAA,IACxD,OAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,iCACd,QACQ;AACR,SAAO,QAAQ,sBAAsB,OAAO,IAAI,CAAC,UAAU;AAAA,IACzD,OAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,sBACd,QAI0B;AAC1B,QAAM,wBAAwB;AAAA,IAC5B,OAAO,aACL,OAAO,gBAAgB,MAAM,aAC7B,OAAO,gBAAgB,OAAO;AAAA,EAClC;AACA,QAAM,mBAAmB;AAAA,IACvB,OAAO,gBAAgB,MAAM,aAC3B,OAAO,gBAAgB,OAAO;AAAA,EAClC;AACA,QAAM,eACJ,yBAAyB,OAAO,kBAAkB;AACpD,MAAI,CAAC,OAAO,WAAW;AACrB,WAAO;AAAA,MACL,WACE;AAAA,MACF;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAAA,EACF;AACA,MAAI,CAAC,kBAAkB;AACrB,WAAO;AAAA,MACL,WACE;AAAA,MACF;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAAA,EACF;AACA,MAAI,CAAC,uBAAuB;AAC1B,WAAO;AAAA,MACL,WACE;AAAA,MACF;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAAA,EACF;AACA,MAAI,cAAc;AAChB,WAAO;AAAA,MACL,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAAA,EACF;AACA,QAAM,YACJ,CAAC,OAAO,eAAe,OAAO,YAAY,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK,KACtE;AACF,MAAI,wBAAwB,OAAO,eAAe,OAAO,YAAY,GAAG;AACtE,WAAO;AAAA,MACL,WAAW,yBAAyB,SAAS;AAAA,MAC7C;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AAAA,IACL,WAAW,0FAA0F,SAAS;AAAA,IAC9G;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,EACnB;AACF;AAEO,SAAS,mBACd,QAIe;AACf,SAAO,sBAAsB,MAAM,EAAE;AACvC;AAEO,SAAS,wBACd,eACA,cACS;AACT,SACE,kBAAkB,iBAClB,kBAAkB,mBAClB,kBAAkB,uBAClB,iBAAiB,2BACjB,iBAAiB,mCACjB,iBAAiB;AAErB;AAEA,SAAS,oBACP,MACQ;AACR,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,YAAY,cAAc;AAC7C,SAAO,KAAK,OAAO,GAAG,KAAK,KAAK,KAAK,IAAI,MAAM;AACjD;AAEA,SAAS,sBACP,MACQ;AACR,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,YAAY,cAAc;AACxC;","names":[]}
@@ -0,0 +1,69 @@
1
+ import { type G1Event, type GlassSide, type SmartglassesAudioEncoding } from "../protocol/smartglasses.js";
2
+ import type { SmartglassesConnectedLenses, SmartglassesTransport, SmartglassesWifiResult } from "./types.js";
3
+ type EvenBridge = {
4
+ requestWifiScan?: () => Promise<unknown> | unknown;
5
+ requestWifiStatus?: () => Promise<unknown> | unknown;
6
+ requestWifiSetup?: (reason?: string) => Promise<unknown> | unknown;
7
+ setWifiCredentials?: (ssid: string, password: string) => Promise<unknown> | unknown;
8
+ sendWifiCredentials?: (ssid: string, password: string) => Promise<unknown> | unknown;
9
+ rawBridge?: {
10
+ audioControl?: (enabled: boolean) => Promise<unknown> | unknown;
11
+ callEvenApp?: (name: string, payload?: Record<string, unknown>) => Promise<unknown> | unknown;
12
+ };
13
+ audioControl?: (enabled: boolean) => Promise<unknown> | unknown;
14
+ createStartUpPageContainer?: (container: Record<string, unknown>) => Promise<unknown> | unknown;
15
+ clearDisplay?: () => Promise<unknown> | unknown;
16
+ displayText?: (params: Record<string, unknown>) => Promise<unknown> | unknown;
17
+ onEvent?: (callback: (event: unknown) => void) => BridgeSubscription;
18
+ onEvenHubEvent?: (callback: (event: unknown) => void) => BridgeSubscription;
19
+ rebuildPageContainer?: (container: Record<string, unknown>) => Promise<unknown> | unknown;
20
+ sendStartUpPage?: (container: unknown) => Promise<unknown> | unknown;
21
+ setMicState?: (sendPcmData: boolean, sendTranscript: boolean, bypassVad: boolean) => Promise<unknown> | unknown;
22
+ write?: (side: GlassSide, data: Uint8Array) => Promise<unknown> | unknown;
23
+ send?: (side: GlassSide, data: Uint8Array) => Promise<unknown> | unknown;
24
+ };
25
+ type BridgeSubscription = undefined | (() => void) | {
26
+ unsubscribe?: () => void;
27
+ off?: () => void;
28
+ remove?: () => void;
29
+ };
30
+ export declare class EvenBridgeTransport implements SmartglassesTransport {
31
+ private readonly bridge;
32
+ readonly name = "even-bridge";
33
+ private connected;
34
+ private eventCallbacks;
35
+ private audioCallbacks;
36
+ private transcriptCallbacks;
37
+ private wifiCallbacks;
38
+ private bridgeDisposer;
39
+ private readonly displayChunks;
40
+ private evenHubStartupCreated;
41
+ constructor(bridge: EvenBridge);
42
+ connect(): Promise<void>;
43
+ disconnect(): Promise<void>;
44
+ isConnected(): boolean;
45
+ getConnectedLenses(): SmartglassesConnectedLenses;
46
+ write(side: GlassSide, data: Uint8Array): Promise<void>;
47
+ writeBoth(data: Uint8Array): Promise<void>;
48
+ openMicrophone(enabled: boolean): Promise<void>;
49
+ scanWifi(): Promise<SmartglassesWifiResult>;
50
+ getWifiStatus(): Promise<SmartglassesWifiResult>;
51
+ configureWifi(ssid: string, password: string): Promise<SmartglassesWifiResult>;
52
+ requestWifiSetup(reason?: string): Promise<SmartglassesWifiResult>;
53
+ supportsWifi(): boolean;
54
+ private assertWifiSupported;
55
+ private supportsEvenHubDisplay;
56
+ private supportsMentraDisplay;
57
+ onEvent(callback: (event: G1Event) => void): () => void;
58
+ onAudio(callback: (audioData: Uint8Array, sampleRate: number, side: GlassSide, encoding?: SmartglassesAudioEncoding, sequence?: number) => void): () => void;
59
+ onTranscript(callback: (text: string, isFinal: boolean, metadata?: Record<string, unknown>) => void): () => void;
60
+ onWifiStatus(callback: (status: SmartglassesWifiResult) => void): () => void;
61
+ private handleBridgeEvent;
62
+ private writeEvenHubDisplay;
63
+ private writeMentraDisplay;
64
+ private sendEvenHubTextPage;
65
+ private decodeDisplayPacket;
66
+ }
67
+ export declare function getGlobalEvenBridgeTransport(): SmartglassesTransport | null;
68
+ export {};
69
+ //# sourceMappingURL=even-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"even-bridge.d.ts","sourceRoot":"","sources":["../../src/transport/even-bridge.ts"],"names":[],"mappings":"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"}