@liveblocks/core 2.24.0 → 2.25.0-aiprivatebeta0

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
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "2.24.0";
9
+ var PKG_VERSION = "2.25.0-aiprivatebeta0";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -1110,6 +1110,7 @@ function url(strings, ...values2) {
1110
1110
  function createApiClient({
1111
1111
  baseUrl,
1112
1112
  authManager,
1113
+ currentUserId,
1113
1114
  fetchPolyfill
1114
1115
  }) {
1115
1116
  const httpClient = new HttpClient(baseUrl, fetchPolyfill);
@@ -1530,6 +1531,103 @@ function createApiClient({
1530
1531
  const batch2 = getOrCreateAttachmentUrlsStore(options.roomId).batch;
1531
1532
  return batch2.get(options.attachmentId);
1532
1533
  }
1534
+ async function uploadChatAttachment(options) {
1535
+ const { chatId, attachment, signal } = options;
1536
+ const userId = currentUserId.get();
1537
+ if (userId === void 0) {
1538
+ throw new Error("Attachment upload requires an authenticated user.");
1539
+ }
1540
+ const ATTACHMENT_PART_SIZE = 5 * 1024 * 1024;
1541
+ if (options.attachment.file.size <= ATTACHMENT_PART_SIZE) {
1542
+ await httpClient.putBlob(
1543
+ url`/v2/c/chats/${chatId}/attachments/${attachment.id}/upload/${encodeURIComponent(attachment.file.name)}`,
1544
+ await authManager.getAuthValue({ requestedScope: "comments:read" }),
1545
+ attachment.file,
1546
+ { fileSize: attachment.file.size },
1547
+ { signal }
1548
+ );
1549
+ } else {
1550
+ const multipartUpload = await httpClient.post(
1551
+ url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${encodeURIComponent(attachment.file.name)}`,
1552
+ await authManager.getAuthValue({ requestedScope: "comments:read" }),
1553
+ void 0,
1554
+ { signal },
1555
+ { fileSize: attachment.file.size }
1556
+ );
1557
+ try {
1558
+ const uploadedParts = [];
1559
+ const parts = [];
1560
+ let start = 0;
1561
+ while (start < attachment.file.size) {
1562
+ const end = Math.min(
1563
+ start + ATTACHMENT_PART_SIZE,
1564
+ attachment.file.size
1565
+ );
1566
+ parts.push({
1567
+ number: parts.length + 1,
1568
+ part: attachment.file.slice(start, end)
1569
+ });
1570
+ start = end;
1571
+ }
1572
+ uploadedParts.push(
1573
+ ...await Promise.all(
1574
+ parts.map(async ({ number, part }) => {
1575
+ return await httpClient.putBlob(
1576
+ url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${multipartUpload.uploadId}/${String(number)}`,
1577
+ await authManager.getAuthValue({
1578
+ requestedScope: "comments:read"
1579
+ }),
1580
+ part,
1581
+ void 0,
1582
+ { signal }
1583
+ );
1584
+ })
1585
+ )
1586
+ );
1587
+ await httpClient.post(
1588
+ url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${multipartUpload.uploadId}/complete`,
1589
+ await authManager.getAuthValue({ requestedScope: "comments:read" }),
1590
+ { parts: uploadedParts.sort((a, b) => a.number - b.number) },
1591
+ { signal }
1592
+ );
1593
+ } catch (err) {
1594
+ try {
1595
+ await httpClient.delete(
1596
+ url`/v2/c/chats/${chatId}/attachments/${attachment.id}/multipart/${multipartUpload.uploadId}`,
1597
+ await authManager.getAuthValue({ requestedScope: "comments:read" })
1598
+ );
1599
+ } catch (err2) {
1600
+ }
1601
+ throw err;
1602
+ }
1603
+ }
1604
+ }
1605
+ const attachmentUrlsBatchStoresByChat = new DefaultMap((chatId) => {
1606
+ const batch2 = new Batch(
1607
+ async (batchedAttachmentIds) => {
1608
+ const attachmentIds = batchedAttachmentIds.flat();
1609
+ const { urls } = await httpClient.post(
1610
+ url`/v2/c/chats/${chatId}/attachments/presigned-urls`,
1611
+ await authManager.getAuthValue({
1612
+ requestedScope: "comments:read"
1613
+ }),
1614
+ { attachmentIds }
1615
+ );
1616
+ return urls.map(
1617
+ (url2) => url2 ?? new Error("There was an error while getting this attachment's URL")
1618
+ );
1619
+ },
1620
+ { delay: 50 }
1621
+ );
1622
+ return createBatchStore(batch2);
1623
+ });
1624
+ function getOrCreateChatAttachmentUrlsStore(chatId) {
1625
+ return attachmentUrlsBatchStoresByChat.getOrCreate(chatId);
1626
+ }
1627
+ function getChatAttachmentUrl(options) {
1628
+ const batch2 = getOrCreateChatAttachmentUrlsStore(options.chatId).batch;
1629
+ return batch2.get(options.attachmentId);
1630
+ }
1533
1631
  async function getSubscriptionSettings(options) {
1534
1632
  return httpClient.get(
1535
1633
  url`/v2/c/rooms/${options.roomId}/subscription-settings`,
@@ -1904,6 +2002,10 @@ function createApiClient({
1904
2002
  getAttachmentUrl,
1905
2003
  uploadAttachment,
1906
2004
  getOrCreateAttachmentUrlsStore,
2005
+ // User attachments
2006
+ uploadChatAttachment,
2007
+ getOrCreateChatAttachmentUrlsStore,
2008
+ getChatAttachmentUrl,
1907
2009
  // Room storage
1908
2010
  streamStorage,
1909
2011
  sendMessages,
@@ -3165,62 +3267,1132 @@ var ManagedSocket = class {
3165
3267
  while (cleanup = this.#cleanups.pop()) {
3166
3268
  cleanup();
3167
3269
  }
3168
- }
3169
- /**
3170
- * Safely send a message to the current WebSocket connection. Will emit a log
3171
- * message if this is somehow impossible.
3172
- */
3173
- send(data) {
3174
- const socket = this.#machine.context?.socket;
3175
- if (socket === null) {
3176
- warn("Cannot send: not connected yet", data);
3177
- } else if (socket.readyState !== 1) {
3178
- warn("Cannot send: WebSocket no longer open", data);
3179
- } else {
3180
- socket.send(data);
3270
+ }
3271
+ /**
3272
+ * Safely send a message to the current WebSocket connection. Will emit a log
3273
+ * message if this is somehow impossible.
3274
+ */
3275
+ send(data) {
3276
+ const socket = this.#machine.context?.socket;
3277
+ if (socket === null) {
3278
+ warn("Cannot send: not connected yet", data);
3279
+ } else if (socket.readyState !== 1) {
3280
+ warn("Cannot send: WebSocket no longer open", data);
3281
+ } else {
3282
+ socket.send(data);
3283
+ }
3284
+ }
3285
+ /**
3286
+ * NOTE: Used by the E2E app only, to simulate explicit events.
3287
+ * Not ideal to keep exposed :(
3288
+ */
3289
+ _privateSendMachineEvent(event) {
3290
+ this.#machine.send(event);
3291
+ }
3292
+ };
3293
+
3294
+ // src/internal.ts
3295
+ var kInternal = Symbol();
3296
+
3297
+ // src/lib/shallow.ts
3298
+ function shallowArray(xs, ys) {
3299
+ if (xs.length !== ys.length) {
3300
+ return false;
3301
+ }
3302
+ for (let i = 0; i < xs.length; i++) {
3303
+ if (!Object.is(xs[i], ys[i])) {
3304
+ return false;
3305
+ }
3306
+ }
3307
+ return true;
3308
+ }
3309
+ function shallowObj(objA, objB) {
3310
+ if (!isPlainObject(objA) || !isPlainObject(objB)) {
3311
+ return false;
3312
+ }
3313
+ const keysA = Object.keys(objA);
3314
+ if (keysA.length !== Object.keys(objB).length) {
3315
+ return false;
3316
+ }
3317
+ return keysA.every(
3318
+ (key) => Object.prototype.hasOwnProperty.call(objB, key) && Object.is(objA[key], objB[key])
3319
+ );
3320
+ }
3321
+ function shallow(a, b) {
3322
+ if (Object.is(a, b)) {
3323
+ return true;
3324
+ }
3325
+ const isArrayA = Array.isArray(a);
3326
+ const isArrayB = Array.isArray(b);
3327
+ if (isArrayA || isArrayB) {
3328
+ if (!isArrayA || !isArrayB) {
3329
+ return false;
3330
+ }
3331
+ return shallowArray(a, b);
3332
+ }
3333
+ return shallowObj(a, b);
3334
+ }
3335
+ function shallow2(a, b) {
3336
+ if (!isPlainObject(a) || !isPlainObject(b)) {
3337
+ return shallow(a, b);
3338
+ }
3339
+ const keysA = Object.keys(a);
3340
+ if (keysA.length !== Object.keys(b).length) {
3341
+ return false;
3342
+ }
3343
+ return keysA.every(
3344
+ (key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
3345
+ );
3346
+ }
3347
+
3348
+ // src/lib/SortedList.ts
3349
+ function bisectRight(arr, x, lt) {
3350
+ let lo = 0;
3351
+ let hi = arr.length;
3352
+ while (lo < hi) {
3353
+ const mid = lo + (hi - lo >> 1);
3354
+ if (lt(x, arr[mid])) {
3355
+ hi = mid;
3356
+ } else {
3357
+ lo = mid + 1;
3358
+ }
3359
+ }
3360
+ return lo;
3361
+ }
3362
+ var SortedList = class _SortedList {
3363
+ #data;
3364
+ #lt;
3365
+ constructor(alreadySortedList, lt) {
3366
+ this.#lt = lt;
3367
+ this.#data = alreadySortedList;
3368
+ }
3369
+ static with(lt) {
3370
+ return _SortedList.fromAlreadySorted([], lt);
3371
+ }
3372
+ static from(arr, lt) {
3373
+ const sorted = new _SortedList([], lt);
3374
+ for (const item of arr) {
3375
+ sorted.add(item);
3376
+ }
3377
+ return sorted;
3378
+ }
3379
+ static fromAlreadySorted(alreadySorted, lt) {
3380
+ return new _SortedList(alreadySorted, lt);
3381
+ }
3382
+ /**
3383
+ * Clones the sorted list to a new instance.
3384
+ */
3385
+ clone() {
3386
+ return new _SortedList(this.#data.slice(), this.#lt);
3387
+ }
3388
+ /**
3389
+ * Adds a new item to the sorted list, such that it remains sorted.
3390
+ */
3391
+ add(value) {
3392
+ const idx = bisectRight(this.#data, value, this.#lt);
3393
+ this.#data.splice(idx, 0, value);
3394
+ }
3395
+ /**
3396
+ * Removes all values from the sorted list, making it empty again.
3397
+ * Returns whether the list was mutated or not.
3398
+ */
3399
+ clear() {
3400
+ const hadData = this.#data.length > 0;
3401
+ this.#data.length = 0;
3402
+ return hadData;
3403
+ }
3404
+ /**
3405
+ * Removes the first value matching the predicate.
3406
+ * Returns whether the list was mutated or not.
3407
+ */
3408
+ removeBy(predicate, limit = Number.POSITIVE_INFINITY) {
3409
+ let deleted = 0;
3410
+ for (let i = 0; i < this.#data.length; i++) {
3411
+ if (predicate(this.#data[i])) {
3412
+ this.#data.splice(i, 1);
3413
+ deleted++;
3414
+ if (deleted >= limit) {
3415
+ break;
3416
+ } else {
3417
+ i--;
3418
+ }
3419
+ }
3420
+ }
3421
+ return deleted > 0;
3422
+ }
3423
+ /**
3424
+ * Removes the given value from the sorted list, if it exists. The given
3425
+ * value must be `===` to one of the list items. Only the first entry will be
3426
+ * removed if the element exists in the sorted list multiple times.
3427
+ *
3428
+ * Returns whether the list was mutated or not.
3429
+ */
3430
+ remove(value) {
3431
+ const idx = this.#data.indexOf(value);
3432
+ if (idx >= 0) {
3433
+ this.#data.splice(idx, 1);
3434
+ return true;
3435
+ }
3436
+ return false;
3437
+ }
3438
+ at(index) {
3439
+ return this.#data[index];
3440
+ }
3441
+ get length() {
3442
+ return this.#data.length;
3443
+ }
3444
+ *filter(predicate) {
3445
+ for (const item of this.#data) {
3446
+ if (predicate(item)) {
3447
+ yield item;
3448
+ }
3449
+ }
3450
+ }
3451
+ // XXXX If we keep this, add unit tests. Or remove it.
3452
+ *findAllRight(predicate) {
3453
+ for (let i = this.#data.length - 1; i >= 0; i--) {
3454
+ const item = this.#data[i];
3455
+ if (predicate(item, i)) {
3456
+ yield item;
3457
+ }
3458
+ }
3459
+ }
3460
+ [Symbol.iterator]() {
3461
+ return this.#data[Symbol.iterator]();
3462
+ }
3463
+ *iterReversed() {
3464
+ for (let i = this.#data.length - 1; i >= 0; i--) {
3465
+ yield this.#data[i];
3466
+ }
3467
+ }
3468
+ /** Finds the leftmost item that matches the predicate. */
3469
+ find(predicate, start) {
3470
+ const idx = this.findIndex(predicate, start);
3471
+ return idx > -1 ? this.#data.at(idx) : void 0;
3472
+ }
3473
+ /** Finds the leftmost index that matches the predicate. */
3474
+ findIndex(predicate, start = 0) {
3475
+ for (let i = Math.max(0, start); i < this.#data.length; i++) {
3476
+ if (predicate(this.#data[i], i)) {
3477
+ return i;
3478
+ }
3479
+ }
3480
+ return -1;
3481
+ }
3482
+ /** Finds the rightmost item that matches the predicate. */
3483
+ findRight(predicate, start) {
3484
+ const idx = this.findIndexRight(predicate, start);
3485
+ return idx > -1 ? this.#data.at(idx) : void 0;
3486
+ }
3487
+ /** Finds the rightmost index that matches the predicate. */
3488
+ findIndexRight(predicate, start = this.#data.length - 1) {
3489
+ for (let i = Math.min(start, this.#data.length - 1); i >= 0; i--) {
3490
+ if (predicate(this.#data[i], i)) {
3491
+ return i;
3492
+ }
3493
+ }
3494
+ return -1;
3495
+ }
3496
+ get rawArray() {
3497
+ return this.#data;
3498
+ }
3499
+ };
3500
+
3501
+ // src/lib/TreePool.ts
3502
+ var TreePool = class {
3503
+ #_items;
3504
+ #_childrenOf;
3505
+ #_sorted;
3506
+ #_primaryKey;
3507
+ #_parentKeyFn;
3508
+ #_lt;
3509
+ constructor(primaryKey, parentKey, lt) {
3510
+ this.#_primaryKey = primaryKey;
3511
+ this.#_parentKeyFn = parentKey;
3512
+ this.#_lt = lt;
3513
+ this.#_items = /* @__PURE__ */ new Map();
3514
+ this.#_childrenOf = new DefaultMap(() => /* @__PURE__ */ new Set());
3515
+ this.#_sorted = SortedList.with(lt);
3516
+ }
3517
+ get(id) {
3518
+ return this.#_items.get(id);
3519
+ }
3520
+ getOrThrow(id) {
3521
+ return this.get(id) ?? raise(`Item with id ${id} not found`);
3522
+ }
3523
+ get sorted() {
3524
+ return this.#_sorted;
3525
+ }
3526
+ getParentId(id) {
3527
+ const item = this.getOrThrow(id);
3528
+ return this.#_parentKeyFn(item);
3529
+ }
3530
+ getParent(id) {
3531
+ const parentId = this.getParentId(id);
3532
+ return parentId ? this.getOrThrow(parentId) : null;
3533
+ }
3534
+ getChildren(id) {
3535
+ const childIds = this.#_childrenOf.get(id);
3536
+ if (!childIds) return [];
3537
+ return Array.from(childIds).map(
3538
+ (id2) => this.#_items.get(id2)
3539
+ // eslint-disable-line no-restricted-syntax
3540
+ );
3541
+ }
3542
+ *walkUp(id, predicate) {
3543
+ const includeSelf = true;
3544
+ let nodeId = id;
3545
+ do {
3546
+ const item = this.getOrThrow(nodeId);
3547
+ if (includeSelf || nodeId !== id) {
3548
+ if (!predicate || predicate(item)) {
3549
+ yield item;
3550
+ }
3551
+ }
3552
+ nodeId = this.#_parentKeyFn(item);
3553
+ } while (nodeId !== null);
3554
+ }
3555
+ // XXXX Generalize
3556
+ *walkLeft(id, predicate) {
3557
+ const self = this.getOrThrow(id);
3558
+ const siblings = SortedList.from(this.getSiblings(id), this.#_lt);
3559
+ for (const sibling of siblings.iterReversed()) {
3560
+ if (this.#_lt(self, sibling)) continue;
3561
+ if (!predicate || predicate(sibling)) {
3562
+ yield sibling;
3563
+ }
3564
+ }
3565
+ }
3566
+ // XXXX Generalize
3567
+ *walkRight(id, predicate) {
3568
+ const self = this.getOrThrow(id);
3569
+ const siblings = SortedList.from(this.getSiblings(id), this.#_lt);
3570
+ for (const sibling of siblings) {
3571
+ if (this.#_lt(sibling, self)) continue;
3572
+ if (!predicate || predicate(sibling)) {
3573
+ yield sibling;
3574
+ }
3575
+ }
3576
+ }
3577
+ // XXXX Generalize
3578
+ *walkDown(id, predicate) {
3579
+ const children = SortedList.from(this.getChildren(id), this.#_lt).rawArray;
3580
+ for (let i = children.length - 1; i >= 0; i--) {
3581
+ const child = children[i];
3582
+ yield* this.walkDown(
3583
+ this.#_primaryKey(child),
3584
+ predicate
3585
+ // "depth-first",
3586
+ // true
3587
+ );
3588
+ if (!predicate || predicate(child)) {
3589
+ yield child;
3590
+ }
3591
+ }
3592
+ }
3593
+ /** Returns all siblings, not including the item itself. */
3594
+ getSiblings(id) {
3595
+ const self = this.getOrThrow(id);
3596
+ const parentId = this.getParentId(id);
3597
+ return this.getChildren(parentId).filter((item) => item !== self);
3598
+ }
3599
+ [Symbol.iterator]() {
3600
+ return this.#_sorted[Symbol.iterator]();
3601
+ }
3602
+ upsert(item) {
3603
+ const pk = this.#_primaryKey(item);
3604
+ const existing = this.#_items.get(pk);
3605
+ if (existing) {
3606
+ if (this.#_parentKeyFn(existing) !== this.#_parentKeyFn(item)) {
3607
+ throw new Error(
3608
+ "Cannot upsert parent ID changes that change the tree structure. Remove the entry first, and recreate it"
3609
+ );
3610
+ }
3611
+ this.#_sorted.remove(existing);
3612
+ }
3613
+ this.#_items.set(pk, item);
3614
+ this.#_sorted.add(item);
3615
+ const parentId = this.#_parentKeyFn(item);
3616
+ this.#_childrenOf.getOrCreate(parentId).add(pk);
3617
+ }
3618
+ remove(pk) {
3619
+ const item = this.#_items.get(pk);
3620
+ if (!item) return false;
3621
+ const childIds = this.#_childrenOf.get(pk);
3622
+ if (childIds) {
3623
+ throw new Error(
3624
+ `Cannot remove item '${pk}' while it still has children. Remove children first.`
3625
+ );
3626
+ }
3627
+ const parentId = this.#_parentKeyFn(item);
3628
+ const siblings = this.#_childrenOf.get(parentId);
3629
+ if (siblings) {
3630
+ siblings.delete(pk);
3631
+ if (siblings.size === 0) {
3632
+ this.#_childrenOf.delete(parentId);
3633
+ }
3634
+ }
3635
+ this.#_sorted.remove(item);
3636
+ this.#_childrenOf.delete(pk);
3637
+ this.#_items.delete(pk);
3638
+ return true;
3639
+ }
3640
+ clear() {
3641
+ if (this.#_items.size === 0) return false;
3642
+ this.#_childrenOf.clear();
3643
+ this.#_items.clear();
3644
+ this.#_sorted.clear();
3645
+ return true;
3646
+ }
3647
+ };
3648
+
3649
+ // src/protocol/AuthToken.ts
3650
+ var Permission = /* @__PURE__ */ ((Permission2) => {
3651
+ Permission2["Read"] = "room:read";
3652
+ Permission2["Write"] = "room:write";
3653
+ Permission2["PresenceWrite"] = "room:presence:write";
3654
+ Permission2["CommentsWrite"] = "comments:write";
3655
+ Permission2["CommentsRead"] = "comments:read";
3656
+ return Permission2;
3657
+ })(Permission || {});
3658
+ function canWriteStorage(scopes) {
3659
+ return scopes.includes("room:write" /* Write */);
3660
+ }
3661
+ function canComment(scopes) {
3662
+ return scopes.includes("comments:write" /* CommentsWrite */) || scopes.includes("room:write" /* Write */);
3663
+ }
3664
+ function isValidAuthTokenPayload(data) {
3665
+ return isPlainObject(data) && (data.k === "acc" /* ACCESS_TOKEN */ || data.k === "id" /* ID_TOKEN */ || data.k === "sec-legacy" /* SECRET_LEGACY */);
3666
+ }
3667
+ function parseAuthToken(rawTokenString) {
3668
+ const tokenParts = rawTokenString.split(".");
3669
+ if (tokenParts.length !== 3) {
3670
+ throw new Error("Authentication error: invalid JWT token");
3671
+ }
3672
+ const payload = tryParseJson(b64decode(tokenParts[1]));
3673
+ if (!(payload && isValidAuthTokenPayload(payload))) {
3674
+ throw new Error(
3675
+ "Authentication error: expected a valid token but did not get one. Hint: if you are using a callback, ensure the room is passed when creating the token. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientCallback"
3676
+ );
3677
+ }
3678
+ return {
3679
+ raw: rawTokenString,
3680
+ parsed: payload
3681
+ };
3682
+ }
3683
+
3684
+ // src/types/ai.ts
3685
+ function appendDelta(content, delta) {
3686
+ const lastPart = content[content.length - 1];
3687
+ switch (delta.type) {
3688
+ case "reasoning":
3689
+ case "text":
3690
+ case "tool-call":
3691
+ content.push(delta);
3692
+ break;
3693
+ case "text-delta":
3694
+ if (lastPart?.type === "text") {
3695
+ lastPart.text += delta.textDelta;
3696
+ } else {
3697
+ content.push({ type: "text", text: delta.textDelta });
3698
+ }
3699
+ break;
3700
+ case "reasoning-delta":
3701
+ if (lastPart?.type === "reasoning") {
3702
+ lastPart.text += delta.textDelta;
3703
+ lastPart.signature ??= delta.signature;
3704
+ } else {
3705
+ content.push({
3706
+ type: "reasoning",
3707
+ text: delta.textDelta ?? "",
3708
+ signature: delta.signature
3709
+ });
3710
+ }
3711
+ break;
3712
+ default:
3713
+ return assertNever(delta, "Unhandled case");
3714
+ }
3715
+ }
3716
+
3717
+ // src/ai.ts
3718
+ var DEFAULT_REQUEST_TIMEOUT = 4e3;
3719
+ var DEFAULT_AI_TIMEOUT = 3e4;
3720
+ function now() {
3721
+ return (/* @__PURE__ */ new Date()).toISOString();
3722
+ }
3723
+ function createStore_forTools() {
3724
+ const toolsByChatId\u03A3 = new DefaultMap((_chatId) => {
3725
+ return new DefaultMap((_toolName) => {
3726
+ return new Signal(void 0);
3727
+ });
3728
+ });
3729
+ function getToolDefinition\u03A3(chatId, toolName) {
3730
+ return toolsByChatId\u03A3.getOrCreate(chatId).getOrCreate(toolName);
3731
+ }
3732
+ function addToolDefinition(chatId, name, definition) {
3733
+ toolsByChatId\u03A3.getOrCreate(chatId).getOrCreate(name).set(definition);
3734
+ }
3735
+ function removeToolDefinition(chatId, toolName) {
3736
+ const tools = toolsByChatId\u03A3.get(chatId);
3737
+ if (tools === void 0) return;
3738
+ const tool = tools.get(toolName);
3739
+ if (tool === void 0) return;
3740
+ tool.set(void 0);
3741
+ }
3742
+ function getToolsForChat(chatId) {
3743
+ const tools = toolsByChatId\u03A3.get(chatId);
3744
+ if (tools === void 0) return [];
3745
+ return Array.from(tools.entries()).map(([name, tool]) => {
3746
+ if (tool.get() === void 0) return null;
3747
+ return {
3748
+ name,
3749
+ definition: tool.get()
3750
+ };
3751
+ }).filter((tool) => tool !== null);
3752
+ }
3753
+ return {
3754
+ getToolCallByName\u03A3: getToolDefinition\u03A3,
3755
+ getToolsForChat,
3756
+ addToolDefinition,
3757
+ removeToolDefinition
3758
+ };
3759
+ }
3760
+ function createStore_forChatMessages() {
3761
+ const messagePoolByChatId\u03A3 = new DefaultMap(
3762
+ (_chatId) => new MutableSignal(
3763
+ new TreePool(
3764
+ (x) => x.id,
3765
+ (x) => x.parentId,
3766
+ (x, y) => x.createdAt < y.createdAt
3767
+ )
3768
+ )
3769
+ );
3770
+ const pendingMessages\u03A3 = new MutableSignal(
3771
+ /* @__PURE__ */ new Map()
3772
+ );
3773
+ function createOptimistically(chatId, role, parentId, third) {
3774
+ const id = `ms_${nanoid()}`;
3775
+ const createdAt = now();
3776
+ if (role === "user") {
3777
+ const content = third;
3778
+ upsert({
3779
+ id,
3780
+ chatId,
3781
+ role,
3782
+ parentId,
3783
+ createdAt,
3784
+ content
3785
+ });
3786
+ } else {
3787
+ upsert({
3788
+ id,
3789
+ chatId,
3790
+ role,
3791
+ parentId,
3792
+ createdAt,
3793
+ status: "pending",
3794
+ contentSoFar: []
3795
+ });
3796
+ }
3797
+ return id;
3798
+ }
3799
+ function upsertMany(messages) {
3800
+ batch(() => {
3801
+ for (const message of messages) {
3802
+ upsert(message);
3803
+ }
3804
+ });
3805
+ }
3806
+ function remove(chatId, messageId) {
3807
+ const chatMsgs\u03A3 = messagePoolByChatId\u03A3.get(chatId);
3808
+ if (!chatMsgs\u03A3) return;
3809
+ const existing = chatMsgs\u03A3.get().get(messageId);
3810
+ if (!existing || existing.deletedAt) return;
3811
+ if (existing.role === "assistant" && (existing.status === "pending" || existing.status === "failed")) {
3812
+ upsert({ ...existing, deletedAt: now(), contentSoFar: [] });
3813
+ } else {
3814
+ upsert({ ...existing, deletedAt: now(), content: [] });
3815
+ }
3816
+ }
3817
+ function removeByChatId(chatId) {
3818
+ const chatMsgs\u03A3 = messagePoolByChatId\u03A3.get(chatId);
3819
+ if (chatMsgs\u03A3 === void 0) return;
3820
+ chatMsgs\u03A3.mutate((pool) => pool.clear());
3821
+ }
3822
+ function upsert(message) {
3823
+ batch(() => {
3824
+ const chatMsgs\u03A3 = messagePoolByChatId\u03A3.getOrCreate(message.chatId);
3825
+ chatMsgs\u03A3.mutate((pool) => pool.upsert(message));
3826
+ if (message.role === "assistant" && message.status === "pending") {
3827
+ pendingMessages\u03A3.mutate((lut) => {
3828
+ lut.set(message.id, structuredClone(message));
3829
+ });
3830
+ } else {
3831
+ pendingMessages\u03A3.mutate((lut) => {
3832
+ lut.delete(message.id);
3833
+ });
3834
+ }
3835
+ });
3836
+ }
3837
+ function addDelta(messageId, delta) {
3838
+ pendingMessages\u03A3.mutate((lut) => {
3839
+ const message = lut.get(messageId);
3840
+ if (message === void 0) return false;
3841
+ appendDelta(message.contentSoFar, delta);
3842
+ lut.set(messageId, message);
3843
+ return true;
3844
+ });
3845
+ }
3846
+ function* iterPendingMessages() {
3847
+ for (const chatMsgs\u03A3 of messagePoolByChatId\u03A3.values()) {
3848
+ for (const m of chatMsgs\u03A3.get()) {
3849
+ if (m.role === "assistant" && m.status === "pending") {
3850
+ yield m;
3851
+ }
3852
+ }
3853
+ }
3854
+ }
3855
+ function failAllPending() {
3856
+ batch(() => {
3857
+ pendingMessages\u03A3.mutate((lut) => lut.clear());
3858
+ upsertMany(
3859
+ Array.from(iterPendingMessages()).map(
3860
+ (message) => ({
3861
+ ...message,
3862
+ status: "failed",
3863
+ errorReason: "Lost connection"
3864
+ })
3865
+ )
3866
+ );
3867
+ });
3868
+ }
3869
+ function getMessageById(messageId) {
3870
+ for (const messages\u03A3 of messagePoolByChatId\u03A3.values()) {
3871
+ const message = messages\u03A3.get().get(messageId);
3872
+ if (message) {
3873
+ return message;
3874
+ }
3875
+ }
3876
+ return void 0;
3877
+ }
3878
+ function first(iterable) {
3879
+ const result = iterable.next();
3880
+ return result.done ? void 0 : result.value;
3881
+ }
3882
+ function selectBranch(pool, preferredBranch) {
3883
+ function isAlive(message2) {
3884
+ if (!message2.deletedAt) {
3885
+ return true;
3886
+ }
3887
+ for (const _ of pool.walkDown(message2.id, (m) => !m.deletedAt)) {
3888
+ return true;
3889
+ }
3890
+ return false;
3891
+ }
3892
+ function selectSpine(leaf) {
3893
+ const spine = [];
3894
+ for (const message2 of pool.walkUp(leaf.id)) {
3895
+ const prev = first(pool.walkLeft(message2.id, isAlive))?.id ?? null;
3896
+ const next = first(pool.walkRight(message2.id, isAlive))?.id ?? null;
3897
+ if (!message2.deletedAt || prev || next) {
3898
+ spine.push({ ...message2, prev, next });
3899
+ }
3900
+ }
3901
+ return spine.reverse();
3902
+ }
3903
+ function fallback() {
3904
+ const latest = pool.sorted.findRight((m) => !m.deletedAt);
3905
+ return latest ? selectSpine(latest) : [];
3906
+ }
3907
+ if (preferredBranch === null) {
3908
+ return fallback();
3909
+ }
3910
+ const message = pool.get(preferredBranch);
3911
+ if (!message) {
3912
+ return fallback();
3913
+ }
3914
+ for (const current of pool.walkUp(message.id)) {
3915
+ for (const desc of pool.walkDown(current.id, (m) => !m.deletedAt)) {
3916
+ return selectSpine(desc);
3917
+ }
3918
+ if (!current.deletedAt) {
3919
+ return selectSpine(current);
3920
+ }
3921
+ }
3922
+ return fallback();
3923
+ }
3924
+ function getLatestUserMessageAncestor(chatId, messageId) {
3925
+ const pool = messagePoolByChatId\u03A3.getOrCreate(chatId).get();
3926
+ const message = pool.get(messageId);
3927
+ if (!message) return null;
3928
+ if (message.role === "user") return message.id;
3929
+ for (const m of pool.walkUp(message.id)) {
3930
+ if (m.role === "user" && !m.deletedAt) {
3931
+ return m.id;
3932
+ }
3933
+ }
3934
+ return null;
3935
+ }
3936
+ const immutableMessagesByBranch = new DefaultMap((chatId) => {
3937
+ return new DefaultMap((branchId) => {
3938
+ const messages\u03A3 = DerivedSignal.from(() => {
3939
+ const pool = messagePoolByChatId\u03A3.getOrCreate(chatId).get();
3940
+ return selectBranch(pool, branchId);
3941
+ }, shallow2);
3942
+ return DerivedSignal.from(() => {
3943
+ const pendingMessages = pendingMessages\u03A3.get();
3944
+ return messages\u03A3.get().map((message) => {
3945
+ if (message.role !== "assistant" || message.status !== "pending") {
3946
+ return message;
3947
+ }
3948
+ const pendingMessage = pendingMessages.get(message.id);
3949
+ if (pendingMessage === void 0) return message;
3950
+ return {
3951
+ ...message,
3952
+ contentSoFar: pendingMessage.contentSoFar
3953
+ };
3954
+ });
3955
+ }, shallow);
3956
+ });
3957
+ });
3958
+ function getChatMessagesForBranch\u03A3(chatId, branch) {
3959
+ return immutableMessagesByBranch.getOrCreate(chatId).getOrCreate(branch || null);
3960
+ }
3961
+ const messagesByChatId\u03A3 = new DefaultMap((chatId) => {
3962
+ return DerivedSignal.from(() => {
3963
+ const pool = messagePoolByChatId\u03A3.getOrCreate(chatId).get();
3964
+ return Array.from(pool.sorted);
3965
+ });
3966
+ });
3967
+ function getMessagesForChat\u03A3(chatId) {
3968
+ return messagesByChatId\u03A3.getOrCreate(chatId);
3969
+ }
3970
+ return {
3971
+ // Readers
3972
+ getMessageById,
3973
+ getChatMessagesForBranch\u03A3,
3974
+ getMessagesForChat\u03A3,
3975
+ getLatestUserMessageAncestor,
3976
+ // Mutations
3977
+ createOptimistically,
3978
+ upsert,
3979
+ upsertMany,
3980
+ remove,
3981
+ removeByChatId,
3982
+ addDelta,
3983
+ failAllPending
3984
+ };
3985
+ }
3986
+ function createStore_forUserAiChats() {
3987
+ const mutable\u03A3 = new MutableSignal(
3988
+ SortedList.with((x, y) => y.createdAt < x.createdAt)
3989
+ );
3990
+ const chats\u03A3 = DerivedSignal.from(
3991
+ () => Array.from(mutable\u03A3.get()).filter((c) => !c.ephemeral && !c.deletedAt)
3992
+ );
3993
+ function upsertMany(chats) {
3994
+ mutable\u03A3.mutate((list) => {
3995
+ for (const chat of chats) {
3996
+ remove(chat.id);
3997
+ list.add(chat);
3998
+ }
3999
+ });
4000
+ }
4001
+ function upsert(chat) {
4002
+ upsertMany([chat]);
4003
+ }
4004
+ function remove(chatId) {
4005
+ mutable\u03A3.mutate((list) => list.removeBy((c) => c.id === chatId, 1));
4006
+ }
4007
+ return {
4008
+ chats\u03A3,
4009
+ // Mutations
4010
+ upsert,
4011
+ upsertMany,
4012
+ remove
4013
+ };
4014
+ }
4015
+ function createAi(config) {
4016
+ const managedSocket = new ManagedSocket(
4017
+ config.delegates,
4018
+ config.enableDebugLogging,
4019
+ false
4020
+ // AI doesn't have actors (yet, but it will)
4021
+ );
4022
+ const clientId = nanoid(7);
4023
+ const chatsStore = createStore_forUserAiChats();
4024
+ const messagesStore = createStore_forChatMessages();
4025
+ const toolsStore = createStore_forTools();
4026
+ const context = {
4027
+ staticSessionInfoSig: new Signal(null),
4028
+ dynamicSessionInfoSig: new Signal(null),
4029
+ pendingCmds: /* @__PURE__ */ new Map(),
4030
+ chatsStore,
4031
+ messagesStore,
4032
+ toolsStore,
4033
+ contextByChatId: /* @__PURE__ */ new Map()
4034
+ };
4035
+ let lastTokenKey;
4036
+ function onStatusDidChange(newStatus) {
4037
+ warn("onStatusDidChange", newStatus);
4038
+ const authValue = managedSocket.authValue;
4039
+ if (authValue !== null) {
4040
+ const tokenKey = getBearerTokenFromAuthValue(authValue);
4041
+ if (tokenKey !== lastTokenKey) {
4042
+ lastTokenKey = tokenKey;
4043
+ if (authValue.type === "secret") {
4044
+ const token = authValue.token.parsed;
4045
+ context.staticSessionInfoSig.set({
4046
+ userId: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.id : token.uid,
4047
+ userInfo: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.info : token.ui
4048
+ });
4049
+ } else {
4050
+ context.staticSessionInfoSig.set({
4051
+ userId: void 0,
4052
+ userInfo: void 0
4053
+ });
4054
+ }
4055
+ }
4056
+ }
4057
+ }
4058
+ let _connectionLossTimerId;
4059
+ let _hasLostConnection = false;
4060
+ function handleConnectionLossEvent(newStatus) {
4061
+ if (newStatus === "reconnecting") {
4062
+ _connectionLossTimerId = setTimeout(() => {
4063
+ _hasLostConnection = true;
4064
+ }, config.lostConnectionTimeout);
4065
+ } else {
4066
+ clearTimeout(_connectionLossTimerId);
4067
+ if (_hasLostConnection) {
4068
+ _hasLostConnection = false;
4069
+ }
4070
+ }
4071
+ }
4072
+ function onDidConnect() {
4073
+ warn("onDidConnect");
4074
+ }
4075
+ function onDidDisconnect() {
4076
+ warn("onDidDisconnect");
4077
+ }
4078
+ function handleServerMessage(event) {
4079
+ if (typeof event.data !== "string")
4080
+ return;
4081
+ const msg = tryParseJson(event.data);
4082
+ if (!msg)
4083
+ return;
4084
+ const cmdId = "cmdId" in msg ? msg.cmdId : msg.event === "cmd-failed" ? msg.failedCmdId : void 0;
4085
+ const pendingCmd = context.pendingCmds.get(cmdId);
4086
+ if (cmdId && !pendingCmd) {
4087
+ warn("Ignoring unexpected command response. Already timed out, or not for us?", msg);
4088
+ return;
4089
+ }
4090
+ if ("event" in msg) {
4091
+ switch (msg.event) {
4092
+ case "cmd-failed":
4093
+ pendingCmd?.reject(new Error(msg.error));
4094
+ break;
4095
+ case "delta": {
4096
+ const { id, delta } = msg;
4097
+ context.messagesStore.addDelta(id, delta);
4098
+ break;
4099
+ }
4100
+ case "settle": {
4101
+ context.messagesStore.upsert(msg.message);
4102
+ break;
4103
+ }
4104
+ case "error":
4105
+ break;
4106
+ case "rebooted":
4107
+ context.messagesStore.failAllPending();
4108
+ break;
4109
+ case "sync":
4110
+ batch(() => {
4111
+ for (const m of msg["-messages"] ?? []) {
4112
+ context.messagesStore.remove(m.chatId, m.id);
4113
+ }
4114
+ for (const chatId of msg["-chats"] ?? []) {
4115
+ context.chatsStore.remove(chatId);
4116
+ context.messagesStore.removeByChatId(chatId);
4117
+ }
4118
+ for (const chatId of msg.clear ?? []) {
4119
+ context.messagesStore.removeByChatId(chatId);
4120
+ }
4121
+ if (msg.chats) {
4122
+ context.chatsStore.upsertMany(msg.chats);
4123
+ }
4124
+ if (msg.messages) {
4125
+ context.messagesStore.upsertMany(msg.messages);
4126
+ }
4127
+ });
4128
+ break;
4129
+ default:
4130
+ return assertNever(msg, "Unhandled case");
4131
+ }
4132
+ } else {
4133
+ switch (msg.cmd) {
4134
+ case "get-chats":
4135
+ context.chatsStore.upsertMany(msg.chats);
4136
+ break;
4137
+ case "create-chat":
4138
+ context.chatsStore.upsert(msg.chat);
4139
+ break;
4140
+ case "delete-chat":
4141
+ context.chatsStore.remove(msg.chatId);
4142
+ context.messagesStore.removeByChatId(msg.chatId);
4143
+ break;
4144
+ case "get-message-tree":
4145
+ context.chatsStore.upsert(msg.chat);
4146
+ context.messagesStore.upsertMany(msg.messages);
4147
+ break;
4148
+ case "add-user-message":
4149
+ context.messagesStore.upsert(msg.message);
4150
+ break;
4151
+ case "delete-message":
4152
+ context.messagesStore.remove(msg.chatId, msg.messageId);
4153
+ break;
4154
+ case "clear-chat":
4155
+ context.messagesStore.removeByChatId(msg.chatId);
4156
+ break;
4157
+ case "ask-ai":
4158
+ if (msg.message) {
4159
+ context.messagesStore.upsert(msg.message);
4160
+ } else {
4161
+ }
4162
+ break;
4163
+ case "abort-ai":
4164
+ break;
4165
+ default:
4166
+ return assertNever(msg, "Unhandled case");
4167
+ }
4168
+ }
4169
+ pendingCmd?.resolve(msg);
4170
+ }
4171
+ managedSocket.events.onMessage.subscribe(handleServerMessage);
4172
+ managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
4173
+ managedSocket.events.statusDidChange.subscribe(handleConnectionLossEvent);
4174
+ managedSocket.events.didConnect.subscribe(onDidConnect);
4175
+ managedSocket.events.didDisconnect.subscribe(onDidDisconnect);
4176
+ managedSocket.events.onConnectionError.subscribe(({ message, code }) => {
4177
+ if (process.env.NODE_ENV !== "production") {
4178
+ error2(
4179
+ `Connection to websocket server closed. Reason: ${message} (code: ${code}).`
4180
+ );
4181
+ }
4182
+ });
4183
+ async function sendClientMsgWithResponse(msg) {
4184
+ if (managedSocket.getStatus() !== "connected") {
4185
+ await managedSocket.events.didConnect.waitUntil();
3181
4186
  }
4187
+ const { promise, resolve, reject } = Promise_withResolvers();
4188
+ const abortSignal = AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT);
4189
+ abortSignal.addEventListener("abort", () => reject(abortSignal.reason), {
4190
+ once: true
4191
+ });
4192
+ const cmdId = nanoid(7);
4193
+ context.pendingCmds.set(cmdId, { resolve, reject });
4194
+ sendClientMsg({ ...msg, cmdId });
4195
+ return promise.finally(() => {
4196
+ context.pendingCmds.delete(cmdId);
4197
+ }).catch((err) => {
4198
+ error2(err.message);
4199
+ throw err;
4200
+ });
3182
4201
  }
3183
- /**
3184
- * NOTE: Used by the E2E app only, to simulate explicit events.
3185
- * Not ideal to keep exposed :(
3186
- */
3187
- _privateSendMachineEvent(event) {
3188
- this.#machine.send(event);
4202
+ function sendClientMsg(msg) {
4203
+ managedSocket.send(
4204
+ JSON.stringify({
4205
+ ...msg
4206
+ })
4207
+ );
3189
4208
  }
3190
- };
3191
-
3192
- // src/protocol/AuthToken.ts
3193
- var Permission = /* @__PURE__ */ ((Permission2) => {
3194
- Permission2["Read"] = "room:read";
3195
- Permission2["Write"] = "room:write";
3196
- Permission2["PresenceWrite"] = "room:presence:write";
3197
- Permission2["CommentsWrite"] = "comments:write";
3198
- Permission2["CommentsRead"] = "comments:read";
3199
- return Permission2;
3200
- })(Permission || {});
3201
- function canWriteStorage(scopes) {
3202
- return scopes.includes("room:write" /* Write */);
3203
- }
3204
- function canComment(scopes) {
3205
- return scopes.includes("comments:write" /* CommentsWrite */) || scopes.includes("room:write" /* Write */);
3206
- }
3207
- function isValidAuthTokenPayload(data) {
3208
- return isPlainObject(data) && (data.k === "acc" /* ACCESS_TOKEN */ || data.k === "id" /* ID_TOKEN */ || data.k === "sec-legacy" /* SECRET_LEGACY */);
3209
- }
3210
- function parseAuthToken(rawTokenString) {
3211
- const tokenParts = rawTokenString.split(".");
3212
- if (tokenParts.length !== 3) {
3213
- throw new Error("Authentication error: invalid JWT token");
4209
+ function getChats(options = {}) {
4210
+ return sendClientMsgWithResponse({
4211
+ cmd: "get-chats",
4212
+ cursor: options.cursor
4213
+ });
3214
4214
  }
3215
- const payload = tryParseJson(b64decode(tokenParts[1]));
3216
- if (!(payload && isValidAuthTokenPayload(payload))) {
3217
- throw new Error(
3218
- "Authentication error: expected a valid token but did not get one. Hint: if you are using a callback, ensure the room is passed when creating the token. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientCallback"
4215
+ function createChat(id, name, options) {
4216
+ return sendClientMsgWithResponse({
4217
+ cmd: "create-chat",
4218
+ id,
4219
+ name,
4220
+ ephemeral: options?.ephemeral ?? false,
4221
+ metadata: options?.metadata ?? {}
4222
+ });
4223
+ }
4224
+ function getMessageTree(chatId) {
4225
+ return sendClientMsgWithResponse({
4226
+ cmd: "get-message-tree",
4227
+ chatId
4228
+ });
4229
+ }
4230
+ function registerChatContext(chatId, data) {
4231
+ const chatContext = context.contextByChatId.get(chatId);
4232
+ if (chatContext === void 0) {
4233
+ context.contextByChatId.set(chatId, /* @__PURE__ */ new Set([data]));
4234
+ } else {
4235
+ chatContext.add(data);
4236
+ }
4237
+ return () => {
4238
+ const chatContext2 = context.contextByChatId.get(chatId);
4239
+ if (chatContext2 !== void 0) {
4240
+ chatContext2.delete(data);
4241
+ if (chatContext2.size === 0) {
4242
+ context.contextByChatId.delete(chatId);
4243
+ }
4244
+ }
4245
+ };
4246
+ }
4247
+ function ask(chatId, messageId, options) {
4248
+ const targetMessageId = context.messagesStore.createOptimistically(
4249
+ chatId,
4250
+ "assistant",
4251
+ messageId
3219
4252
  );
4253
+ const copilotId = options?.copilotId;
4254
+ const stream = options?.stream ?? false;
4255
+ const timeout = options?.timeout ?? DEFAULT_AI_TIMEOUT;
4256
+ const chatContext = context.contextByChatId.get(chatId);
4257
+ return sendClientMsgWithResponse({
4258
+ cmd: "ask-ai",
4259
+ chatId,
4260
+ sourceMessageId: messageId,
4261
+ targetMessageId,
4262
+ copilotId,
4263
+ clientId,
4264
+ stream,
4265
+ tools: context.toolsStore.getToolsForChat(chatId).map((tool) => ({
4266
+ name: tool.name,
4267
+ description: tool.definition.description,
4268
+ parameters: tool.definition.parameters
4269
+ })),
4270
+ timeout,
4271
+ context: chatContext ? Array.from(chatContext.values()) : void 0
4272
+ });
3220
4273
  }
3221
- return {
3222
- raw: rawTokenString,
3223
- parsed: payload
4274
+ return Object.defineProperty(
4275
+ {
4276
+ [kInternal]: {
4277
+ debugContext: () => context
4278
+ },
4279
+ connect: () => managedSocket.connect(),
4280
+ reconnect: () => managedSocket.reconnect(),
4281
+ disconnect: () => managedSocket.disconnect(),
4282
+ getChats,
4283
+ createChat,
4284
+ deleteChat: (chatId) => {
4285
+ return sendClientMsgWithResponse({
4286
+ cmd: "delete-chat",
4287
+ chatId
4288
+ });
4289
+ },
4290
+ getMessageTree,
4291
+ deleteMessage: (chatId, messageId) => sendClientMsgWithResponse({ cmd: "delete-message", chatId, messageId }),
4292
+ clearChat: (chatId) => sendClientMsgWithResponse({ cmd: "clear-chat", chatId }),
4293
+ addUserMessage: (chatId, parentMessageId, message) => {
4294
+ const content = [{ type: "text", text: message }];
4295
+ const newMessageId = context.messagesStore.createOptimistically(
4296
+ chatId,
4297
+ "user",
4298
+ parentMessageId,
4299
+ content
4300
+ );
4301
+ return sendClientMsgWithResponse({
4302
+ cmd: "add-user-message",
4303
+ id: newMessageId,
4304
+ chatId,
4305
+ parentMessageId,
4306
+ content
4307
+ });
4308
+ },
4309
+ ask,
4310
+ regenerateMessage: (chatId, messageId, options) => {
4311
+ const parentUserMessageId = context.messagesStore.getLatestUserMessageAncestor(chatId, messageId);
4312
+ if (parentUserMessageId === null) {
4313
+ throw new Error(
4314
+ `Unable to find user message ancestor for messageId: ${messageId}`
4315
+ );
4316
+ }
4317
+ return ask(chatId, parentUserMessageId, options);
4318
+ },
4319
+ addUserMessageAndAsk: async (chatId, parentMessageId, message, options) => {
4320
+ const content = [{ type: "text", text: message }];
4321
+ const newMessageId = context.messagesStore.createOptimistically(
4322
+ chatId,
4323
+ "user",
4324
+ parentMessageId,
4325
+ content
4326
+ );
4327
+ const targetMessageId = context.messagesStore.createOptimistically(
4328
+ chatId,
4329
+ "assistant",
4330
+ newMessageId
4331
+ );
4332
+ await sendClientMsgWithResponse({
4333
+ cmd: "add-user-message",
4334
+ id: newMessageId,
4335
+ chatId,
4336
+ parentMessageId,
4337
+ content
4338
+ });
4339
+ const copilotId = options?.copilotId;
4340
+ const stream = options?.stream ?? false;
4341
+ const timeout = options?.timeout ?? DEFAULT_AI_TIMEOUT;
4342
+ const chatContext = context.contextByChatId.get(chatId);
4343
+ return sendClientMsgWithResponse({
4344
+ cmd: "ask-ai",
4345
+ chatId,
4346
+ sourceMessageId: newMessageId,
4347
+ targetMessageId,
4348
+ copilotId,
4349
+ clientId,
4350
+ stream,
4351
+ tools: context.toolsStore.getToolsForChat(chatId).map((tool) => ({
4352
+ name: tool.name,
4353
+ description: tool.definition.description,
4354
+ parameters: tool.definition.parameters
4355
+ })),
4356
+ timeout,
4357
+ context: chatContext ? Array.from(chatContext.values()) : void 0
4358
+ });
4359
+ },
4360
+ abort: (messageId) => sendClientMsgWithResponse({ cmd: "abort-ai", messageId }),
4361
+ getStatus: () => managedSocket.getStatus(),
4362
+ signals: {
4363
+ chats\u03A3: context.chatsStore.chats\u03A3,
4364
+ getChatMessagesForBranch\u03A3: context.messagesStore.getChatMessagesForBranch\u03A3,
4365
+ getToolDefinition\u03A3: context.toolsStore.getToolCallByName\u03A3,
4366
+ getMessagesForChat\u03A3: context.messagesStore.getMessagesForChat\u03A3
4367
+ },
4368
+ registerChatContext,
4369
+ registerChatTool: context.toolsStore.addToolDefinition,
4370
+ unregisterChatTool: context.toolsStore.removeToolDefinition
4371
+ },
4372
+ kInternal,
4373
+ { enumerable: false }
4374
+ );
4375
+ }
4376
+ function makeCreateSocketDelegateForAi(baseUrl, WebSocketPolyfill) {
4377
+ return (authValue) => {
4378
+ const ws = WebSocketPolyfill ?? (typeof WebSocket === "undefined" ? void 0 : WebSocket);
4379
+ if (ws === void 0) {
4380
+ throw new StopRetrying(
4381
+ "To use Liveblocks client in a non-DOM environment, you need to provide a WebSocket polyfill."
4382
+ );
4383
+ }
4384
+ const url2 = new URL(baseUrl);
4385
+ url2.protocol = url2.protocol === "http:" ? "ws" : "wss";
4386
+ url2.pathname = "/ai/v1";
4387
+ if (authValue.type === "secret") {
4388
+ url2.searchParams.set("tok", authValue.token.raw);
4389
+ } else if (authValue.type === "public") {
4390
+ throw new Error("Public key not supported with AI Copilots");
4391
+ } else {
4392
+ return assertNever(authValue, "Unhandled case");
4393
+ }
4394
+ url2.searchParams.set("version", PKG_VERSION || "dev");
4395
+ return new ws(url2.toString());
3224
4396
  };
3225
4397
  }
3226
4398
 
@@ -3246,11 +4418,11 @@ function createAuthManager(authOptions, onAuthenticate) {
3246
4418
  return false;
3247
4419
  }
3248
4420
  function getCachedToken(requestOptions) {
3249
- const now = Math.ceil(Date.now() / 1e3);
4421
+ const now2 = Math.ceil(Date.now() / 1e3);
3250
4422
  for (let i = tokens.length - 1; i >= 0; i--) {
3251
4423
  const token = tokens[i];
3252
4424
  const expiresAt = expiryTimes[i];
3253
- if (expiresAt <= now) {
4425
+ if (expiresAt <= now2) {
3254
4426
  tokens.splice(i, 1);
3255
4427
  expiryTimes.splice(i, 1);
3256
4428
  continue;
@@ -3444,9 +4616,6 @@ async function fetchAuthEndpoint(fetch, endpoint, body) {
3444
4616
  // src/constants.ts
3445
4617
  var DEFAULT_BASE_URL = "https://api.liveblocks.io";
3446
4618
 
3447
- // src/internal.ts
3448
- var kInternal = Symbol();
3449
-
3450
4619
  // src/devtools/bridge.ts
3451
4620
  var _bridgeActive = false;
3452
4621
  function activateBridge(allowed) {
@@ -6561,6 +7730,14 @@ function defaultMessageFromContext(context) {
6561
7730
  return "Could not connect to the room";
6562
7731
  }
6563
7732
  }
7733
+ case "AI_CONNECTION_ERROR": {
7734
+ switch (context.code) {
7735
+ case 4001:
7736
+ return "Not allowed to connect to ai";
7737
+ default:
7738
+ return "Could not connect to the room";
7739
+ }
7740
+ }
6564
7741
  case "CREATE_THREAD_ERROR":
6565
7742
  return "Could not create new thread";
6566
7743
  case "DELETE_THREAD_ERROR":
@@ -7440,8 +8617,8 @@ ${Array.from(traces).join("\n\n")}`
7440
8617
  context.buffer.storageOperations = [];
7441
8618
  return;
7442
8619
  }
7443
- const now = Date.now();
7444
- const elapsedMillis = now - context.buffer.lastFlushedAt;
8620
+ const now2 = Date.now();
8621
+ const elapsedMillis = now2 - context.buffer.lastFlushedAt;
7445
8622
  if (elapsedMillis >= config.throttleDelay) {
7446
8623
  const messagesToFlush = serializeBuffer();
7447
8624
  if (messagesToFlush.length === 0) {
@@ -7450,7 +8627,7 @@ ${Array.from(traces).join("\n\n")}`
7450
8627
  sendMessages(messagesToFlush);
7451
8628
  context.buffer = {
7452
8629
  flushTimerID: void 0,
7453
- lastFlushedAt: now,
8630
+ lastFlushedAt: now2,
7454
8631
  messages: [],
7455
8632
  storageOperations: [],
7456
8633
  presenceUpdates: null
@@ -8177,9 +9354,26 @@ function createClient(options) {
8177
9354
  const httpClient = createApiClient({
8178
9355
  baseUrl,
8179
9356
  fetchPolyfill,
9357
+ currentUserId,
8180
9358
  authManager
8181
9359
  });
8182
9360
  const roomsById = /* @__PURE__ */ new Map();
9361
+ const ai = createAi({
9362
+ userId: currentUserId.get(),
9363
+ lostConnectionTimeout,
9364
+ backgroundKeepAliveTimeout: getBackgroundKeepAliveTimeout(
9365
+ clientOptions.backgroundKeepAliveTimeout
9366
+ ),
9367
+ polyfills: clientOptions.polyfills,
9368
+ delegates: {
9369
+ createSocket: makeCreateSocketDelegateForAi(
9370
+ baseUrl,
9371
+ clientOptions.polyfills?.WebSocket
9372
+ ),
9373
+ authenticate: makeAuthDelegateForRoom("default", authManager),
9374
+ canZombie: () => true
9375
+ }
9376
+ });
8183
9377
  function teardownRoom(room) {
8184
9378
  unlinkDevTools(room.id);
8185
9379
  roomsById.delete(room.id);
@@ -8393,6 +9587,7 @@ function createClient(options) {
8393
9587
  [kInternal]: {
8394
9588
  currentUserId,
8395
9589
  mentionSuggestionsCache,
9590
+ ai,
8396
9591
  resolveMentionSuggestions: clientOptions.resolveMentionSuggestions,
8397
9592
  usersStore,
8398
9593
  roomsInfoStore,
@@ -9269,129 +10464,6 @@ function makePoller(callback, intervalMs, options) {
9269
10464
  };
9270
10465
  }
9271
10466
 
9272
- // src/lib/shallow.ts
9273
- function shallowArray(xs, ys) {
9274
- if (xs.length !== ys.length) {
9275
- return false;
9276
- }
9277
- for (let i = 0; i < xs.length; i++) {
9278
- if (!Object.is(xs[i], ys[i])) {
9279
- return false;
9280
- }
9281
- }
9282
- return true;
9283
- }
9284
- function shallowObj(objA, objB) {
9285
- if (!isPlainObject(objA) || !isPlainObject(objB)) {
9286
- return false;
9287
- }
9288
- const keysA = Object.keys(objA);
9289
- if (keysA.length !== Object.keys(objB).length) {
9290
- return false;
9291
- }
9292
- return keysA.every(
9293
- (key) => Object.prototype.hasOwnProperty.call(objB, key) && Object.is(objA[key], objB[key])
9294
- );
9295
- }
9296
- function shallow(a, b) {
9297
- if (Object.is(a, b)) {
9298
- return true;
9299
- }
9300
- const isArrayA = Array.isArray(a);
9301
- const isArrayB = Array.isArray(b);
9302
- if (isArrayA || isArrayB) {
9303
- if (!isArrayA || !isArrayB) {
9304
- return false;
9305
- }
9306
- return shallowArray(a, b);
9307
- }
9308
- return shallowObj(a, b);
9309
- }
9310
- function shallow2(a, b) {
9311
- if (!isPlainObject(a) || !isPlainObject(b)) {
9312
- return shallow(a, b);
9313
- }
9314
- const keysA = Object.keys(a);
9315
- if (keysA.length !== Object.keys(b).length) {
9316
- return false;
9317
- }
9318
- return keysA.every(
9319
- (key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
9320
- );
9321
- }
9322
-
9323
- // src/lib/SortedList.ts
9324
- function bisectRight(arr, x, lt) {
9325
- let lo = 0;
9326
- let hi = arr.length;
9327
- while (lo < hi) {
9328
- const mid = lo + (hi - lo >> 1);
9329
- if (lt(x, arr[mid])) {
9330
- hi = mid;
9331
- } else {
9332
- lo = mid + 1;
9333
- }
9334
- }
9335
- return lo;
9336
- }
9337
- var SortedList = class _SortedList {
9338
- #data;
9339
- #lt;
9340
- constructor(alreadySortedList, lt) {
9341
- this.#lt = lt;
9342
- this.#data = alreadySortedList;
9343
- }
9344
- static from(arr, lt) {
9345
- const sorted = new _SortedList([], lt);
9346
- for (const item of arr) {
9347
- sorted.add(item);
9348
- }
9349
- return sorted;
9350
- }
9351
- static fromAlreadySorted(alreadySorted, lt) {
9352
- return new _SortedList(alreadySorted, lt);
9353
- }
9354
- /**
9355
- * Clones the sorted list to a new instance.
9356
- */
9357
- clone() {
9358
- return new _SortedList(this.#data.slice(), this.#lt);
9359
- }
9360
- /**
9361
- * Adds a new item to the sorted list, such that it remains sorted.
9362
- */
9363
- add(value) {
9364
- const idx = bisectRight(this.#data, value, this.#lt);
9365
- this.#data.splice(idx, 0, value);
9366
- }
9367
- /**
9368
- * Removes the given value from the sorted list, if it exists. The given
9369
- * value must be `===` to one of the list items. Only the first entry will be
9370
- * removed if the element exists in the sorted list multiple times.
9371
- */
9372
- remove(value) {
9373
- const idx = this.#data.indexOf(value);
9374
- if (idx >= 0) {
9375
- this.#data.splice(idx, 1);
9376
- return true;
9377
- }
9378
- return false;
9379
- }
9380
- get length() {
9381
- return this.#data.length;
9382
- }
9383
- *filter(predicate) {
9384
- for (const item of this.#data) {
9385
- if (predicate(item)) {
9386
- yield item;
9387
- }
9388
- }
9389
- }
9390
- [Symbol.iterator]() {
9391
- return this.#data[Symbol.iterator]();
9392
- }
9393
- };
9394
-
9395
10467
  // src/protocol/Subscriptions.ts
9396
10468
  function getSubscriptionKey(subscription, subjectId) {
9397
10469
  if (typeof subscription === "string") {