@neta-art/cohub 4.8.0 → 4.9.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { A as CronJobsApi, C as ReferencesApi, D as PromptsApi, E as SkillsApi, O as ModelsApi, S as SessionAccessApi, T as PublicAssetsApi, _ as createSessionGenerationStreamClient, a as ReferralsApi, b as createSessionPatchReducer, c as TasksApi, d as SpaceClient, f as SpacesApi, g as SessionGenerationStreamClient, h as buildSpacePath, i as WorksApi, j as ChannelsApi, k as GenerationsApi, l as BoardClient, m as buildSpaceInvitePath, n as createHttpClient, o as UsersApi, p as PublicInviteApi, r as WorkCommerceApi, s as UserApi, t as CohubHttpClient, u as BoardTransactionError, v as parseAssistantMessageCommit, w as SearchApi, x as ensureRealtimeConnected, y as SessionPatchReducer } from "./chunks/http.js";
2
- import { a as COHUB_SOURCE_HEADER, c as hasRequestSourceIdentity, d as mergeRequestSourceIntoMeta, f as normalizeRequestSource, g as resolveRequestSourceChannel, h as requestSourceToHeaders, i as sanitizeAccessToken, l as isRequestSourceEmpty, m as readRequestSourceFromEnv, n as HttpTransport, o as COHUB_SOURCE_HEADER_NAMES, p as parseRequestSourceFromHeaders, r as joinApiUrl, s as REQUEST_SOURCE_VIA_MAX_LENGTH, t as HttpError, u as isRequestSourceUuid } from "./chunks/transport.js";
2
+ import { _ as REALTIME_ROOM_EVENT_NAME_PATTERN, a as COHUB_SOURCE_HEADER, c as hasRequestSourceIdentity, d as mergeRequestSourceIntoMeta, f as normalizeRequestSource, g as resolveRequestSourceChannel, h as requestSourceToHeaders, i as sanitizeAccessToken, l as isRequestSourceEmpty, m as readRequestSourceFromEnv, n as HttpTransport, o as COHUB_SOURCE_HEADER_NAMES, p as parseRequestSourceFromHeaders, r as joinApiUrl, s as REQUEST_SOURCE_VIA_MAX_LENGTH, t as HttpError, u as isRequestSourceUuid } from "./chunks/transport.js";
3
3
  import { a as resolveApiBaseUrl, c as resolveWebsocketUrl, i as normalizeWebsocketUrl, n as normalizeBaseUrl, o as resolveCohubEnvironment, r as normalizeVoiceInputWebsocketUrl, s as resolveVoiceInputWebsocketUrl, t as COHUB_ENVIRONMENTS } from "./chunks/environment.js";
4
4
  import { a as extractBillingPayload, c as isFeatureNotEntitledError, i as FEATURE_NOT_ENTITLED_ERROR_CODE, l as isHttpErrorCode, n as createWebsocketClient, o as isBillingAccessBlockedCode, r as BILLING_ACCESS_BLOCKED_ERROR_CODE, s as isBillingAccessBlockedError, t as WebsocketClient } from "./chunks/websocket.js";
5
5
  import { VoiceApi, VoiceInputClient, createVoiceInputClient } from "./voice-input.js";
@@ -81,6 +81,779 @@ var ExploreApi = class {
81
81
  }
82
82
  };
83
83
  //#endregion
