@vibexnpm/talkx 8.2.0 → 8.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 방장 위임 · 위임 후 나가기 · 방 삭제 타입 — docs/chat.md 의 예제가 <b>실제로 컴파일되는지</b>.
3
+ *
4
+ * <p>jest 는 `.d.ts` 를 보지 않는다. 최상위 client 와 chat 양쪽에 같은 모양이 선언됐는지,
5
+ * leaveRoom 의 두 번째 인자가 선택인지(일반 참가자 기존 호출 유지), 새 이벤트가 구독 가능한지를 tsc 로 고정한다.</p>
6
+ *
7
+ * <p>실행: {@code npm run typecheck}</p>
8
+ */
9
+ import TalkFlowClient, { RoomListEventType } from '../index';
10
+ import type { ChatClient, ChatRoomResponse, LeaveRoomOptions, RoomListUpdateEvent } from '../index';
11
+
12
+ declare const talkflow: TalkFlowClient;
13
+ /** {@code talkflow.chat} 은 연결 전 null 이라 선언이 nullable 이다 — 연결된 chat 을 직접 받는 모양으로 본다. */
14
+ declare const chat: ChatClient;
15
+ declare const roomId: string;
16
+
17
+ async function ownerExit(): Promise<void> {
18
+ // 일반 참가자 — 기존 호출 그대로
19
+ await talkflow.leaveRoom(roomId);
20
+ // 방장 — 위임 후 나가기
21
+ const options: LeaveRoomOptions = { newOwnerId: 'u2' };
22
+ await talkflow.leaveRoom(roomId, options);
23
+ await chat.leaveRoom(roomId, { newOwnerId: 'u2' });
24
+ // 방장 — 방 삭제
25
+ await talkflow.deleteRoom(roomId);
26
+ await chat.deleteRoom(roomId);
27
+ }
28
+
29
+ async function transfer(): Promise<ChatRoomResponse> {
30
+ const fromRoot: ChatRoomResponse = await talkflow.transferOwnership(roomId, 'u2');
31
+ void fromRoot;
32
+ return chat.transferOwnership(roomId, 'u2');
33
+ }
34
+
35
+ talkflow.on('roomListOwnerChanged', (event: RoomListUpdateEvent) => {
36
+ const previousOwner: string | undefined = event.actorId;
37
+ // docs/chat.md 예제 — members[0] 이 새 방장
38
+ const newOwnerId: string | undefined = event.members?.[0]?.userId;
39
+ const nickname: string | null | undefined = event.members?.[0]?.nickname;
40
+ const count: number | undefined = event.activeParticipantCount;
41
+ void [previousOwner, newOwnerId, nickname, count];
42
+ });
43
+ talkflow.on('roomListDeleted', (event: RoomListUpdateEvent) => {
44
+ const deletedRoom: string = event.roomId;
45
+ void deletedRoom;
46
+ });
47
+
48
+ const ownerChanged: 'ROOM_OWNER_CHANGED' = RoomListEventType.ROOM_OWNER_CHANGED;
49
+ const deleted: 'ROOM_DELETED' = RoomListEventType.ROOM_DELETED;
50
+
51
+ void [ownerExit, transfer, ownerChanged, deleted];
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 영상 분석 타입 — docs/video-analysis.md 의 예제가 <b>실제로 컴파일되는지</b>.
3
+ *
4
+ * <p>jest 는 `.d.ts` 를 보지 않는다. 문서가 약속한 모양(멱등키 필수 · 대상 두 종류 · 종료 판정은 status ·
5
+ * 시각은 문자열 · 취소 응답에는 결과가 없음 · 진행 단계 VIDEO_ANALYSIS)이 선언과 맞는지 여기서 tsc 로 고정한다.</p>
6
+ *
7
+ * <p>실행: {@code npm run typecheck}</p>
8
+ */
9
+ import TalkFlowClient from '../index';
10
+ import type {
11
+ AssistantProgressEvent,
12
+ ChatMessage,
13
+ VideoAnalysisCancellation,
14
+ VideoAnalysisCapabilities,
15
+ VideoAnalysisJob,
16
+ VideoAnalysisRequest,
17
+ VideoAnalysisSource,
18
+ VideoAsset,
19
+ } from '../index';
20
+
21
+ declare const talkflow: TalkFlowClient;
22
+ declare const file: File;
23
+ declare const roomId: string;
24
+ declare const personaId: string;
25
+ declare const message: ChatMessage;
26
+ declare const idempotencyKey: string;
27
+
28
+ // 1) 사용 가능 여부와 한도 — 화면은 이 응답의 숫자를 쓴다.
29
+ async function gate(): Promise<boolean> {
30
+ const caps: VideoAnalysisCapabilities = await talkflow.getVideoAnalysisCapabilities();
31
+ const limits: number[] = [caps.maxSources, caps.maxBytesPerSource, caps.maxTotalDurationMs, caps.framesPerSecond];
32
+ const formats: string[] = caps.mimeTypes;
33
+ void [limits, formats, caps.model];
34
+ return caps.available && caps.standaloneUploadConfigured;
35
+ }
36
+
37
+ // 2) 업로드 — 진행률은 객체 하나, 만료 시각은 문자열(ISO-8601)이다.
38
+ async function upload(controller: AbortController): Promise<VideoAsset> {
39
+ const asset = await talkflow.uploadVideoAsset(file, {
40
+ onProgress: ({ loaded, total, percent }) => { void [loaded, total, percent]; },
41
+ signal: controller.signal,
42
+ });
43
+ const expiresAt: string = asset.expiresAt;
44
+ const lengthMs: number = asset.durationMs;
45
+ void [expiresAt, lengthMs, asset.fileName, asset.mimeType, asset.sizeBytes];
46
+ return asset;
47
+ }
48
+
49
+ // 3) 대상은 두 종류다 — 업로드 자산과 채팅 첨부. 구간은 선택이다.
50
+ const uploaded: VideoAnalysisSource = { type: 'UPLOADED_ASSET', assetId: 'asset-1' };
51
+ const attachment: VideoAnalysisSource = {
52
+ type: 'CHAT_ATTACHMENT',
53
+ roomId,
54
+ messageId: message.messageId,
55
+ attachmentId: message.fileMetaDataList?.[0]?.fileId ?? '',
56
+ startMs: 10_000,
57
+ endMs: 30_000,
58
+ };
59
+ // @ts-expect-error 채팅 첨부는 방·메시지·첨부 ID 가 모두 있어야 한다
60
+ const incompleteAttachment: VideoAnalysisSource = { type: 'CHAT_ATTACHMENT', roomId };
61
+ // @ts-expect-error 외부 URL 은 대상이 될 수 없다
62
+ const external: VideoAnalysisSource = { type: 'URL', url: 'https://example.com/video.mp4' };
63
+ void [incompleteAttachment, external];
64
+
65
+ // 4) 접수는 멱등키가 필수다 — 비공개 분석과 방 게시 분석 모두.
66
+ const analysis: VideoAnalysisRequest = { instruction: '요약해 주세요.', locale: 'ko-KR', sources: [uploaded] };
67
+ const privateJob: Promise<VideoAnalysisJob> = talkflow.analyzeVideo(analysis, { idempotencyKey });
68
+ const roomJob: Promise<VideoAnalysisJob> = talkflow.analyzeVideoWithAssistant(roomId, {
69
+ personaId,
70
+ triggerMessageId: message.messageId,
71
+ analysis: { instruction: '이 구간의 발언을 요약해 주세요.', sources: [attachment] },
72
+ }, { idempotencyKey });
73
+ // @ts-expect-error 키 없이 접수할 수 없다
74
+ talkflow.analyzeVideo(analysis);
75
+ // @ts-expect-error 방 게시 분석도 키가 필수다
76
+ talkflow.analyzeVideoWithAssistant(roomId, { personaId, triggerMessageId: message.messageId, analysis });
77
+ void [privateJob, roomJob];
78
+
79
+ // 5) 종료는 status 로 판정한다 — 값이 늘면 이 switch 가 컴파일되지 않는다.
80
+ function isTerminal(job: VideoAnalysisJob): boolean {
81
+ switch (job.status) {
82
+ case 'QUEUED':
83
+ case 'RUNNING':
84
+ return false;
85
+ case 'SUCCEEDED':
86
+ case 'FAILED':
87
+ case 'CANCELLED':
88
+ return true;
89
+ default: {
90
+ const unreachable: never = job.status;
91
+ return unreachable;
92
+ }
93
+ }
94
+ }
95
+
96
+ // 6) 결과는 성공일 때만 있고, 근거 시각은 없을 수 있다(둘 다 null). 시각 필드는 숫자가 아니다.
97
+ function render(job: VideoAnalysisJob): string[] {
98
+ const createdAt: string = job.createdAt;
99
+ const deadline: number = Date.parse(job.deadlineAt);
100
+ // @ts-expect-error createdAt 은 epoch millis 가 아니라 ISO-8601 문자열이다
101
+ const wrong: number = job.createdAt;
102
+ void [createdAt, deadline, wrong, job.detail, job.errorCode, job.resultMessageId, job.deliveryPending];
103
+ if (job.result === null) return [];
104
+ const known = new Set(job.sources.map((source) => source.sourceId));
105
+ return job.result.evidence
106
+ .filter((evidence) => known.has(evidence.sourceId))
107
+ .map((evidence) => {
108
+ const range = evidence.startMs === null || evidence.endMs === null
109
+ ? '(시각 미상)'
110
+ : `${evidence.startMs}–${evidence.endMs}`;
111
+ const modality: 'VISUAL' | 'AUDIO' | 'BOTH' = evidence.modality;
112
+ return `${evidence.sourceId} ${range} ${modality} ${evidence.observation}`;
113
+ });
114
+ }
115
+
116
+ // 7) 취소 응답에는 상태만 있다 — 결과는 getVideoAnalysis 로 읽는다.
117
+ async function cancel(jobId: string): Promise<boolean> {
118
+ const cancelled: VideoAnalysisCancellation = await talkflow.cancelVideoAnalysis(jobId);
119
+ // @ts-expect-error 취소 응답은 결과를 담지 않는다
120
+ void cancelled.result;
121
+ const latest: VideoAnalysisJob = await talkflow.getVideoAnalysis(jobId);
122
+ void [cancelled.deliveryPending, latest];
123
+ return cancelled.status === 'CANCELLED';
124
+ }
125
+
126
+ // 8) 진행 단계에 VIDEO_ANALYSIS 가 있다 — phase 를 직접 분기하는 화면이 쓸 수 있어야 한다.
127
+ function isVideoAnalysis(event: AssistantProgressEvent): boolean {
128
+ return event.active && event.phase === 'VIDEO_ANALYSIS';
129
+ }
130
+
131
+ void [gate, upload, isTerminal, render, cancel, isVideoAnalysis];
package/types/index.d.ts CHANGED
@@ -501,6 +501,12 @@ export interface PmPromptPreviewResponse {
501
501
  composedPrompt: string;
502
502
  }
