@elizaos/plugin-xr 2.0.3-beta.5
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.
- package/AGENTS.md +151 -0
- package/CLAUDE.md +151 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/package.json +57 -0
- package/simulator/bun.lock +159 -0
- package/simulator/package.json +28 -0
- package/simulator/src/emulator.ts +174 -0
- package/simulator/src/mock-agent.ts +233 -0
- package/simulator/src/node.ts +9 -0
- package/simulator/src/playwright-fixture.ts +169 -0
- package/simulator/src/types.ts +51 -0
- package/simulator/tsconfig.json +13 -0
- package/simulator/vite.config.ts +25 -0
- package/src/__tests__/audio-pipeline.test.ts +129 -0
- package/src/__tests__/protocol.test.ts +53 -0
- package/src/__tests__/routes-e2e.test.ts +276 -0
- package/src/__tests__/vision-pipeline.test.ts +73 -0
- package/src/__tests__/xr-bundle-coverage.test.ts +303 -0
- package/src/__tests__/xr-feature-parity.test.ts +524 -0
- package/src/__tests__/xr-functional-parity.test.ts +522 -0
- package/src/__tests__/xr-view-host-http.test.ts +239 -0
- package/src/__tests__/xr-view-host.test.ts +174 -0
- package/src/actions/xr-query-vision.ts +64 -0
- package/src/actions/xr-view-actions.ts +386 -0
- package/src/index.ts +55 -0
- package/src/protocol.ts +126 -0
- package/src/providers/xr-context.ts +49 -0
- package/src/routes/xr-connect.ts +89 -0
- package/src/routes/xr-simulator-route.ts +37 -0
- package/src/routes/xr-status.ts +36 -0
- package/src/routes/xr-view-host.ts +359 -0
- package/src/routes/xr-views.ts +43 -0
- package/src/services/audio-pipeline.ts +120 -0
- package/src/services/vision-pipeline.ts +57 -0
- package/src/services/xr-session-service.ts +388 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +30 -0
- package/vitest.config.ts +21 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# @elizaos/plugin-xr
|
|
2
|
+
|
|
3
|
+
WebXR audio/video streaming for elizaOS — Quest 3 and XReal glasses.
|
|
4
|
+
|
|
5
|
+
## Purpose / role
|
|
6
|
+
|
|
7
|
+
Adds a WebXR streaming surface to an Eliza agent: a WebSocket server accepts
|
|
8
|
+
connections from XR headsets (Quest 3, XReal, or a browser simulator), pipes
|
|
9
|
+
microphone audio through the runtime's TRANSCRIPTION model, and routes the
|
|
10
|
+
transcript through the standard message pipeline. Voice responses are generated
|
|
11
|
+
via the TEXT_TO_SPEECH model and sent back as binary audio frames. The plugin
|
|
12
|
+
is opt-in — register `xrPlugin` in your character's plugins array.
|
|
13
|
+
|
|
14
|
+
## Plugin surface
|
|
15
|
+
|
|
16
|
+
**Service**
|
|
17
|
+
- `XRSessionService` (`xr-session`) — WebSocket server on port 31338 (default).
|
|
18
|
+
Manages per-connection lifecycle, delegates audio to `AudioPipeline` and
|
|
19
|
+
camera frames to `VisionPipeline`, routes transcripts into the agent message
|
|
20
|
+
pipeline, and sends TTS audio back to the headset.
|
|
21
|
+
|
|
22
|
+
**Actions**
|
|
23
|
+
- `XR_QUERY_VISION` — describes what the user's XR camera currently sees
|
|
24
|
+
(calls `VisionPipeline.describeFrame` → `ModelType.IMAGE_DESCRIPTION`).
|
|
25
|
+
Only validates when a recent camera frame exists.
|
|
26
|
+
- `XR_OPEN_VIEW` — opens a named view panel on the headset (sends
|
|
27
|
+
`view_open` control message).
|
|
28
|
+
- `XR_CLOSE_VIEW` — closes a named view (or all views if no id given).
|
|
29
|
+
- `XR_SWITCH_VIEW` — brings a view to the foreground without closing others.
|
|
30
|
+
- `XR_LIST_VIEWS` — enumerates views with `viewType: "xr"` from all loaded
|
|
31
|
+
plugins and optionally sends the catalog to the device.
|
|
32
|
+
- `XR_RESIZE_VIEW` — resizes/repositions the active panel (`scale`,
|
|
33
|
+
`distance`, `fullscreen` options).
|
|
34
|
+
|
|
35
|
+
**Provider**
|
|
36
|
+
- `XR_SESSION` (`xr-context.ts`) — injects connected device list and camera
|
|
37
|
+
state into the agent context block when at least one headset is connected.
|
|
38
|
+
|
|
39
|
+
**Routes** (all under `/api/xr/`)
|
|
40
|
+
- `GET /xr/status` — JSON list of connected sessions and camera-frame state.
|
|
41
|
+
- `GET /xr/connect` — HTML page with QR code for pairing a headset.
|
|
42
|
+
- `GET /xr/views` — JSON list of all `viewType: "xr"` views registered by
|
|
43
|
+
loaded plugins, plus active connections.
|
|
44
|
+
- `GET /xr/view-host/:id` — self-contained HTML shell that dynamically imports
|
|
45
|
+
a plugin view bundle and renders it with an XR-optimised chrome.
|
|
46
|
+
- `GET /xr/simulator.js` — serves the built WebXR emulator bundle (only
|
|
47
|
+
available after `bun run build:all`).
|
|
48
|
+
|
|
49
|
+
## Layout
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
plugins/plugin-xr/
|
|
53
|
+
src/
|
|
54
|
+
index.ts Plugin entry — exports xrPlugin and public types
|
|
55
|
+
protocol.ts Wire types: XRClientControl, XRServerControl,
|
|
56
|
+
XRBinaryHeader, XRTTSAudioHeader; encode/decode helpers
|
|
57
|
+
actions/
|
|
58
|
+
xr-query-vision.ts XR_QUERY_VISION action
|
|
59
|
+
xr-view-actions.ts XR_OPEN/CLOSE/SWITCH/LIST/RESIZE_VIEW actions
|
|
60
|
+
providers/
|
|
61
|
+
xr-context.ts XR_SESSION provider
|
|
62
|
+
services/
|
|
63
|
+
xr-session-service.ts XRSessionService (WebSocket server, main orchestrator)
|
|
64
|
+
audio-pipeline.ts AudioPipeline — buffers audio chunks, flushes to
|
|
65
|
+
TRANSCRIPTION model after 2 s or 1.5 s silence
|
|
66
|
+
vision-pipeline.ts VisionPipeline — stores latest camera frame (max age
|
|
67
|
+
10 s), calls IMAGE_DESCRIPTION model on demand
|
|
68
|
+
routes/
|
|
69
|
+
xr-status.ts GET /xr/status
|
|
70
|
+
xr-connect.ts GET /xr/connect
|
|
71
|
+
xr-views.ts GET /xr/views
|
|
72
|
+
xr-view-host.ts GET /xr/view-host/:id
|
|
73
|
+
xr-simulator-route.ts GET /xr/simulator.js
|
|
74
|
+
__tests__/
|
|
75
|
+
audio-pipeline.test.ts
|
|
76
|
+
protocol.test.ts
|
|
77
|
+
vision-pipeline.test.ts
|
|
78
|
+
xr-bundle-coverage.test.ts
|
|
79
|
+
xr-feature-parity.test.ts
|
|
80
|
+
xr-functional-parity.test.ts
|
|
81
|
+
xr-view-host.test.ts
|
|
82
|
+
xr-view-host-http.test.ts
|
|
83
|
+
simulator/ Browser-side WebXR emulator (Vite build)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Commands
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
bun run --cwd plugins/plugin-xr typecheck
|
|
90
|
+
bun run --cwd plugins/plugin-xr lint
|
|
91
|
+
bun run --cwd plugins/plugin-xr test
|
|
92
|
+
bun run --cwd plugins/plugin-xr build
|
|
93
|
+
bun run --cwd plugins/plugin-xr build:all # also builds simulator/
|
|
94
|
+
bun run --cwd plugins/plugin-xr simulator:build
|
|
95
|
+
bun run --cwd plugins/plugin-xr simulator:watch
|
|
96
|
+
bun run --cwd plugins/plugin-xr clean
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Config / env vars
|
|
100
|
+
|
|
101
|
+
| Var | Default | Required | Purpose |
|
|
102
|
+
|-----|---------|----------|---------|
|
|
103
|
+
| `XR_WS_PORT` | `31338` | no | WebSocket server port |
|
|
104
|
+
| `XR_AGENT_URL` | `http://localhost:<agent-port>` | no | Public base URL sent to the headset for view bundles |
|
|
105
|
+
| `XR_APP_URL` | derived from `VITE_PORT` | no | URL shown on the `/xr/connect` pairing page |
|
|
106
|
+
|
|
107
|
+
The plugin sets `config: { XR_WS_PORT: 31338 }` in the Plugin object so the
|
|
108
|
+
runtime exposes it via `runtime.getSetting("XR_WS_PORT")`.
|
|
109
|
+
|
|
110
|
+
The agent must have a TRANSCRIPTION model and TEXT_TO_SPEECH model configured
|
|
111
|
+
(e.g., via `@elizaos/plugin-openai` or a local inference plugin) for audio
|
|
112
|
+
streaming to work. IMAGE_DESCRIPTION is required for `XR_QUERY_VISION`.
|
|
113
|
+
|
|
114
|
+
## How to extend
|
|
115
|
+
|
|
116
|
+
**Add an action** — create `src/actions/<name>.ts`, implement `Action` from
|
|
117
|
+
`@elizaos/core`, get `XRSessionService` via
|
|
118
|
+
`runtime.getService<XRSessionService>(XR_SERVICE_TYPE)`, then add the import
|
|
119
|
+
and the object to the `actions` array in `src/index.ts`.
|
|
120
|
+
|
|
121
|
+
**Add a provider** — create `src/providers/<name>.ts`, implement `Provider`,
|
|
122
|
+
add to `providers` array in `src/index.ts`.
|
|
123
|
+
|
|
124
|
+
**Add a route** — create `src/routes/<name>.ts`, implement `Route`, add to
|
|
125
|
+
`routes` array in `src/index.ts`.
|
|
126
|
+
|
|
127
|
+
**Expose a view in XR** — in any other plugin, add a `views` array entry with
|
|
128
|
+
`viewType: "xr"`. `XR_LIST_VIEWS` and `GET /xr/views` collect views with this
|
|
129
|
+
field across all loaded plugins at runtime.
|
|
130
|
+
|
|
131
|
+
## Conventions / gotchas
|
|
132
|
+
|
|
133
|
+
- **WebXR requires HTTPS on device.** The `/xr/connect` page warns when the
|
|
134
|
+
URL is plain HTTP. Use a local tunnel (e.g., `cloudflared`) and set
|
|
135
|
+
`XR_APP_URL` to the HTTPS tunnel URL.
|
|
136
|
+
- **Binary frame framing** is defined in `src/protocol.ts`: 4-byte big-endian
|
|
137
|
+
header length, then UTF-8 JSON header, then raw payload. Use
|
|
138
|
+
`encodeBinaryFrame` / `decodeBinaryFrame` from that module — do not
|
|
139
|
+
reimplement the framing.
|
|
140
|
+
- **Audio buffering** — `AudioPipeline` accumulates chunks and flushes after
|
|
141
|
+
2 000 ms of audio or 1 500 ms of silence. Chunks shorter than 512 bytes are
|
|
142
|
+
dropped. `pcm-f32` encoding (ScriptProcessorNode fallback) is wrapped in a
|
|
143
|
+
WAV header before being passed to TRANSCRIPTION.
|
|
144
|
+
- **Simulator bundle** — `simulator/` is a separate Vite project. Run
|
|
145
|
+
`bun run build:all` (or `simulator:build`) before the `/xr/simulator.js`
|
|
146
|
+
route will serve anything. The route returns 404 until the bundle exists.
|
|
147
|
+
- **`XR_AGENT_URL`** must be a reachable URL from inside the XR headset
|
|
148
|
+
browser when loading view bundles. `localhost` will only work when testing
|
|
149
|
+
on the same machine via the browser simulator.
|
|
150
|
+
- See repo root `AGENTS.md` for repo-wide architecture rules, logger
|
|
151
|
+
conventions, ESM requirements, and naming standards.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# @elizaos/plugin-xr
|
|
2
|
+
|
|
3
|
+
WebXR audio/video streaming for elizaOS — Quest 3 and XReal glasses.
|
|
4
|
+
|
|
5
|
+
## Purpose / role
|
|
6
|
+
|
|
7
|
+
Adds a WebXR streaming surface to an Eliza agent: a WebSocket server accepts
|
|
8
|
+
connections from XR headsets (Quest 3, XReal, or a browser simulator), pipes
|
|
9
|
+
microphone audio through the runtime's TRANSCRIPTION model, and routes the
|
|
10
|
+
transcript through the standard message pipeline. Voice responses are generated
|
|
11
|
+
via the TEXT_TO_SPEECH model and sent back as binary audio frames. The plugin
|
|
12
|
+
is opt-in — register `xrPlugin` in your character's plugins array.
|
|
13
|
+
|
|
14
|
+
## Plugin surface
|
|
15
|
+
|
|
16
|
+
**Service**
|
|
17
|
+
- `XRSessionService` (`xr-session`) — WebSocket server on port 31338 (default).
|
|
18
|
+
Manages per-connection lifecycle, delegates audio to `AudioPipeline` and
|
|
19
|
+
camera frames to `VisionPipeline`, routes transcripts into the agent message
|
|
20
|
+
pipeline, and sends TTS audio back to the headset.
|
|
21
|
+
|
|
22
|
+
**Actions**
|
|
23
|
+
- `XR_QUERY_VISION` — describes what the user's XR camera currently sees
|
|
24
|
+
(calls `VisionPipeline.describeFrame` → `ModelType.IMAGE_DESCRIPTION`).
|
|
25
|
+
Only validates when a recent camera frame exists.
|
|
26
|
+
- `XR_OPEN_VIEW` — opens a named view panel on the headset (sends
|
|
27
|
+
`view_open` control message).
|
|
28
|
+
- `XR_CLOSE_VIEW` — closes a named view (or all views if no id given).
|
|
29
|
+
- `XR_SWITCH_VIEW` — brings a view to the foreground without closing others.
|
|
30
|
+
- `XR_LIST_VIEWS` — enumerates views with `viewType: "xr"` from all loaded
|
|
31
|
+
plugins and optionally sends the catalog to the device.
|
|
32
|
+
- `XR_RESIZE_VIEW` — resizes/repositions the active panel (`scale`,
|
|
33
|
+
`distance`, `fullscreen` options).
|
|
34
|
+
|
|
35
|
+
**Provider**
|
|
36
|
+
- `XR_SESSION` (`xr-context.ts`) — injects connected device list and camera
|
|
37
|
+
state into the agent context block when at least one headset is connected.
|
|
38
|
+
|
|
39
|
+
**Routes** (all under `/api/xr/`)
|
|
40
|
+
- `GET /xr/status` — JSON list of connected sessions and camera-frame state.
|
|
41
|
+
- `GET /xr/connect` — HTML page with QR code for pairing a headset.
|
|
42
|
+
- `GET /xr/views` — JSON list of all `viewType: "xr"` views registered by
|
|
43
|
+
loaded plugins, plus active connections.
|
|
44
|
+
- `GET /xr/view-host/:id` — self-contained HTML shell that dynamically imports
|
|
45
|
+
a plugin view bundle and renders it with an XR-optimised chrome.
|
|
46
|
+
- `GET /xr/simulator.js` — serves the built WebXR emulator bundle (only
|
|
47
|
+
available after `bun run build:all`).
|
|
48
|
+
|
|
49
|
+
## Layout
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
plugins/plugin-xr/
|
|
53
|
+
src/
|
|
54
|
+
index.ts Plugin entry — exports xrPlugin and public types
|
|
55
|
+
protocol.ts Wire types: XRClientControl, XRServerControl,
|
|
56
|
+
XRBinaryHeader, XRTTSAudioHeader; encode/decode helpers
|
|
57
|
+
actions/
|
|
58
|
+
xr-query-vision.ts XR_QUERY_VISION action
|
|
59
|
+
xr-view-actions.ts XR_OPEN/CLOSE/SWITCH/LIST/RESIZE_VIEW actions
|
|
60
|
+
providers/
|
|
61
|
+
xr-context.ts XR_SESSION provider
|
|
62
|
+
services/
|
|
63
|
+
xr-session-service.ts XRSessionService (WebSocket server, main orchestrator)
|
|
64
|
+
audio-pipeline.ts AudioPipeline — buffers audio chunks, flushes to
|
|
65
|
+
TRANSCRIPTION model after 2 s or 1.5 s silence
|
|
66
|
+
vision-pipeline.ts VisionPipeline — stores latest camera frame (max age
|
|
67
|
+
10 s), calls IMAGE_DESCRIPTION model on demand
|
|
68
|
+
routes/
|
|
69
|
+
xr-status.ts GET /xr/status
|
|
70
|
+
xr-connect.ts GET /xr/connect
|
|
71
|
+
xr-views.ts GET /xr/views
|
|
72
|
+
xr-view-host.ts GET /xr/view-host/:id
|
|
73
|
+
xr-simulator-route.ts GET /xr/simulator.js
|
|
74
|
+
__tests__/
|
|
75
|
+
audio-pipeline.test.ts
|
|
76
|
+
protocol.test.ts
|
|
77
|
+
vision-pipeline.test.ts
|
|
78
|
+
xr-bundle-coverage.test.ts
|
|
79
|
+
xr-feature-parity.test.ts
|
|
80
|
+
xr-functional-parity.test.ts
|
|
81
|
+
xr-view-host.test.ts
|
|
82
|
+
xr-view-host-http.test.ts
|
|
83
|
+
simulator/ Browser-side WebXR emulator (Vite build)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Commands
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
bun run --cwd plugins/plugin-xr typecheck
|
|
90
|
+
bun run --cwd plugins/plugin-xr lint
|
|
91
|
+
bun run --cwd plugins/plugin-xr test
|
|
92
|
+
bun run --cwd plugins/plugin-xr build
|
|
93
|
+
bun run --cwd plugins/plugin-xr build:all # also builds simulator/
|
|
94
|
+
bun run --cwd plugins/plugin-xr simulator:build
|
|
95
|
+
bun run --cwd plugins/plugin-xr simulator:watch
|
|
96
|
+
bun run --cwd plugins/plugin-xr clean
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Config / env vars
|
|
100
|
+
|
|
101
|
+
| Var | Default | Required | Purpose |
|
|
102
|
+
|-----|---------|----------|---------|
|
|
103
|
+
| `XR_WS_PORT` | `31338` | no | WebSocket server port |
|
|
104
|
+
| `XR_AGENT_URL` | `http://localhost:<agent-port>` | no | Public base URL sent to the headset for view bundles |
|
|
105
|
+
| `XR_APP_URL` | derived from `VITE_PORT` | no | URL shown on the `/xr/connect` pairing page |
|
|
106
|
+
|
|
107
|
+
The plugin sets `config: { XR_WS_PORT: 31338 }` in the Plugin object so the
|
|
108
|
+
runtime exposes it via `runtime.getSetting("XR_WS_PORT")`.
|
|
109
|
+
|
|
110
|
+
The agent must have a TRANSCRIPTION model and TEXT_TO_SPEECH model configured
|
|
111
|
+
(e.g., via `@elizaos/plugin-openai` or a local inference plugin) for audio
|
|
112
|
+
streaming to work. IMAGE_DESCRIPTION is required for `XR_QUERY_VISION`.
|
|
113
|
+
|
|
114
|
+
## How to extend
|
|
115
|
+
|
|
116
|
+
**Add an action** — create `src/actions/<name>.ts`, implement `Action` from
|
|
117
|
+
`@elizaos/core`, get `XRSessionService` via
|
|
118
|
+
`runtime.getService<XRSessionService>(XR_SERVICE_TYPE)`, then add the import
|
|
119
|
+
and the object to the `actions` array in `src/index.ts`.
|
|
120
|
+
|
|
121
|
+
**Add a provider** — create `src/providers/<name>.ts`, implement `Provider`,
|
|
122
|
+
add to `providers` array in `src/index.ts`.
|
|
123
|
+
|
|
124
|
+
**Add a route** — create `src/routes/<name>.ts`, implement `Route`, add to
|
|
125
|
+
`routes` array in `src/index.ts`.
|
|
126
|
+
|
|
127
|
+
**Expose a view in XR** — in any other plugin, add a `views` array entry with
|
|
128
|
+
`viewType: "xr"`. `XR_LIST_VIEWS` and `GET /xr/views` collect views with this
|
|
129
|
+
field across all loaded plugins at runtime.
|
|
130
|
+
|
|
131
|
+
## Conventions / gotchas
|
|
132
|
+
|
|
133
|
+
- **WebXR requires HTTPS on device.** The `/xr/connect` page warns when the
|
|
134
|
+
URL is plain HTTP. Use a local tunnel (e.g., `cloudflared`) and set
|
|
135
|
+
`XR_APP_URL` to the HTTPS tunnel URL.
|
|
136
|
+
- **Binary frame framing** is defined in `src/protocol.ts`: 4-byte big-endian
|
|
137
|
+
header length, then UTF-8 JSON header, then raw payload. Use
|
|
138
|
+
`encodeBinaryFrame` / `decodeBinaryFrame` from that module — do not
|
|
139
|
+
reimplement the framing.
|
|
140
|
+
- **Audio buffering** — `AudioPipeline` accumulates chunks and flushes after
|
|
141
|
+
2 000 ms of audio or 1 500 ms of silence. Chunks shorter than 512 bytes are
|
|
142
|
+
dropped. `pcm-f32` encoding (ScriptProcessorNode fallback) is wrapped in a
|
|
143
|
+
WAV header before being passed to TRANSCRIPTION.
|
|
144
|
+
- **Simulator bundle** — `simulator/` is a separate Vite project. Run
|
|
145
|
+
`bun run build:all` (or `simulator:build`) before the `/xr/simulator.js`
|
|
146
|
+
route will serve anything. The route returns 404 until the bundle exists.
|
|
147
|
+
- **`XR_AGENT_URL`** must be a reachable URL from inside the XR headset
|
|
148
|
+
browser when loading view bundles. `localhost` will only work when testing
|
|
149
|
+
on the same machine via the browser simulator.
|
|
150
|
+
- See repo root `AGENTS.md` for repo-wide architecture rules, logger
|
|
151
|
+
conventions, ESM requirements, and naming standards.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shaw Walters and elizaOS Contributors
|
|
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,106 @@
|
|
|
1
|
+
# @elizaos/plugin-xr
|
|
2
|
+
|
|
3
|
+
WebXR audio/video streaming for elizaOS — connects Quest 3, XReal glasses, or
|
|
4
|
+
any WebXR-capable browser to an Eliza agent.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
- Opens a WebSocket server (default port 31338) that XR headsets connect to.
|
|
9
|
+
- Streams microphone audio from the headset, transcribes it via the runtime's
|
|
10
|
+
TRANSCRIPTION model, and routes the transcript through the agent's normal
|
|
11
|
+
message pipeline.
|
|
12
|
+
- Sends voice responses back to the headset as TTS audio (TEXT_TO_SPEECH model).
|
|
13
|
+
- Captures camera frames from the headset and can describe them using the
|
|
14
|
+
IMAGE_DESCRIPTION model (`XR_QUERY_VISION` action).
|
|
15
|
+
- Manages floating view panels on the headset — open, close, switch, resize,
|
|
16
|
+
and list views that other plugins have declared with `viewType: "xr"`.
|
|
17
|
+
|
|
18
|
+
## Capabilities added to an Eliza agent
|
|
19
|
+
|
|
20
|
+
| Action | What it does |
|
|
21
|
+
|--------|-------------|
|
|
22
|
+
| `XR_QUERY_VISION` | Describes what the user's XR camera currently sees |
|
|
23
|
+
| `XR_OPEN_VIEW` | Opens a named floating panel on the headset |
|
|
24
|
+
| `XR_CLOSE_VIEW` | Closes a named panel (or all panels) |
|
|
25
|
+
| `XR_SWITCH_VIEW` | Brings a panel to the foreground |
|
|
26
|
+
| `XR_LIST_VIEWS` | Lists all XR-capable views and sends a launcher catalog to the device |
|
|
27
|
+
| `XR_RESIZE_VIEW` | Resizes or repositions the active panel |
|
|
28
|
+
|
|
29
|
+
The `XR_SESSION` context provider automatically injects connected-device status
|
|
30
|
+
and camera state into every conversation turn when a headset is connected.
|
|
31
|
+
|
|
32
|
+
## HTTP routes
|
|
33
|
+
|
|
34
|
+
| Route | Purpose |
|
|
35
|
+
|-------|---------|
|
|
36
|
+
| `GET /api/xr/status` | JSON status of connected sessions |
|
|
37
|
+
| `GET /api/xr/connect` | HTML pairing page with QR code for the headset browser |
|
|
38
|
+
| `GET /api/xr/views` | JSON list of all registered XR views |
|
|
39
|
+
| `GET /api/xr/view-host/:id` | Self-contained XR shell page for a specific view |
|
|
40
|
+
| `GET /api/xr/simulator.js` | WebXR emulator bundle for testing (after build) |
|
|
41
|
+
|
|
42
|
+
## Enabling the plugin
|
|
43
|
+
|
|
44
|
+
Add `@elizaos/plugin-xr` to your character's plugin list:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"name": "my-agent",
|
|
49
|
+
"plugins": ["@elizaos/plugin-xr"]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The plugin also requires a character configuration that loads:
|
|
54
|
+
- A TRANSCRIPTION model provider (for speech-to-text)
|
|
55
|
+
- A TEXT_TO_SPEECH model provider (for voice responses)
|
|
56
|
+
- An IMAGE_DESCRIPTION model provider (for `XR_QUERY_VISION`)
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
| Environment variable | Default | Purpose |
|
|
61
|
+
|---------------------|---------|---------|
|
|
62
|
+
| `XR_WS_PORT` | `31338` | WebSocket server port |
|
|
63
|
+
| `XR_AGENT_URL` | `http://localhost:<agent-port>` | Public base URL used by the headset to load view bundles — must be reachable from the device |
|
|
64
|
+
| `XR_APP_URL` | derived from `VITE_PORT` | URL shown on the `/api/xr/connect` pairing page |
|
|
65
|
+
|
|
66
|
+
## Connecting a headset
|
|
67
|
+
|
|
68
|
+
1. Start the agent with this plugin enabled.
|
|
69
|
+
2. Open `http://localhost:31337/api/xr/connect` in a browser (adjust port to
|
|
70
|
+
your agent's API port).
|
|
71
|
+
3. Scan the QR code with your Quest 3 or XReal browser, or navigate to the
|
|
72
|
+
shown URL.
|
|
73
|
+
4. Allow microphone and camera access when prompted.
|
|
74
|
+
5. The agent will start receiving audio immediately.
|
|
75
|
+
|
|
76
|
+
> **Note:** WebXR APIs require HTTPS on physical devices. For local
|
|
77
|
+
> development, run a tunnel (e.g., `cloudflared tunnel`) and set `XR_APP_URL`
|
|
78
|
+
> to the HTTPS tunnel URL before starting the agent.
|
|
79
|
+
|
|
80
|
+
## Building the simulator
|
|
81
|
+
|
|
82
|
+
The `simulator/` sub-project provides a browser-side WebXR emulator for
|
|
83
|
+
Playwright tests.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
bun run --cwd plugins/plugin-xr build:all
|
|
87
|
+
# or just the simulator:
|
|
88
|
+
bun run --cwd plugins/plugin-xr simulator:build
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
After building, `GET /api/xr/simulator.js` serves the emulator bundle.
|
|
92
|
+
|
|
93
|
+
## Wire protocol
|
|
94
|
+
|
|
95
|
+
The plugin uses a custom binary framing on top of WebSocket:
|
|
96
|
+
|
|
97
|
+
- **Text frames** — JSON control messages (`XRClientControl` / `XRServerControl`).
|
|
98
|
+
- **Binary frames** — 4-byte big-endian header length, UTF-8 JSON header
|
|
99
|
+
(`XRAudioHeader` or `XRFrameHeader`), then raw payload (audio or JPEG/WebP).
|
|
100
|
+
|
|
101
|
+
The wire types (`XRClientControl`, `XRServerControl`, `XRBinaryHeader`,
|
|
102
|
+
`XRTTSAudioHeader`, `XRPanelConfig`, and the rest of `protocol.ts`) are
|
|
103
|
+
re-exported as types from the package root via `export type *`. The framing
|
|
104
|
+
helpers `encodeBinaryFrame` / `decodeBinaryFrame` are runtime functions that
|
|
105
|
+
live in `src/protocol.ts` and are used internally by the WebSocket server;
|
|
106
|
+
they are not part of the package's public runtime exports.
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elizaos/plugin-xr",
|
|
3
|
+
"version": "2.0.3-beta.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "WebXR audio/video streaming for elizaOS — Quest 3 and XReal glasses",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./package.json": "./package.json",
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"eliza-source": {
|
|
12
|
+
"types": "./src/index.ts",
|
|
13
|
+
"import": "./src/index.ts",
|
|
14
|
+
"default": "./src/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"typecheck": "tsgo --noEmit -p tsconfig.json",
|
|
22
|
+
"lint": "bunx @biomejs/biome check src",
|
|
23
|
+
"test": "bunx vitest run --config ./vitest.config.ts",
|
|
24
|
+
"build": "bun run build:js && bun run build:types",
|
|
25
|
+
"build:all": "bun run build && bun run simulator:build",
|
|
26
|
+
"clean": "node ../../packages/scripts/rm-path-recursive.mjs dist simulator/dist",
|
|
27
|
+
"build:js": "tsup --config ../tsup.plugin-packages.shared.ts",
|
|
28
|
+
"build:types": "tsc --noCheck -p tsconfig.build.json",
|
|
29
|
+
"simulator:build": "cd simulator && bun install && bun run build",
|
|
30
|
+
"simulator:watch": "cd simulator && bun run build:watch"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@elizaos/agent": "2.0.3-beta.5",
|
|
34
|
+
"@elizaos/core": "2.0.3-beta.5",
|
|
35
|
+
"ws": "^8.18.0",
|
|
36
|
+
"zod": "^4.4.3"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@biomejs/biome": "^2.4.14",
|
|
40
|
+
"@types/node": "^25.0.3",
|
|
41
|
+
"@types/ws": "^8.5.10",
|
|
42
|
+
"bun-types": "^1.3.14",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"typescript": "^6.0.3",
|
|
45
|
+
"vitest": "^4.0.17"
|
|
46
|
+
},
|
|
47
|
+
"elizaos": {
|
|
48
|
+
"plugin": {
|
|
49
|
+
"displayName": "XR Streaming",
|
|
50
|
+
"category": "media"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
|
|
57
|
+
}
|