@openmarket/rooms-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +25 -0
  3. package/dist/agent/armed-mode.d.ts +187 -0
  4. package/dist/agent/armed-mode.js +306 -0
  5. package/dist/agent/clients/common.d.ts +70 -0
  6. package/dist/agent/clients/common.js +46 -0
  7. package/dist/agent/lane-draft.d.ts +15 -0
  8. package/dist/agent/lane-draft.js +43 -0
  9. package/dist/agent/lane-prompts.d.ts +102 -0
  10. package/dist/agent/lane-prompts.js +120 -0
  11. package/dist/agent/library-context.d.ts +38 -0
  12. package/dist/agent/library-context.js +57 -0
  13. package/dist/agent/library-model.d.ts +41 -0
  14. package/dist/agent/library-model.js +173 -0
  15. package/dist/agent/room-context.d.ts +151 -0
  16. package/dist/agent/room-context.js +251 -0
  17. package/dist/agent/sidebar-model.d.ts +86 -0
  18. package/dist/agent/sidebar-model.js +162 -0
  19. package/dist/agent/topic-inbox.d.ts +119 -0
  20. package/dist/agent/topic-inbox.js +266 -0
  21. package/dist/agent/topic-view-batcher.d.ts +54 -0
  22. package/dist/agent/topic-view-batcher.js +115 -0
  23. package/dist/client.d.ts +421 -0
  24. package/dist/client.js +1428 -0
  25. package/dist/doc-reconcile.d.ts +100 -0
  26. package/dist/doc-reconcile.js +110 -0
  27. package/dist/doc-uri.d.ts +29 -0
  28. package/dist/doc-uri.js +55 -0
  29. package/dist/endpoints.d.ts +9 -0
  30. package/dist/endpoints.js +13 -0
  31. package/dist/jwt.d.ts +10 -0
  32. package/dist/jwt.js +51 -0
  33. package/dist/library-publisher.d.ts +52 -0
  34. package/dist/library-publisher.js +49 -0
  35. package/dist/merge3.d.ts +50 -0
  36. package/dist/merge3.js +280 -0
  37. package/dist/names.d.ts +6 -0
  38. package/dist/names.js +35 -0
  39. package/dist/shared/emoji-data.d.ts +22 -0
  40. package/dist/shared/emoji-data.js +8834 -0
  41. package/dist/shared/mentions.d.ts +6 -0
  42. package/dist/shared/mentions.js +32 -0
  43. package/dist/shared/rooms-protocol.d.ts +1183 -0
  44. package/dist/shared/rooms-protocol.js +160 -0
  45. package/dist/social-client.d.ts +361 -0
  46. package/dist/social-client.js +686 -0
  47. package/dist/social-types.d.ts +338 -0
  48. package/dist/social-types.js +1 -0
  49. package/dist/suggestion-attribution.d.ts +10 -0
  50. package/dist/suggestion-attribution.js +56 -0
  51. package/dist/topic-uri.d.ts +26 -0
  52. package/dist/topic-uri.js +40 -0
  53. package/dist/types.d.ts +2 -0
  54. package/dist/types.js +1 -0
  55. package/dist/version.d.ts +2 -0
  56. package/dist/version.js +2 -0
  57. package/dist/ws-client.d.ts +115 -0
  58. package/dist/ws-client.js +491 -0
  59. package/package.json +180 -0
  60. package/src/agent/armed-mode.ts +368 -0
  61. package/src/agent/clients/common.ts +91 -0
  62. package/src/agent/lane-draft.ts +47 -0
  63. package/src/agent/lane-prompts.ts +267 -0
  64. package/src/agent/library-context.ts +97 -0
  65. package/src/agent/library-model.ts +210 -0
  66. package/src/agent/room-context.ts +351 -0
  67. package/src/agent/sidebar-model.ts +235 -0
  68. package/src/agent/topic-inbox.ts +297 -0
  69. package/src/agent/topic-view-batcher.ts +134 -0
  70. package/src/client.ts +2331 -0
  71. package/src/doc-reconcile.ts +160 -0
  72. package/src/doc-uri.ts +83 -0
  73. package/src/endpoints.ts +14 -0
  74. package/src/jwt.ts +59 -0
  75. package/src/library-publisher.ts +93 -0
  76. package/src/merge3.ts +326 -0
  77. package/src/names.ts +44 -0
  78. package/src/shared/emoji-data.ts +8868 -0
  79. package/src/shared/mentions.ts +32 -0
  80. package/src/shared/rooms-protocol.ts +1339 -0
  81. package/src/social-client.ts +1287 -0
  82. package/src/social-types.ts +376 -0
  83. package/src/suggestion-attribution.ts +83 -0
  84. package/src/topic-uri.ts +64 -0
  85. package/src/types.ts +83 -0
  86. package/src/version.ts +2 -0
  87. package/src/ws-client.ts +611 -0