503
503
 
504
+ /** {@code leaveRoom} 옵션 — 방장의 «위임 후 나가기». */
505
+ export interface LeaveRoomOptions {
506
+ /** 방장을 넘겨받을 사람 userId. 방의 활성 사람 참가자이면서 활성 계정이어야 한다. 일반 참가자는 생략. */
507
+ newOwnerId?: string;
508
+ }
509
+
504
510
  /**
505
511
  * {@link ChatClient.inviteToGroupRoom} 의 신규 형식 입력. 회원 / 어시스턴트 동시 초대 지원.
506
512
  * 둘 중 하나 이상 필수.
@@ -522,6 +528,8 @@ export const RoomListEventType: {
522
528
  readonly ROOM_UPDATED: 'ROOM_UPDATED';
523
529
  readonly ROOM_KICKED: 'ROOM_KICKED';
524
530
  readonly ROOM_BANNED: 'ROOM_BANNED';
531
+ readonly ROOM_OWNER_CHANGED: 'ROOM_OWNER_CHANGED';
532
+ readonly ROOM_DELETED: 'ROOM_DELETED';
525
533
  readonly MESSAGE_RETENTION_CLEANUP: 'MESSAGE_RETENTION_CLEANUP';
526
534
  readonly ROOM_UNREAD_UPDATED: 'ROOM_UNREAD_UPDATED';
527
535
  };
@@ -1266,10 +1274,24 @@ export interface RoomMeta {
1266
1274
  }
1267
1275
 
1268
1276
  /** 채팅방 리스트 실시간 업데이트 이벤트. */
