@voicethere/agent 0.7.12 → 0.8.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.
- package/README.md +14 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +11 -2
- package/dist/runtime.js.map +1 -1
- package/dist/templates/echo/agent.js +1 -1
- package/dist/templates/echo-dc/agent.js +1 -1
- package/dist/templates/game-sync/agent.js +162 -125
- package/dist/templates/registry.d.ts +1 -1
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +42 -26
- package/dist/templates/registry.js.map +1 -1
- package/dist/templates/spatial-showcase/agent.js +261 -22
- package/dist/templates/voice-starter/agent.js +1 -1
- package/dist/templates/webhooks/agent.js +2 -2
- package/dist/templates/webhooks-redis/agent.js +11 -4
- package/dist/templates/world-sync/agent.js +829 -0
- package/dist/templates/world-sync-binary/agent.js +1031 -0
- package/package.json +3 -2
- package/templates/README.md +41 -69
- package/templates/crash/README.md +13 -0
- package/templates/echo/README.md +13 -0
- package/templates/{echo.ts → echo/agent.ts} +1 -1
- package/templates/echo-dc/README.md +13 -0
- package/templates/{echo-dc.ts → echo-dc/agent.ts} +1 -1
- package/templates/echo-smoke/README.md +13 -0
- package/templates/game-sync/README.md +21 -0
- package/templates/{game-sync.ts → game-sync/agent.ts} +126 -94
- package/templates/{game-sync-redis.ts → game-sync/redis.ts} +4 -13
- package/templates/{game-sync-sim.ts → game-sync/sim.ts} +14 -7
- package/templates/{game-sync-world-layout.ts → game-sync/world-layout.ts} +72 -30
- package/templates/game-sync-smoke/README.md +13 -0
- package/templates/mix-smoke/README.md +13 -0
- package/templates/positional-tts/README.md +13 -0
- package/templates/recording-consent/README.md +13 -0
- package/templates/redis-sync/README.md +23 -0
- package/templates/redis-sync/agent.ts +72 -95
- package/templates/redis-sync/world-layout.ts +103 -26
- package/templates/spatial-showcase/README.md +13 -0
- package/templates/spatial-showcase/agent.ts +245 -18
- package/templates/spatial-showcase/protocol.ts +79 -0
- package/templates/spatial-showcase/sine.ts +28 -5
- package/templates/voice-showcase/README.md +13 -0
- package/templates/voice-starter/README.md +13 -0
- package/templates/webhooks/README.md +13 -0
- package/templates/{webhooks.ts → webhooks/agent.ts} +1 -1
- package/templates/webhooks-redis/README.md +13 -0
- package/templates/{webhooks-redis.ts → webhooks-redis/agent.ts} +9 -9
- package/templates/world-sync/README.md +20 -0
- package/templates/world-sync/agent.ts +62 -0
- package/templates/world-sync-binary/README.md +21 -0
- package/templates/world-sync-binary/agent.ts +107 -0
- package/templates/world-sync-binary/protocol.ts +281 -0
- /package/templates/{crash.ts → crash/agent.ts} +0 -0
- /package/templates/{echo-smoke.ts → echo-smoke/agent.ts} +0 -0
- /package/templates/{game-sync-protocol.ts → game-sync/protocol.ts} +0 -0
- /package/templates/{game-sync-smoke.ts → game-sync-smoke/agent.ts} +0 -0
- /package/templates/{mix-smoke.ts → mix-smoke/agent.ts} +0 -0
- /package/templates/{agent.ts → voice-starter/agent.ts} +0 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary pose protocol for the single-agent world-sync-binary template.
|
|
3
|
+
*
|
|
4
|
+
* Client → agent (12 bytes): Float32Array [x, y, z] — reuse one Float32Array.
|
|
5
|
+
* Agent → client world snapshot (one growable buffer, rewritten in place):
|
|
6
|
+
* uint32le peerCount
|
|
7
|
+
* for each peer:
|
|
8
|
+
* uint16le sessionIdByteLength
|
|
9
|
+
* utf8 sessionId
|
|
10
|
+
* float32le x, y, z
|
|
11
|
+
*/
|
|
12
|
+
export const POSE_FLOAT_COUNT = 3;
|
|
13
|
+
export const POSE_BYTE_LENGTH = POSE_FLOAT_COUNT * 4;
|
|
14
|
+
|
|
15
|
+
export type Pose = { x: number; y: number; z: number };
|
|
16
|
+
|
|
17
|
+
export type WorldPoseEntry = { sessionId: string; pose: Pose };
|
|
18
|
+
|
|
19
|
+
const encoder = new TextEncoder();
|
|
20
|
+
const decoder = new TextDecoder();
|
|
21
|
+
|
|
22
|
+
const POSE_BYTES = new Uint8Array(POSE_BYTE_LENGTH);
|
|
23
|
+
const POSE_VIEW = new DataView(
|
|
24
|
+
POSE_BYTES.buffer,
|
|
25
|
+
POSE_BYTES.byteOffset,
|
|
26
|
+
POSE_BYTE_LENGTH,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
function asDataView(data: ArrayBufferLike | ArrayBufferView): DataView {
|
|
30
|
+
if (ArrayBuffer.isView(data)) {
|
|
31
|
+
return new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
32
|
+
}
|
|
33
|
+
return new DataView(data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function asUint8View(data: ArrayBufferLike | ArrayBufferView): Uint8Array {
|
|
37
|
+
if (data instanceof Uint8Array) {
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
if (ArrayBuffer.isView(data)) {
|
|
41
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
42
|
+
}
|
|
43
|
+
return new Uint8Array(data);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function utf8ByteLength(value: string): number {
|
|
47
|
+
let bytes = 0;
|
|
48
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
49
|
+
const code = value.charCodeAt(i);
|
|
50
|
+
if (code < 0x80) {
|
|
51
|
+
bytes += 1;
|
|
52
|
+
} else if (code < 0x800) {
|
|
53
|
+
bytes += 2;
|
|
54
|
+
} else if (code >= 0xd800 && code <= 0xdbff) {
|
|
55
|
+
bytes += 4;
|
|
56
|
+
i += 1;
|
|
57
|
+
} else {
|
|
58
|
+
bytes += 3;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return bytes;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function decodePoseBuffer(
|
|
65
|
+
data: ArrayBufferLike | ArrayBufferView | null | undefined,
|
|
66
|
+
): Pose | null {
|
|
67
|
+
if (data == null) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const view = asDataView(data);
|
|
71
|
+
if (view.byteLength < POSE_BYTE_LENGTH) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const x = view.getFloat32(0, true);
|
|
75
|
+
const y = view.getFloat32(4, true);
|
|
76
|
+
const z = view.getFloat32(8, true);
|
|
77
|
+
if (!isFinitePose(x, y, z)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return { x, y, z };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isFinitePose(x: number, y: number, z: number): boolean {
|
|
84
|
+
return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Write xyz from an inbound frame into a persistent Float32Array (no copy). */
|
|
88
|
+
export function decodePoseInto(
|
|
89
|
+
data: ArrayBufferLike | ArrayBufferView | null | undefined,
|
|
90
|
+
dest: Float32Array,
|
|
91
|
+
floatOffset = 0,
|
|
92
|
+
): boolean {
|
|
93
|
+
if (data == null || dest.length < floatOffset + POSE_FLOAT_COUNT) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const view = asDataView(data);
|
|
97
|
+
if (view.byteLength < POSE_BYTE_LENGTH) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const x = view.getFloat32(0, true);
|
|
101
|
+
const y = view.getFloat32(4, true);
|
|
102
|
+
const z = view.getFloat32(8, true);
|
|
103
|
+
if (!isFinitePose(x, y, z)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
dest[floatOffset] = x;
|
|
107
|
+
dest[floatOffset + 1] = y;
|
|
108
|
+
dest[floatOffset + 2] = z;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Pack a pose into the shared 12-byte scratch buffer (overwritten on the next call).
|
|
114
|
+
*/
|
|
115
|
+
export function encodePoseBuffer(pose: Pose): Uint8Array {
|
|
116
|
+
POSE_VIEW.setFloat32(0, pose.x, true);
|
|
117
|
+
POSE_VIEW.setFloat32(4, pose.y, true);
|
|
118
|
+
POSE_VIEW.setFloat32(8, pose.z, true);
|
|
119
|
+
return POSE_BYTES;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** One growable snapshot buffer; encode* rewrite it in place and return a view. */
|
|
123
|
+
export class WorldSnapshotBuffer {
|
|
124
|
+
private bytes: Uint8Array;
|
|
125
|
+
private view: DataView;
|
|
126
|
+
private used = 0;
|
|
127
|
+
private out: Uint8Array;
|
|
128
|
+
|
|
129
|
+
constructor(capacity = 256) {
|
|
130
|
+
this.bytes = new Uint8Array(capacity);
|
|
131
|
+
this.view = new DataView(
|
|
132
|
+
this.bytes.buffer,
|
|
133
|
+
this.bytes.byteOffset,
|
|
134
|
+
this.bytes.byteLength,
|
|
135
|
+
);
|
|
136
|
+
this.out = this.bytes.subarray(0, 0);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get byteLength(): number {
|
|
140
|
+
return this.used;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* View of the last encoded snapshot (same backing buffer, length = used).
|
|
145
|
+
* The view object is reused while the encoded length is unchanged.
|
|
146
|
+
*/
|
|
147
|
+
bytesView(): Uint8Array {
|
|
148
|
+
if (
|
|
149
|
+
this.out.byteLength !== this.used ||
|
|
150
|
+
this.out.buffer !== this.bytes.buffer
|
|
151
|
+
) {
|
|
152
|
+
this.out = this.bytes.subarray(0, this.used);
|
|
153
|
+
}
|
|
154
|
+
return this.out;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
encode(entries: Iterable<WorldPoseEntry>): Uint8Array {
|
|
158
|
+
const list = Array.isArray(entries) ? entries : [...entries];
|
|
159
|
+
let size = 4;
|
|
160
|
+
for (const entry of list) {
|
|
161
|
+
size += 2 + utf8ByteLength(entry.sessionId) + POSE_BYTE_LENGTH;
|
|
162
|
+
}
|
|
163
|
+
this.ensure(size);
|
|
164
|
+
this.view.setUint32(0, list.length, true);
|
|
165
|
+
let offset = 4;
|
|
166
|
+
for (const entry of list) {
|
|
167
|
+
offset = this.writePeer(
|
|
168
|
+
offset,
|
|
169
|
+
entry.sessionId,
|
|
170
|
+
entry.pose.x,
|
|
171
|
+
entry.pose.y,
|
|
172
|
+
entry.pose.z,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
this.used = offset;
|
|
176
|
+
return this.bytesView();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
encodePacked(
|
|
180
|
+
sessionIds: readonly string[],
|
|
181
|
+
xyz: Float32Array,
|
|
182
|
+
count: number,
|
|
183
|
+
): Uint8Array {
|
|
184
|
+
let size = 4;
|
|
185
|
+
for (let i = 0; i < count; i += 1) {
|
|
186
|
+
size += 2 + utf8ByteLength(sessionIds[i] ?? "") + POSE_BYTE_LENGTH;
|
|
187
|
+
}
|
|
188
|
+
this.ensure(size);
|
|
189
|
+
this.view.setUint32(0, count, true);
|
|
190
|
+
let offset = 4;
|
|
191
|
+
for (let i = 0; i < count; i += 1) {
|
|
192
|
+
const floatOffset = i * POSE_FLOAT_COUNT;
|
|
193
|
+
offset = this.writePeer(
|
|
194
|
+
offset,
|
|
195
|
+
sessionIds[i] ?? "",
|
|
196
|
+
xyz[floatOffset] ?? 0,
|
|
197
|
+
xyz[floatOffset + 1] ?? 0,
|
|
198
|
+
xyz[floatOffset + 2] ?? 0,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
this.used = offset;
|
|
202
|
+
return this.bytesView();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private writePeer(
|
|
206
|
+
offset: number,
|
|
207
|
+
sessionId: string,
|
|
208
|
+
x: number,
|
|
209
|
+
y: number,
|
|
210
|
+
z: number,
|
|
211
|
+
): number {
|
|
212
|
+
const { written } = encoder.encodeInto(
|
|
213
|
+
sessionId,
|
|
214
|
+
this.bytes.subarray(offset + 2),
|
|
215
|
+
);
|
|
216
|
+
this.view.setUint16(offset, written, true);
|
|
217
|
+
offset += 2 + written;
|
|
218
|
+
this.view.setFloat32(offset, x, true);
|
|
219
|
+
this.view.setFloat32(offset + 4, y, true);
|
|
220
|
+
this.view.setFloat32(offset + 8, z, true);
|
|
221
|
+
return offset + POSE_BYTE_LENGTH;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private ensure(size: number): void {
|
|
225
|
+
if (this.bytes.byteLength >= size) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
let capacity = Math.max(this.bytes.byteLength, 1);
|
|
229
|
+
while (capacity < size) {
|
|
230
|
+
capacity *= 2;
|
|
231
|
+
}
|
|
232
|
+
this.bytes = new Uint8Array(capacity);
|
|
233
|
+
this.view = new DataView(
|
|
234
|
+
this.bytes.buffer,
|
|
235
|
+
this.bytes.byteOffset,
|
|
236
|
+
this.bytes.byteLength,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const defaultSnapshot = new WorldSnapshotBuffer();
|
|
242
|
+
|
|
243
|
+
export function encodeWorldSnapshot(
|
|
244
|
+
entries: Iterable<WorldPoseEntry>,
|
|
245
|
+
): Uint8Array {
|
|
246
|
+
return defaultSnapshot.encode(entries);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function decodeWorldSnapshot(
|
|
250
|
+
data: ArrayBufferLike | ArrayBufferView,
|
|
251
|
+
): WorldPoseEntry[] {
|
|
252
|
+
const bytes = asUint8View(data);
|
|
253
|
+
if (bytes.byteLength < 4) {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
const view = asDataView(bytes);
|
|
257
|
+
const count = view.getUint32(0, true);
|
|
258
|
+
const entries: WorldPoseEntry[] = [];
|
|
259
|
+
let offset = 4;
|
|
260
|
+
for (let i = 0; i < count; i += 1) {
|
|
261
|
+
if (offset + 2 > bytes.byteLength) {
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
const idLength = view.getUint16(offset, true);
|
|
265
|
+
offset += 2;
|
|
266
|
+
if (offset + idLength + POSE_BYTE_LENGTH > bytes.byteLength) {
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
const sessionId = decoder.decode(bytes.subarray(offset, offset + idLength));
|
|
270
|
+
offset += idLength;
|
|
271
|
+
const x = view.getFloat32(offset, true);
|
|
272
|
+
const y = view.getFloat32(offset + 4, true);
|
|
273
|
+
const z = view.getFloat32(offset + 8, true);
|
|
274
|
+
offset += POSE_BYTE_LENGTH;
|
|
275
|
+
if (!isFinitePose(x, y, z)) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
entries.push({ sessionId, pose: { x, y, z } });
|
|
279
|
+
}
|
|
280
|
+
return entries;
|
|
281
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|