@@ -0,0 +1,491 @@
1
+ import { RoomClientMessageType, RoomServerMessageType, SUPPORTED_ROOM_PROTOCOL_VERSION, } from "./types.js";
2
+ import { VERSION } from "./version.js";
3
+ export class RoomProtocolError extends Error {
4
+ name = "RoomProtocolError";
5
+ code;
6
+ details;
7
+ requestId;
8
+ constructor(payload) {
9
+ super(payload.message);
10
+ this.code = payload.code;
11
+ if (payload.details !== undefined)
12
+ this.details = payload.details;
13
+ if (payload.requestId)
14
+ this.requestId = payload.requestId;
15
+ }
16
+ }
17
+ export class RoomTransportError extends Error {
18
+ name = "RoomTransportError";
19
+ }
20
+ export class RoomWsClient {
21
+ url;
22
+ // Mutable: a guest session re-mints its key via refreshAuthToken.
23
+ apiKey;
24
+ agentIdentity;
25
+ webSocketCtor;
26
+ timeoutMs;
27
+ ws = null;
28
+ auth = null;
29
+ serverProtocolVersionValue = null;
30
+ listeners = new Set();
31
+ connectionListeners = new Set();
32
+ pending = [];
33
+ connected = false;
34
+ reconnectEnabled;
35
+ reconnectBaseMs;
36
+ reconnectMaxMs;
37
+ reconnectHerdJitterMs;
38
+ reconnectMaxAttempts;
39
+ refreshAuthToken;
40
+ onProtocolMismatch;
41
+ reconnectAttempts = 0;
42
+ reconnectTimer = null;
43
+ closedByClient = false;
44
+ authRefreshRequested = false;
45
+ authRefreshInFlight = false;
46
+ /** True after a successful re-mint whose refreshed attempt hasn't authed yet.
47
+ * Caps the mint-succeeds-but-server-rejects cycle (config skew) at ONE
48
+ * refreshed attempt per episode — without it, openSocket → fail → re-mint →
49
+ * openSocket recurses in a hot loop with no backoff. Reset on AUTH_SUCCESS.
50
+ * A FAILED re-mint does not set it: that path already backs off through
51
+ * maybeReconnect, so transient auth-service outages keep retrying. */
52
+ authRefreshAttempted = false;
53
+ constructor(options) {
54
+ this.url = options.url;
55
+ this.apiKey = options.apiKey;
56
+ this.agentIdentity = options.agentIdentity;
57
+ this.webSocketCtor = options.webSocketCtor ?? globalThis.WebSocket;
58
+ this.timeoutMs = options.timeoutMs ?? 15_000;
59
+ this.reconnectEnabled = options.reconnect ?? false;
60
+ this.reconnectBaseMs = options.reconnectBaseMs ?? 500;
61
+ this.reconnectMaxMs = options.reconnectMaxMs ?? 15_000;
62
+ this.reconnectHerdJitterMs = options.reconnectHerdJitterMs ?? 5_000;
63
+ this.reconnectMaxAttempts = options.reconnectMaxAttempts ?? Number.POSITIVE_INFINITY;
64
+ this.refreshAuthToken = options.refreshAuthToken;
65
+ this.onProtocolMismatch = options.onProtocolMismatch;
66
+ }
67
+ /** Subscribe to connection-state transitions (reconnecting/reconnected/failed). */
68
+ onConnectionChange(listener) {
69
+ this.connectionListeners.add(listener);
70
+ return () => {
71
+ this.connectionListeners.delete(listener);
72
+ };
73
+ }
74
+ emitConnectionState(state, detail) {
75
+ for (const listener of this.connectionListeners) {
76
+ try {
77
+ listener(state, detail);
78
+ }
79
+ catch {
80
+ // A listener error must not break reconnection.
81
+ }
82
+ }
83
+ }
84
+ connect() {
85
+ if (this.ws) {
86
+ if (this.auth)
87
+ return Promise.resolve(this.auth);
88
+ return this.waitFor(RoomServerMessageType.AUTH_SUCCESS).then((msg) => msg.payload);
89
+ }
90
+ this.closedByClient = false;
91
+ return this.openSocket();
92
+ }
93
+ openSocket() {
94
+ // Fence any prior socket so a slow-to-die predecessor (e.g. one whose auth
95
+ // timed out while still connecting) can't feed record() or null out the
96
+ // fresh socket. Handlers below no-op unless they belong to the current ws.
97
+ const previous = this.ws;
98
+ if (previous) {
99
+ this.ws = null;
100
+ try {
101
+ previous.close(1000, "superseded");
102
+ }
103
+ catch {
104
+ // best-effort
105
+ }
106
+ }
107
+ let ws;
108
+ try {
109
+ ws = new this.webSocketCtor(this.url);
110
+ }
111
+ catch (err) {
112
+ throw new RoomTransportError(errorMessage(err));
113
+ }
114
+ this.ws = ws;
115
+ ws.addEventListener("open", () => {
116
+ if (this.ws !== ws)
117
+ return;
118
+ this.connected = true;
119
+ this.send({
120
+ type: RoomClientMessageType.AUTHENTICATE,
121
+ payload: {
122
+ token: this.apiKey,
123
+ protocolVersion: SUPPORTED_ROOM_PROTOCOL_VERSION,
124
+ // Sidekick sessions assert their agenthood and display name;
125
+ // ghost and user sessions send the bare frame unchanged.
126
+ ...(this.agentIdentity ? { isAgent: true, username: this.agentIdentity.name } : {}),
127
+ },
128
+ timestamp: Date.now(),
129
+ });
130
+ });
131
+ ws.addEventListener("message", (event) => {
132
+ if (this.ws !== ws)
133
+ return;
134
+ const data = event.data;
135
+ if (typeof data !== "string" || data.length === 0)
136
+ return;
137
+ let parsed;
138
+ try {
139
+ parsed = JSON.parse(data);
140
+ }
141
+ catch {
142
+ this.rejectAll(new RoomTransportError("rooms ws: dropped non-json frame"));
143
+ return;
144
+ }
145
+ if (!isRoomShapedFrame(parsed)) {
146
+ this.rejectAll(new RoomTransportError("rooms ws: dropped malformed frame"));
147
+ return;
148
+ }
149
+ if (!isRoomServerMessage(parsed)) {
150
+ // Additive-only protocol: a newer relay may send frame types this
151
+ // client does not know yet. Ignore them; never fail the session.
152
+ return;
153
+ }
154
+ this.record(parsed);
155
+ });
156
+ ws.addEventListener("close", (event) => {
157
+ // A superseded socket's close must not touch current state or trigger
158
+ // another reconnect.
159
+ if (this.ws !== ws)
160
+ return;
161
+ const e = event;
162
+ const hadAuth = this.auth !== null;
163
+ this.connected = false;
164
+ this.auth = null;
165
+ this.ws = null;
166
+ if (!this.closedByClient && !hadAuth && e.code === 1008 && this.refreshAuthToken) {
167
+ this.authRefreshRequested = true;
168
+ }
169
+ this.rejectAll(new RoomTransportError(`rooms ws closed${e.code ? ` (${e.code})` : ""}${e.reason ? `: ${e.reason}` : ""}`));
170
+ this.maybeReconnect(e.reason || (e.code ? `code ${e.code}` : undefined));
171
+ });
172
+ ws.addEventListener("error", () => {
173
+ if (this.ws !== ws)
174
+ return;
175
+ this.rejectAll(new RoomTransportError("rooms ws error"));
176
+ });
177
+ return this.waitFor(RoomServerMessageType.AUTH_SUCCESS)
178
+ .then((msg) => {
179
+ this.reconnectAttempts = 0;
180
+ this.authRefreshAttempted = false;
181
+ return msg.payload;
182
+ })
183
+ .catch(async (error) => this.retryGuestAuthentication(error));
184
+ }
185
+ maybeReconnect(reason) {
186
+ if (!this.reconnectEnabled ||
187
+ this.closedByClient ||
188
+ this.authRefreshRequested ||
189
+ this.authRefreshInFlight) {
190
+ return;
191
+ }
192
+ if (this.reconnectTimer)
193
+ return;
194
+ if (this.reconnectAttempts >= this.reconnectMaxAttempts) {
195
+ this.emitConnectionState("failed", {
196
+ attempt: this.reconnectAttempts,
197
+ ...(reason ? { error: reason } : {}),
198
+ });
199
+ return;
200
+ }
201
+ const attempt = this.reconnectAttempts + 1;
202
+ this.reconnectAttempts = attempt;
203
+ // Exponential backoff with full jitter, capped.
204
+ const ceiling = Math.min(this.reconnectMaxMs, this.reconnectBaseMs * 2 ** (attempt - 1));
205
+ const delay = Math.floor(ceiling * (0.5 + Math.random() * 0.5) + Math.random() * this.reconnectHerdJitterMs);
206
+ this.emitConnectionState("reconnecting", {
207
+ attempt,
208
+ ...(reason ? { error: reason } : {}),
209
+ });
210
+ this.reconnectTimer = setTimeout(() => {
211
+ this.reconnectTimer = null;
212
+ if (this.closedByClient)
213
+ return;
214
+ try {
215
+ this.openSocket()
216
+ .then(() => {
217
+ this.emitConnectionState("reconnected", { attempt });
218
+ })
219
+ .catch((err) => {
220
+ // Re-auth failed; drive the next backoff attempt.
221
+ this.maybeReconnect(errorMessage(err));
222
+ });
223
+ }
224
+ catch (err) {
225
+ // openSocket can throw synchronously (constructor failure) before it
226
+ // returns a promise — that must not escape the timer as an uncaught
227
+ // exception. Fold it back into the backoff loop.
228
+ this.maybeReconnect(errorMessage(err));
229
+ }
230
+ }, delay);
231
+ if (typeof this.reconnectTimer.unref === "function") {
232
+ this.reconnectTimer.unref();
233
+ }
234
+ }
235
+ send(msg) {
236
+ if (!this.ws || !this.connected) {
237
+ throw new RoomTransportError("rooms ws: not open");
238
+ }
239
+ this.ws.send(JSON.stringify(msg));
240
+ }
241
+ waitFor(type, predicate, timeoutMs = this.timeoutMs, requestId) {
242
+ return new Promise((resolve, reject) => {
243
+ let pending;
244
+ const timer = setTimeout(() => {
245
+ this.pending = this.pending.filter((wait) => wait !== pending);
246
+ reject(new RoomTransportError(`timed out waiting for ${type}`));
247
+ }, timeoutMs);
248
+ pending = {
249
+ type,
250
+ ...(predicate
251
+ ? { predicate: (msg) => predicate(msg) }
252
+ : {}),
253
+ ...(requestId ? { requestId } : {}),
254
+ resolve: (msg) => resolve(msg),
255
+ reject,
256
+ timer,
257
+ };
258
+ this.pending.push(pending);
259
+ });
260
+ }
261
+ waitForAny(types, predicate, timeoutMs = this.timeoutMs, requestId) {
262
+ return new Promise((resolve, reject) => {
263
+ let pending;
264
+ const timer = setTimeout(() => {
265
+ this.pending = this.pending.filter((wait) => wait !== pending);
266
+ reject(new RoomTransportError(`timed out waiting for ${types.join(" or ")}`));
267
+ }, timeoutMs);
268
+ const typeSet = new Set(types);
269
+ pending = {
270
+ type: types[0],
271
+ types: typeSet,
272
+ predicate: (msg) => typeSet.has(msg.type) &&
273
+ (!predicate || predicate(msg)),
274
+ ...(requestId ? { requestId } : {}),
275
+ resolve: (msg) => resolve(msg),
276
+ reject,
277
+ timer,
278
+ };
279
+ this.pending.push(pending);
280
+ });
281
+ }
282
+ onMessage(listener) {
283
+ this.listeners.add(listener);
284
+ return () => {
285
+ this.listeners.delete(listener);
286
+ };
287
+ }
288
+ close() {
289
+ this.closedByClient = true;
290
+ if (this.reconnectTimer) {
291
+ clearTimeout(this.reconnectTimer);
292
+ this.reconnectTimer = null;
293
+ }
294
+ const ws = this.ws;
295
+ this.ws = null;
296
+ this.auth = null;
297
+ this.connected = false;
298
+ this.rejectAll(new RoomTransportError("rooms ws closed by client"));
299
+ ws?.close(1000, "client closed");
300
+ }
301
+ waiterHandledErrors = new WeakSet();
302
+ serverProtocolVersion() {
303
+ return this.serverProtocolVersionValue;
304
+ }
305
+ isGuest() {
306
+ return this.auth?.isGuest === true;
307
+ }
308
+ record(msg) {
309
+ if (msg.type === RoomServerMessageType.AUTH_SUCCESS) {
310
+ if (!this.handleAuthSuccess(msg.payload))
311
+ return;
312
+ }
313
+ if (msg.type === RoomServerMessageType.ERROR) {
314
+ if (!this.auth &&
315
+ this.refreshAuthToken &&
316
+ isAuthenticationError(msg.payload.code) &&
317
+ !this.closedByClient) {
318
+ this.authRefreshRequested = true;
319
+ }
320
+ const err = new RoomProtocolError(msg.payload);
321
+ if (msg.payload.requestId) {
322
+ if (this.rejectByRequestId(msg.payload.requestId, err)) {
323
+ // Precisely correlated: the request's caller owns this failure. Mark
324
+ // the frame so passive listeners (the tape pump) don't ALSO surface
325
+ // it — background requests against older relays degrade silently.
326
+ this.waiterHandledErrors.add(msg);
327
+ }
328
+ // A requestId we don't recognize is someone else's request (another
329
+ // session, a stale retry): never misattribute it to an unrelated
330
+ // waiter. Passive surfaces still see the frame.
331
+ }
332
+ else if (this.rejectOldest(err)) {
333
+ // Heuristic old-relay fallback: the oldest waiter got the rejection,
334
+ // but the correlation is a guess. Only hide pure protocol noise;
335
+ // user-actionable errors (MUTED, READ_ONLY, …) must stay visible.
336
+ if (msg.payload.code === "BAD_FRAME")
337
+ this.waiterHandledErrors.add(msg);
338
+ }
339
+ }
340
+ else {
341
+ this.resolvePending(msg);
342
+ }
343
+ for (const listener of this.listeners)
344
+ listener(msg);
345
+ }
346
+ handleAuthSuccess(payload) {
347
+ this.auth = payload;
348
+ this.serverProtocolVersionValue = payload.protocolVersion;
349
+ if (payload.protocolVersion !== SUPPORTED_ROOM_PROTOCOL_VERSION) {
350
+ this.onProtocolMismatch?.({
351
+ supported: SUPPORTED_ROOM_PROTOCOL_VERSION,
352
+ server: payload.protocolVersion,
353
+ });
354
+ const err = new RoomTransportError(`rooms protocol mismatch: server v${payload.protocolVersion}, client supports v${SUPPORTED_ROOM_PROTOCOL_VERSION}`);
355
+ this.closedByClient = true;
356
+ this.rejectAll(err);
357
+ this.ws?.close(1002, "protocol mismatch");
358
+ return false;
359
+ }
360
+ if (!payload.minClientBuild ||
361
+ !isRoomClientBuildBelowMinimum(VERSION, payload.minClientBuild)) {
362
+ return true;
363
+ }
364
+ const err = new RoomTransportError(`rooms client upgrade required: server requires om v${payload.minClientBuild} or newer (this build is v${VERSION})`);
365
+ this.closedByClient = true;
366
+ this.rejectAll(err);
367
+ this.ws?.close(1008, "client upgrade required");
368
+ return false;
369
+ }
370
+ async retryGuestAuthentication(error) {
371
+ if (!this.authRefreshRequested || !this.refreshAuthToken || this.closedByClient) {
372
+ throw error;
373
+ }
374
+ if (this.authRefreshAttempted) {
375
+ // The previous re-mint SUCCEEDED and the server still refused the fresh
376
+ // key — retrying would mint in a hot loop. Fail the episode instead.
377
+ throw error;
378
+ }
379
+ this.authRefreshRequested = false;
380
+ this.authRefreshInFlight = true;
381
+ try {
382
+ this.apiKey = await this.refreshAuthToken();
383
+ this.authRefreshAttempted = true;
384
+ return await this.openSocket();
385
+ }
386
+ catch (refreshError) {
387
+ this.authRefreshInFlight = false;
388
+ this.maybeReconnect(errorMessage(refreshError));
389
+ throw refreshError;
390
+ }
391
+ finally {
392
+ this.authRefreshInFlight = false;
393
+ }
394
+ }
395
+ resolvePending(msg) {
396
+ const remaining = [];
397
+ for (const wait of this.pending) {
398
+ if (wait.types ? !wait.types.has(msg.type) : wait.type !== msg.type) {
399
+ remaining.push(wait);
400
+ continue;
401
+ }
402
+ if (wait.predicate && !wait.predicate(msg)) {
403
+ remaining.push(wait);
404
+ continue;
405
+ }
406
+ clearTimeout(wait.timer);
407
+ wait.resolve(msg);
408
+ }
409
+ this.pending = remaining;
410
+ }
411
+ rejectAll(err) {
412
+ for (const wait of this.pending) {
413
+ clearTimeout(wait.timer);
414
+ wait.reject(err);
415
+ }
416
+ this.pending = [];
417
+ }
418
+ rejectByRequestId(requestId, err) {
419
+ if (!requestId)
420
+ return false;
421
+ const index = this.pending.findIndex((wait) => wait.requestId === requestId);
422
+ if (index < 0)
423
+ return false;
424
+ const [wait] = this.pending.splice(index, 1);
425
+ if (!wait)
426
+ return false;
427
+ clearTimeout(wait.timer);
428
+ wait.reject(err);
429
+ return true;
430
+ }
431
+ // Fallback for old relays that do not include requestId on ERROR frames.
432
+ rejectOldest(err) {
433
+ const wait = this.pending.shift();
434
+ if (!wait)
435
+ return false;
436
+ clearTimeout(wait.timer);
437
+ wait.reject(err);
438
+ return true;
439
+ }
440
+ /** Was this ERROR frame already delivered to a waiting request's caller? */
441
+ errorHandledByWaiter(msg) {
442
+ return this.waiterHandledErrors.has(msg);
443
+ }
444
+ }
445
+ /** Returns false for an invalid server value so a typo cannot lock out every client. */
446
+ export function isRoomClientBuildBelowMinimum(clientBuild, minimumBuild) {
447
+ const client = parseBuild(clientBuild);
448
+ const minimum = parseBuild(minimumBuild);
449
+ if (!client || !minimum)
450
+ return false;
451
+ for (const index of [0, 1, 2]) {
452
+ const clientPart = client[index];
453
+ const minimumPart = minimum[index];
454
+ if (clientPart !== minimumPart)
455
+ return clientPart < minimumPart;
456
+ }
457
+ return false;
458
+ }
459
+ function parseBuild(value) {
460
+ const match = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?$/.exec(value.trim());
461
+ if (!match)
462
+ return null;
463
+ const parts = [match[1], match[2] ?? "0", match[3] ?? "0"].map(Number);
464
+ if (parts.some((part) => !Number.isSafeInteger(part)))
465
+ return null;
466
+ return parts;
467
+ }
468
+ const KNOWN_SERVER_MESSAGE_TYPES = new Set(Object.values(RoomServerMessageType));
469
+ function isRoomShapedFrame(value) {
470
+ if (!value || typeof value !== "object")
471
+ return false;
472
+ const candidate = value;
473
+ if (typeof candidate.type !== "string")
474
+ return false;
475
+ if (typeof candidate.timestamp !== "number")
476
+ return false;
477
+ if (!candidate.payload || typeof candidate.payload !== "object")
478
+ return false;
479
+ return true;
480
+ }
481
+ function isRoomServerMessage(value) {
482
+ // A hostile or buggy relay cannot smuggle an unexpected shape past the
483
+ // type switch that consumers rely on: only known frame types pass.
484
+ return isRoomShapedFrame(value) && KNOWN_SERVER_MESSAGE_TYPES.has(value.type);
485
+ }
486
+ function errorMessage(err) {
487
+ return err instanceof Error ? err.message : String(err);
488
+ }
489
+ function isAuthenticationError(code) {
490
+ return code === "AUTH_FAILED" || code === "AUTH_TIMEOUT" || code === "TOKEN_REQUIRED";
491
+ }
package/package.json ADDED
@@ -0,0 +1,180 @@
1
+ {
2
+ "name": "@openmarket/rooms-client",
3
+ "version": "0.1.0",
4
+ "description": "OM Rooms protocol client: wire types, WebSocket + REST clients, and chat view-models. Browser-safe (no node:/bun: imports).",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "author": "OpenMarket",
8
+ "homepage": "https://github.com/openmarket-xyz/openmarket-releases#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/openmarket-xyz/openmarket-releases/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/openmarket-xyz/openmarket-releases.git"
15
+ },
16
+ "engines": {
17
+ "bun": ">=1.3.0",
18
+ "node": ">=22"
19
+ },
20
+ "files": ["src", "dist", "README.md", "LICENSE"],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "exports": {
25
+ "./client": {
26
+ "bun": "./src/client.ts",
27
+ "types": "./dist/client.d.ts",
28
+ "default": "./dist/client.js"
29
+ },
30
+ "./ws-client": {
31
+ "bun": "./src/ws-client.ts",
32
+ "types": "./dist/ws-client.d.ts",
33
+ "default": "./dist/ws-client.js"
34
+ },
35
+ "./social-client": {
36
+ "bun": "./src/social-client.ts",
37
+ "types": "./dist/social-client.d.ts",
38
+ "default": "./dist/social-client.js"
39
+ },
40
+ "./social-types": {
41
+ "bun": "./src/social-types.ts",
42
+ "types": "./dist/social-types.d.ts",
43
+ "default": "./dist/social-types.js"
44
+ },
45
+ "./library-publisher": {
46
+ "bun": "./src/library-publisher.ts",
47
+ "types": "./dist/library-publisher.d.ts",
48
+ "default": "./dist/library-publisher.js"
49
+ },
50
+ "./doc-reconcile": {
51
+ "bun": "./src/doc-reconcile.ts",
52
+ "types": "./dist/doc-reconcile.d.ts",
53
+ "default": "./dist/doc-reconcile.js"
54
+ },
55
+ "./topic-uri": {
56
+ "bun": "./src/topic-uri.ts",
57
+ "types": "./dist/topic-uri.d.ts",
58
+ "default": "./dist/topic-uri.js"
59
+ },
60
+ "./doc-uri": {
61
+ "bun": "./src/doc-uri.ts",
62
+ "types": "./dist/doc-uri.d.ts",
63
+ "default": "./dist/doc-uri.js"
64
+ },
65
+ "./endpoints": {
66
+ "bun": "./src/endpoints.ts",
67
+ "types": "./dist/endpoints.d.ts",
68
+ "default": "./dist/endpoints.js"
69
+ },
70
+ "./jwt": {
71
+ "bun": "./src/jwt.ts",
72
+ "types": "./dist/jwt.d.ts",
73
+ "default": "./dist/jwt.js"
74
+ },
75
+ "./names": {
76
+ "bun": "./src/names.ts",
77
+ "types": "./dist/names.d.ts",
78
+ "default": "./dist/names.js"
79
+ },
80
+ "./suggestion-attribution": {
81
+ "bun": "./src/suggestion-attribution.ts",
82
+ "types": "./dist/suggestion-attribution.d.ts",
83
+ "default": "./dist/suggestion-attribution.js"
84
+ },
85
+ "./merge3": {
86
+ "bun": "./src/merge3.ts",
87
+ "types": "./dist/merge3.d.ts",
88
+ "default": "./dist/merge3.js"
89
+ },
90
+ "./types": {
91
+ "bun": "./src/types.ts",
92
+ "types": "./dist/types.d.ts",
93
+ "default": "./dist/types.js"
94
+ },
95
+ "./agent/armed-mode": {
96
+ "bun": "./src/agent/armed-mode.ts",
97
+ "types": "./dist/agent/armed-mode.d.ts",
98
+ "default": "./dist/agent/armed-mode.js"
99
+ },
100
+ "./agent/lane-draft": {
101
+ "bun": "./src/agent/lane-draft.ts",
102
+ "types": "./dist/agent/lane-draft.d.ts",
103
+ "default": "./dist/agent/lane-draft.js"
104
+ },
105
+ "./agent/lane-prompts": {
106
+ "bun": "./src/agent/lane-prompts.ts",
107
+ "types": "./dist/agent/lane-prompts.d.ts",
108
+ "default": "./dist/agent/lane-prompts.js"
109
+ },
110
+ "./agent/library-context": {
111
+ "bun": "./src/agent/library-context.ts",
112
+ "types": "./dist/agent/library-context.d.ts",
113
+ "default": "./dist/agent/library-context.js"
114
+ },
115
+ "./agent/library-model": {
116
+ "bun": "./src/agent/library-model.ts",
117
+ "types": "./dist/agent/library-model.d.ts",
118
+ "default": "./dist/agent/library-model.js"
119
+ },
120
+ "./agent/room-context": {
121
+ "bun": "./src/agent/room-context.ts",
122
+ "types": "./dist/agent/room-context.d.ts",
123
+ "default": "./dist/agent/room-context.js"
124
+ },
125
+ "./agent/sidebar-model": {
126
+ "bun": "./src/agent/sidebar-model.ts",
127
+ "types": "./dist/agent/sidebar-model.d.ts",
128
+ "default": "./dist/agent/sidebar-model.js"
129
+ },
130
+ "./agent/topic-inbox": {
131
+ "bun": "./src/agent/topic-inbox.ts",
132
+ "types": "./dist/agent/topic-inbox.d.ts",
133
+ "default": "./dist/agent/topic-inbox.js"
134
+ },
135
+ "./agent/topic-view-batcher": {
136
+ "bun": "./src/agent/topic-view-batcher.ts",
137
+ "types": "./dist/agent/topic-view-batcher.d.ts",
138
+ "default": "./dist/agent/topic-view-batcher.js"
139
+ },
140
+ "./agent/clients/common": {
141
+ "bun": "./src/agent/clients/common.ts",
142
+ "types": "./dist/agent/clients/common.d.ts",
143
+ "default": "./dist/agent/clients/common.js"
144
+ },
145
+ "./shared/rooms-protocol": {
146
+ "bun": "./src/shared/rooms-protocol.ts",
147
+ "types": "./dist/shared/rooms-protocol.d.ts",
148
+ "default": "./dist/shared/rooms-protocol.js"
149
+ },
150
+ "./shared/mentions": {
151
+ "bun": "./src/shared/mentions.ts",
152
+ "types": "./dist/shared/mentions.d.ts",
153
+ "default": "./dist/shared/mentions.js"
154
+ },
155
+ "./shared/emoji-data": {
156
+ "bun": "./src/shared/emoji-data.ts",
157
+ "types": "./dist/shared/emoji-data.d.ts",
158
+ "default": "./dist/shared/emoji-data.js"
159
+ },
160
+ "./version": {
161
+ "bun": "./src/version.ts",
162
+ "types": "./dist/version.d.ts",
163
+ "default": "./dist/version.js"
164
+ },
165
+ "./package.json": "./package.json"
166
+ },
167
+ "scripts": {
168
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
169
+ "prepublishOnly": "bun run build",
170
+ "test": "bun test",
171
+ "typecheck": "tsc --noEmit -p tsconfig.json"
172
+ },
173
+ "dependencies": {
174
+ "@earendil-works/pi-ai": "0.78.1",
175
+ "picocolors": "1.1.1"
176
+ },
177
+ "devDependencies": {
178
+ "@types/node": "25.9.2"
179
+ }
180
+ }