1277
+ /** 방 목록 이벤트의 사용자 정보 — 서버 {@code RoomListEvent.MemberInfo}. */
1278
+ export interface RoomListMemberInfo {
1279
+ userId: string;
1280
+ nickname?: string | null;
1281
+ profileUrl?: string | null;
1282
+ }
1283
+
1269
1284
  export interface RoomListUpdateEvent {
1270
1285
  eventType: RoomListEventTypeValue;
1271
1286
  roomId: string;
1272
1287
  actorId?: string;
1288
+ /**
1289
+ * 입장·퇴장·추방·차단한 사용자, 또는 {@code ROOM_OWNER_CHANGED} 의 새 방장(1명). 서버 {@code RoomListEvent.MemberInfo}.
1290
+ * {@code ROOM_JOINED}·{@code ROOM_LEFT}·{@code ROOM_KICKED}·{@code ROOM_BANNED}·{@code ROOM_OWNER_CHANGED} 에서만 채워진다.
1291
+ */
1292
+ members?: RoomListMemberInfo[];
1293
+ /** 이벤트 직후 활성 참가자 수 — 입장·퇴장·추방·차단·위임 이벤트에서 채워지고, {@code ROOM_DELETED} 는 0. */
1294
+ activeParticipantCount?: number;
1273
1295
  /** {@code MESSAGE_*} 이벤트의 방 마지막 메시지 시각(epoch millis). 그 외 이벤트에서는 undefined. */
1274
1296
  lastMessageAt?: number | null;
1275
1297
  /** 이벤트 발행 시각(epoch millis). */
@@ -1318,6 +1340,71 @@ export interface RoomUnreadUpdatedEvent {
1318
1340
  unreadVersion: number;
1319
1341
  }
1320
1342
 
1343
+ /** Gmail 새 메일 미리보기 — 서버 `MAIL_CHANGED.messages[]` 원소. 본문은 없다. */
1344
+ export interface MailPreview {
1345
+ messageId: string;
1346
+ threadId?: string | null;
1347
+ from?: { name?: string | null; email: string } | null;
1348
+ subject?: string | null;
1349
+ /** ISO-8601 UTC. */
1350
+ receivedAt?: string | null;
1351
+ }
1352
+
1353
+ /**
1354
+ * `mailChanged` 이벤트 — 서버 `/user/queue/mail` 그대로. 받으면 열려 있는 메일 목록·안읽은 수를 다시 읽는다.
1355
+ * 읽음·보관 같은 변화면 `messages` 가 비어 있다(새 메일 알림을 띄우지 않는다).
1356
+ */
1357
+ export interface MailChangedEvent {
1358
+ eventType: 'MAIL_CHANGED';
1359
+ connectionId: string;
1360
+ /** 받은메일함 안읽은 수, 모르면 -1. */
1361
+ inboxUnread: number;
1362
+ newMessages: number;
1363
+ /** true 면 놓친 변화가 있을 수 있다 — 목록을 첫 쪽부터 다시 읽는다. */
1364
+ resync: boolean;
1365
+ timestamp: string;
1366
+ messages: MailPreview[];
1367
+ }
1368
+
1369
+ /**
1370
+ * `newMail` 이벤트 — 새 메일 한 통. 웹소켓 미리보기(`source: 'socket'`)와 포그라운드 FCM 메일 푸시(`source: 'push'`)를
1371
+ * `(projectId, connectionId, messageId)` 로 합쳐 메일마다 한 번만 온다.
1372
+ */
1373
+ export interface NewMailEvent {
1374
+ projectId: string | null;
1375
+ connectionId: string;
1376
+ messageId: string;
1377
+ threadId: string | null;
1378
+ /** 보낸 사람 표시(이름, 없으면 주소). */
1379
+ sender: string | null;
1380
+ /** 웹소켓으로 왔을 때만. */
1381
+ from: { name?: string | null; email: string } | null;
1382
+ subject: string | null;
1383
+ /** 웹소켓으로 왔을 때만(ISO-8601 UTC). */
1384
+ receivedAt: string | null;
1385
+ source: 'socket' | 'push';
1386
+ }
1387
+
1388
+ /**
1389
+ * `newMailSummary` 이벤트 — 한 번에 4통 이상 온 묶음 푸시(포그라운드). 받은메일함을 다시 읽는다.
1390
+ * 묶은 메일 중 아직 `newMail` 로 알리지 않은 것만 센다 — 전부 알린 메일이면 오지 않는다.
1391
+ */
1392
+ export interface NewMailSummaryEvent {
1393
+ projectId: string | null;
1394
+ connectionId: string;
1395
+ /** 처음 알리는 메일 수(구버전 서버의 묶음이면 서버가 준 건수). */
1396
+ count: number | null;
1397
+ /** 처음 알리는 메일 ID(구버전 서버의 묶음이면 빈 배열). */
1398
+ messageIds: string[];
1399
+ source: 'push';
1400
+ }
1401
+
1402
+ /** 알림 클릭으로 열린 앱이 꺼내는 메일 이동 — `messageId` 가 없으면(묶음 알림) 받은메일함으로. */
1403
+ export interface PendingMail {
1404
+ connectionId: string;
1405
+ messageId: string | null;
1406
+ }
1407
+
1321
1408
  /** 포그라운드 푸시 알림 payload. */
1322
1409
  export interface PushNotificationPayload {
1323
1410
  title: string;
@@ -1373,6 +1460,8 @@ export interface DocumentAnalysisProgress {
1373
1460
  * 렌더가 그대로 동작한다.
1374
1461
  *
1375
1462
  * 현재는 PM_BACKSTAGE(방당 단일 PM) 만 phase 를 발행 — 멘션/버튼/14인 경로는 본 이벤트 없이 typing 만.
1463
+ * 예외는 영상 분석 버튼({@code analyzeVideoWithAssistant})으로, 어시스턴트가 붙어 있는 방이면 방 종류와
1464
+ * 무관하게 `VIDEO_ANALYSIS` 를 발행한다(docs/video-analysis.md).
1376
1465
  */
1377
1466
 
1378
1467
  export interface AssistantProgressEvent {
@@ -1388,7 +1477,7 @@ export interface AssistantProgressEvent {
1388
1477
  /** 진행 주체 페르소나 ID. */
1389
1478
  personaId: string | null;
1390
1479
  /** 진행 단계 — `active:true` 일 때. 종료 시 null. */
1391
- phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
1480
+ phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'VIDEO_ANALYSIS' | 'WRITING' | null;
1392
1481
  /** DOCUMENT_ANALYSIS의 실측 진행률. 같은 phase heartbeat 사이와 재입장 후에도 보존된다. */
1393
1482
  progress?: DocumentAnalysisProgress | null;
1394
1483
  /** phase 의 기본 표시 라벨(예: "생각 중…"). SDK 매핑 — 커스텀하려면 `phase` 사용. 종료 시 null. */
@@ -1416,7 +1505,10 @@ export interface AssistantProgressEvent {
1416
1505
  textAvailable?: boolean;
1417
1506
  /** 종료(DONE) 결과 — DONE 수신 시만. */
1418
1507
  status?: 'SUCCEEDED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT' | null;
1419
- /** 종료(DONE, SUCCEEDED) 시 영속된 최종 메시지 ID. */
1508
+ /**
1509
+ * 종료(DONE) 시 방에 올라간 메시지 ID. 보통 `SUCCEEDED` 에만 있다.
1510
+ * 영상 분석은 실패 안내 메시지의 ID 가 `FAILED`·`TIMEOUT` 에도 실린다 — 성공 여부는 `status` 로 판정한다.
1511
+ */
1420
1512
  messageId?: string | null;
1421
1513
  /**
1422
1514
  * 종료 사유 — 'timeout'(TTL 만료). DONE 수신 해제엔 없음.
@@ -1456,7 +1548,7 @@ export interface AssistantRoomProgressEvent {
1456
1548
  /** 표시 기준이 된 stream (가장 최근 갱신). 없으면 null. */
1457
1549
  streamId: string | null;
1458
1550
  personaId: string | null;
1459
- phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
1551
+ phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'VIDEO_ANALYSIS' | 'WRITING' | null;
1460
1552
  progress?: DocumentAnalysisProgress | null;
1461
1553
  label: string | null;
1462
1554
  /** 표시 기준 stream 의 누적 본문. `textAvailable:false` 거나 진행 중이 아니면 null. */
@@ -1469,7 +1561,7 @@ export interface AssistantRoomProgressEvent {
1469
1561
  export interface AssistantProgressStreamSnapshot {
1470
1562
  streamId: string;
1471
1563
  personaId: string | null;
1472
- phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
1564
+ phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'VIDEO_ANALYSIS' | 'WRITING' | null;
1473
1565
  progress?: DocumentAnalysisProgress | null;
1474
1566
  label: string | null;
1475
1567
  text: string | null;
@@ -1946,9 +2038,16 @@ export interface TalkFlowClientEvents {
1946
2038
  roomListBanned: RoomListUpdateEvent;
1947
2039
  /** 본인이 영구 차단당함. */
1948
2040
  roomListSelfBanned: RoomListUpdateEvent;
2041
+ /** 방장 위임 (ROOM_OWNER_CHANGED) — actorId 는 이전 방장, members[0] 이 새 방장. */
2042
+ roomListOwnerChanged: RoomListUpdateEvent;
2043
+ /** 방장이 방을 삭제 (ROOM_DELETED) — 리스트에서 제거 + 열린 방 화면 닫기. actorId 는 삭제한 방장. */
2044
+ roomListDeleted: RoomListUpdateEvent;
1949
2045
  roomListRoomUpdated: RoomListUpdateEvent;
1950
2046
  /** 내가 어느 기기에서 읽든 방별 미독 절대값(+version)을 통지 — 크로스-디바이스 badge 동기화. */
1951
2047
  roomListUnreadUpdated: RoomUnreadUpdatedEvent;
2048
+ mailSubscribed: Record<string, unknown>;
2049
+ mailUnsubscribed: Record<string, unknown>;
2050
+ mailChanged: MailChangedEvent;
1952
2051
  retentionCleanup: RetentionCleanupPayload;
1953
2052
 
1954
2053
  // 갭 복구 — 복구된 메시지 자체는 일반 chatMessage 로 재생되므로 아래는 모두 선택 구독용.
@@ -1979,6 +2078,10 @@ export interface TalkFlowClientEvents {
1979
2078
  */
1980
2079
  pushFailed: PushFailedPayload;
1981
2080
  pushNotification: PushNotificationPayload;
2081
+ /** Gmail 새 메일 한 통 — 웹소켓·포그라운드 푸시를 합쳐 메일마다 한 번. 메일 푸시는 pushNotification 으로 오지 않는다. */
2082
+ newMail: NewMailEvent;
2083
+ /** Gmail 묶음 푸시(4통 이상) — 포그라운드. */
2084
+ newMailSummary: NewMailSummaryEvent;
1982
2085
 
1983
2086
  // WebRTC (re-emit from WebRTCClient)
1984
2087
  localStreamStarted: unknown;
@@ -2049,9 +2152,16 @@ export interface ChatClientEvents {
2049
2152
  roomListBanned: RoomListUpdateEvent;
2050
2153
  /** 본인이 영구 차단당함. */
2051
2154
  roomListSelfBanned: RoomListUpdateEvent;
2155
+ /** 방장 위임 (ROOM_OWNER_CHANGED) — actorId 는 이전 방장, members[0] 이 새 방장. */
2156
+ roomListOwnerChanged: RoomListUpdateEvent;
2157
+ /** 방장이 방을 삭제 (ROOM_DELETED) — 리스트에서 제거 + 열린 방 화면 닫기. actorId 는 삭제한 방장. */
2158
+ roomListDeleted: RoomListUpdateEvent;
2052
2159
  roomListRoomUpdated: RoomListUpdateEvent;
2053
2160
  /** 내가 어느 기기에서 읽든 방별 미독 절대값(+version)을 통지 — 크로스-디바이스 badge 동기화. */
2054
2161
  roomListUnreadUpdated: RoomUnreadUpdatedEvent;
2162
+ mailSubscribed: Record<string, unknown>;
2163
+ mailUnsubscribed: Record<string, unknown>;
2164
+ mailChanged: MailChangedEvent;
2055
2165
  retentionCleanup: RetentionCleanupPayload;
2056
2166
  messageRead: MessageReadEventPayload;
2057
2167
  typing: TypingEvent;
@@ -2124,6 +2234,12 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2124
2234
  summarizeWithAssistant(roomId: string, options: SummarizeToolRequest): Promise<AssistantToolResult>;
2125
2235
  /** 특정 메시지 온디맨드 번역 (버튼 진입점, 동기, 요청자 전용 응답). */
2126
2236
  translateWithAssistant(roomId: string, options: TranslateToolRequest): Promise<OnDemandTranslationResponse>;
2237
+ uploadVideoAsset(file: File, options?: FileUploadOptions): Promise<VideoAsset>;
2238
+ analyzeVideo(analysis: VideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
2239
+ analyzeVideoWithAssistant(roomId: string, request: RoomVideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
2240
+ getVideoAnalysis(jobId: string): Promise<VideoAnalysisJob>;
2241
+ cancelVideoAnalysis(jobId: string): Promise<VideoAnalysisCancellation>;
2242
+ getVideoAnalysisCapabilities(): Promise<VideoAnalysisCapabilities>;
2127
2243
  /** Rolling 누적 요약 갱신 트리거 (비동기). 완료는 {@code summaryUpdate} 이벤트 + {@link getSummary} 로 확인. */
2128
2244
  refreshSummary(roomId: string, options?: RefreshSummaryOptions): Promise<RefreshSummaryResult>;
2129
2245
  /** Rolling 누적 요약 조회 — rollup + segment 페이지. 저장된 요약의 단일 진실 소스. */
@@ -2161,7 +2277,15 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2161
2277
  /** PM 프롬프트 합성 미리보기 (전역+회사+방) — 참여자 전체. */
2162
2278
  previewRoomPmPrompt(roomId: string): Promise<PmPromptPreviewResponse>;
2163
2279
  joinGroupRoom(roomId: string, password?: string): Promise<ChatRoomResponse>;
2164
- leaveRoom(roomId: string): Promise<void>;
2280
+ /**
2281
+ * 방 나가기. 방장은 {@code options.newOwnerId} 로 «위임 후 나가기» — 없으면 409 {@code ROOM_OWNER_CANNOT_LEAVE}.
2282
+ * 방을 없애고 나가려면 {@link deleteRoom}.
2283
+ */
2284
+ leaveRoom(roomId: string, options?: LeaveRoomOptions): Promise<void>;
2285
+ /** 방장 위임 — 방장은 방에 남고 MEMBER 가 된다. 방장(OWNER) 전용. 갱신된 방 상세(역할 반영)를 돌려준다. */
2286
+ transferOwnership(roomId: string, newOwnerId: string): Promise<ChatRoomResponse>;
2287
+ /** 방 삭제(소프트) — 방장(OWNER) 전용. 참가자 전원 퇴장, 이후 enterRoom·getRoomInfo 403·joinGroupRoom 404. 본인 구독 자동 해제. */
2288
+ deleteRoom(roomId: string): Promise<void>;
2165
2289
  updateGroupRoom(roomId: string, data: UpdateGroupRoomData): Promise<ChatRoomResponse>;
2166
2290
  inviteToGroupRoom(roomId: string, userIds: string[]): Promise<ChatRoomResponse>;
2167
2291
  inviteToGroupRoom(roomId: string, data: InviteToRoomData): Promise<ChatRoomResponse>;
@@ -2248,6 +2372,10 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2248
2372
  subscribeRoomList(): Promise<void>;
2249
2373
  unsubscribeRoomList(): void;
2250
2374
  isRoomListSubscribed(): boolean;
2375
+ /** Gmail 메일함 변화 구독(`/user/queue/mail`) — 자동 구독하지 않는다. */
2376
+ subscribeMail(): Promise<void>;
2377
+ unsubscribeMail(): void;
2378
+ isMailSubscribed(): boolean;
2251
2379
  getSubscribedRooms(): string[];
2252
2380
  isSubscribed(roomId: string): boolean;
2253
2381
 
@@ -2398,6 +2526,8 @@ export const PushErrorCode: {
2398
2526
 
2399
2527
  export class PushManager {
2400
2528
  consumePendingRoom(): Promise<string | null>;
2529
+ /** 알림 클릭(cold start)으로 저장된 Gmail 메일 이동을 꺼내고 지운다 — 10분 안의 것만. */
2530
+ consumePendingMail(): Promise<PendingMail | null>;
2401
2531
  reset(): void;
2402
2532
  getToken(): string | null;
2403
2533
  isEnabled(): boolean;
@@ -2408,6 +2538,7 @@ export class PushManager {
2408
2538
  /** 현재 디바이스의 푸시 수신 여부 토글. */
2409
2539
  setCurrentDeviceEnabled(enabled: boolean): Promise<void>;
2410
2540
  static consumePendingRoom(projectId?: string): Promise<string | null>;
2541
+ static consumePendingMail(projectId?: string): Promise<PendingMail | null>;
2411
2542
  /**
2412
2543
  * 현재 브라우저의 푸시 권한 상태. 인스턴스 없이도 호출 가능.
2413
2544
  */
@@ -2539,6 +2670,8 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2539
2670
  /** 푸시 상태 초기화 — 로그아웃 또는 사용자 전환 시 호출. logout() 내부에서 자동 호출됨. */
2540
2671
  resetPush(): void;
2541
2672
  consumePendingRoom(): Promise<string | null>;
2673
+ /** 알림 클릭(cold start)으로 저장된 Gmail 메일 이동을 꺼내고 지운다 — 앱 부팅 때 한 번. */
2674
+ consumePendingMail(): Promise<PendingMail | null>;
2542
2675
  /** 현재 디바이스의 푸시 수신 여부 토글. enable 선행 안 됐으면 undefined 반환. */
2543
2676
  setCurrentDeviceEnabled(enabled: boolean): Promise<void> | undefined;
2544
2677
  /** 특정 디바이스 토글 (관리 UI 용). enable 선행 안 됐으면 undefined 반환. */
@@ -2567,6 +2700,12 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2567
2700
  summarizeWithAssistant(roomId: string, options: SummarizeToolRequest): Promise<AssistantToolResult>;
2568
2701
  /** 특정 메시지 온디맨드 번역 (버튼 진입점, 동기, 요청자 전용 응답). */
2569
2702
  translateWithAssistant(roomId: string, options: TranslateToolRequest): Promise<OnDemandTranslationResponse>;
2703
+ uploadVideoAsset(file: File, options?: FileUploadOptions): Promise<VideoAsset>;
2704
+ analyzeVideo(analysis: VideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
2705
+ analyzeVideoWithAssistant(roomId: string, request: RoomVideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
2706
+ getVideoAnalysis(jobId: string): Promise<VideoAnalysisJob>;
2707
+ cancelVideoAnalysis(jobId: string): Promise<VideoAnalysisCancellation>;
2708
+ getVideoAnalysisCapabilities(): Promise<VideoAnalysisCapabilities>;
2570
2709
  /** Rolling 누적 요약 갱신 트리거 (비동기). 완료는 {@code summaryUpdate} 이벤트 + {@link getSummary} 로 확인. */
2571
2710
  refreshSummary(roomId: string, options?: RefreshSummaryOptions): Promise<RefreshSummaryResult>;
2572
2711
  /** Rolling 누적 요약 조회 — rollup + segment 페이지. 저장된 요약의 단일 진실 소스. */
@@ -2604,7 +2743,15 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2604
2743
  /** PM 프롬프트 합성 미리보기 (전역+회사+방) — 참여자 전체. */
2605
2744
  previewRoomPmPrompt(roomId: string): Promise<PmPromptPreviewResponse>;
2606
2745
  joinGroupRoom(roomId: string, password?: string): Promise<ChatRoomResponse>;
2607
- leaveRoom(roomId: string): Promise<void>;
2746
+ /**
2747
+ * 방 나가기. 방장은 {@code options.newOwnerId} 로 «위임 후 나가기» — 없으면 409 {@code ROOM_OWNER_CANNOT_LEAVE}.
2748
+ * 방을 없애고 나가려면 {@link deleteRoom}.
2749
+ */
2750
+ leaveRoom(roomId: string, options?: LeaveRoomOptions): Promise<void>;
2751
+ /** 방장 위임 — 방장은 방에 남고 MEMBER 가 된다. 방장(OWNER) 전용. 갱신된 방 상세(역할 반영)를 돌려준다. */
2752
+ transferOwnership(roomId: string, newOwnerId: string): Promise<ChatRoomResponse>;
2753
+ /** 방 삭제(소프트) — 방장(OWNER) 전용. 참가자 전원 퇴장, 이후 enterRoom·getRoomInfo 403·joinGroupRoom 404. 본인 구독 자동 해제. */
2754
+ deleteRoom(roomId: string): Promise<void>;
2608
2755
  updateGroupRoom(roomId: string, data: UpdateGroupRoomData): Promise<ChatRoomResponse>;
2609
2756
  inviteToGroupRoom(roomId: string, userIds: string[]): Promise<ChatRoomResponse>;
2610
2757
  inviteToGroupRoom(roomId: string, data: InviteToRoomData): Promise<ChatRoomResponse>;
@@ -2687,6 +2834,10 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2687
2834
  subscribeRoomList(): Promise<void>;
2688
2835
  unsubscribeRoomList(): void;
2689
2836
  isRoomListSubscribed(): boolean;
2837
+ /** Gmail 메일함 변화 구독(`/user/queue/mail`) — 자동 구독하지 않는다. */
2838
+ subscribeMail(): Promise<void>;
2839
+ unsubscribeMail(): void;
2840
+ isMailSubscribed(): boolean;
2690
2841
  getSubscribedRooms(): string[];
2691
2842
  isSubscribed(roomId: string): boolean;
2692
2843
 
@@ -2815,3 +2966,35 @@ export class RoomUnreadReconciler {
2815
2966
  /** 전체 초기화. */
2816
2967
  clear(): void;
2817
2968
  }
2969
+
2970
+
2971
+ /** Caller retains this key and reuses it after a lost response. */
2972
+ export interface VideoAnalysisOptions { idempotencyKey: string; }
2973
+ export type VideoAnalysisSource =
2974
+ | { type: 'UPLOADED_ASSET'; assetId: string; startMs?: number; endMs?: number }
2975
+ | { type: 'CHAT_ATTACHMENT'; roomId: string; messageId: string; attachmentId: string; startMs?: number; endMs?: number };
2976
+ export interface VideoAnalysisRequest { instruction: string; locale?: string; sources: VideoAnalysisSource[]; }
2977
+ export interface RoomVideoAnalysisRequest { personaId: string; triggerMessageId: string; analysis: VideoAnalysisRequest; }
2978
+ export interface VideoAsset { assetId: string; fileName: string; mimeType: string; sizeBytes: number; durationMs: number; expiresAt: string; }
2979
+ export interface VideoAnalysisResult {
2980
+ answerMarkdown: string;
2981
+ evidence: { sourceId: string; startMs: number | null; endMs: number | null; modality: 'VISUAL' | 'AUDIO' | 'BOTH'; observation: string }[];
2982
+ limitations: string[];
2983
+ /** Requested temporal coverage and sampling; not a guarantee every frame was inspected. */
2984
+ coverage: { sourceId: string; startMs: number; endMs: number; framesPerSecond: number }[];
2985
+ model: string; promptVersion: string;
2986
+ usage: { uncachedInputTokens: number; cachedInputTokens: number; outputTokens: number; thinkingTokens: number };
2987
+ }
2988
+ export interface VideoAnalysisJob {
2989
+ jobId: string; status: 'QUEUED' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED'; phase: string;
2990
+ errorCode: string | null; detail: string | null;
2991
+ sources: { sourceId: string; assetId: string | null; roomId: string | null; messageId: string | null; attachmentId: string | null; startMs: number | null; endMs: number | null }[];
2992
+ result: VideoAnalysisResult | null; createdAt: string; deadlineAt: string;
2993
+ roomId: string | null; streamId: string | null; resultMessageId: string | null; deliveryPending: boolean;
2994
+ }
2995
+ export interface VideoAnalysisCapabilities {
2996
+ available: boolean; model: string; standaloneUploadConfigured: boolean; maxSources: number;
2997
+ maxBytesPerSource: number; maxTotalDurationMs: number; framesPerSecond: number; mimeTypes: string[];
2998
+ }
2999
+
3000
+ export interface VideoAnalysisCancellation { jobId: string; status: VideoAnalysisJob['status']; deliveryPending: boolean; }