k2-im 0.1.18 → 0.1.20

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.
@@ -43,7 +43,7 @@ interface ConversationDraft$1 {
43
43
  updatedAt: number;
44
44
  }
45
45
  /** 消息内容类型。sticker=表情/贴图(BMessage contentType=7);call=单聊通话记录(contentType=10);transfer=转账(contentType=16);miniProgram=小程序分享卡(contentType=22);redPacket=红包卡(contentType=25);game=小游戏(contentType=27);未知 wire 正文统一归一为 unknown。 */
46
- type MessageContentType = 'text' | 'system' | 'image' | 'video' | 'voice' | 'file' | 'sticker' | 'contact' | 'miniProgram' | 'game' | 'transfer' | 'call' | 'redPacket' | 'unknown';
46
+ type MessageContentType = 'text' | 'system' | 'image' | 'video' | 'voice' | 'file' | 'sticker' | 'contact' | 'miniProgram' | 'game' | 'transfer' | 'call' | 'redPacket' | 'mediaAlbum' | 'unknown';
47
47
  /**
48
48
  * 发送态(客户端态):消息从本地创建到服务端确认的生命周期。
49
49
  * `uploading` 仅媒体消息使用:文件上传阶段(uploading→pending→sending→sent);
@@ -53,7 +53,7 @@ type MessageSendStatus = 'pending' | 'uploading' | 'sending' | 'sent' | 'failed'
53
53
  /** 送达/已读态(服务端态):消息在对端的回执状态。 */
54
54
  type MessageReceipt = 'unread' | 'delivered' | 'read';
55
55
  /** 群系统提示事件类型(结构化语义;UI/i18n 层据此渲染文案,**不在 mapper 写死中文**)。 */
56
- type ImSystemEventType = 'direct_contact_established' | 'direct_contact_required' | 'direct_message_refused' | 'group_info_changed' | 'group_activated' | 'group_member_joined' | 'group_member_invited' | 'group_member_left' | 'group_member_removed' | 'group_master_changed' | 'group_dismissed' | 'group_member_renamed' | 'group_message_pinned' | 'group_message_unpinned' | 'channel_name_changed' | 'channel_avatar_changed' | 'channel_announcement_changed' | 'channel_member_joined' | 'channel_member_invited' | 'channel_member_left' | 'channel_member_removed' | 'channel_owner_transferred' | 'channel_admin_added' | 'channel_admin_removed' | 'channel_dismissed' | 'channel_all_muted' | 'channel_all_unmuted' | 'channel_created' | 'channel_private_chat_disabled' | 'channel_private_chat_enabled' | 'channel_member_renamed' | 'channel_member_muted' | 'channel_member_unmuted' | 'channel_message_pinned' | 'channel_message_unpinned' | 'channel_unknown' | 'red_packet_claimed' | 'red_packet_claimed_thunder' | 'unknown';
56
+ type ImSystemEventType = 'direct_contact_established' | 'direct_contact_required' | 'direct_message_refused' | 'group_info_changed' | 'group_activated' | 'group_member_joined' | 'group_member_invited' | 'group_member_left' | 'group_member_removed' | 'group_master_changed' | 'group_dismissed' | 'group_member_renamed' | 'group_message_pinned' | 'group_message_unpinned' | 'group_call_started' | 'group_call_ended' | 'group_call_missed' | 'channel_name_changed' | 'channel_avatar_changed' | 'channel_announcement_changed' | 'channel_member_joined' | 'channel_member_invited' | 'channel_member_left' | 'channel_member_removed' | 'channel_owner_transferred' | 'channel_admin_added' | 'channel_admin_removed' | 'channel_dismissed' | 'channel_all_muted' | 'channel_all_unmuted' | 'channel_created' | 'channel_private_chat_disabled' | 'channel_private_chat_enabled' | 'channel_member_renamed' | 'channel_member_muted' | 'channel_member_unmuted' | 'channel_message_pinned' | 'channel_message_unpinned' | 'channel_unknown' | 'red_packet_claimed' | 'red_packet_claimed_thunder' | 'unknown';
57
57
  /** 系统提示结构化内容(contentType==='system'):存语义,不把最终中文句子当主数据源(UI/composable 据 eventType + 资料生成文案)。 */