84
+ //#region ../protocol/dist/board-constants.js
85
+ const DEFAULT_BOARD_RENDER_LIMITS = {
86
+ particles: 2e4,
87
+ vertices: 5e5,
88
+ dynamicVertices: 15e4,
89
+ drawCalls: 400,
90
+ filterPasses: 24,
91
+ renderTexturePixels: 16777216,
92
+ textureBytes: 512 * 1024 * 1024,
93
+ bufferBytes: 256 * 1024 * 1024,
94
+ simulationSteps: 1e5
95
+ };
96
+ const BOARD_BUILTIN_CLIP_KINDS = [
97
+ "motion.keyframes",
98
+ "motion.path",
99
+ "draw.reveal",
100
+ "draw.handwrite",
101
+ "text.reveal",
102
+ "effects.particles",
103
+ "effects.trail",
104
+ "effects.impact",
105
+ "effects.flash",
106
+ "effects.color",
107
+ "camera.pan",
108
+ "camera.zoom",
109
+ "camera.shake"
110
+ ];
111
+ const BOARD_BUILTIN_EFFECT_KINDS = ["effects.pulse", "effects.float"];
112
+ const BOARD_ARROW_STROKE_SIZE = 2.5;
113
+ const BOARD_BUILTIN_CAPABILITIES = [...BOARD_BUILTIN_CLIP_KINDS.map((id) => ({
114
+ kind: "clip",
115
+ id,
116
+ version: 1,
117
+ renderers: ["webgpu", "webgl"]
118
+ })), ...BOARD_BUILTIN_EFFECT_KINDS.map((id) => ({
119
+ kind: "effect",
120
+ id,
121
+ version: 1,
122
+ renderers: ["webgpu", "webgl"]
123
+ }))];
124
+ //#endregion
125
+ //#region ../protocol/dist/realtime/board-awareness.js
126
+ const idSchema$1 = z.string().min(1).max(160);
127
+ const finiteSchema$1 = z.number().finite();
128
+ const BoardAwarenessPointSchema = z.object({
129
+ x: finiteSchema$1,
130
+ y: finiteSchema$1
131
+ });
132
+ const BoardAwarenessDrawPointSchema = BoardAwarenessPointSchema.extend({ p: finiteSchema$1.min(0).max(1) });
133
+ const BoardAwarenessFrameSchema = z.object({
134
+ x: finiteSchema$1,
135
+ y: finiteSchema$1,
136
+ width: finiteSchema$1.positive(),
137
+ height: finiteSchema$1.positive(),
138
+ rotation: finiteSchema$1
139
+ });
140
+ const arrowEndpointSchema = z.union([z.object({
141
+ kind: z.literal("point"),
142
+ x: finiteSchema$1,
143
+ y: finiteSchema$1
144
+ }), z.object({
145
+ kind: z.literal("binding"),
146
+ target: idSchema$1,
147
+ nx: finiteSchema$1,
148
+ ny: finiteSchema$1,
149
+ precise: z.boolean()
150
+ })]);
151
+ const BoardAwarenessNodePreviewSchema = z.object({
152
+ nodeId: idSchema$1,
153
+ frame: BoardAwarenessFrameSchema,
154
+ arrow: z.object({
155
+ start: arrowEndpointSchema,
156
+ end: arrowEndpointSchema,
157
+ bend: finiteSchema$1
158
+ }).optional()
159
+ });
160
+ const BoardAwarenessStateUpdateSchema = z.object({
161
+ type: z.literal("state"),
162
+ client: z.object({ formFactor: z.enum(["desktop", "mobile"]) }).optional(),
163
+ cursor: BoardAwarenessPointSchema.extend({ pointerType: z.enum([
164
+ "mouse",
165
+ "pen",
166
+ "touch"
167
+ ]) }).nullable(),
168
+ tool: z.string().min(1).max(40),
169
+ selection: z.object({
170
+ ids: z.array(idSchema$1).max(64),
171
+ count: z.number().int().nonnegative().max(5e4),
172
+ bounds: BoardAwarenessFrameSchema.nullable()
173
+ }),
174
+ editingId: idSchema$1.nullable()
175
+ });
176
+ const BoardAwarenessGestureSchema = z.discriminatedUnion("kind", [
177
+ z.object({
178
+ kind: z.literal("draw"),
179
+ id: idSchema$1,
180
+ nodeId: idSchema$1,
181
+ color: z.string().min(1).max(64),
182
+ size: finiteSchema$1.positive().max(256),
183
+ from: z.number().int().nonnegative().max(1e5),
184
+ points: z.array(BoardAwarenessDrawPointSchema).min(1).max(64)
185
+ }),
186
+ z.object({
187
+ kind: z.literal("arrow"),
188
+ id: idSchema$1,
189
+ nodeId: idSchema$1,
190
+ start: BoardAwarenessPointSchema,
191
+ current: BoardAwarenessPointSchema,
192
+ color: z.string().min(1).max(64),
193
+ size: finiteSchema$1.positive().max(256).default(BOARD_ARROW_STROKE_SIZE)
194
+ }),
195
+ z.object({
196
+ kind: z.literal("box"),
197
+ id: idSchema$1,
198
+ nodeId: idSchema$1,
199
+ shape: z.enum(["geo", "frame"]),
200
+ start: BoardAwarenessPointSchema,
201
+ current: BoardAwarenessPointSchema,
202
+ color: z.string().min(1).max(64),
203
+ geo: z.string().min(1).max(40)
204
+ }),
205
+ z.object({
206
+ kind: z.literal("transform"),
207
+ id: idSchema$1,
208
+ mode: z.enum([
209
+ "translate",
210
+ "resize",
211
+ "rotate",
212
+ "arrow"
213
+ ]),
214
+ nodes: z.array(BoardAwarenessNodePreviewSchema).max(64),
215
+ bounds: BoardAwarenessFrameSchema.nullable()
216
+ })
217
+ ]);
218
+ const BoardAwarenessUpdateSchema = z.discriminatedUnion("type", [
219
+ BoardAwarenessStateUpdateSchema,
220
+ z.object({
221
+ type: z.literal("gesture"),
222
+ gesture: BoardAwarenessGestureSchema
223
+ }),
224
+ z.object({
225
+ type: z.literal("gesture.end"),
226
+ gestureId: idSchema$1,
227
+ resultingNodeIds: z.array(idSchema$1).max(64)
228
+ }),
229
+ z.object({
230
+ type: z.literal("gesture.cancel"),
231
+ gestureId: idSchema$1
232
+ })
233
+ ]);
234
+ const BoardAwarenessClientPayloadSchema = z.object({
235
+ spaceId: z.string().uuid(),
236
+ boardId: z.string().uuid(),
237
+ seq: z.number().int().nonnegative(),
238
+ update: BoardAwarenessUpdateSchema
239
+ });
240
+ //#endregion
241
+ //#region ../protocol/dist/realtime/schema.js
242
+ const contentBlockMetaSchema = z.record(z.string(), z.unknown());
243
+ const realtimeRoomSchema = z.string().regex(/^(space|user|board|room):[^:]+$/);
244
+ const contentBlockSchema = z.discriminatedUnion("type", [
245
+ z.object({
246
+ type: z.literal("text"),
247
+ text: z.string(),
248
+ _meta: contentBlockMetaSchema.optional()
249
+ }),
250
+ z.object({
251
+ type: z.literal("thinking"),
252
+ thinking: z.string(),
253
+ signature: z.string().optional(),
254
+ _meta: contentBlockMetaSchema.optional()
255
+ }),
256
+ z.object({
257
+ type: z.literal("image"),
258
+ source: z.union([z.object({
259
+ type: z.literal("url"),
260
+ url: z.string().url()
261
+ }), z.object({
262
+ type: z.literal("base64"),
263
+ media_type: z.string(),
264
+ data: z.string()
265
+ })]),
266
+ _meta: contentBlockMetaSchema.optional()
267
+ }),
268
+ z.object({
269
+ type: z.literal("shell_command"),
270
+ command: z.string(),
271
+ rawText: z.string(),
272
+ _meta: contentBlockMetaSchema.optional()
273
+ }),
274
+ z.object({
275
+ type: z.literal("tool_use"),
276
+ id: z.string(),
277
+ name: z.string(),
278
+ input: z.record(z.string(), z.unknown()),
279
+ _meta: contentBlockMetaSchema.optional()
280
+ }),
281
+ z.object({
282
+ type: z.literal("tool_result"),
283
+ tool_use_id: z.string(),
284
+ content: z.union([z.string(), z.array(z.unknown())]),
285
+ is_error: z.boolean().optional(),
286
+ _meta: contentBlockMetaSchema.optional()
287
+ }),
288
+ z.object({
289
+ type: z.literal("system_note"),
290
+ note_type: z.enum([
291
+ "session_created",
292
+ "forked",
293
+ "compacted",
294
+ "info"
295
+ ]),
296
+ text: z.string(),
297
+ _meta: contentBlockMetaSchema.optional()
298
+ })
299
+ ]);
300
+ z.discriminatedUnion("type", [
301
+ z.object({
302
+ type: z.literal("auth"),
303
+ requestId: z.string().optional(),
304
+ payload: z.object({
305
+ token: z.string().min(1),
306
+ capabilities: z.array(z.string().min(1)).optional()
307
+ })
308
+ }),
309
+ z.object({
310
+ type: z.literal("subscribe"),
311
+ requestId: z.string().optional(),
312
+ payload: z.object({ rooms: z.array(realtimeRoomSchema).min(1) })
313
+ }),
314
+ z.object({
315
+ type: z.literal("unsubscribe"),
316
+ requestId: z.string().optional(),
317
+ payload: z.object({ rooms: z.array(realtimeRoomSchema).min(1) })
318
+ }),
319
+ z.object({
320
+ type: z.literal("session.message.create"),
321
+ requestId: z.string().optional(),
322
+ payload: z.object({
323
+ spaceId: z.string().uuid(),
324
+ sessionId: z.string().uuid(),
325
+ clientMessageId: z.string().optional(),
326
+ content: z.array(contentBlockSchema).min(1),
327
+ model: z.string().optional(),
328
+ provider: z.string().optional(),
329
+ thinkingLevel: z.enum([
330
+ "off",
331
+ "minimal",
332
+ "low",
333
+ "medium",
334
+ "high",
335
+ "xhigh",
336
+ "max"
337
+ ]).optional()
338
+ })
339
+ }),
340
+ z.object({
341
+ type: z.literal("presence.update"),
342
+ requestId: z.string().optional(),
343
+ payload: z.object({
344
+ spaceId: z.string().uuid(),
345
+ meta: z.record(z.string(), z.unknown()).nullable().optional()
346
+ })
347
+ }),
348
+ z.object({
349
+ type: z.literal("board.awareness.update"),
350
+ requestId: z.string().optional(),
351
+ payload: BoardAwarenessClientPayloadSchema
352
+ }),
353
+ z.object({
354
+ type: z.literal("realtime.room.join"),
355
+ requestId: z.string().optional(),
356
+ payload: z.object({
357
+ roomId: z.string().uuid(),
358
+ ticket: z.string().min(1)
359
+ })
360
+ }),
361
+ z.object({
362
+ type: z.literal("realtime.room.publish"),
363
+ requestId: z.string().optional(),
364
+ payload: z.object({
365
+ roomId: z.string().uuid(),
366
+ event: z.string().regex(REALTIME_ROOM_EVENT_NAME_PATTERN),
367
+ data: z.unknown(),
368
+ clientEventId: z.string().max(128).optional()
369
+ })
370
+ }),
371
+ z.object({
372
+ type: z.literal("realtime.room.leave"),
373
+ requestId: z.string().optional(),
374
+ payload: z.object({ roomId: z.string().uuid() })
375
+ }),
376
+ z.object({
377
+ type: z.literal("realtime.room.presence.update"),
378
+ requestId: z.string().optional(),
379
+ payload: z.object({
380
+ roomId: z.string().uuid(),
381
+ presence: z.record(z.string(), z.unknown()).nullable()
382
+ })
383
+ }),
384
+ z.object({
385
+ type: z.literal("ping"),
386
+ requestId: z.string().optional(),
387
+ payload: z.record(z.string(), z.unknown()).optional()
388
+ }),
389
+ z.object({
390
+ type: z.literal("ack"),
391
+ requestId: z.string().optional(),
392
+ payload: z.object({ eventId: z.string().optional() }).optional()
393
+ })
394
+ ]);
395
+ z.object({
396
+ id: z.string(),
397
+ timestamp: z.number(),
398
+ domain: z.enum([
399
+ "system",
400
+ "session",
401
+ "space",
402
+ "label",
403
+ "room"
404
+ ]),
405
+ type: z.string(),
406
+ requestId: z.string().nullable().optional(),
407
+ spaceId: z.string().nullable().optional(),
408
+ sessionId: z.string().nullable().optional(),
409
+ rooms: z.array(realtimeRoomSchema).optional(),
410
+ payload: z.record(z.string(), z.unknown())
411
+ });
412
+ z.discriminatedUnion("t", [z.object({
413
+ t: z.literal("d"),
414
+ sid: z.string().min(1),
415
+ s: z.number().int().nonnegative(),
416
+ b: z.number().int().nonnegative(),
417
+ v: z.unknown()
418
+ }), z.object({
419
+ t: z.literal("p"),
420
+ sid: z.string().min(1),
421
+ s: z.number().int().nonnegative(),
422
+ b: z.number().int().nonnegative(),
423
+ o: z.enum([
424
+ "append",
425
+ "replace",
426
+ "add",
427
+ "merge",
428
+ "remove"
429
+ ]),
430
+ p: z.string().min(1),
431
+ v: z.unknown().optional()
432
+ })]);
433
+ //#endregion
434
+ //#region src/apis/work-realtime.ts
435
+ const randomId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`;
436
+ const textEncoder = new TextEncoder();
437
+ const byteLength = (text) => textEncoder.encode(text).byteLength;
438
+ /**
439
+ * Ceiling on deltas held during a join, so a stalled handshake cannot buffer without
440
+ * limit. Dropping the tail needs no extra signal: the gap detection below reports it.
441
+ */
442
+ const WORK_ROOM_MAX_JOIN_BUFFER = 512;
443
+ /** Room events that mutate state, so they must not be applied before the join snapshot. */
444
+ const WORK_ROOM_DELTA_EVENTS = /* @__PURE__ */ new Set([
445
+ "realtime.room.event",
446
+ "realtime.room.member.joined",
447
+ "realtime.room.member.left",
448
+ "realtime.room.presence.updated"
449
+ ]);
450
+ const requestError = (event) => {
451
+ const payload = event.payload;
452
+ return new Error(typeof payload.message === "string" ? payload.message : typeof payload.code === "string" ? payload.code : "room request failed");
453
+ };
454
+ const isRoomEvent = (event, roomId) => {
455
+ if (event.domain !== "room") return false;
456
+ return event.payload.roomId === roomId || event.rooms?.includes(`room:${roomId}`);
457
+ };
458
+ var WorkRoom = class {
459
+ websocket;
460
+ admission;
461
+ id;
462
+ code;
463
+ createdAt;
464
+ expiresAt;
465
+ maxParticipants;
466
+ seatPerUser;
467
+ /** Opaque, stable identity of this viewer inside the room. */
468
+ userKey;
469
+ _participantId;
470
+ _state = "connecting";
471
+ _members = [];
472
+ joinBuffer = null;
473
+ lastSequence = 0;
474
+ hasJoined = false;
475
+ explicitLeave = false;
476
+ expiryTimer = null;
477
+ pending = /* @__PURE__ */ new Map();
478
+ handlers = /* @__PURE__ */ new Map();
479
+ allHandlers = /* @__PURE__ */ new Set();
480
+ stateHandlers = /* @__PURE__ */ new Set();
481
+ membersHandlers = /* @__PURE__ */ new Set();
482
+ outOfSyncHandlers = /* @__PURE__ */ new Set();
483
+ sendErrorHandlers = /* @__PURE__ */ new Set();
484
+ offEvent;
485
+ offOpen;
486
+ offClose;
487
+ offReconnecting;
488
+ constructor(websocket, admission) {
489
+ this.websocket = websocket;
490
+ this.admission = admission;
491
+ this.id = admission.room.id;
492
+ this.code = admission.room.code;
493
+ this.createdAt = admission.room.createdAt;
494
+ this.expiresAt = admission.room.expiresAt;
495
+ this.maxParticipants = admission.room.maxParticipants;
496
+ this.seatPerUser = admission.room.seatPerUser === true;
497
+ this.userKey = admission.userKey;
498
+ this._participantId = admission.participantId;
499
+ this.offEvent = websocket.on("event", (event) => this.handleEvent(event));
500
+ this.offOpen = websocket.on("open", () => {
501
+ if (!this.hasJoined || this.explicitLeave || this._state === "expired" || this._state === "closed") return;
502
+ this.rejoin();
503
+ });
504
+ this.offReconnecting = websocket.on("reconnecting", () => {
505
+ if (this.hasJoined && !this.explicitLeave) this.setState("reconnecting");
506
+ });
507
+ this.offClose = websocket.on("close", ({ willReconnect }) => {
508
+ this.rejectPending(/* @__PURE__ */ new Error("room connection closed"));
509
+ if (this.explicitLeave || !willReconnect) {
510
+ if (this._state !== "expired") this.setState("closed");
511
+ this.hasJoined = false;
512
+ this.dispose();
513
+ } else if (this.hasJoined) this.setState("reconnecting");
514
+ });
515
+ const delay = Math.max(0, Date.parse(this.expiresAt) - Date.now());
516
+ this.expiryTimer = setTimeout(() => this.expire(), Math.min(delay, 2147e6));
517
+ }
518
+ get state() {
519
+ return this._state;
520
+ }
521
+ /**
522
+ * Identity of this connection inside the room. A `seatPerUser` join can take over a
523
+ * seat the viewer already holds, so this may differ from the id issued at admission.
524
+ */
525
+ get participantId() {
526
+ return this._participantId;
527
+ }
528
+ get members() {
529
+ return this._members.slice();
530
+ }
531
+ async connect() {
532
+ await this.joinOverWebsocket(false);
533
+ return this;
534
+ }
535
+ subscribe(type, handler) {
536
+ const handlers = this.handlers.get(type) ?? /* @__PURE__ */ new Set();
537
+ handlers.add(handler);
538
+ this.handlers.set(type, handlers);
539
+ return () => handlers.delete(handler);
540
+ }
541
+ subscribeAll(handler) {
542
+ this.allHandlers.add(handler);
543
+ return () => this.allHandlers.delete(handler);
544
+ }
545
+ onStateChange(handler) {
546
+ this.stateHandlers.add(handler);
547
+ return () => this.stateHandlers.delete(handler);
548
+ }
549
+ onMembersChanged(handler) {
550
+ this.membersHandlers.add(handler);
551
+ return () => this.membersHandlers.delete(handler);
552
+ }
553
+ onOutOfSync(handler) {
554
+ this.outOfSyncHandlers.add(handler);
555
+ return () => this.outOfSyncHandlers.delete(handler);
556
+ }
557
+ /** Reports failures of {@link send}, which has no ack to reject. */
558
+ onSendError(handler) {
559
+ this.sendErrorHandlers.add(handler);
560
+ return () => this.sendErrorHandlers.delete(handler);
561
+ }
562
+ /**
563
+ * Publishes without waiting for the server ack, for high-frequency traffic such as
564
+ * input frames. Awaiting {@link publish} instead caps a loop at `1000 / rtt` events
565
+ * per second. Ordering still holds, but failures surface through
566
+ * {@link onSendError} rather than a rejected promise, and calls are ignored while
567
+ * the room is not joined; watch {@link onStateChange} to know when it resumes.
568
+ */
569
+ send(type, data) {
570
+ if (this._state !== "joined" || !this.hasJoined) return;
571
+ const failure = this.validateSend(type, data);
572
+ if (failure) {
573
+ for (const handler of this.sendErrorHandlers) handler(failure);
574
+ return;
575
+ }
576
+ this.websocket.publishRealtimeRoom({
577
+ roomId: this.id,
578
+ event: type,
579
+ data
580
+ }).catch((error) => {
581
+ const reason = error instanceof Error ? error : new Error(String(error));
582
+ for (const handler of this.sendErrorHandlers) handler(reason);
583
+ });
584
+ }
585
+ validateSend(type, data) {
586
+ if (!REALTIME_ROOM_EVENT_NAME_PATTERN.test(type)) return /* @__PURE__ */ new Error(`invalid room event name: ${type}`);
587
+ let encoded;
588
+ try {
589
+ encoded = JSON.stringify(data);
590
+ } catch {
591
+ return /* @__PURE__ */ new Error("room event data is not serializable");
592
+ }
593
+ if (encoded === void 0) return /* @__PURE__ */ new Error("room event data is not serializable");
594
+ if (byteLength(encoded) > 16384) return /* @__PURE__ */ new Error("room event payload is too large");
595
+ return null;
596
+ }
597
+ async publish(type, data, options) {
598
+ this.assertJoined();
599
+ const failure = this.validateSend(type, data);
600
+ if (failure) throw failure;
601
+ const requestId = randomId();
602
+ const response = await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.request.ok"]), () => this.websocket.publishRealtimeRoom({
603
+ roomId: this.id,
604
+ event: type,
605
+ data,
606
+ clientEventId: options?.clientEventId,
607
+ requestId
608
+ }));
609
+ const payload = response.payload;
610
+ return {
611
+ eventId: typeof payload.eventId === "string" ? payload.eventId : response.id,
612
+ sequence: typeof payload.sequence === "number" ? payload.sequence : 0,
613
+ clientEventId: typeof payload.clientEventId === "string" ? payload.clientEventId : null
614
+ };
615
+ }
616
+ async setPresence(presence) {
617
+ this.assertJoined();
618
+ const requestId = randomId();
619
+ await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.request.ok"]), () => this.websocket.updateRealtimeRoomPresence({
620
+ roomId: this.id,
621
+ presence,
622
+ requestId
623
+ }));
624
+ }
625
+ async leave() {
626
+ if (this.explicitLeave || this._state === "closed" || this._state === "expired") return;
627
+ this.explicitLeave = true;
628
+ try {
629
+ if (this.hasJoined && this.websocket.state === "open") {
630
+ const requestId = randomId();
631
+ await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.request.ok"]), () => this.websocket.leaveRealtimeRoom({
632
+ roomId: this.id,
633
+ requestId
634
+ }));
635
+ }
636
+ } finally {
637
+ this.hasJoined = false;
638
+ this.setState("closed");
639
+ this.dispose();
640
+ }
641
+ }
642
+ async rejoin() {
643
+ if (this.explicitLeave || this._state === "expired" || this._state === "closed") return;
644
+ try {
645
+ await this.joinOverWebsocket(true);
646
+ } catch (error) {
647
+ if (this.state === "expired") return;
648
+ this.setState("closed");
649
+ this.dispose();
650
+ console.warn("[Cohub WorkRoom] failed to rejoin room", error);
651
+ }
652
+ }
653
+ async joinOverWebsocket(isReconnect) {
654
+ if (!this.websocket.supportsCapability("realtime.room.v1") && this.websocket.state === "open") throw new Error("Realtime rooms are not supported by the Gateway");
655
+ this.setState(isReconnect ? "reconnecting" : "connecting");
656
+ const requestId = randomId();
657
+ this.joinBuffer = [];
658
+ try {
659
+ const payload = (await this.request(requestId, /* @__PURE__ */ new Set(["realtime.room.joined"]), () => this.websocket.joinRealtimeRoom({
660
+ roomId: this.id,
661
+ ticket: this.admission.ticket,
662
+ requestId
663
+ }))).payload;
664
+ if (typeof payload.participantId !== "string" || !Array.isArray(payload.members)) throw new Error("invalid room join response");
665
+ this._participantId = payload.participantId;
666
+ this._members = payload.members;
667
+ this.lastSequence = typeof payload.sequence === "number" ? payload.sequence : 0;
668
+ } catch (error) {
669
+ this.joinBuffer = null;
670
+ throw error;
671
+ }
672
+ this.hasJoined = true;
673
+ this.setState("joined");
674
+ this.notifyMembers();
675
+ const buffered = this.joinBuffer ?? [];
676
+ this.joinBuffer = null;
677
+ for (const item of buffered) {
678
+ const sequence = item.payload.sequence;
679
+ if (typeof sequence === "number" && sequence <= this.lastSequence) continue;
680
+ this.handleEvent(item);
681
+ }
682
+ }
683
+ request(requestId, expected, send) {
684
+ return new Promise((resolve, reject) => {
685
+ const timer = setTimeout(() => {
686
+ this.pending.delete(requestId);
687
+ reject(/* @__PURE__ */ new Error("room request timed out"));
688
+ }, 2e4);
689
+ this.pending.set(requestId, {
690
+ expected,
691
+ resolve,
692
+ reject,
693
+ timer
694
+ });
695
+ send().catch((error) => {
696
+ this.pending.delete(requestId);
697
+ clearTimeout(timer);
698
+ reject(error instanceof Error ? error : new Error(String(error)));
699
+ });
700
+ });
701
+ }
702
+ /** Detaches the entry awaiting this requestId, so the caller can settle it. */
703
+ takePending(requestId) {
704
+ const pending = requestId ? this.pending.get(requestId) : void 0;
705
+ if (!pending || !requestId) return null;
706
+ this.pending.delete(requestId);
707
+ clearTimeout(pending.timer);
708
+ return pending;
709
+ }
710
+ handleEvent(event) {
711
+ if (!isRoomEvent(event, this.id)) {
712
+ if (event.type === "system.request.error") this.takePending(event.requestId)?.reject(requestError(event));
713
+ return;
714
+ }
715
+ if (event.type === "realtime.room.request.error") {
716
+ const failure = requestError(event);
717
+ if (event.requestId) {
718
+ this.takePending(event.requestId)?.reject(failure);
719
+ return;
720
+ }
721
+ for (const handler of this.sendErrorHandlers) handler(failure);
722
+ return;
723
+ }
724
+ const awaiting = event.requestId ? this.pending.get(event.requestId) : void 0;
725
+ if (awaiting?.expected.has(event.type)) {
726
+ this.takePending(event.requestId);
727
+ awaiting.resolve(event);
728
+ }
729
+ if (this.joinBuffer && WORK_ROOM_DELTA_EVENTS.has(event.type)) {
730
+ if (this.joinBuffer.length < WORK_ROOM_MAX_JOIN_BUFFER) this.joinBuffer.push(event);
731
+ return;
732
+ }
733
+ const payload = event.payload;
734
+ const sequence = typeof payload.sequence === "number" ? payload.sequence : null;
735
+ if (sequence !== null) this.observeSequence(sequence);
736
+ if (event.type === "realtime.room.member.joined" || event.type === "realtime.room.member.left" || event.type === "realtime.room.presence.updated") {
737
+ const member = payload.member;
738
+ if (member?.participantId) {
739
+ const members = new Map(this._members.map((item) => [item.participantId, item]));
740
+ if (event.type === "realtime.room.member.left") members.delete(member.participantId);
741
+ else members.set(member.participantId, member);
742
+ this._members = [...members.values()];
743
+ this.notifyMembers();
744
+ }
745
+ return;
746
+ }
747
+ if (event.type === "realtime.room.closed") {
748
+ this.hasJoined = false;
749
+ if (payload.reason === "expired") this.setState("expired");
750
+ else this.setState("closed");
751
+ this.rejectPending(/* @__PURE__ */ new Error(`room closed: ${typeof payload.reason === "string" ? payload.reason : "unknown"}`));
752
+ this.dispose();
753
+ return;
754
+ }
755
+ if (event.type !== "realtime.room.event") return;
756
+ const roomEvent = event;
757
+ const roomPayload = roomEvent.payload;
758
+ const item = {
759
+ id: roomEvent.id,
760
+ timestamp: roomEvent.timestamp,
761
+ roomId: roomPayload.roomId,
762
+ sequence: roomPayload.sequence,
763
+ type: roomPayload.event,
764
+ data: roomPayload.data,
765
+ clientEventId: roomPayload.clientEventId,
766
+ sender: roomPayload.sender,
767
+ self: roomPayload.sender.participantId === this.participantId
768
+ };
769
+ this.handlers.get(item.type)?.forEach((handler) => {
770
+ handler(item);
771
+ });
772
+ this.allHandlers.forEach((handler) => {
773
+ handler(item);
774
+ });
775
+ }
776
+ observeSequence(sequence) {
777
+ if (this.lastSequence > 0 && sequence > this.lastSequence + 1) for (const handler of this.outOfSyncHandlers) handler(this.lastSequence + 1, sequence);
778
+ if (sequence > this.lastSequence) this.lastSequence = sequence;
779
+ }
780
+ assertJoined() {
781
+ if (this._state !== "joined" || !this.hasJoined) throw new Error("room is not joined");
782
+ }
783
+ setState(state) {
784
+ if (this._state === state) return;
785
+ this._state = state;
786
+ for (const handler of this.stateHandlers) handler(state);
787
+ }
788
+ notifyMembers() {
789
+ const members = this.members;
790
+ for (const handler of this.membersHandlers) handler(members);
791
+ }
792
+ rejectPending(error) {
793
+ for (const [requestId, pending] of this.pending) {
794
+ this.pending.delete(requestId);
795
+ clearTimeout(pending.timer);
796
+ pending.reject(error);
797
+ }
798
+ }
799
+ expire() {
800
+ if (this.explicitLeave || this._state === "closed" || this._state === "expired") return;
801
+ this.hasJoined = false;
802
+ this.setState("expired");
803
+ this.rejectPending(/* @__PURE__ */ new Error("room expired"));
804
+ this.dispose();
805
+ }
806
+ dispose() {
807
+ this.offEvent();
808
+ this.offOpen();
809
+ this.offClose();
810
+ this.offReconnecting();
811
+ if (this.expiryTimer) clearTimeout(this.expiryTimer);
812
+ this.expiryTimer = null;
813
+ }
814
+ };
815
+ var WorkRealtimeApi = class {
816
+ transport;
817
+ websocket;
818
+ getContext;
819
+ constructor(transport, websocket, getContext) {
820
+ this.transport = transport;
821
+ this.websocket = websocket;
822
+ this.getContext = getContext;
823
+ }
824
+ async createRoom(input = {}) {
825
+ const admission = await this.transport.request(`/api/works/${encodeURIComponent(await this.requireWorkId())}/realtime/rooms`, {
826
+ method: "POST",
827
+ headers: { "Content-Type": "application/json" },
828
+ body: JSON.stringify(input)
829
+ });
830
+ return this.openRoom(admission);
831
+ }
832
+ async joinRoom(input) {
833
+ const admission = await this.transport.request(`/api/works/${encodeURIComponent(await this.requireWorkId())}/realtime/rooms/join`, {
834
+ method: "POST",
835
+ headers: { "Content-Type": "application/json" },
836
+ body: JSON.stringify(input)
837
+ });
838
+ return this.openRoom(admission);
839
+ }
840
+ async requireWorkId() {
841
+ const context = await this.getContext();
842
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
843
+ return context.work.id;
844
+ }
845
+ async openRoom(admission) {
846
+ const room = new WorkRoom(this.websocket, admission);
847
+ try {
848
+ await room.connect();
849
+ return room;
850
+ } catch (error) {
851
+ await room.leave().catch(() => void 0);
852
+ throw error;
853
+ }
854
+ }
855
+ };
856
+ //#endregion
84
857
  //#region src/work-runtime.ts
85
858
  const isBrowser$1 = () => typeof window !== "undefined" && typeof window.parent !== "undefined";
86
859
  const hasParent = () => isBrowser$1() && window.parent !== window;
@@ -540,41 +1313,45 @@ var CohubClient = class {
540
1313
  this.referrals = new ReferralsApi(this.transport);
541
1314
  this.works = new WorksApi(this.transport);
542
1315
  this.workCommerce = new WorkCommerceApi(this.transport);
1316
+ this.work.realtime = new WorkRealtimeApi(this.transport, this.websocketClient, () => this.workRuntime.context());
543
1317
  }
544
1318
  context() {
545
1319
  return this.workRuntime.context();
546
1320
  }
547
1321
  auth = { request: (input) => this.workRuntime.requestAuthorization(input) };
548
- work = { commerce: {
549
- resolveProducts: async (input) => {
550
- const context = await this.workRuntime.context();
551
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
552
- return this.workCommerce.resolveProducts(context.work.id, input);
553
- },
554
- getEntitlements: async () => {
555
- const context = await this.workRuntime.context();
556
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
557
- return this.workCommerce.getEntitlements(context.work.id);
558
- },
559
- consumeCredits: async (input) => {
560
- const context = await this.workRuntime.context();
561
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
562
- return this.workCommerce.consumeCredits(context.work.id, input);
563
- },
564
- purchase: async (input) => this.workRuntime.purchase(input),
565
- getCheckoutState: async () => {
566
- const result = await this.workRuntime.checkoutState();
567
- return {
568
- status: result?.status ?? null,
569
- orderId: result?.orderId ?? null
570
- };
571
- },
572
- getOrder: async (orderId) => {
573
- const context = await this.workRuntime.context();
574
- if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
575
- return this.workCommerce.getOrder(context.work.id, orderId);
1322
+ work = {
1323
+ realtime: null,
1324
+ commerce: {
1325
+ resolveProducts: async (input) => {
1326
+ const context = await this.workRuntime.context();
1327
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1328
+ return this.workCommerce.resolveProducts(context.work.id, input);
1329
+ },
1330
+ getEntitlements: async () => {
1331
+ const context = await this.workRuntime.context();
1332
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1333
+ return this.workCommerce.getEntitlements(context.work.id);
1334
+ },
1335
+ consumeCredits: async (input) => {
1336
+ const context = await this.workRuntime.context();
1337
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1338
+ return this.workCommerce.consumeCredits(context.work.id, input);
1339
+ },
1340
+ purchase: async (input) => this.workRuntime.purchase(input),
1341
+ getCheckoutState: async () => {
1342
+ const result = await this.workRuntime.checkoutState();
1343
+ return {
1344
+ status: result?.status ?? null,
1345
+ orderId: result?.orderId ?? null
1346
+ };
1347
+ },
1348
+ getOrder: async (orderId) => {
1349
+ const context = await this.workRuntime.context();
1350
+ if (!context?.work?.id) throw new Error("Work context is unavailable — not running inside a published Work runtime.");
1351
+ return this.workCommerce.getOrder(context.work.id, orderId);
1352
+ }
576
1353
  }
577
- } };
1354
+ };
578
1355
  space(spaceId) {
579
1356
  return new SpaceClient(spaceId, this.transport, this.websocketClient);
580
1357
  }
@@ -1290,53 +2067,12 @@ function filterGenerationDeclarationsByPolicy(declarations, policy) {
1290
2067
  });
1291
2068
  }
1292
2069
  //#endregion
1293
- //#region ../protocol/dist/board-constants.js
1294
- const DEFAULT_BOARD_RENDER_LIMITS = {
1295
- particles: 2e4,
1296
- vertices: 5e5,
1297
- dynamicVertices: 15e4,
1298
- drawCalls: 400,
1299
- filterPasses: 24,
1300
- renderTexturePixels: 16777216,
1301
- textureBytes: 512 * 1024 * 1024,
1302
- bufferBytes: 256 * 1024 * 1024,
1303
- simulationSteps: 1e5
1304
- };
1305
- const BOARD_BUILTIN_CLIP_KINDS = [
1306
- "motion.keyframes",
1307
- "motion.path",
1308
- "draw.reveal",
1309
- "draw.handwrite",
1310
- "text.reveal",
1311
- "effects.particles",
1312
- "effects.trail",
1313
- "effects.impact",
1314
- "effects.flash",
1315
- "effects.color",
1316
- "camera.pan",
1317
- "camera.zoom",
1318
- "camera.shake"
1319
- ];
1320
- const BOARD_BUILTIN_EFFECT_KINDS = ["effects.pulse", "effects.float"];
1321
- const BOARD_ARROW_STROKE_SIZE = 2.5;
1322
- const BOARD_BUILTIN_CAPABILITIES = [...BOARD_BUILTIN_CLIP_KINDS.map((id) => ({
1323
- kind: "clip",
1324
- id,
1325
- version: 1,
1326
- renderers: ["webgpu", "webgl"]
1327
- })), ...BOARD_BUILTIN_EFFECT_KINDS.map((id) => ({
1328
- kind: "effect",
1329
- id,
1330
- version: 1,
1331
- renderers: ["webgpu", "webgl"]
1332
- }))];
1333
- //#endregion
1334
2070
  //#region ../protocol/dist/board.js
1335
2071
  const BOARD_MANIFEST_KIND = "cohub.board.manifest";
1336
- const idSchema$1 = z.string().min(1).max(160);
2072
+ const idSchema = z.string().min(1).max(160);
1337
2073
  const extensionIdSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
1338
2074
  const jsonObjectSchema = z.record(z.string(), z.unknown());
1339
- const finiteSchema$1 = z.number().finite();
2075
+ const finiteSchema = z.number().finite();
1340
2076
  z.object({
1341
2077
  kind: z.literal(BOARD_MANIFEST_KIND),
1342
2078
  version: z.literal(1),
@@ -1346,11 +2082,11 @@ z.object({
1346
2082
  const BoardTargetSchema = z.discriminatedUnion("type", [
1347
2083
  z.object({
1348
2084
  type: z.literal("node"),
1349
- nodeId: idSchema$1
2085
+ nodeId: idSchema
1350
2086
  }),
1351
2087
  z.object({
1352
2088
  type: z.literal("effect"),
1353
- effectId: idSchema$1
2089
+ effectId: idSchema
1354
2090
  }),
1355
2091
  z.object({ type: z.literal("board") }),
1356
2092
  z.object({ type: z.literal("camera") })
@@ -1361,18 +2097,18 @@ const BoardAssetRefSchema = z.object({
1361
2097
  digest: z.string().min(16).max(160).optional()
1362
2098
  });
1363
2099
  const BoardKeyframeSchema = z.object({
1364
- at: finiteSchema$1.nonnegative(),
2100
+ at: finiteSchema.nonnegative(),
1365
2101
  value: z.unknown(),
1366
2102
  easing: z.string().min(1).max(80).optional()
1367
2103
  });
1368
2104
  const BoardClipSchema = z.object({
1369
- id: idSchema$1,
1370
- sequenceId: idSchema$1,
2105
+ id: idSchema,
2106
+ sequenceId: idSchema,
1371
2107
  kind: extensionIdSchema,
1372
2108
  kindVersion: z.number().int().positive(),
1373
2109
  target: BoardTargetSchema,
1374
- start: finiteSchema$1.nonnegative(),
1375
- duration: finiteSchema$1.positive(),
2110
+ start: finiteSchema.nonnegative(),
2111
+ duration: finiteSchema.positive(),
1376
2112
  layer: z.enum([
1377
2113
  "behind",
1378
2114
  "content",
@@ -1393,11 +2129,11 @@ const BoardClipSchema = z.object({
1393
2129
  metadata: jsonObjectSchema.default({})
1394
2130
  });
1395
2131
  const BoardEffectSchema = z.object({
1396
- id: idSchema$1,
2132
+ id: idSchema,
1397
2133
  boardId: z.string().uuid(),
1398
2134
  target: z.discriminatedUnion("type", [z.object({
1399
2135
  type: z.literal("node"),
1400
- nodeId: idSchema$1
2136
+ nodeId: idSchema
1401
2137
  }), z.object({ type: z.literal("board") })]),
1402
2138
  kind: extensionIdSchema,
1403
2139
  kindVersion: z.number().int().positive(),
@@ -1424,25 +2160,25 @@ const BoardEffectSchema = z.object({
1424
2160
  revision: z.number().int().nonnegative()
1425
2161
  });
1426
2162
  const BoardSequenceSchema = z.object({
1427
- id: idSchema$1,
2163
+ id: idSchema,
1428
2164
  boardId: z.string().uuid(),
1429
2165
  name: z.string().min(1).max(255),
1430
- duration: finiteSchema$1.nonnegative(),
2166
+ duration: finiteSchema.nonnegative(),
1431
2167
  seed: z.string().min(1).max(160),
1432
2168
  restPose: jsonObjectSchema.default({}),
1433
2169
  metadata: jsonObjectSchema.default({}),
1434
2170
  revision: z.number().int().nonnegative()
1435
2171
  });
1436
2172
  const BoardNodeInputSchema = z.object({
1437
- nodeId: idSchema$1,
2173
+ nodeId: idSchema,
1438
2174
  type: z.string().min(1).max(40),
1439
- parentId: idSchema$1.nullable(),
2175
+ parentId: idSchema.nullable(),
1440
2176
  orderKey: z.string().max(4096).nullable(),
1441
- x: finiteSchema$1,
1442
- y: finiteSchema$1,
1443
- width: finiteSchema$1.positive(),
1444
- height: finiteSchema$1.positive(),
1445
- rotation: finiteSchema$1,
2177
+ x: finiteSchema,
2178
+ y: finiteSchema,
2179
+ width: finiteSchema.positive(),
2180
+ height: finiteSchema.positive(),
2181
+ rotation: finiteSchema,
1446
2182
  refKind: z.string().max(40).nullable(),
1447
2183
  refPath: z.string().max(4096).nullable(),
1448
2184
  refUrl: z.string().max(4096).nullable(),
@@ -1478,17 +2214,17 @@ z.object({
1478
2214
  "playback"
1479
2215
  ])).optional(),
1480
2216
  viewport: z.object({
1481
- x: finiteSchema$1,
1482
- y: finiteSchema$1,
1483
- width: finiteSchema$1.positive(),
1484
- height: finiteSchema$1.positive()
2217
+ x: finiteSchema,
2218
+ y: finiteSchema,
2219
+ width: finiteSchema.positive(),
2220
+ height: finiteSchema.positive()
1485
2221
  }).optional()
1486
2222
  });
1487
2223
  /** Persisted on `boards.metadata.playback`: how a Board plays when opened. */
1488
2224
  const BoardPlaybackPolicySchema = z.object({
1489
- sequenceId: idSchema$1,
2225
+ sequenceId: idSchema,
1490
2226
  /** Delay before the first local playback after opening the Board, in milliseconds. */
1491
- delayMs: finiteSchema$1.nonnegative().default(0),
2227
+ delayMs: finiteSchema.nonnegative().default(0),
1492
2228
  loop: z.boolean().default(false)
1493
2229
  });
1494
2230
  function parseBoardPlaybackPolicy(metadata) {
@@ -1497,148 +2233,32 @@ function parseBoardPlaybackPolicy(metadata) {
1497
2233
  }
1498
2234
  z.discriminatedUnion("type", [
1499
2235
  z.object({
1500
- commandId: idSchema$1,
2236
+ commandId: idSchema,
1501
2237
  type: z.literal("play"),
1502
- sequenceId: idSchema$1,
1503
- position: finiteSchema$1.nonnegative().optional(),
1504
- timeScale: finiteSchema$1.positive().max(4).optional(),
2238
+ sequenceId: idSchema,
2239
+ position: finiteSchema.nonnegative().optional(),
2240
+ timeScale: finiteSchema.positive().max(4).optional(),
1505
2241
  shared: z.boolean().optional(),
1506
- seed: idSchema$1.optional()
2242
+ seed: idSchema.optional()
1507
2243
  }),
1508
2244
  z.object({
1509
- commandId: idSchema$1,
2245
+ commandId: idSchema,
1510
2246
  type: z.literal("pause"),
1511
2247
  playbackId: z.string().uuid()
1512
2248
  }),
1513
2249
  z.object({
1514
- commandId: idSchema$1,
2250
+ commandId: idSchema,
1515
2251
  type: z.literal("seek"),
1516
2252
  playbackId: z.string().uuid(),
1517
- position: finiteSchema$1.nonnegative()
2253
+ position: finiteSchema.nonnegative()
1518
2254
  }),
1519
2255
  z.object({
1520
- commandId: idSchema$1,
2256
+ commandId: idSchema,
1521
2257
  type: z.literal("stop"),
1522
2258
  playbackId: z.string().uuid()
1523
2259
  })
1524
2260
  ]);
1525
2261
  //#endregion
1526
- //#region ../protocol/dist/realtime/board-awareness.js
1527
- const idSchema = z.string().min(1).max(160);
1528
- const finiteSchema = z.number().finite();
1529
- const BoardAwarenessPointSchema = z.object({
1530
- x: finiteSchema,
1531
- y: finiteSchema
1532
- });
1533
- const BoardAwarenessDrawPointSchema = BoardAwarenessPointSchema.extend({ p: finiteSchema.min(0).max(1) });
1534
- const BoardAwarenessFrameSchema = z.object({
1535
- x: finiteSchema,
1536
- y: finiteSchema,
1537
- width: finiteSchema.positive(),
1538
- height: finiteSchema.positive(),
1539
- rotation: finiteSchema
1540
- });
1541
- const arrowEndpointSchema = z.union([z.object({
1542
- kind: z.literal("point"),
1543
- x: finiteSchema,
1544
- y: finiteSchema
1545
- }), z.object({
1546
- kind: z.literal("binding"),
1547
- target: idSchema,
1548
- nx: finiteSchema,
1549
- ny: finiteSchema,
1550
- precise: z.boolean()
1551
- })]);
1552
- const BoardAwarenessNodePreviewSchema = z.object({
1553
- nodeId: idSchema,
1554
- frame: BoardAwarenessFrameSchema,
1555
- arrow: z.object({
1556
- start: arrowEndpointSchema,
1557
- end: arrowEndpointSchema,
1558
- bend: finiteSchema
1559
- }).optional()
1560
- });
1561
- const BoardAwarenessStateUpdateSchema = z.object({
1562
- type: z.literal("state"),
1563
- client: z.object({ formFactor: z.enum(["desktop", "mobile"]) }).optional(),
1564
- cursor: BoardAwarenessPointSchema.extend({ pointerType: z.enum([
1565
- "mouse",
1566
- "pen",
1567
- "touch"
1568
- ]) }).nullable(),
1569
- tool: z.string().min(1).max(40),
1570
- selection: z.object({
1571
- ids: z.array(idSchema).max(64),
1572
- count: z.number().int().nonnegative().max(5e4),
1573
- bounds: BoardAwarenessFrameSchema.nullable()
1574
- }),
1575
- editingId: idSchema.nullable()
1576
- });
1577
- const BoardAwarenessGestureSchema = z.discriminatedUnion("kind", [
1578
- z.object({
1579
- kind: z.literal("draw"),
1580
- id: idSchema,
1581
- nodeId: idSchema,
1582
- color: z.string().min(1).max(64),
1583
- size: finiteSchema.positive().max(256),
1584
- from: z.number().int().nonnegative().max(1e5),
1585
- points: z.array(BoardAwarenessDrawPointSchema).min(1).max(64)
1586
- }),
1587
- z.object({
1588
- kind: z.literal("arrow"),
1589
- id: idSchema,
1590
- nodeId: idSchema,
1591
- start: BoardAwarenessPointSchema,
1592
- current: BoardAwarenessPointSchema,
1593
- color: z.string().min(1).max(64),
1594
- size: finiteSchema.positive().max(256).default(BOARD_ARROW_STROKE_SIZE)
1595
- }),
1596
- z.object({
1597
- kind: z.literal("box"),
1598
- id: idSchema,
1599
- nodeId: idSchema,
1600
- shape: z.enum(["geo", "frame"]),
1601
- start: BoardAwarenessPointSchema,
1602
- current: BoardAwarenessPointSchema,
1603
- color: z.string().min(1).max(64),
1604
- geo: z.string().min(1).max(40)
1605
- }),
1606
- z.object({
1607
- kind: z.literal("transform"),
1608
- id: idSchema,
1609
- mode: z.enum([
1610
- "translate",
1611
- "resize",
1612
- "rotate",
1613
- "arrow"
1614
- ]),
1615
- nodes: z.array(BoardAwarenessNodePreviewSchema).max(64),
1616
- bounds: BoardAwarenessFrameSchema.nullable()
1617
- })
1618
- ]);
1619
- const BoardAwarenessUpdateSchema = z.discriminatedUnion("type", [
1620
- BoardAwarenessStateUpdateSchema,
1621
- z.object({
1622
- type: z.literal("gesture"),
1623
- gesture: BoardAwarenessGestureSchema
1624
- }),
1625
- z.object({
1626
- type: z.literal("gesture.end"),
1627
- gestureId: idSchema,
1628
- resultingNodeIds: z.array(idSchema).max(64)
1629
- }),
1630
- z.object({
1631
- type: z.literal("gesture.cancel"),
1632
- gestureId: idSchema
1633
- })
1634
- ]);
1635
- z.object({
1636
- spaceId: z.string().uuid(),
1637
- boardId: z.string().uuid(),
1638
- seq: z.number().int().nonnegative(),
1639
- update: BoardAwarenessUpdateSchema
1640
- });
1641
- //#endregion
1642
2262
  //#region ../protocol/dist/public-identifiers.js
1643
2263
  /**
1644
2264
  * Platform-owned path segments that must not be newly assigned to public
@@ -2026,4 +2646,4 @@ function createBoardExtensionRegistry(input = {}) {
2026
2646
  return registry;
2027
2647
  }
2028
2648
  //#endregion
2029
- export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRuntimeApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceEmpty, isRequestSourceUuid, joinApiUrl, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };
2649
+ export { BILLING_ACCESS_BLOCKED_ERROR_CODE, BillingApi, BoardClient, BoardExtensionRegistry, BoardPlaybackPolicySchema, BoardTransactionError, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, ReferencesApi, ReferralsApi, SessionGenerationStreamClient, SessionPatchReducer, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRoom, WorkRuntimeApi, WorksApi, assertGenerationRequestAllowedByPolicy, buildSpaceInvitePath, buildSpacePath, clearGrantedWorkScopes, clip, compileSequence, createBoardExtensionRegistry, createCohubClient, createHttpClient, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, createWorkBridgeCore, createWorkRuntime, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, findGenerationModelPolicy, getAllowedGenerationModelIds, hasGrantedWorkScopes, hasRequestSourceIdentity, isBillingAccessBlockedCode, isBillingAccessBlockedError, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isRequestSourceEmpty, isRequestSourceUuid, joinApiUrl, mergeRequestSourceIntoMeta, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, parseAssistantMessageCommit, parseBoardPlaybackPolicy, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveCohubEnvironment, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebsocketUrl, resolveWorkTransport, sanitizeAccessToken, setGrantedWorkScopes, timeline };