@vibexnpm/talkx 8.2.0 → 8.4.1
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/README.md +3 -0
- package/dist/index.d.ts +101 -6
- package/dist/talkflow-sdk.esm.js +1 -1
- package/dist/talkflow-sdk.esm.js.map +1 -1
- package/dist/talkflow-sdk.standalone.js +1 -1
- package/dist/talkflow-sdk.standalone.js.map +1 -1
- package/dist/talkflow-sdk.umd.js +1 -1
- package/dist/talkflow-sdk.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/chat/ChatClient.js +135 -3
- package/src/constants.js +12 -0
- package/src/talkflow/delegates.js +9 -0
- package/src/talkflow/eventForwarding.js +5 -0
- package/types/__typetests__/roomOwnership.types.ts +51 -0
- package/types/__typetests__/videoAnalysis.types.ts +131 -0
- package/types/index.d.ts +101 -6
package/package.json
CHANGED
package/src/chat/ChatClient.js
CHANGED
|
@@ -143,6 +143,7 @@ class ChatClient extends EventEmitter {
|
|
|
143
143
|
SEARCHING: '검색 중…',
|
|
144
144
|
PLANNING: '기획 중…',
|
|
145
145
|
DOCUMENT_ANALYSIS: '문서 분석 중…',
|
|
146
|
+
VIDEO_ANALYSIS: '영상 분석 중…',
|
|
146
147
|
WRITING: '작성 중…'
|
|
147
148
|
};
|
|
148
149
|
// PHASE 의 expiresAt 누락/이상 시 fallback TTL (ms) — 서버 ttl(최소 30s) 보다 약간 길게.
|
|
@@ -595,6 +596,50 @@ class ChatClient extends EventEmitter {
|
|
|
595
596
|
return this._unwrapSuccessResponse(response);
|
|
596
597
|
}
|
|
597
598
|
|
|
599
|
+
/** 영상 분석용 비공개 업로드. 반환한 assetId를 analyzeVideo에 전달한다. */
|
|
600
|
+
async uploadVideoAsset(file, options = {}) {
|
|
601
|
+
if (!file) throw new Error('file is required');
|
|
602
|
+
return this._unwrapSuccessResponse(await this.apiClient.upload('/api/v1/video-assets', file, {
|
|
603
|
+
onProgress: options.onProgress, signal: options.signal
|
|
604
|
+
}));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** 비공개 분석 접수. 네트워크 재시도에는 같은 idempotencyKey를 사용한다. */
|
|
608
|
+
async analyzeVideo(analysis, options = {}) {
|
|
609
|
+
if (typeof options.idempotencyKey !== 'string' || !options.idempotencyKey.trim()) {
|
|
610
|
+
throw new Error('idempotencyKey is required for video analysis');
|
|
611
|
+
}
|
|
612
|
+
return this._unwrapSuccessResponse(await this.apiClient.post('/api/v1/video-analyses', analysis,
|
|
613
|
+
undefined, { 'Idempotency-Key': options.idempotencyKey }));
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** 같은 분석 코어를 호출하고 결과를 지정한 방에 게시한다. */
|
|
617
|
+
async analyzeVideoWithAssistant(roomId, request, options = {}) {
|
|
618
|
+
if (!roomId || !request?.personaId || !request?.triggerMessageId) {
|
|
619
|
+
throw new Error('roomId, personaId and triggerMessageId are required');
|
|
620
|
+
}
|
|
621
|
+
if (typeof options.idempotencyKey !== 'string' || !options.idempotencyKey.trim()) {
|
|
622
|
+
throw new Error('idempotencyKey is required for video analysis');
|
|
623
|
+
}
|
|
624
|
+
return this._unwrapSuccessResponse(await this.apiClient.post(
|
|
625
|
+
`/api/v1/rooms/${encodeURIComponent(roomId)}/assistant/tools/analyze-video`, request,
|
|
626
|
+
undefined, { 'Idempotency-Key': options.idempotencyKey }));
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async getVideoAnalysis(jobId) {
|
|
630
|
+
if (!jobId) throw new Error('jobId is required');
|
|
631
|
+
return this._unwrapSuccessResponse(await this.apiClient.get(`/api/v1/video-analyses/${encodeURIComponent(jobId)}`));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async cancelVideoAnalysis(jobId) {
|
|
635
|
+
if (!jobId) throw new Error('jobId is required');
|
|
636
|
+
return this._unwrapSuccessResponse(await this.apiClient.post(`/api/v1/video-analyses/${encodeURIComponent(jobId)}/cancel`));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
async getVideoAnalysisCapabilities() {
|
|
640
|
+
return this._unwrapSuccessResponse(await this.apiClient.get('/api/v1/video-analyses/capabilities'));
|
|
641
|
+
}
|
|
642
|
+
|
|
598
643
|
/**
|
|
599
644
|
* 특정 메시지 온디맨드 번역 (버튼 진입점, 동기).
|
|
600
645
|
*
|
|
@@ -868,12 +913,84 @@ class ChatClient extends EventEmitter {
|
|
|
868
913
|
}
|
|
869
914
|
|
|
870
915
|
/**
|
|
871
|
-
* 채팅방 나가기 (구독도 자동 해제됨)
|
|
916
|
+
* 채팅방 나가기 (구독도 자동 해제됨).
|
|
917
|
+
*
|
|
918
|
+
* <p>일반 참가자는 {@code leaveRoom(roomId)} 그대로. <b>방장</b>은 그냥 나갈 수 없다 — 나가면 방에 관리자가
|
|
919
|
+
* 0명이 되기 때문이다. 방장은 둘 중 하나를 고른다:</p>
|
|
920
|
+
* <ul>
|
|
921
|
+
* <li>위임 후 나가기 — {@code leaveRoom(roomId, { newOwnerId })}</li>
|
|
922
|
+
* <li>방 삭제 — {@link #deleteRoom}</li>
|
|
923
|
+
* </ul>
|
|
924
|
+
*
|
|
925
|
+
* <p>서버 응답:</p>
|
|
926
|
+
* <ul>
|
|
927
|
+
* <li>409 {@code ROOM_OWNER_CANNOT_LEAVE} — 방장이 newOwnerId 없이 나가려 함 (선택 화면을 띄울 신호)</li>
|
|
928
|
+
* <li>403 {@code NOT_ROOM_ADMIN} — 방장이 아닌데 newOwnerId 를 보냄</li>
|
|
929
|
+
* <li>400 {@code CANNOT_TRANSFER_OWNERSHIP_TO_SELF} / {@code INVALID_OWNERSHIP_TARGET}, 404 {@code USER_NOT_IN_ROOM}
|
|
930
|
+
* — 위임 대상 오류 ({@link #transferOwnership} 와 같다). 이때는 나가지도 않는다.</li>
|
|
931
|
+
* </ul>
|
|
932
|
+
*
|
|
872
933
|
* @param {string} roomId - 채팅방 ID
|
|
934
|
+
* @param {Object} [options]
|
|
935
|
+
* @param {string} [options.newOwnerId] - 방장이 나갈 때 방장을 넘겨받을 사람 (방의 활성 사람 참가자 · 활성 계정)
|
|
936
|
+
* @returns {Promise<Object>}
|
|
937
|
+
*
|
|
938
|
+
* @example
|
|
939
|
+
* try {
|
|
940
|
+
* await chat.leaveRoom(roomId);
|
|
941
|
+
* } catch (e) {
|
|
942
|
+
* // 서버 코드는 e.response.code — 방장이면 «위임 후 나가기 / 방 삭제» 선택 화면
|
|
943
|
+
* if (e.response?.code === 'ROOM_OWNER_CANNOT_LEAVE') openOwnerExitDialog(roomId);
|
|
944
|
+
* }
|
|
945
|
+
* // 위임 후 나가기
|
|
946
|
+
* await chat.leaveRoom(roomId, { newOwnerId: 'user-2' });
|
|
947
|
+
*/
|
|
948
|
+
async leaveRoom(roomId, options = {}) {
|
|
949
|
+
const body = options.newOwnerId ? { newOwnerId: options.newOwnerId } : undefined;
|
|
950
|
+
const result = await this.apiClient.post(`/api/v1/rooms/${roomId}/leave`, body);
|
|
951
|
+
this.unsubscribeRoom(roomId);
|
|
952
|
+
return result;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* 방장 위임 — 방장은 방에 남고 MEMBER 가 된다. 방장(OWNER) 전용.
|
|
957
|
+
*
|
|
958
|
+
* <p>나가면서 넘기려면 {@code leaveRoom(roomId, { newOwnerId })}. 방 참가자 전원에게
|
|
959
|
+
* {@code roomListOwnerChanged} 가 간다(actorId = 이전 방장, members[0] = 새 방장).</p>
|
|
960
|
+
*
|
|
961
|
+
* <p>서버 응답:</p>
|
|
962
|
+
* <ul>
|
|
963
|
+
* <li>403 {@code NOT_ROOM_ADMIN} — 방장 아님 (DIRECT 방은 방장이 없다)</li>
|
|
964
|
+
* <li>400 {@code CANNOT_TRANSFER_OWNERSHIP_TO_SELF} — 자기 자신에게 위임</li>
|
|
965
|
+
* <li>404 {@code USER_NOT_IN_ROOM} — 대상이 현재 참가자가 아님</li>
|
|
966
|
+
* <li>400 {@code INVALID_OWNERSHIP_TARGET} — 대상이 어시스턴트이거나 퇴사·차단 계정</li>
|
|
967
|
+
* </ul>
|
|
968
|
+
*
|
|
969
|
+
* @param {string} roomId
|
|
970
|
+
* @param {string} newOwnerId - 새 방장 userId
|
|
971
|
+
* @returns {Promise<Object>} 갱신된 방 상세 (participants[].role 반영 — 봉투를 벗긴 data)
|
|
972
|
+
*/
|
|
973
|
+
async transferOwnership(roomId, newOwnerId) {
|
|
974
|
+
const response = await this.apiClient.put(`/api/v1/rooms/${roomId}/owner`, { newOwnerId });
|
|
975
|
+
return response && typeof response === 'object' && 'data' in response
|
|
976
|
+
? response.data
|
|
977
|
+
: response;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* 방 삭제 — 방장(OWNER) 전용. 참가자 전원이 나가게 되고 방은 목록에서 사라진다.
|
|
982
|
+
* 이후 enterRoom·getRoomInfo 는 403(ACCESS_FORBIDDEN — 더는 참가자가 아님), joinGroupRoom 은 404(CHAT_ROOM_NOT_FOUND).
|
|
983
|
+
*
|
|
984
|
+
* <p>참가자 전원(방장 포함)에게 {@code roomListDeleted} 가 간다. 본인 구독은 자동 해제된다.
|
|
985
|
+
* 메시지·첨부는 서버에 보존된다(소프트 삭제) — 되살리는 API 는 없다.</p>
|
|
986
|
+
*
|
|
987
|
+
* <p>서버 응답: 403 {@code NOT_ROOM_ADMIN} — 방장 아님.</p>
|
|
988
|
+
*
|
|
989
|
+
* @param {string} roomId
|
|
873
990
|
* @returns {Promise<Object>}
|
|
874
991
|
*/
|
|
875
|
-
async
|
|
876
|
-
const result = await this.apiClient.
|
|
992
|
+
async deleteRoom(roomId) {
|
|
993
|
+
const result = await this.apiClient.delete(`/api/v1/rooms/${roomId}`);
|
|
877
994
|
this.unsubscribeRoom(roomId);
|
|
878
995
|
return result;
|
|
879
996
|
}
|
|
@@ -2551,6 +2668,8 @@ class ChatClient extends EventEmitter {
|
|
|
2551
2668
|
* - roomListJoined — 방 입장 (ROOM_JOINED)
|
|
2552
2669
|
* - roomListLeft — 방 퇴장 (ROOM_LEFT)
|
|
2553
2670
|
* - roomListSelfLeft — 본인이 나간 경우 (리스트에서 제거 신호)
|
|
2671
|
+
* - roomListOwnerChanged — 방장 위임 (ROOM_OWNER_CHANGED, members[0] = 새 방장)
|
|
2672
|
+
* - roomListDeleted — 방장이 방을 삭제 (ROOM_DELETED, 리스트 제거 + 방 화면 닫기)
|
|
2554
2673
|
*
|
|
2555
2674
|
* @returns {Promise<void>}
|
|
2556
2675
|
*
|
|
@@ -2819,6 +2938,19 @@ class ChatClient extends EventEmitter {
|
|
|
2819
2938
|
this.emit('roomListSelfKicked', event);
|
|
2820
2939
|
}
|
|
2821
2940
|
break;
|
|
2941
|
+
case RoomListEventType.ROOM_OWNER_CHANGED:
|
|
2942
|
+
// 방장 위임 — actorId=이전 방장, members[0]=새 방장. 방장 표시·관리 메뉴를 갱신한다.
|
|
2943
|
+
this.emit('roomListOwnerChanged', event);
|
|
2944
|
+
break;
|
|
2945
|
+
case RoomListEventType.ROOM_DELETED:
|
|
2946
|
+
// 방장이 방을 삭제 — 모든 참가자가 리스트에서 제거하고 열린 방 화면을 닫는다.
|
|
2947
|
+
// 🔴 그 방 구독은 SDK 가 «먼저» 푼다. 남겨 두면 아무것도 못 받으면서 재연결 때마다 되살아나 서버에
|
|
2948
|
+
// 거부된다 — 그 거부로 연결을 끊던 서버 버전에서는 «재연결 → 거부 → 끊김» 이 반복돼 다른 방 실시간까지 멈췄다.
|
|
2949
|
+
if (this.subscribedRooms.has(event.roomId)) {
|
|
2950
|
+
this.unsubscribeRoom(event.roomId);
|
|
2951
|
+
}
|
|
2952
|
+
this.emit('roomListDeleted', event);
|
|
2953
|
+
break;
|
|
2822
2954
|
case RoomListEventType.ROOM_BANNED:
|
|
2823
2955
|
this.emit('roomListBanned', event);
|
|
2824
2956
|
// 본인이 영구 차단당한 경우 편의 이벤트 — "영구 차단되었습니다" 표시 + 리스트 제거
|
package/src/constants.js
CHANGED
|
@@ -333,6 +333,18 @@ export const RoomListEventType = {
|
|
|
333
333
|
ROOM_KICKED: 'ROOM_KICKED',
|
|
334
334
|
/** 방장에 의한 추방 + 영구 차단 → payload 는 ROOM_KICKED 와 동일, 재입장 거부됨. */
|
|
335
335
|
ROOM_BANNED: 'ROOM_BANNED',
|
|
336
|
+
/**
|
|
337
|
+
* 방장 위임 → actorId 는 이전 방장, members[0] 이 새 방장. 방장 표시·관리 권한을 갱신한다.
|
|
338
|
+
* «위임 후 나가기» 면 이어서 같은 이전 방장의 ROOM_LEFT 가 온다.
|
|
339
|
+
* SDK 는 이를 roomListOwnerChanged 이벤트로 emit 한다.
|
|
340
|
+
*/
|
|
341
|
+
ROOM_OWNER_CHANGED: 'ROOM_OWNER_CHANGED',
|
|
342
|
+
/**
|
|
343
|
+
* 방장이 방을 삭제 → 리스트에서 제거 + 열린 방 화면 닫기. actorId 는 삭제한 방장.
|
|
344
|
+
* 이후 enterRoom·getRoomInfo 는 403(ACCESS_FORBIDDEN — 더는 참가자가 아님), joinGroupRoom 은 404.
|
|
345
|
+
* SDK 는 이를 roomListDeleted 이벤트로 emit 한다.
|
|
346
|
+
*/
|
|
347
|
+
ROOM_DELETED: 'ROOM_DELETED',
|
|
336
348
|
/** 메시지 보관 기간 정리 → cutoffTime 이전 메시지를 UI에서 제거. */
|
|
337
349
|
MESSAGE_RETENTION_CLEANUP: 'MESSAGE_RETENTION_CLEANUP',
|
|
338
350
|
/**
|
|
@@ -7,6 +7,9 @@ const CHAT_DELEGATE_METHODS = [
|
|
|
7
7
|
'createGroupRoom',
|
|
8
8
|
'joinGroupRoom',
|
|
9
9
|
'leaveRoom',
|
|
10
|
+
// 방장 위임(방에 남음) · 방 삭제 — 방장의 «위임 후 나가기» 는 leaveRoom(roomId, { newOwnerId }).
|
|
11
|
+
'transferOwnership',
|
|
12
|
+
'deleteRoom',
|
|
10
13
|
'updateGroupRoom',
|
|
11
14
|
'inviteToGroupRoom',
|
|
12
15
|
'kickMember',
|
|
@@ -61,6 +64,12 @@ const CHAT_DELEGATE_METHODS = [
|
|
|
61
64
|
'rateAssistantMessage',
|
|
62
65
|
'summarizeWithAssistant',
|
|
63
66
|
'translateWithAssistant',
|
|
67
|
+
'uploadVideoAsset',
|
|
68
|
+
'analyzeVideo',
|
|
69
|
+
'analyzeVideoWithAssistant',
|
|
70
|
+
'getVideoAnalysis',
|
|
71
|
+
'cancelVideoAnalysis',
|
|
72
|
+
'getVideoAnalysisCapabilities',
|
|
64
73
|
'refreshSummary',
|
|
65
74
|
'getSummary',
|
|
66
75
|
'getRoomPmPrompt',
|
|
@@ -34,6 +34,8 @@ const CHAT_EVENT_MAP = [
|
|
|
34
34
|
['roomListUnsubscribed', 'roomListUnsubscribed'],
|
|
35
35
|
['roomListUpdate', 'roomListUpdate'],
|
|
36
36
|
['roomListMessage', 'roomListMessage'],
|
|
37
|
+
['roomListMessageDeleted', 'roomListMessageDeleted'],
|
|
38
|
+
['roomListMessageUpdated', 'roomListMessageUpdated'],
|
|
37
39
|
['roomListCreated', 'roomListCreated'],
|
|
38
40
|
['roomListJoined', 'roomListJoined'],
|
|
39
41
|
['roomListLeft', 'roomListLeft'],
|
|
@@ -42,6 +44,9 @@ const CHAT_EVENT_MAP = [
|
|
|
42
44
|
['roomListSelfKicked', 'roomListSelfKicked'],
|
|
43
45
|
['roomListBanned', 'roomListBanned'],
|
|
44
46
|
['roomListSelfBanned', 'roomListSelfBanned'],
|
|
47
|
+
// 방장 위임 · 방 삭제 — 타입에 선언한 이벤트는 여기 없으면 최상위 client.on 에서 안 울린다.
|
|
48
|
+
['roomListOwnerChanged', 'roomListOwnerChanged'],
|
|
49
|
+
['roomListDeleted', 'roomListDeleted'],
|
|
45
50
|
['roomListRoomUpdated', 'roomListRoomUpdated'],
|
|
46
51
|
['roomListUnreadUpdated', 'roomListUnreadUpdated'],
|
|
47
52
|
['retentionCleanup', 'retentionCleanup']
|
|
@@ -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). */
|
|
@@ -1373,6 +1395,8 @@ export interface DocumentAnalysisProgress {
|
|
|
1373
1395
|
* 렌더가 그대로 동작한다.
|
|
1374
1396
|
*
|
|
1375
1397
|
* 현재는 PM_BACKSTAGE(방당 단일 PM) 만 phase 를 발행 — 멘션/버튼/14인 경로는 본 이벤트 없이 typing 만.
|
|
1398
|
+
* 예외는 영상 분석 버튼({@code analyzeVideoWithAssistant})으로, 어시스턴트가 붙어 있는 방이면 방 종류와
|
|
1399
|
+
* 무관하게 `VIDEO_ANALYSIS` 를 발행한다(docs/video-analysis.md).
|
|
1376
1400
|
*/
|
|
1377
1401
|
|
|
1378
1402
|
export interface AssistantProgressEvent {
|
|
@@ -1388,7 +1412,7 @@ export interface AssistantProgressEvent {
|
|
|
1388
1412
|
/** 진행 주체 페르소나 ID. */
|
|
1389
1413
|
personaId: string | null;
|
|
1390
1414
|
/** 진행 단계 — `active:true` 일 때. 종료 시 null. */
|
|
1391
|
-
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
|
|
1415
|
+
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'VIDEO_ANALYSIS' | 'WRITING' | null;
|
|
1392
1416
|
/** DOCUMENT_ANALYSIS의 실측 진행률. 같은 phase heartbeat 사이와 재입장 후에도 보존된다. */
|
|
1393
1417
|
progress?: DocumentAnalysisProgress | null;
|
|
1394
1418
|
/** phase 의 기본 표시 라벨(예: "생각 중…"). SDK 매핑 — 커스텀하려면 `phase` 사용. 종료 시 null. */
|
|
@@ -1416,7 +1440,10 @@ export interface AssistantProgressEvent {
|
|
|
1416
1440
|
textAvailable?: boolean;
|
|
1417
1441
|
/** 종료(DONE) 결과 — DONE 수신 시만. */
|
|
1418
1442
|
status?: 'SUCCEEDED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT' | null;
|
|
1419
|
-
/**
|
|
1443
|
+
/**
|
|
1444
|
+
* 종료(DONE) 시 방에 올라간 메시지 ID. 보통 `SUCCEEDED` 에만 있다.
|
|
1445
|
+
* 영상 분석은 실패 안내 메시지의 ID 가 `FAILED`·`TIMEOUT` 에도 실린다 — 성공 여부는 `status` 로 판정한다.
|
|
1446
|
+
*/
|
|
1420
1447
|
messageId?: string | null;
|
|
1421
1448
|
/**
|
|
1422
1449
|
* 종료 사유 — 'timeout'(TTL 만료). DONE 수신 해제엔 없음.
|
|
@@ -1456,7 +1483,7 @@ export interface AssistantRoomProgressEvent {
|
|
|
1456
1483
|
/** 표시 기준이 된 stream (가장 최근 갱신). 없으면 null. */
|
|
1457
1484
|
streamId: string | null;
|
|
1458
1485
|
personaId: string | null;
|
|
1459
|
-
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
|
|
1486
|
+
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'VIDEO_ANALYSIS' | 'WRITING' | null;
|
|
1460
1487
|
progress?: DocumentAnalysisProgress | null;
|
|
1461
1488
|
label: string | null;
|
|
1462
1489
|
/** 표시 기준 stream 의 누적 본문. `textAvailable:false` 거나 진행 중이 아니면 null. */
|
|
@@ -1469,7 +1496,7 @@ export interface AssistantRoomProgressEvent {
|
|
|
1469
1496
|
export interface AssistantProgressStreamSnapshot {
|
|
1470
1497
|
streamId: string;
|
|
1471
1498
|
personaId: string | null;
|
|
1472
|
-
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
|
|
1499
|
+
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'VIDEO_ANALYSIS' | 'WRITING' | null;
|
|
1473
1500
|
progress?: DocumentAnalysisProgress | null;
|
|
1474
1501
|
label: string | null;
|
|
1475
1502
|
text: string | null;
|
|
@@ -1946,6 +1973,10 @@ export interface TalkFlowClientEvents {
|
|
|
1946
1973
|
roomListBanned: RoomListUpdateEvent;
|
|
1947
1974
|
/** 본인이 영구 차단당함. */
|
|
1948
1975
|
roomListSelfBanned: RoomListUpdateEvent;
|
|
1976
|
+
/** 방장 위임 (ROOM_OWNER_CHANGED) — actorId 는 이전 방장, members[0] 이 새 방장. */
|
|
1977
|
+
roomListOwnerChanged: RoomListUpdateEvent;
|
|
1978
|
+
/** 방장이 방을 삭제 (ROOM_DELETED) — 리스트에서 제거 + 열린 방 화면 닫기. actorId 는 삭제한 방장. */
|
|
1979
|
+
roomListDeleted: RoomListUpdateEvent;
|
|
1949
1980
|
roomListRoomUpdated: RoomListUpdateEvent;
|
|
1950
1981
|
/** 내가 어느 기기에서 읽든 방별 미독 절대값(+version)을 통지 — 크로스-디바이스 badge 동기화. */
|
|
1951
1982
|
roomListUnreadUpdated: RoomUnreadUpdatedEvent;
|
|
@@ -2049,6 +2080,10 @@ export interface ChatClientEvents {
|
|
|
2049
2080
|
roomListBanned: RoomListUpdateEvent;
|
|
2050
2081
|
/** 본인이 영구 차단당함. */
|
|
2051
2082
|
roomListSelfBanned: RoomListUpdateEvent;
|
|
2083
|
+
/** 방장 위임 (ROOM_OWNER_CHANGED) — actorId 는 이전 방장, members[0] 이 새 방장. */
|
|
2084
|
+
roomListOwnerChanged: RoomListUpdateEvent;
|
|
2085
|
+
/** 방장이 방을 삭제 (ROOM_DELETED) — 리스트에서 제거 + 열린 방 화면 닫기. actorId 는 삭제한 방장. */
|
|
2086
|
+
roomListDeleted: RoomListUpdateEvent;
|
|
2052
2087
|
roomListRoomUpdated: RoomListUpdateEvent;
|
|
2053
2088
|
/** 내가 어느 기기에서 읽든 방별 미독 절대값(+version)을 통지 — 크로스-디바이스 badge 동기화. */
|
|
2054
2089
|
roomListUnreadUpdated: RoomUnreadUpdatedEvent;
|
|
@@ -2124,6 +2159,12 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
|
|
|
2124
2159
|
summarizeWithAssistant(roomId: string, options: SummarizeToolRequest): Promise<AssistantToolResult>;
|
|
2125
2160
|
/** 특정 메시지 온디맨드 번역 (버튼 진입점, 동기, 요청자 전용 응답). */
|
|
2126
2161
|
translateWithAssistant(roomId: string, options: TranslateToolRequest): Promise<OnDemandTranslationResponse>;
|
|
2162
|
+
uploadVideoAsset(file: File, options?: FileUploadOptions): Promise<VideoAsset>;
|
|
2163
|
+
analyzeVideo(analysis: VideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
|
|
2164
|
+
analyzeVideoWithAssistant(roomId: string, request: RoomVideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
|
|
2165
|
+
getVideoAnalysis(jobId: string): Promise<VideoAnalysisJob>;
|
|
2166
|
+
cancelVideoAnalysis(jobId: string): Promise<VideoAnalysisCancellation>;
|
|
2167
|
+
getVideoAnalysisCapabilities(): Promise<VideoAnalysisCapabilities>;
|
|
2127
2168
|
/** Rolling 누적 요약 갱신 트리거 (비동기). 완료는 {@code summaryUpdate} 이벤트 + {@link getSummary} 로 확인. */
|
|
2128
2169
|
refreshSummary(roomId: string, options?: RefreshSummaryOptions): Promise<RefreshSummaryResult>;
|
|
2129
2170
|
/** Rolling 누적 요약 조회 — rollup + segment 페이지. 저장된 요약의 단일 진실 소스. */
|
|
@@ -2161,7 +2202,15 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
|
|
|
2161
2202
|
/** PM 프롬프트 합성 미리보기 (전역+회사+방) — 참여자 전체. */
|
|
2162
2203
|
previewRoomPmPrompt(roomId: string): Promise<PmPromptPreviewResponse>;
|
|
2163
2204
|
joinGroupRoom(roomId: string, password?: string): Promise<ChatRoomResponse>;
|
|
2164
|
-
|
|
2205
|
+
/**
|
|
2206
|
+
* 방 나가기. 방장은 {@code options.newOwnerId} 로 «위임 후 나가기» — 없으면 409 {@code ROOM_OWNER_CANNOT_LEAVE}.
|
|
2207
|
+
* 방을 없애고 나가려면 {@link deleteRoom}.
|
|
2208
|
+
*/
|
|
2209
|
+
leaveRoom(roomId: string, options?: LeaveRoomOptions): Promise<void>;
|
|
2210
|
+
/** 방장 위임 — 방장은 방에 남고 MEMBER 가 된다. 방장(OWNER) 전용. 갱신된 방 상세(역할 반영)를 돌려준다. */
|
|
2211
|
+
transferOwnership(roomId: string, newOwnerId: string): Promise<ChatRoomResponse>;
|
|
2212
|
+
/** 방 삭제(소프트) — 방장(OWNER) 전용. 참가자 전원 퇴장, 이후 enterRoom·getRoomInfo 403·joinGroupRoom 404. 본인 구독 자동 해제. */
|
|
2213
|
+
deleteRoom(roomId: string): Promise<void>;
|
|
2165
2214
|
updateGroupRoom(roomId: string, data: UpdateGroupRoomData): Promise<ChatRoomResponse>;
|
|
2166
2215
|
inviteToGroupRoom(roomId: string, userIds: string[]): Promise<ChatRoomResponse>;
|
|
2167
2216
|
inviteToGroupRoom(roomId: string, data: InviteToRoomData): Promise<ChatRoomResponse>;
|
|
@@ -2567,6 +2616,12 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
|
|
|
2567
2616
|
summarizeWithAssistant(roomId: string, options: SummarizeToolRequest): Promise<AssistantToolResult>;
|
|
2568
2617
|
/** 특정 메시지 온디맨드 번역 (버튼 진입점, 동기, 요청자 전용 응답). */
|
|
2569
2618
|
translateWithAssistant(roomId: string, options: TranslateToolRequest): Promise<OnDemandTranslationResponse>;
|
|
2619
|
+
uploadVideoAsset(file: File, options?: FileUploadOptions): Promise<VideoAsset>;
|
|
2620
|
+
analyzeVideo(analysis: VideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
|
|
2621
|
+
analyzeVideoWithAssistant(roomId: string, request: RoomVideoAnalysisRequest, options: VideoAnalysisOptions): Promise<VideoAnalysisJob>;
|
|
2622
|
+
getVideoAnalysis(jobId: string): Promise<VideoAnalysisJob>;
|
|
2623
|
+
cancelVideoAnalysis(jobId: string): Promise<VideoAnalysisCancellation>;
|
|
2624
|
+
getVideoAnalysisCapabilities(): Promise<VideoAnalysisCapabilities>;
|
|
2570
2625
|
/** Rolling 누적 요약 갱신 트리거 (비동기). 완료는 {@code summaryUpdate} 이벤트 + {@link getSummary} 로 확인. */
|
|
2571
2626
|
refreshSummary(roomId: string, options?: RefreshSummaryOptions): Promise<RefreshSummaryResult>;
|
|
2572
2627
|
/** Rolling 누적 요약 조회 — rollup + segment 페이지. 저장된 요약의 단일 진실 소스. */
|
|
@@ -2604,7 +2659,15 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
|
|
|
2604
2659
|
/** PM 프롬프트 합성 미리보기 (전역+회사+방) — 참여자 전체. */
|
|
2605
2660
|
previewRoomPmPrompt(roomId: string): Promise<PmPromptPreviewResponse>;
|
|
2606
2661
|
joinGroupRoom(roomId: string, password?: string): Promise<ChatRoomResponse>;
|
|
2607
|
-
|
|
2662
|
+
/**
|
|
2663
|
+
* 방 나가기. 방장은 {@code options.newOwnerId} 로 «위임 후 나가기» — 없으면 409 {@code ROOM_OWNER_CANNOT_LEAVE}.
|
|
2664
|
+
* 방을 없애고 나가려면 {@link deleteRoom}.
|
|
2665
|
+
*/
|
|
2666
|
+
leaveRoom(roomId: string, options?: LeaveRoomOptions): Promise<void>;
|
|
2667
|
+
/** 방장 위임 — 방장은 방에 남고 MEMBER 가 된다. 방장(OWNER) 전용. 갱신된 방 상세(역할 반영)를 돌려준다. */
|
|
2668
|
+
transferOwnership(roomId: string, newOwnerId: string): Promise<ChatRoomResponse>;
|
|
2669
|
+
/** 방 삭제(소프트) — 방장(OWNER) 전용. 참가자 전원 퇴장, 이후 enterRoom·getRoomInfo 403·joinGroupRoom 404. 본인 구독 자동 해제. */
|
|
2670
|
+
deleteRoom(roomId: string): Promise<void>;
|
|
2608
2671
|
updateGroupRoom(roomId: string, data: UpdateGroupRoomData): Promise<ChatRoomResponse>;
|
|
2609
2672
|
inviteToGroupRoom(roomId: string, userIds: string[]): Promise<ChatRoomResponse>;
|
|
2610
2673
|
inviteToGroupRoom(roomId: string, data: InviteToRoomData): Promise<ChatRoomResponse>;
|
|
@@ -2815,3 +2878,35 @@ export class RoomUnreadReconciler {
|
|
|
2815
2878
|
/** 전체 초기화. */
|
|
2816
2879
|
clear(): void;
|
|
2817
2880
|
}
|
|
2881
|
+
|
|
2882
|
+
|
|
2883
|
+
/** Caller retains this key and reuses it after a lost response. */
|
|
2884
|
+
export interface VideoAnalysisOptions { idempotencyKey: string; }
|
|
2885
|
+
export type VideoAnalysisSource =
|
|
2886
|
+
| { type: 'UPLOADED_ASSET'; assetId: string; startMs?: number; endMs?: number }
|
|
2887
|
+
| { type: 'CHAT_ATTACHMENT'; roomId: string; messageId: string; attachmentId: string; startMs?: number; endMs?: number };
|
|
2888
|
+
export interface VideoAnalysisRequest { instruction: string; locale?: string; sources: VideoAnalysisSource[]; }
|
|
2889
|
+
export interface RoomVideoAnalysisRequest { personaId: string; triggerMessageId: string; analysis: VideoAnalysisRequest; }
|
|
2890
|
+
export interface VideoAsset { assetId: string; fileName: string; mimeType: string; sizeBytes: number; durationMs: number; expiresAt: string; }
|
|
2891
|
+
export interface VideoAnalysisResult {
|
|
2892
|
+
answerMarkdown: string;
|
|
2893
|
+
evidence: { sourceId: string; startMs: number | null; endMs: number | null; modality: 'VISUAL' | 'AUDIO' | 'BOTH'; observation: string }[];
|
|
2894
|
+
limitations: string[];
|
|
2895
|
+
/** Requested temporal coverage and sampling; not a guarantee every frame was inspected. */
|
|
2896
|
+
coverage: { sourceId: string; startMs: number; endMs: number; framesPerSecond: number }[];
|
|
2897
|
+
model: string; promptVersion: string;
|
|
2898
|
+
usage: { uncachedInputTokens: number; cachedInputTokens: number; outputTokens: number; thinkingTokens: number };
|
|
2899
|
+
}
|
|
2900
|
+
export interface VideoAnalysisJob {
|
|
2901
|
+
jobId: string; status: 'QUEUED' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED'; phase: string;
|
|
2902
|
+
errorCode: string | null; detail: string | null;
|
|
2903
|
+
sources: { sourceId: string; assetId: string | null; roomId: string | null; messageId: string | null; attachmentId: string | null; startMs: number | null; endMs: number | null }[];
|
|
2904
|
+
result: VideoAnalysisResult | null; createdAt: string; deadlineAt: string;
|
|
2905
|
+
roomId: string | null; streamId: string | null; resultMessageId: string | null; deliveryPending: boolean;
|
|
2906
|
+
}
|
|
2907
|
+
export interface VideoAnalysisCapabilities {
|
|
2908
|
+
available: boolean; model: string; standaloneUploadConfigured: boolean; maxSources: number;
|
|
2909
|
+
maxBytesPerSource: number; maxTotalDurationMs: number; framesPerSecond: number; mimeTypes: string[];
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
export interface VideoAnalysisCancellation { jobId: string; status: VideoAnalysisJob['status']; deliveryPending: boolean; }
|