58
58
  interface ImSystemMessageBody {
59
59
  eventType: ImSystemEventType;
@@ -120,6 +120,18 @@ type LastMessageSummary = {
120
120
  } | {
121
121
  kind: 'voice';
122
122
  durationMs?: number;
123
+ }
124
+ /**
125
+ * 媒体合集(contentType 28)。
126
+ *
127
+ * 只给结构化计数与配文,**不在这里拼「2 张照片, 1 个视频」这种文案**——中英文的量词、复数与
128
+ * 连接符各不相同,必须由 im-i18n 按 locale 组装。`caption` 非空时宿主优先显示配文(对齐三端)。
129
+ */
130
+ | {
131
+ kind: 'mediaAlbum';
132
+ imageCount: number;
133
+ videoCount: number;
134
+ caption?: string;
123
135
  } | {
124
136
  kind: 'encrypted';
125
137
  } | {
@@ -422,6 +434,59 @@ interface UnknownMessageBody {
422
434
  reason: 'unsupported-content-type' | 'malformed-content';
423
435
  }
424
436
  /** 按 contentType 的结构化内容体。 */
437
+ /**
438
+ * 媒体合集单项:一张图片或一个视频。
439
+ *
440
+ * 对应协议 `content` JSON 里 `items[]` 的一个元素(wire `mediaType` 1=图片 2=视频)。
441
+ * 与 `ImImageBody` / `ImVideoBody` 的区别是它**不是**独立的一条消息——同一条合集消息里的
442
+ * 若干项共享消息级的撤回、已读、引用与转发语义。
443
+ */
444
+ interface ImMediaAlbumItem {
445
+ /** 该项是图片还是视频;由协议 `mediaType` 归一而来,未知取值的项在解析阶段已被丢弃。 */
446
+ kind: 'image' | 'video';
447
+ /** 远端地址。加密项也是这个地址,解密用本项自己的 `teaKey`。 */
448
+ url: string;
449
+ /** 原始像素宽。宫格按它判定朝向,为 0 时宿主用兜底几何。 */
450
+ width: number;
451
+ /** 原始像素高。 */
452
+ height: number;
453
+ /**
454
+ * 视频时长,**毫秒**。
455
+ *
456
+ * 协议 `items[].duration` 本身就是毫秒,这里不做换算——注意这与单视频消息
457
+ * `ImVideoBody.duration`(秒)单位不同,所以刻意用 `durationMs` 命名区分。图片项没有此字段。
458
+ */
459
+ durationMs?: number;
460
+ /** 远端缩略图地址;可能缺省,此时只能用 `thumbData`。 */
461
+ thumbUrl?: string;
462
+ /** 内嵌缩略图/封面 JPEG 字节。加密消息服务端出不了封面,宫格首屏只能靠它。 */
463
+ thumbData?: Uint8Array;
464
+ /** 原文件 MD5。 */
465
+ md5?: string;
466
+ /** 原文件大小(字节)。 */
467
+ size?: number;
468
+ /** 本项的文件解密 TEA key;`fileEncryptType` 为 0/2/3 时必有。 */
469
+ teaKey?: Uint8Array;
470
+ /** 文件加密类型:0=跟随消息 1=不加密 2=前 32 字节 TEA 3=完整 TEA。 */
471
+ fileEncryptType?: number;
472
+ /** 发送方勾选了「原图」。仅作展示角标,不影响下载与解密。 */
473
+ hd?: boolean;
474
+ }
475
+ /**
476
+ * 媒体合集正文(`contentType === 'mediaAlbum'`,协议 28)。
477
+ *
478
+ * 一条消息同时承载一段可选配文和 1–9 个图片/视频项。产品上的「图文消息」就是
479
+ * `items.length === 1 && caption` 非空的情形,所以**分档判断一律看 `items.length`,不要看 contentType**。
480
+ *
481
+ * `items` 为空数组只会出现在入站解析残缺时(所有项的 `mediaType` 都不认识,或都无法解密);
482
+ * 这种正文不可出站——出站编码会显式拒绝,避免把坏消息转发出去。
483
+ */
484
+ interface ImMediaAlbumBody {
485
+ /** 配文;缺省与空串等价,出站不写空串。 */
486
+ caption?: string;
487
+ /** 媒体项,顺序即用户选择顺序(决定宫格 index 与首项朝向)。 */
488
+ items: readonly ImMediaAlbumItem[];
489
+ }
425
490
  interface ImMessageBody {
426
491
  /** P0 文本内容(明文,解密后的结果)。 */
427
492
  text?: string;
@@ -449,6 +514,8 @@ interface ImMessageBody {
449
514
  call?: ImCallRecordBody;
450
515
  /** contentType==='redPacket' 时的红包卡片。 */
451
516
  redPacket?: ImRedPacketBody;
517
+ /** contentType==='mediaAlbum' 时的媒体合集正文(配文 + 1~9 个图片/视频项)。 */
518
+ mediaAlbum?: ImMediaAlbumBody;
452
519
  /** contentType==='unknown' 时的稳定未知正文,不含原始 wire type。 */
453
520
  unknown?: UnknownMessageBody;
454
521
  }
@@ -483,7 +550,7 @@ type ImMessageFailureReason = 'timeout' | 'rejected' | 'encrypt_failed' | 'netwo
483
550
  * 失败**展示语义**码(强类型枚举,非裸串):供 im-i18n 按 locale 渲染失败气泡文案。
484
551
  * 由 `reason` / `SendMediaFailReason` / ACK errcode 穷尽映射而来,与 im-i18n failure catalog 键一一对应(media.* 带 mediaKind 参数)。
485
552
  */
486
- type ImFailureDisplayCode = 'media.empty' | 'media.noCrypto' | 'media.previewFailed' | 'media.encryptFailed' | 'media.uploadFailed' | 'media.cancelled' | 'media.invalidDuration' | 'media.uploadInterrupted' | 'contactRequired' | 'messageRefused' | 'forbiddenOrLeft' | 'contentRejected' | 'groupDismissed' | 'groupUnavailable' | 'channelDismissed' | 'channelMuted' | 'channelNotMember' | 'channelAdminRequired' | 'conversationUnsupported' | 'sendCanceled' | 'sessionLoggedOut' | 'timeout' | 'network' | 'unknown';
553
+ type ImFailureDisplayCode = 'media.empty' | 'media.noCrypto' | 'media.previewFailed' | 'media.encryptFailed' | 'media.uploadFailed' | 'media.cancelled' | 'media.invalidDuration' | 'media.uploadInterrupted' | 'media.contentTooLarge' | 'contactRequired' | 'messageRefused' | 'forbiddenOrLeft' | 'contentRejected' | 'groupDismissed' | 'groupUnavailable' | 'channelDismissed' | 'channelMuted' | 'channelNotMember' | 'channelAdminRequired' | 'conversationUnsupported' | 'sendCanceled' | 'sessionLoggedOut' | 'timeout' | 'network' | 'unknown';
487
554
  interface ImMessageFailure {
488
555
  /** 归一后的错误码(来自 MESChatAck.errcode 或本地失败码,本地码用负值)。 */
489
556
  code: number;
@@ -511,7 +578,12 @@ interface MediaTaskUpdate {
511
578
  direction: 'upload' | 'download';
512
579
  state: 'queued' | 'running' | 'completed' | 'failed' | 'canceled';
513
580
  percent: number;
514
- /** upload 任务子阶段;finalizing 是等待远端 URL 最终确认。 */
581
+ /**
582
+ * upload 任务子阶段。
583
+ *
584
+ * `waiting-upload-slot` 与 `state: 'queued'` 同时出现,表示还没开始传字节(在生成缩略图或在等上传槽位),
585
+ * 此时 percent 恒为 0;`transferring` 正在传字节;`finalizing` 是等待远端 URL 最终确认。
586
+ */
515
587
  phase?: 'waiting-upload-slot' | 'transferring' | 'finalizing';
516
588
  failure?: ImMessageFailure;
517
589
  }
@@ -1612,9 +1684,23 @@ interface MediaAssetCacheKey {
1612
1684
  messageId: string;
1613
1685
  resourceVersion: string;
1614
1686
  }
1615
- /** 已解密的真实媒体资产;字节可结构化克隆并可在未来作为 Worker transferable。 */
1687
+ /**
1688
+ * 已解密的真实媒体资产。
1689
+ *
1690
+ * 内容用 `Blob` 承载而不是 `Uint8Array`:图片、尤其视频可达上百 MB,读成 `Uint8Array` 就等于把整份
1691
+ * 媒体搬进 JS 堆;`Blob` 的字节由浏览器自己托管,写缓存、读缓存和交给 `<img>` / `<video>` 全程都不需要
1692
+ * 整读一遍。字段名从 `bytes` 改成 `blob` 是刻意的——改名让编译器强制每个读写点重新被检查,也不会留下
1693
+ * 「有时是字节、有时是 Blob」两条内存表现完全不同的路径。
1694
+ *
1695
+ * `Blob` 可结构化克隆,因此 IndexedDB 能直接存,未来跨 Worker 传递也不需要改形状。
1696
+ */
1616
1697
  interface MediaAssetCacheEntry extends MediaAssetCacheKey {
1617
- bytes: Uint8Array;
1698
+ /** 解密后的真实媒体内容;字节量一律按 `blob.size` 核算(LRU、单条上限、诊断都用它)。 */
1699
+ blob: Blob;
1700
+ /**
1701
+ * 消息体声明的 MIME。保留该字段而不是依赖 `blob.type`:解密产物的 Blob 可能没有类型,也可能带的是
1702
+ * 下载响应的类型,与消息声明不一定一致;读缓存时要以消息声明为准。
1703
+ */
1618
1704
  mimeType?: string;
1619
1705
  }
1620
1706
  /** 单种媒体的 sibling 缓存策略。 */
@@ -1632,7 +1718,7 @@ interface MediaAssetCacheStrategyRegistry {
1632
1718
  /**
1633
1719
  * 明文媒体资产缓存 Port。
1634
1720
  *
1635
- * Core/SDK 只通过该端口读写真实媒体字节;IndexedDB、配额与 LRU 由浏览器实现负责。
1721
+ * Core/SDK 只通过该端口读写真实媒体内容(以 Blob 承载);IndexedDB、配额与 LRU 由浏览器实现负责。
1636
1722
  */
1637
1723
  interface MediaAssetCachePort {
1638
1724
  get: (key: MediaAssetCacheKey) => Promise<MediaAssetCacheEntry | null>;
@@ -1652,8 +1738,14 @@ interface ImProfilePort {
1652
1738
  clearCache?: () => void;
1653
1739
  }
1654
1740
 
1655
- /** 会话收藏可展示的内容类型;与 iOS `isSupportCollectionMsgType` 对齐。 */
1656
- type ImSessionFavoriteKind = 'text' | 'image' | 'video' | 'voice' | 'location' | 'file';
1741
+ /**
1742
+ * 会话收藏可展示的内容类型;与 iOS `isSupportCollectionMsgType` 对齐。
1743
+ *
1744
+ * @remarks 这些值与收藏接口的 `content_type` 数值一一对应,而那个数值就是消息自己的 contentType
1745
+ * (1=文本 2=图片 3=视频 4=语音 5=位置 11=文件 **28=媒体合集**),不是另一套收藏专用枚举。
1746
+ * Android `MessageCollectExecute` 直接写 `message.getContentType()`,iOS 写 `msg.msgType`,两端一致。
1747
+ */
1748
+ type ImSessionFavoriteKind = 'text' | 'image' | 'video' | 'voice' | 'location' | 'file' | 'mediaAlbum';
1657
1749
  /** 服务端一条会话收藏快照;列表以 HTTP 为准,不回写原消息。 */
1658
1750
  interface ImSessionFavoriteItem$1 {
1659
1751
  readonly collectId: string;
@@ -2390,6 +2482,10 @@ type ForwardCandidateMessage = ForwardContentCandidate & Pick<ImMessage, 'encryp
2390
2482
  /**
2391
2483
  * 多选转发时是否需要先提示用户“剔除后继续”。
2392
2484
  * 覆盖语音、所有显式 unknown 与解密失败;原始 wire 类型不进入产品判定。
2485
+ *
2486
+ * 点名的都是**根本转不出去**的类型(语音、通话记录、unknown 都没有注册转发策略),提示的含义是
2487
+ * “这几条会被剔除”。已注册策略的类型一律不在此列——**媒体合集也不在**:它和图片/视频一样整条正文
2488
+ * 克隆、不重新上传,把它加进来会对一条能完整转发的消息发出假警告。
2393
2489
  */
2394
2490
  declare function requiresForwardTypeConfirmation(message: ForwardCandidateMessage): boolean;
2395
2491
  /**
@@ -2410,6 +2506,38 @@ declare function canTransmit(message: ForwardCandidateMessage): boolean;
2410
2506
 
2411
2507
  type RecallFailReason = 'not-found' | 'not-own' | 'not-sent' | 'expired' | 'unsupported' | 'timeout' | 'server-expired' | 'failed' | 'disconnected';
2412
2508
 
2509
+ /** 浏览器 PushSubscription.toJSON() 的可移植数据;不依赖 DOM 或浏览器全局。 */
2510
+ interface ImWebPushSubscription {
2511
+ endpoint?: string;
2512
+ expirationTime?: number | null;
2513
+ keys?: Record<string, string>;
2514
+ }
2515
+ /** 状态不包含 endpoint、加密密钥或登录凭据,可安全用于设置页。 */
2516
+ interface ImPushSnapshot {
2517
+ status: 'idle' | 'pending' | 'syncing' | 'synced' | 'unconfirmed' | 'error' | 'disposed';
2518
+ error?: 'timeout' | 'invalid-ack' | 'server-rejected' | 'send-failed';
2519
+ /** unconfirmed 表示已收到旧后端默认错误码 ACK,不能据此证明绑定成功或失败。 */
2520
+ serverCode?: number;
2521
+ /** 超时等失败后等下一条连接恢复;不主动反复重报或制造重连。 */
2522
+ requiresReconnect?: boolean;
2523
+ }
2524
+ /**
2525
+ * 当前账号的推送绑定。浏览器授权、订阅和取消订阅由宿主拥有。
2526
+ * setWebSubscription 只提交同步意图;只有 snapshot.status === 'synced' 表示收到服务端成功 ACK。
2527
+ */
2528
+ interface ImPushApi {
2529
+ /** 必须携带创建订阅时的账号,防止异步结果被交给另一个账号。离线时保留最新意图。 */
2530
+ setWebSubscription: (input: {
2531
+ userId: string;
2532
+ subscription: ImWebPushSubscription;
2533
+ }) => void;
2534
+ /** 停止本地同步并释放订阅数据;不宣称服务端解绑,宿主仍须调用 PushSubscription.unsubscribe()。 */
2535
+ clear: () => void;
2536
+ snapshot: () => ImPushSnapshot;
2537
+ /** 立即提供快照,返回退订函数;监听器的异常经宿主错误端口报告。 */
2538
+ subscribe: (listener: (snapshot: ImPushSnapshot) => void) => () => void;
2539
+ }
2540
+
2413
2541
  /**
2414
2542
  * IM 语义文案 catalog 的键、参数和完整性合同。
2415
2543
  * 键描述领域含义而非组件位置,使 Web/H5/SSR 可共享 formatter,宿主布局文案不进入此 schema。
@@ -2420,7 +2548,7 @@ type RecallFailReason = 'not-found' | 'not-own' | 'not-sent' | 'expired' | 'unsu
2420
2548
  * 二期收敛:kit 框架无关层不再直接产中文,改产本联合里的码 + 参数(如 {reason}/{label}/{size}),由宿主经 im-i18n 渲染。
2421
2549
  * 动态原始报错(error.message)作为 `{reason}` 参数拼接,不入 catalog(属诊断,不翻译)。
2422
2550
  */
2423
- type ImServiceTextKey = 'common.notConnected' | 'common.selectOrOpenConversation' | 'common.conversationTypeNotReady' | 'common.conversationNotReady' | `recall.${RecallFailReason}` | `edit.${EditFailReason}` | 'localDelete.done' | 'localDelete.notFound' | 'localDelete.inFlight' | 'localDelete.failed' | 'channelDelete.notFound' | 'channelDelete.unsupported' | 'channelDelete.notSent' | 'channelDelete.unavailable' | 'channelDelete.forbidden' | 'channelDelete.timeout' | 'channelDelete.failed' | 'channelDelete.disconnected' | 'directBlock.contactRequired' | 'groupBlock.left' | 'groupBlock.removed' | 'groupBlock.dismissed' | 'groupBlock.unknownRestricted' | 'channelBlock.guest' | 'channelBlock.left' | 'channelBlock.removed' | 'channelBlock.dismissed' | 'channelBlock.muted' | 'channelBlock.allMembersMuted' | 'channelBlock.currentUserMuted' | 'channelBlock.blacklisted' | 'channelBlock.unknownRestricted' | 'channelProfile.loadFailed' | 'channelProfile.notFound' | 'channelProfile.retryAction' | 'channelMembers.title' | 'channelMembers.searchPlaceholder' | 'channelMembers.loading' | 'channelMembers.loadFailed' | 'channelMembers.retryAction' | 'channelMembers.empty' | 'channelMembers.loadingMore' | 'channelMembers.loadMoreFailed' | 'channelMembers.exhausted' | 'channelMembers.loadMoreAction' | 'label.image' | 'label.video' | 'label.file' | 'label.sticker' | 'send.failed' | 'send.retryFailed' | 'send.cancelFailed' | 'send.mediaFailed' | 'send.mediaEmpty' | 'send.mediaCancelled' | 'send.mediaCountLimit' | 'send.mediaTooLarge' | 'send.videoTooLong' | 'send.videoDurationUnavailable' | 'send.voiceEmpty' | 'send.voiceStorageNotConfigured' | 'send.voiceCancelled' | 'send.voiceFailed' | 'media.voiceNotReady' | 'media.voiceLoadEmpty' | 'media.voiceLoadFailed' | 'media.videoOpenEmpty' | 'media.videoLoadFailed' | 'media.imageInvalid' | 'media.imageLoadFailed' | 'media.fileDownloadEmpty' | 'media.fileDownloadFailed' | 'conversation.muteEnableFailed' | 'conversation.muteDisableFailed' | 'read.markReadFailed' | 'pinned.forbidden' | 'pinned.network' | 'pinned.contract' | 'pinned.unsupported' | 'pinned.failed' | 'pinned.previewUnavailable' | 'pinned.locateFailed' | 'pinned.recalled' | 'pinned.deleted' | 'pinned.protected' | 'pinned.pinAction' | 'pinned.unpinAction' | 'pinned.unpinConfirm' | 'pinned.cancelAction' | 'lifecycle.missingLoginState' | 'lifecycle.prepareFailed' | 'lifecycle.missingConnectParams' | 'lifecycle.connectFailed' | 'lifecycle.disposed' | 'forward.emptyInput' | 'forward.allSkipped' | 'forward.partialSkipped' | 'forward.done' | 'forward.failed';
2551
+ type ImServiceTextKey = 'common.notConnected' | 'common.selectOrOpenConversation' | 'common.conversationTypeNotReady' | 'common.conversationNotReady' | `recall.${RecallFailReason}` | `edit.${EditFailReason}` | 'localDelete.done' | 'localDelete.notFound' | 'localDelete.inFlight' | 'localDelete.failed' | 'channelDelete.notFound' | 'channelDelete.unsupported' | 'channelDelete.notSent' | 'channelDelete.unavailable' | 'channelDelete.forbidden' | 'channelDelete.timeout' | 'channelDelete.failed' | 'channelDelete.disconnected' | 'directBlock.contactRequired' | 'groupBlock.left' | 'groupBlock.removed' | 'groupBlock.dismissed' | 'groupBlock.unknownRestricted' | 'channelBlock.guest' | 'channelBlock.left' | 'channelBlock.removed' | 'channelBlock.dismissed' | 'channelBlock.muted' | 'channelBlock.allMembersMuted' | 'channelBlock.currentUserMuted' | 'channelBlock.blacklisted' | 'channelBlock.unknownRestricted' | 'channelProfile.loadFailed' | 'channelProfile.notFound' | 'channelProfile.retryAction' | 'channelMembers.title' | 'channelMembers.searchPlaceholder' | 'channelMembers.loading' | 'channelMembers.loadFailed' | 'channelMembers.retryAction' | 'channelMembers.empty' | 'channelMembers.loadingMore' | 'channelMembers.loadMoreFailed' | 'channelMembers.exhausted' | 'channelMembers.loadMoreAction' | 'label.image' | 'label.video' | 'label.file' | 'label.sticker' | 'send.failed' | 'send.retryFailed' | 'send.cancelFailed' | 'send.mediaFailed' | 'send.mediaEmpty' | 'send.mediaCancelled' | 'send.mediaCountLimit' | 'send.mediaTooLarge' | 'send.mediaFormatMismatch' | 'send.videoTooLong' | 'send.videoDurationUnavailable' | 'send.voiceEmpty' | 'send.voiceStorageNotConfigured' | 'send.voiceCancelled' | 'send.voiceFailed' | 'media.uploadWaiting' | 'media.voiceNotReady' | 'media.voiceLoadEmpty' | 'media.voiceLoadFailed' | 'media.videoOpenEmpty' | 'media.videoLoadFailed' | 'media.imageInvalid' | 'media.imageLoadFailed' | 'media.fileDownloadEmpty' | 'media.fileDownloadFailed' | 'conversation.muteEnableFailed' | 'conversation.muteDisableFailed' | 'conversation.backgroundLoadFailed' | 'read.markReadFailed' | 'storage.failed' | 'sync.failed' | 'draft.saveFailed' | 'history.returnLatestFailed' | 'pinned.forbidden' | 'pinned.network' | 'pinned.contract' | 'pinned.unsupported' | 'pinned.failed' | 'pinned.previewUnavailable' | 'pinned.locateFailed' | 'pinned.recalled' | 'pinned.deleted' | 'pinned.protected' | 'pinned.pinAction' | 'pinned.unpinAction' | 'pinned.unpinConfirm' | 'pinned.cancelAction' | 'lifecycle.missingLoginState' | 'lifecycle.prepareFailed' | 'lifecycle.missingConnectParams' | 'lifecycle.connectFailed' | 'lifecycle.disposed' | 'forward.emptyInput' | 'forward.allSkipped' | 'forward.partialSkipped' | 'forward.done' | 'forward.failed';
2424
2552
 
2425
2553
  /**
2426
2554
  * 后端协议的目标服务命名。
@@ -2652,11 +2780,27 @@ interface MediaTransferAdapterConfig {
2652
2780
  * 部署可置 false 立即退回全量单次 PutObject(大文件回到单连接与整包内存的旧行为),无需发版。
2653
2781
  */
2654
2782
  multipartUpload?: boolean;
2783
+ /**
2784
+ * 大于 16MiB 的明文/simple 文件是否尝试 Range 并发下载(默认 true)。
2785
+ * 需要下载域名支持 Range,并暴露 Content-Range 与强 ETag;返回 200 时消费完整响应并记录诊断。
2786
+ * 置 false 使用单次 GET。此开关不改变上传或整文件 TEA 的行为。
2787
+ */
2788
+ rangeDownload?: boolean;
2655
2789
  /**
2656
2790
  * 媒体发送阶段的并发上限;由 im-core 消费,adapter 本身仍只负责单个上传任务。
2657
- * 缺省为 4,允许 1-6;高并发会提高吞吐,也会增加文件加密的内存峰值。
2791
+ *
2792
+ * 并发按媒体体积分两档:图片与不超过分片阈值(16 MiB)的小文件走单次 PutObject、不占分片缓冲,
2793
+ * 缺省 5;视频与超过阈值(或字节数未知)的文件走分片,每路各带分片缓冲,缺省 3。
2794
+ * 两档都只接受 1-6 的整数,超出范围由 core 显式报错。
2795
+ * - 传一个数字:所有媒体共用这一个并发数,与分档前的语义一致。
2796
+ * - 传 `{ total, largeFiles }`:显式分档,`largeFiles` 不得超过 `total`。
2658
2797
  */
2659
- maxConcurrentUploads?: number;
2798
+ maxConcurrentUploads?: number | {
2799
+ /** 在途上传总数上限(两档合计);缺省 5。 */
2800
+ total?: number;
2801
+ /** 其中大文件档的上限(视频、超过分片阈值或字节数未知的内容);缺省 min(3, total)。 */
2802
+ largeFiles?: number;
2803
+ };
2660
2804
  }
2661
2805
 
2662
2806
  interface NetworkEventTarget {
@@ -2802,12 +2946,16 @@ interface ImMessageReactions {
2802
2946
  aggregates: readonly ImMessageReactionAggregate[];
2803
2947
  }
2804
2948
  /** 可渲染媒体正文;密钥和底层文件加密策略只允许留在 SDK/Core。 */
2805
- interface ImMessageViewBody extends Omit<ImMessageBody, 'image' | 'video' | 'voice' | 'file' | 'sticker'> {
2949
+ interface ImMessageViewBody extends Omit<ImMessageBody, 'image' | 'video' | 'voice' | 'file' | 'sticker' | 'mediaAlbum'> {
2806
2950
  image?: Readonly<Omit<NonNullable<ImMessageBody['image']>, 'teaKey' | 'fileEncryptType'>>;
2807
2951
  video?: Readonly<Omit<NonNullable<ImMessageBody['video']>, 'teaKey' | 'fileEncryptType'>>;
2808
2952
  voice?: Readonly<Omit<NonNullable<ImMessageBody['voice']>, 'teaKey' | 'fileEncryptType'>>;
2809
2953
  file?: Readonly<Omit<NonNullable<ImMessageBody['file']>, 'teaKey' | 'fileEncryptType'>>;
2810
2954
  sticker?: Readonly<NonNullable<ImMessageBody['sticker']>>;
2955
+ /** 合集的每一项都带自己的密钥材料,逐项剥离后才交给产品层;按项下载走 SDK handle,不需要这些字段。 */
2956
+ mediaAlbum?: Readonly<Omit<NonNullable<ImMessageBody['mediaAlbum']>, 'items'> & {
2957
+ items: readonly Readonly<Omit<ImMediaAlbumItem, 'teaKey' | 'fileEncryptType'>>[];
2958
+ }>;
2811
2959
  }
2812
2960
  /**
2813
2961
  * 官方 Chat Kit 与宿主消息线程使用的完整稳定消息 DTO。
@@ -2883,6 +3031,44 @@ interface MiniProgramMessageOptions {
2883
3031
  interface TransferMessageOptions {
2884
3032
  replyToMessageId?: string;
2885
3033
  }
3034
+ /**
3035
+ * 媒体合集里的一项:一张图片或一个视频。
3036
+ *
3037
+ * @remarks `kind` 由**调用方**按 MIME 分类,SDK 不嗅探文件内容——宿主的文件选择器本来就要按类型分流
3038
+ * (GIF 得走 `sendGif`),再让 SDK 猜一次只会多一处口径不一致。
3039
+ */
3040
+ interface MediaAlbumItemInput {
3041
+ /** 这一项是图片还是视频。 */
3042
+ kind: 'image' | 'video';
3043
+ /** 原文件;`File` 可直接传,`Blob` 需调用方保证 `type` 可判定。 */
3044
+ file: File | Blob;
3045
+ /** 宿主创建的本地 object URL,仅供上传中的宫格显示这一格;URL 释放由宿主生命周期负责。 */
3046
+ localPreviewUrl?: string;
3047
+ /**
3048
+ * 这一项的本地宽高(视频再带时长与首帧)。
3049
+ *
3050
+ * 不传时 SDK 会自己读一次(与单图/单视频同一条预览路径)。宫格的格子几何按原始宽高算,
3051
+ * 缺了它整条消息的格子比例会在上传完成时跳变。
3052
+ */
3053
+ localPreviewMetadata?: ImagePreviewMetadata | VideoPreviewMetadata;
3054
+ }
3055
+ /**
3056
+ * 媒体合集发送选项。
3057
+ *
3058
+ * @remarks 恰好 1 项且没有配文时,SDK 内部会回落成普通图片/视频消息(与 Android / iOS / 旧 H5 一致),
3059
+ * 此时返回的仍是同样的 `SendMessageResult`,调用方不需要区分。
3060
+ */
3061
+ interface MediaAlbumMessageOptions {
3062
+ /** 配文,最长 1024 字符;纯空白等同于没有配文。超长由 SDK 明确拒绝,**不静默截断**。 */
3063
+ caption?: string;
3064
+ replyToMessageId?: string;
3065
+ /**
3066
+ * 发送方勾选了「原图」,写进每一项的 `hd` 标记。
3067
+ *
3068
+ * 本期不做任何图片/视频压缩,勾与不勾都发原文件,这个字段只让接收端显示「原图」角标。
3069
+ */
3070
+ hd?: boolean;
3071
+ }
2886
3072
  /**
2887
3073
  * 本地视频的固有尺寸、时长与首帧封面。
2888
3074
  *
@@ -3945,6 +4131,18 @@ type ConversationLastMessageSummary = {
3945
4131
  } | {
3946
4132
  kind: 'voice';
3947
4133
  durationMs?: number;
4134
+ }
4135
+ /**
4136
+ * 媒体合集(一条消息内 1~9 个图片/视频 + 可选配文)。
4137
+ *
4138
+ * 只给计数与配文,数量文案由宿主经 im-i18n 按 locale 组装;有配文时优先显示配文。
4139
+ * 两个计数都为 0 表示入站正文残缺(所有项都无法解析),宿主应按未知正文处理。
4140
+ */
4141
+ | {
4142
+ kind: 'mediaAlbum';
4143
+ imageCount: number;
4144
+ videoCount: number;
4145
+ caption?: string;
3948
4146
  } | {
3949
4147
  kind: 'encrypted';
3950
4148
  } | {
@@ -4397,6 +4595,16 @@ interface ConversationMessageApi {
4397
4595
  sendFile: (file: File | Blob, options?: FileMessageOptions) => Promise<SendMessageResult>;
4398
4596
  /** 发送 GIF 贴图;原文件明文上传 sticker bucket,消息固定 EXPRESSION/LINK/GIF。 */
4399
4597
  sendGif: (file: File | Blob, options?: GifMessageOptions) => Promise<SendMessageResult>;
4598
+ /**
4599
+ * 发送媒体合集:1~9 个图片/视频合成**一条**消息,可带一段配文。
4600
+ *
4601
+ * @remarks 全部项上传成功才发出;任一项失败整条失败,不发半条消息。恰好 1 项且没有配文时
4602
+ * SDK 内部回落成普通图片/视频消息(三端一致)。**校验是整条的**——某一项超限会拒绝整条,
4603
+ * 因为协议上它就是一条消息,没有「发一半」这种状态。
4604
+ *
4605
+ * GIF 不能进合集(三端一致),调用方要先按 MIME 把它拆出去走 `sendGif`。
4606
+ */
4607
+ sendMediaAlbum: (items: readonly MediaAlbumItemInput[], options?: MediaAlbumMessageOptions) => Promise<SendMessageResult>;
4400
4608
  /** 发送已经托管在远端的静态贴图;资源和贴图包身份均为必填合同。 */
4401
4609
  sendSticker: (asset: ImStickerAssetInput, options?: ExpressionMessageOptions) => Promise<SendMessageResult>;
4402
4610
  /** 发送已经托管在远端的 GIF;Giphy 没有 package identity,输入不得提供 packageId。 */
@@ -4477,6 +4685,43 @@ interface ConversationMessageApi {
4477
4685
  * ```
4478
4686
  */
4479
4687
  forward: (messageIds: readonly string[], targets: readonly ConversationRef<ProductHandleConversationType>[]) => Promise<ForwardMessagesResult>;
4688
+ /**
4689
+ * 把媒体合集里的第 `itemIndex` 项,当成**一条独立的图片或视频消息**转发到多个目标会话。
4690
+ *
4691
+ * 用途:全屏预览里点转发时,用户要转的是当前正在看的这一格,而不是整条合集。
4692
+ *
4693
+ * @remarks
4694
+ * 与 `forward` 的区别是产出的消息类型变了(合集 → 图片/视频),所以是独立方法而不是给 `forward`
4695
+ * 放宽入参。与 `forward` 相同的是:复用该项已有的远端地址与解密 key,**不重新上传**;目标会话仍由
4696
+ * direct/GROUP/CHANNEL 各自的 SendStrategy 组帧入 Outbox。
4697
+ *
4698
+ * 视频项在合集里的时长是毫秒,转成单视频消息时 SDK 会换算成协议要求的秒,调用方不需要处理。
4699
+ * 合集项上的「原图」角标与远端缩略图地址不会带到单媒体消息上——单图/单视频正文没有这两个字段。
4700
+ *
4701
+ * 三种情况进 `failed`:源消息不是媒体合集(`IM_SDK_FORWARD_CONTENT_UNSUPPORTED`)、
4702
+ * `itemIndex` 越界或该项已不存在(`IM_SDK_MEDIA_ALBUM_ITEM_INVALID`)、该项还没有可复用的远端地址
4703
+ * (还在上传或入站残缺,`IM_SDK_FORWARD_MEDIA_NOT_READY`,可重试)。
4704
+ *
4705
+ * **还有一类结果三个数组会全空**:源消息已撤回、已被双删、解密失败、在本地已经查不到,
4706
+ * 或它的类型本身就不可转发时,这条消息在预检阶段就被剔除,只出现在 `skippedSourceMessageIds` 里。
4707
+ * 这与 `forward` 的批量语义一致(批量转发本来就要把不可转的源剔掉再发其余的),
4708
+ * 但单条调用时它意味着**什么都没发出去**。所以调用方判成功要看 `sent`,
4709
+ * 不要只判 `failed.length > 0`——那样在「预览期间原消息被撤回」这种竞态下会既不发送也不提示。
4710
+ *
4711
+ * @param messageId 当前 handle 中的合集消息 id。
4712
+ * @param itemIndex 宫格里的项下标,从 0 起;必须是非负整数。
4713
+ * @param targets 目标会话,最多 9 个。
4714
+ * @example
4715
+ * ```ts
4716
+ * const result = await handle.messages.forwardMediaAlbumItem('message-42', 2, [
4717
+ * { conversationType: 'direct', conversationId: '1001' },
4718
+ * ])
4719
+ * // 一条都没发出去就要提示:失败原因在 failed 里,源消息被剔除时 failed 也是空的。
4720
+ * if (result.sent.length === 0)
4721
+ * showError(result.failed[0]?.error.code ?? 'IM_SDK_FORWARD_CONTENT_UNSUPPORTED')
4722
+ * ```
4723
+ */
4724
+ forwardMediaAlbumItem: (messageId: string, itemIndex: number, targets: readonly ConversationRef<ProductHandleConversationType>[]) => Promise<ForwardMessagesResult>;
4480
4725
  /**
4481
4726
  * 按 id 向服务端取回本地没有的消息,并回填进本地历史。
4482
4727
  *
@@ -4686,6 +4931,14 @@ interface ImMessageRef {
4686
4931
  conversation: ConversationRef<ProductHandleConversationType>;
4687
4932
  /** 当前会话内的稳定消息 id。 */
4688
4933
  messageId: string;
4934
+ /**
4935
+ * 媒体合集里的第几项(从 0 起)。
4936
+ *
4937
+ * **只有合集消息需要它,也只有合集消息接受它**:一条合集是一条消息、多个文件,光靠 messageId
4938
+ * 指不到具体某一项。给单媒体消息传这个字段会被显式拒绝,而不是忽略——忽略的话调用方以为自己
4939
+ * 在下载第 3 项,实际拿到的是整条消息的唯一媒体。
4940
+ */
4941
+ itemIndex?: number;
4689
4942
  }
4690
4943
  /**
4691
4944
  * 可观察媒体上传/下载任务的只读产品投影。
@@ -4700,13 +4953,27 @@ interface ImMediaTask {
4700
4953
  messageId: string;
4701
4954
  /** 关联消息的精确会话 sibling。 */
4702
4955
  ref: ConversationRef<ProductHandleConversationType>;
4956
+ /**
4957
+ * 媒体合集的项下标;单媒体消息与全部上传任务为 undefined。
4958
+ *
4959
+ * 一条合集的多项共用同一个 messageId,各自是独立的下载任务。调用方按 messageId 找任务时
4960
+ * 必须连这个字段一起比,否则并发下载两项时会把另一项的进度读成自己的。
4961
+ * 上传任务不带下标:一条合集整体只有一条上传进度(所有项都传完才算这条消息发出去)。
4962
+ */
4963
+ itemIndex?: number;
4703
4964
  /** upload 来自发送链路,download 来自本产品 API。 */
4704
4965
  direction: 'upload' | 'download';
4705
4966
  /** 终态 completed/failed/canceled 不会被同一尝试的迟到 progress 回退。 */
4706
4967
  state: 'queued' | 'running' | 'completed' | 'failed' | 'canceled';
4707
4968
  /** 0-100 的归一化整数。 */
4708
4969
  percent: number;
4709
- /** upload 子阶段;finalizing 为等待对象存储确认。 */
4970
+ /**
4971
+ * upload 子阶段。
4972
+ *
4973
+ * `waiting-upload-slot` 与 `state: 'queued'` 同时出现,表示这条媒体还没开始传字节(在生成缩略图或在
4974
+ * 等上传槽位),此时 `percent` 恒为 0——宿主应显示「等待中」而不是把 0 当成停住的进度。
4975
+ * `transferring` 是正在传字节,`finalizing` 是字节已传完、在等对象存储确认。
4976
+ */
4710
4977
  phase?: 'waiting-upload-slot' | 'transferring' | 'finalizing';
4711
4978
  /** 可供产品按 code/category 决策的稳定错误;不含底层 URL、密钥或响应正文。 */
4712
4979
  error?: ImSdkErrorShape;
@@ -5736,6 +6003,8 @@ interface AdvancedConversationsApi extends ImConversationsApi, ImConversationBac
5736
6003
  * ```
5737
6004
  */
5738
6005
  interface ImProductClient {
6006
+ /** 当前设备推送订阅的服务端绑定与同步状态。 */
6007
+ readonly push: ImPushApi;
5739
6008
  readonly session: ImSessionApi;
5740
6009
  readonly capabilities: ImConversationCapabilitiesApi;
5741
6010
  readonly conversations: ImConversationListApi;
@@ -5957,6 +6226,26 @@ declare function resolveBestMediaPresentationSource<TValue>(candidates: readonly
5957
6226
  /** 返回 candidate 是否允许替换当前源;资源质量只能保持或提高,不能异步降级。 */
5958
6227
  declare function canPromoteMediaPresentationSource(current: MediaPresentationSourceQuality, candidate: MediaPresentationSourceQuality): boolean;
5959
6228
 
6229
+ /** 新消息提醒所需的领域与宿主事实;本模块不读取浏览器、路由或全局设置。 */
6230
+ interface MessageNotificationInput {
6231
+ source: 'live' | 'offline';
6232
+ senderId: string;
6233
+ accountId: string;
6234
+ contentType: string;
6235
+ muted: boolean;
6236
+ globallyEnabled: boolean;
6237
+ visibilityState: 'visible' | 'hidden';
6238
+ permission: 'default' | 'denied' | 'granted' | null;
6239
+ }
6240
+ type MessageNotificationDecision = {
6241
+ show: true;
6242
+ } | {
6243
+ show: false;
6244
+ reason: 'not-live' | 'unsupported' | 'permission-denied' | 'page-visible' | 'self-message' | 'system-message' | 'conversation-muted' | 'globally-disabled';
6245
+ };
6246
+ /** PC/H5 共用的实时消息提醒规则;离线历史回放不逐条弹通知。 */
6247
+ declare function resolveMessageNotificationDecision(input: MessageNotificationInput): MessageNotificationDecision;
6248
+
5960
6249
  /**
5961
6250
  * 官方 Chat Kit 和受控诊断工具使用的 SDK internal 入口。
5962
6251
  * 它暴露完整 client、adapter 装配和领域原子,但仍不属于普通产品 API;生产宿主不得据此解析协议或持有密钥。
@@ -6149,6 +6438,13 @@ interface ChatMediaMessageRef {
6149
6438
  conversationId: string;
6150
6439
  /** 需要查找本地媒体预览的消息 ID。 */
6151
6440
  messageId: string;
6441
+ /**
6442
+ * 媒体合集的项下标(第几格)。
6443
+ *
6444
+ * 一条合集的 9 格共用同一个 messageId,各自是独立资源;不带下标会让它们落到同一个注册表键上,
6445
+ * 后绑定的一格把先绑定的挤掉并撤销其地址。单媒体消息不传这个字段。
6446
+ */
6447
+ itemIndex?: number;
6152
6448
  }
6153
6449
  /**
6154
6450
  * Chat Kit 提供给共享 Store 和业务 Service 的账号运行接口。
@@ -6652,8 +6948,11 @@ declare function detectVideoMime(bytes: Uint8Array): string;
6652
6948
  *
6653
6949
  * 命中时返回带 `url` 和 `release()` 的使用凭证;组件卸载或换源时必须释放。未命中时返回
6654
6950
  * `undefined`,调用方继续走远端下载。
6951
+ *
6952
+ * 合集必须传 `itemIndex`:发送链路是**按格**把本地地址登记进注册表的,不带下标查的是一个
6953
+ * 不存在的键,结果是刚发出去的合集点开预览还要重新下载一遍远端。
6655
6954
  */
6656
- declare function acquireMediaBlob(runtime: ChatRuntime, message: ImMessageView): MediaObjectUrlLease | undefined;
6955
+ declare function acquireMediaBlob(runtime: ChatRuntime, message: ImMessageView, itemIndex?: number): MediaObjectUrlLease | undefined;
6657
6956
  /**
6658
6957
  * 取得语音播放器需要的完整明文字节。
6659
6958
  *
@@ -6670,6 +6969,25 @@ declare function loadVoiceBytes(runtime: ChatRuntime, message: ImMessageView, ho
6670
6969
  declare function loadVideoObjectUrl(runtime: ChatRuntime, message: ImMessageView, hooks: ServiceNoteHooks, onProgress?: (percent: number) => void): Promise<string | null>;
6671
6970
  /** 仅读取已缓存视频,命中时返回可播放 object URL;miss 不创建下载任务、不显示进度。 */
6672
6971
  declare function loadCachedVideoObjectUrl(runtime: ChatRuntime, message: ImMessageView): Promise<string | null>;
6972
+ /**
6973
+ * 下载并解密媒体合集里的某一项,返回预览器可用的临时地址。
6974
+ *
6975
+ * 图片项与视频项走同一条路径:SDK 端按下标取到这一项的 url/teaKey 后,复用的就是单图/单视频的
6976
+ * 解密 loader,所以这里不需要按 kind 分流。图片项额外做一次真实格式校验(与 `loadImageObjectUrl`
6977
+ * 同一条门槛),不把无效二进制交给 `<img>`。
6978
+ *
6979
+ * 返回 `null` 表示下标越界、正文不是合集、内容非法或下载失败,失败原因经 `setNote` 反馈。
6980
+ * **调用方在不再使用地址时必须 `URL.revokeObjectURL`。**
6981
+ */
6982
+ declare function loadMediaAlbumItemObjectUrl(runtime: ChatRuntime, message: ImMessageView, itemIndex: number, hooks: ServiceNoteHooks, onProgress?: (percent: number) => void): Promise<string | null>;
6983
+ /**
6984
+ * 只读取合集某一项已持久化的明文资产并创建 object URL。
6985
+ *
6986
+ * 与 `loadCachedImageObjectUrl` 同一条合同:未缓存、下标越界、账号切换或缓存读取失败都返回 `null`,
6987
+ * 永不触发网络下载、不建下载任务、不显示用户提示,供预览器优先秒开已下载过的那一项。
6988
+ * 还在上传中的项地址是本地 `blob:`,不进这条路径——那一份要走 `acquireMediaBlob`。
6989
+ */
6990
+ declare function loadCachedMediaAlbumItemObjectUrl(runtime: ChatRuntime, message: ImMessageView, itemIndex: number): Promise<string | null>;
6673
6991
  /**
6674
6992
  * 下载并解密原图,校验真实图片格式后返回大图查看器可使用的临时地址。
6675
6993
  *
@@ -6695,9 +7013,11 @@ declare const media_service_acquireMediaBlob: typeof acquireMediaBlob;
6695
7013
  declare const media_service_detectImageMime: typeof detectImageMime;
6696
7014
  declare const media_service_detectVideoMime: typeof detectVideoMime;
6697
7015
  declare const media_service_loadCachedImageObjectUrl: typeof loadCachedImageObjectUrl;
7016
+ declare const media_service_loadCachedMediaAlbumItemObjectUrl: typeof loadCachedMediaAlbumItemObjectUrl;
6698
7017
  declare const media_service_loadCachedVideoObjectUrl: typeof loadCachedVideoObjectUrl;
6699
7018
  declare const media_service_loadFileObjectUrl: typeof loadFileObjectUrl;
6700
7019
  declare const media_service_loadImageObjectUrl: typeof loadImageObjectUrl;
7020
+ declare const media_service_loadMediaAlbumItemObjectUrl: typeof loadMediaAlbumItemObjectUrl;
6701
7021
  declare const media_service_loadVideoObjectUrl: typeof loadVideoObjectUrl;
6702
7022
  declare const media_service_loadVoiceBytes: typeof loadVoiceBytes;
6703
7023
  declare namespace media_service {
@@ -6706,9 +7026,11 @@ declare namespace media_service {
6706
7026
  media_service_detectImageMime as detectImageMime,
6707
7027
  media_service_detectVideoMime as detectVideoMime,
6708
7028
  media_service_loadCachedImageObjectUrl as loadCachedImageObjectUrl,
7029
+ media_service_loadCachedMediaAlbumItemObjectUrl as loadCachedMediaAlbumItemObjectUrl,
6709
7030
  media_service_loadCachedVideoObjectUrl as loadCachedVideoObjectUrl,
6710
7031
  media_service_loadFileObjectUrl as loadFileObjectUrl,
6711
7032
  media_service_loadImageObjectUrl as loadImageObjectUrl,
7033
+ media_service_loadMediaAlbumItemObjectUrl as loadMediaAlbumItemObjectUrl,
6712
7034
  media_service_loadVideoObjectUrl as loadVideoObjectUrl,
6713
7035
  media_service_loadVoiceBytes as loadVoiceBytes,
6714
7036
  };
@@ -7178,6 +7500,13 @@ declare function fetchConversationVoiceBatch(runtime: ChatRuntime, input: Browse
7178
7500
  */
7179
7501
  declare function fetchNextConversationSearchBatch(runtime: ChatRuntime, pageToken: ImPageToken): Promise<SearchBatchResult>;
7180
7502
 
7503
+ type GifFileClassification = 'gif' | 'not-gif' | 'invalid-gif';
7504
+ /**
7505
+ * 同时核对 MIME 与文件头,避免 GIF 合同冲突的文件回落到普通 IMAGE 链路。
7506
+ *
7507
+ * 浏览器 MIME 可以为空;一旦 MIME 或文件头任一方声明 GIF,另一方就必须一致。
7508
+ */
7509
+ declare function classifyGifFile(file: Blob): Promise<GifFileClassification>;
7181
7510
  declare function isGifFile(file: Blob): Promise<boolean>;
7182
7511
 
7183
7512
  /**
@@ -7311,12 +7640,18 @@ declare function sendVoice(runtime: ChatRuntime, input: {
7311
7640
  conversationType: ConversationType | undefined;
7312
7641
  refMessageId?: string;
7313
7642
  }, hooks: SendCommandHooks): Promise<boolean>;
7314
- /**
7315
- * 批量发送用户选择的媒体,按 MIME 自动将每项识别为图片、视频或普通文件。
7316
- *
7317
- * 超过单批数量上限时只处理前 9 项并提示其余已跳过;随后按选择顺序创建独立消息,
7318
- * 实际文件读取与上传并发仍由 SDK 控制。
7319
- */
7643
+ declare function sendMediaAlbum(runtime: ChatRuntime, input: {
7644
+ items: readonly {
7645
+ kind: 'image' | 'video';
7646
+ file: File;
7647
+ localPreviewMetadata?: ImagePreviewMetadata | VideoPreviewMetadata;
7648
+ }[];
7649
+ conversationId: string;
7650
+ conversationType: ConversationType | undefined;
7651
+ caption?: string;
7652
+ hd?: boolean;
7653
+ refMessageId?: string;
7654
+ }, hooks: SendCommandHooks): Promise<boolean>;
7320
7655
  declare function sendMediaFilesBatch(runtime: ChatRuntime, input: {
7321
7656
  files: File[];
7322
7657
  conversationId: string;
@@ -8111,5 +8446,5 @@ declare function resampleWaveform(data: readonly number[], targetCount: number):
8111
8446
  */
8112
8447
  declare function placeholderWaveform(seed: string, count: number): number[];
8113
8448
 
8114
- export { DEFAULT_EMOJI_ASSET_BASE, DEFAULT_LOADING_MINIMUM_VISIBLE_MS, DEFAULT_LOADING_SHOW_DELAY_MS, EMOJI_CATALOG, EMOJI_GAME_DICE_STABLE_KEY, EMOJI_GAME_RPS_STABLE_KEY, EMOJI_TOKEN_RE, MAX_BATCH_FILES, MAX_RECENT_EMOJI_COUNT, SPIKE_STEP, UNICODE_EMOJI_CATALOG, blockReasonForChannelSession, blockReasonForChannelState, blockReasonForGroupState, blockReasonForSession, calculateMediaBox, canEditMessage, canPinChannelMessage, canPinGroupMessage, canPinMessage, canPromoteMediaPresentationSource, canRecallMessage, canTransmit, canTransmitByType, canTransmitContent, cancelMediaUpload, captureRuntimeClientOperation, captureRuntimeOperation, createChatRuntime, createDelayedLoadingIndicator, createEmojiMatcher, createLinkMatcher, createNewMessageBadgeController, createThreadItems, decideConversationAction, decideMemberTargetAction, decideMessageAction, emojiAssetUrl, emojiUrlByCode, fetchConversationFileBatch, fetchConversationImageBatch, fetchConversationMessageBatch, fetchConversationVideoBatch, fetchConversationVoiceBatch, fetchNextConversationSearchBatch, formatChatDateSeparator, formatConversationDisplayTime, formatFileSize, formatMediaDuration, forward_service as forwardService, getFileExtension, getLocalDayKey, hasEmojiCandidate, hasLinkCandidate, highlightSegments, isGifFile, isMessageLocateScrollProtected, isMessageTailChangeFromOlderWindowTrim, isMessageViewportMeasurable, isMessageVisibleForNewMessageBadge, isPinnedMessageProtected, isVoiceStorageConfigured, loadOlderUntilBoundary, mediaBatchBudgetBytes, media_service as mediaService, message_service as messageService, normalizeLinkHref, parseUnicodeEmojiCode, pinnedMessage_service as pinnedMessageService, placeholderWaveform, prependRecentEmoji, profile_service as profileService, read_service as readService, refreshPrependMessageAnchorPosition, replaceInterruptedMediaFile, requiresConversationEncryption, requiresForwardTypeConfirmation, resampleWaveform, resolveBestMediaPresentationSource, resolveCenteredMessageScrollTop, resolveChannelMemberAvatarUrl, resolveChannelMemberDisplayName, resolveConversationListPresentation, resolveDisplayTimestamp, resolveEmojiToken, resolveFriendDisplayName, resolveImProfileAccessDestination, resolveImThreadActiveDate, resolveMediaPresentation, resolveMemberDisplayName, resolveMessageBodyKind, resolveMessageTimelineTimestamp, resolvePrependMessageAnchor, resolvePrependMessageAnchorAdjustment, resolvePrependMessageAnchorPosition, resolvePublicNickname, resolveQuotedMessagePresentation, resolveSenderAvatarUrl, resolveStandaloneEmojiMessage, resolveSurvivingMessageAnchorAdjustment, resolveUnicodeEmoji, resolveVisibleMessageAnchorPositions, retryMessage, runMediaSendBatch, sendAsFilesBatch, sendBlockReasonForDecision, sendMediaFile, sendMediaFilesBatch, sendText, sendVoice, shouldCompleteInitialViewport, shouldFillInitialHistoryViewport, shouldFollowIncomingMessage, shouldLoadNewerHistoryPage, shouldPrefetchOlderHistory, shouldRestoreMessageViewportFocus, shouldShowChatDateSeparator, shouldTrackRemoteMessageForBadge, spikeCountForDuration, supportsConversationMention, unicodeEmojiTextFromCode };
8115
- export type { ApplySessionResult, BrowseMessagesInput, CanPinMessageInput, CenteredMessageScrollInput, ChatMediaMessageRef, ChatProfileAdapter, ChatRuntime, ChatRuntimeCredentials, ChatRuntimeFactories, ChatRuntimeHostDeps, ChatRuntimeOpResult, ConversationCapabilityAction, ConversationListPresentation, ConversationListPresentationLabels, ConversationListPresentationOptions, ConversationListPreviewSegment, ConversationSearchCategory, CreateThreadItemsInput, DelayedLoadingIndicator, DelayedLoadingIndicatorOptions, EmojiEntry, EmojiRecentItem, EmojiSegment, FormatTimeDisplayContext, ForwardCommandHooks, HighlightSegment, ImChannelMemberPresentation, ImChannelMentionPresentation, ImChannelProfilePresentation, ImForwardOperationResult, ImGroupMemberPresentation, ImMediaBatchSendResult, ImMessageActionResult, ImMessageBodyKind, ImMessageView, ImPinnedMessageActionResult, ImPinnedMessageSnapshot, ImProfileAccessDestination, ImQuotedMessagePresentation, ImTextResolver, ImThreadActiveDateSnapshot, ImThreadDateMarkerMetric, ImThreadDecoration, ImThreadEncryptionItem, ImThreadIntent, ImThreadItem, ImThreadPresentationAdapter, ImThreadReadActions, ImThreadReadTargetResolver, ImThreadViewportMetrics, IncomingMessageFollowInput, InitialHistoryViewportFillInput, InitialViewportCompletionInput, LinkSegment, MediaBatchBudgetOptions, MediaBatchLimitInfo, MediaLimitItem, MediaObjectUrlLease, MediaPresentationFit, MediaPresentationPolicy, MediaPresentationRule, MediaPresentationSourceCandidate, MediaPresentationSourceQuality, MediaSendBatchResult, MemberTargetCapabilityAction, MemberTargetCapabilityInput, MessageCapabilityAction, MessageCapabilityInput, MessageLocateScrollProtectionInput, NewMessageBadgeController, NewMessageBadgeCountListener, NewMessageBadgeVisibilityInput, NewerHistoryPageTriggerInput, OlderHistoryPrefetchInput, PrependAnchorCandidate, PrependAnchorViewport, PrependMessageAnchorPosition, RemoteMessageBadgeTrackingInput, ResolveMediaPresentationInput, ResolvedMediaPresentation, RichExtraSegment, RuntimeClientOperation, RuntimeOperation, SearchBatchResult, SearchFilesInput, SearchMessagesInput, SearchTextMessagesInput, SendCapabilityMode, SendCommandHooks, SendVoicePayload, SenderProfileEntry, ServiceNoteHooks, StandaloneEmojiMessage, UnicodeEmojiEntry };
8449
+ export { DEFAULT_EMOJI_ASSET_BASE, DEFAULT_LOADING_MINIMUM_VISIBLE_MS, DEFAULT_LOADING_SHOW_DELAY_MS, EMOJI_CATALOG, EMOJI_GAME_DICE_STABLE_KEY, EMOJI_GAME_RPS_STABLE_KEY, EMOJI_TOKEN_RE, MAX_BATCH_FILES, MAX_RECENT_EMOJI_COUNT, SPIKE_STEP, UNICODE_EMOJI_CATALOG, blockReasonForChannelSession, blockReasonForChannelState, blockReasonForGroupState, blockReasonForSession, calculateMediaBox, canEditMessage, canPinChannelMessage, canPinGroupMessage, canPinMessage, canPromoteMediaPresentationSource, canRecallMessage, canTransmit, canTransmitByType, canTransmitContent, cancelMediaUpload, captureRuntimeClientOperation, captureRuntimeOperation, classifyGifFile, createChatRuntime, createDelayedLoadingIndicator, createEmojiMatcher, createLinkMatcher, createNewMessageBadgeController, createThreadItems, decideConversationAction, decideMemberTargetAction, decideMessageAction, emojiAssetUrl, emojiUrlByCode, fetchConversationFileBatch, fetchConversationImageBatch, fetchConversationMessageBatch, fetchConversationVideoBatch, fetchConversationVoiceBatch, fetchNextConversationSearchBatch, formatChatDateSeparator, formatConversationDisplayTime, formatFileSize, formatMediaDuration, forward_service as forwardService, getFileExtension, getLocalDayKey, hasEmojiCandidate, hasLinkCandidate, highlightSegments, isGifFile, isMessageLocateScrollProtected, isMessageTailChangeFromOlderWindowTrim, isMessageViewportMeasurable, isMessageVisibleForNewMessageBadge, isPinnedMessageProtected, isVoiceStorageConfigured, loadOlderUntilBoundary, mediaBatchBudgetBytes, media_service as mediaService, message_service as messageService, normalizeLinkHref, parseUnicodeEmojiCode, pinnedMessage_service as pinnedMessageService, placeholderWaveform, prependRecentEmoji, profile_service as profileService, read_service as readService, refreshPrependMessageAnchorPosition, replaceInterruptedMediaFile, requiresConversationEncryption, requiresForwardTypeConfirmation, resampleWaveform, resolveBestMediaPresentationSource, resolveCenteredMessageScrollTop, resolveChannelMemberAvatarUrl, resolveChannelMemberDisplayName, resolveConversationListPresentation, resolveDisplayTimestamp, resolveEmojiToken, resolveFriendDisplayName, resolveImProfileAccessDestination, resolveImThreadActiveDate, resolveMediaPresentation, resolveMemberDisplayName, resolveMessageBodyKind, resolveMessageNotificationDecision, resolveMessageTimelineTimestamp, resolvePrependMessageAnchor, resolvePrependMessageAnchorAdjustment, resolvePrependMessageAnchorPosition, resolvePublicNickname, resolveQuotedMessagePresentation, resolveSenderAvatarUrl, resolveStandaloneEmojiMessage, resolveSurvivingMessageAnchorAdjustment, resolveUnicodeEmoji, resolveVisibleMessageAnchorPositions, retryMessage, runMediaSendBatch, sendAsFilesBatch, sendBlockReasonForDecision, sendMediaAlbum, sendMediaFile, sendMediaFilesBatch, sendText, sendVoice, shouldCompleteInitialViewport, shouldFillInitialHistoryViewport, shouldFollowIncomingMessage, shouldLoadNewerHistoryPage, shouldPrefetchOlderHistory, shouldRestoreMessageViewportFocus, shouldShowChatDateSeparator, shouldTrackRemoteMessageForBadge, spikeCountForDuration, supportsConversationMention, unicodeEmojiTextFromCode };
8450
+ export type { ApplySessionResult, BrowseMessagesInput, CanPinMessageInput, CenteredMessageScrollInput, ChatMediaMessageRef, ChatProfileAdapter, ChatRuntime, ChatRuntimeCredentials, ChatRuntimeFactories, ChatRuntimeHostDeps, ChatRuntimeOpResult, ConversationCapabilityAction, ConversationListPresentation, ConversationListPresentationLabels, ConversationListPresentationOptions, ConversationListPreviewSegment, ConversationSearchCategory, CreateThreadItemsInput, DelayedLoadingIndicator, DelayedLoadingIndicatorOptions, EmojiEntry, EmojiRecentItem, EmojiSegment, FormatTimeDisplayContext, ForwardCommandHooks, GifFileClassification, HighlightSegment, ImChannelMemberPresentation, ImChannelMentionPresentation, ImChannelProfilePresentation, ImForwardOperationResult, ImGroupMemberPresentation, ImMediaBatchSendResult, ImMessageActionResult, ImMessageBodyKind, ImMessageView, ImPinnedMessageActionResult, ImPinnedMessageSnapshot, ImProfileAccessDestination, ImQuotedMessagePresentation, ImTextResolver, ImThreadActiveDateSnapshot, ImThreadDateMarkerMetric, ImThreadDecoration, ImThreadEncryptionItem, ImThreadIntent, ImThreadItem, ImThreadPresentationAdapter, ImThreadReadActions, ImThreadReadTargetResolver, ImThreadViewportMetrics, IncomingMessageFollowInput, InitialHistoryViewportFillInput, InitialViewportCompletionInput, LinkSegment, MediaBatchBudgetOptions, MediaBatchLimitInfo, MediaLimitItem, MediaObjectUrlLease, MediaPresentationFit, MediaPresentationPolicy, MediaPresentationRule, MediaPresentationSourceCandidate, MediaPresentationSourceQuality, MediaSendBatchResult, MemberTargetCapabilityAction, MemberTargetCapabilityInput, MessageCapabilityAction, MessageCapabilityInput, MessageLocateScrollProtectionInput, MessageNotificationDecision, MessageNotificationInput, NewMessageBadgeController, NewMessageBadgeCountListener, NewMessageBadgeVisibilityInput, NewerHistoryPageTriggerInput, OlderHistoryPrefetchInput, PrependAnchorCandidate, PrependAnchorViewport, PrependMessageAnchorPosition, RemoteMessageBadgeTrackingInput, ResolveMediaPresentationInput, ResolvedMediaPresentation, RichExtraSegment, RuntimeClientOperation, RuntimeOperation, SearchBatchResult, SearchFilesInput, SearchMessagesInput, SearchTextMessagesInput, SendCapabilityMode, SendCommandHooks, SendVoicePayload, SenderProfileEntry, ServiceNoteHooks, StandaloneEmojiMessage, UnicodeEmojiEntry };