@vibexnpm/talkx 5.1.0 → 5.3.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.
package/README.md CHANGED
@@ -75,6 +75,7 @@ await client.connect(jwtToken, { enablePush: true });
75
75
  | [chat.md](docs/chat.md) | 채팅방 관리, 메시지 전송, 파일 업로드, 답글, 리액션, 핀, 타이핑, 채팅방 리스트 실시간 갱신, **AI 어시스턴트 페르소나 (멘션 응답)** |
76
76
  | [push.md](docs/push.md) | **웹 푸시 알림.** 권한 lifecycle, 활성화 패턴 (자동 vs 사용자 액션), 디바이스 토글, 알림 클릭 라우팅 |
77
77
  | [webrtc.md](docs/webrtc.md) | 1:1 / 그룹 통화, 미디어 제어, 디바이스 관리, TURN 서버 |
78
+ | [vote.md](docs/vote.md) | **투표.** 생성(멱등키 필수) · 투표/재투표 · 결과 마스킹 · 이미지 선택지 · 채팅 카드 렌더링 |
78
79
  | [events.md](docs/events.md) | 모든 이벤트 레퍼런스. 채팅 이벤트 처리 패턴 (`eventType` 분기 vs 상태 필드 렌더) |
79
80
  | [errors.md](docs/errors.md) | 에러 분류 (`PushErrorReason`, `ErrorTypes`), HTTP 상태 코드, 입력 제약 |
80
81
 
package/dist/index.d.ts CHANGED
@@ -753,6 +753,177 @@ export interface ChatMessage {
753
753
  decisionRequest?: DecisionRequest | null;
754
754
  /** 사용자의 선택. 어느 카드인지는 {@code replyTo} 가 특정한다. */
755
755
  decisionResponse?: DecisionResponse | null;
756
+ /**
757
+ * 투표 카드 — 투표가 열리거나 마감·취소됐음을 알리는 메시지의 payload.
758
+ *
759
+ * <p><b>모르면 그냥 텍스트로 그려라 — 그게 정상 동작이다.</b> {@code content} 에 같은 내용이
760
+ * 사람이 읽는 형태로 들어 있다. 반대로 카드를 그린다면 {@code content} 를 다시 그리지 마라.</p>
761
+ *
762
+ * <p>카드에는 집계가 없다. 결과는 {@code voteId} 로 {@link ChatClient.getVote} 를 호출해 받는다 —
763
+ * 마스킹이 호출자마다 다르게 계산되기 때문이다.</p>
764
+ */
765
+ voteCard?: VoteCard | null;
766
+ }
767
+
768
+ /**
769
+ * 투표 카드 payload — 이벤트 하나당 메시지 하나다(개설·마감·취소).
770
+ *
771
+ * <p>이미 발행된 카드는 고쳐지지 않는다. 투표가 삭제돼도 개설 카드는 그대로 남고 조회가 404 가 되며,
772
+ * 「취소됨」 표시는 화면 몫이다(별도의 취소 카드도 함께 발행된다).</p>
773
+ */
774
+ export interface VoteCard {
775
+ voteId: string;
776
+ /**
777
+ * {@code 'OPENED'} | {@code 'CLOSED'} | {@code 'CANCELED'}.
778
+ *
779
+ * <p>🔴 <b>모르는 값이 올 수 있다</b> — 서버가 나중에 종류를 늘려도 구버전이 깨지지 않도록 열린 문자열이다.
780
+ * switch 의 default 는 텍스트 폴백으로 둔다.</p>
781
+ */
782
+ kind: 'OPENED' | 'CLOSED' | 'CANCELED' | (string & {});
783
+ /**
784
+ * 발행 시점의 질문 <b>스냅샷</b>. 참여자가 0명인 동안 투표가 수정될 수 있으므로 최신값이 필요하면
785
+ * {@link ChatClient.getVote} 로 조회한다. 마감·취소 카드에서는 비어 있을 수 있다.
786
+ */
787
+ question?: string | null;
788
+ /** 마감 예정 시각 (epoch millis, UTC). 마감·취소 카드에서는 null 일 수 있다. */
789
+ closeAt?: number | null;
790
+ }
791
+
792
+ // ── 투표 (createVote / getVotes / getVote / submitVote ...) ──
793
+
794
+ /** 투표 등록 폼 — 생성과 수정이 같은 타입이다({@code PATCH} 지만 부분 수정이 아니다). */
795
+ export interface VoteForm {
796
+ /** 최대 500자. 공백만이면 거절. */
797
+ question: string;
798
+ type: 'SINGLE' | 'MULTIPLE';
799
+ /** 양수. {@code SINGLE} 이면 1. */
800
+ maxSelections: number;
801
+ /** {@code 'IMAGE'} 라는 값은 없다. */
802
+ optionType: 'TEXT' | 'TEXT_WITH_IMAGE';
803
+ /** 마감 시각 (epoch millis). 과거·현재 불가 · 최대 30일 뒤. 무기한 투표는 없다. */
804
+ closeAt: number;
805
+ /** 2~10개. */
806
+ options: VoteFormOption[];
807
+ }
808
+
809
+ export interface VoteFormOption {
810
+ /** {@code .} {@code $} NUL 사용 불가 · 최대 100자 · 중복 불가 (집계 필드명으로 쓰인다). */
811
+ id: string;
812
+ /** 최대 200자. */
813
+ content: string;
814
+ /** 표시 순서. */
815
+ order: number;
816
+ /**
817
+ * {@link ChatClient.uploadVoteImage} 가 돌려준 값을 <b>가공 없이</b> 넣는다 — 서버가 문자열로 비교한다.
818
+ * 채팅 첨부 URL 은 쓸 수 없다. {@code optionType: 'TEXT'} 면 넣으면 안 되고, 빈 문자열도 거절된다.
819
+ */
820
+ imageUrl?: string | null;
821
+ }
822
+
823
+ /** 투표 단건 + 집계 — 생성 · 수정 · 단건조회 · 접수 응답. */
824
+ export interface Vote {
825
+ voteId: string;
826
+ roomId: string;
827
+ question: string;
828
+ type: 'SINGLE' | 'MULTIPLE';
829
+ optionType: 'TEXT' | 'TEXT_WITH_IMAGE';
830
+ maxSelections: number;
831
+ /**
832
+ * {@code 'OPEN'} | {@code 'CLOSED'}.
833
+ *
834
+ * <p>🔴 <b>이 값만으로 마감을 판정하면 안 된다</b> — 서버 자동 마감이 미구현이라 {@code closeAt} 이
835
+ * 지났는데도 {@code 'OPEN'} 인 투표가 있고, 그건 마감된 것으로 동작한다. 둘을 함께 본다.</p>
836
+ */
837
+ status: 'OPEN' | 'CLOSED' | (string & {});
838
+ /** epoch millis. */
839
+ closeAt: number;
840
+ /** 마감 전이면 null. epoch millis. */
841
+ closedAt?: number | null;
842
+ creatorId: string;
843
+ createdAt: number;
844
+ updatedAt: number;
845
+ /**
846
+ * 결과 공개 여부. 공개 조건은 「내가 응답했다」 또는 「마감됐다」.
847
+ *
848
+ * <p>🔴 {@code false} 일 때의 {@code count}/{@code ratio} 0 은 「참여자가 없다」가 아니라
849
+ * <b>서버가 가린 값</b>이다. 「아직 0표」로 표시하면 거짓말이 된다.</p>
850
+ */
851
+ revealed: boolean;
852
+ /** 응답자 수. <b>득표 합이 아니다</b> — 복수 선택에서 값이 다르다. */
853
+ totalResponses: number;
854
+ /** 호출자가 고른 선택지. 없으면 빈 배열. 비공개 상태에서도 채워진다(재투표 화면용). */
855
+ myOptionIds: string[];
856
+ options: VoteOptionResult[];
857
+ }
858
+
859
+ export interface VoteOptionResult {
860
+ optionId: string;
861
+ content: string;
862
+ order: number;
863
+ /** {@code optionType} 이 {@code 'TEXT'} 면 항상 null. */
864
+ imageUrl?: string | null;
865
+ /** 득표 수. 비공개면 0(가려진 값). */
866
+ count: number;
867
+ /**
868
+ * 응답자 대비 %, 소수 첫째 자리.
869
+ *
870
+ * <p>분모가 <b>응답자 수</b>이지 총 선택 수가 아니다 — 복수 선택 투표에서는 합이 100을 넘는 것이
871
+ * 정상이고, 화면이 「복수 응답」임을 표기해야 한다.</p>
872
+ */
873
+ ratio: number;
874
+ }
875
+
876
+ /** {@link ChatClient.getVotes} 옵션. */
877
+ export interface GetVotesOptions {
878
+ /** 이전 응답의 {@code nextCursor} 를 <b>그대로</b>. 불투명 문자열이라 파싱하지 않는다. */
879
+ cursor?: string;
880
+ /** 기본 20, 최대 100. 초과분은 서버가 조용히 100 으로 보정한다(에러 아님). */
881
+ limit?: number;
882
+ }
883
+
884
+ /** {@link ChatClient.getVotes} 반환. */
885
+ export interface VoteList {
886
+ /** 최신순. 비면 끝이다. */
887
+ items: VoteListItem[];
888
+ /** null 이면 끝. 마지막 페이지가 정확히 {@code limit} 개면 채워진 채 오고, 그 커서로 부르면 빈 목록이 온다. */
889
+ nextCursor?: string | null;
890
+ }
891
+
892
+ /** 목록 항목 — {@code optionType} · {@code options} · {@code myOptionIds} · {@code revealed} 가 <b>없다</b>. */
893
+ export interface VoteListItem {
894
+ voteId: string;
895
+ question: string;
896
+ type: 'SINGLE' | 'MULTIPLE';
897
+ status: 'OPEN' | 'CLOSED' | (string & {});
898
+ closeAt: number;
899
+ creatorId: string;
900
+ createdAt: number;
901
+ /** 참여자 수. 선택지별 집계는 목록에 없다 — 마스킹이 사용자마다 다르기 때문이다. */
902
+ responseCount: number;
903
+ }
904
+
905
+ /** {@link ChatClient.createVote} 옵션. */
906
+ export interface CreateVoteOptions {
907
+ /**
908
+ * 🔴 <b>필수.</b> SDK 가 대신 만들지 않는다 — 만들면 그 값이 호출자에게 남지 않아 응답만 유실된
909
+ * 타임아웃에서 복구가 불가능하다(다음 호출이 새 키를 만들어 투표가 두 개 생긴다).
910
+ *
911
+ * <p>한 투표당 한 번 만들어 보관하고, 같은 시도의 재시도에는 <b>같은 값</b>을 넘긴다 —
912
+ * 그러면 서버가 기존 투표를 돌려준다(201, 같은 voteId).</p>
913
+ */
914
+ idempotencyKey: string;
915
+ }
916
+
917
+ /** {@link ChatClient.uploadVoteImage} 옵션. */
918
+ export interface UploadVoteImageOptions {
919
+ onProgress?: (progress: { loaded: number; total: number; percent: number }) => void;
920
+ signal?: AbortSignal;
921
+ }
922
+
923
+ /** {@link ChatClient.uploadVoteImage} 반환. */
924
+ export interface VoteImage {
925
+ /** 생성·수정 본문의 {@code options[].imageUrl} 에 <b>가공 없이</b> 넣는다. */
926
+ imageUrl: string;
756
927
  }
757
928
 
758
929
  /**
@@ -1089,6 +1260,29 @@ export interface AssistantRoomProgressEvent {
1089
1260
  textAvailable?: boolean;
1090
1261
  }
1091
1262
 
1263
+ /** {@link ChatClient.getAssistantProgress} 가 돌려주는 stream 1건. */
1264
+ export interface AssistantProgressStreamSnapshot {
1265
+ streamId: string;
1266
+ personaId: string | null;
1267
+ phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'WRITING' | null;
1268
+ label: string | null;
1269
+ text: string | null;
1270
+ textAvailable: boolean;
1271
+ /** 서버 스냅샷으로 복원된 진행분이면 true. */
1272
+ resumed: boolean;
1273
+ }
1274
+
1275
+ /**
1276
+ * 방의 **현재** AI 진행 상태 (v5.3.0 — {@link ChatClient.getAssistantProgress} 반환).
1277
+ *
1278
+ * <p>{@link AssistantRoomProgressEvent} 와 같은 형태에 stream 단위 UI 를 위한 `streams` 가 더해진다.
1279
+ * 이벤트는 transient 라 화면을 나갔다 들어오면 다시 오지 않는다 — 마운트 시 이걸 한 번 읽는다.</p>
1280
+ */
1281
+ export interface AssistantProgressSnapshot extends AssistantRoomProgressEvent {
1282
+ /** 살아 있는 stream 전체 (도착 순서). 없으면 빈 배열. */
1283
+ streams: AssistantProgressStreamSnapshot[];
1284
+ }
1285
+
1092
1286
  // ============================================================================
1093
1287
  // Options — constructor & method
1094
1288
  // ============================================================================
@@ -1721,6 +1915,24 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
1721
1915
  refreshSummary(roomId: string, options?: RefreshSummaryOptions): Promise<RefreshSummaryResult>;
1722
1916
  /** Rolling 누적 요약 조회 — rollup + segment 페이지. 저장된 요약의 단일 진실 소스. */
1723
1917
  getSummary(roomId: string, options?: GetSummaryOptions): Promise<RoomSummaryView>;
1918
+
1919
+ // ── 투표 ──
1920
+ /** 투표 생성. {@code idempotencyKey} 필수 — 재시도에 같은 값을 넘겨야 투표가 두 개 안 생긴다. */
1921
+ createVote(roomId: string, vote: VoteForm, options: CreateVoteOptions): Promise<Vote>;
1922
+ /** 방의 투표 목록(최신순). 집계는 없고 참여자 수만 있다. */
1923
+ getVotes(roomId: string, options?: GetVotesOptions): Promise<VoteList>;
1924
+ /** 투표 단건 + 집계. 마스킹이 호출자마다 다르므로 공유 캐시에 넣지 않는다. */
1925
+ getVote(roomId: string, voteId: string): Promise<Vote>;
1926
+ /** 투표 수정 — 폼 전체를 보낸다. 개설자 + 참여자 0명일 때만. */
1927
+ updateVote(roomId: string, voteId: string, vote: VoteForm): Promise<Vote>;
1928
+ /** 투표 삭제(204). 이미 발행된 카드는 남고 조회가 404 가 된다. */
1929
+ deleteVote(roomId: string, voteId: string): Promise<void>;
1930
+ /** 수동 마감(204). 결과를 보려면 {@link getVote} 를 다시 호출한다. */
1931
+ closeVote(roomId: string, voteId: string): Promise<void>;
1932
+ /** 투표 접수 · 재투표 — 같은 메서드. 응답에 갱신된 집계가 함께 온다. */
1933
+ submitVote(roomId: string, voteId: string, optionIds: string[]): Promise<Vote>;
1934
+ /** 선택지 이미지 업로드. 반환 {@code imageUrl} 을 가공 없이 폼에 넣는다. */
1935
+ uploadVoteImage(roomId: string, file: File | Blob, options?: UploadVoteImageOptions): Promise<VoteImage>;
1724
1936
  /** 방 PM 프롬프트 레이어 조회 — PM_BACKSTAGE 방 전용, 참여자 전체. 미등록 시 404. */
1725
1937
  getRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
1726
1938
  /** 방 PM 프롬프트 레이어 등록/수정 (upsert, 새 버전 자동 기록) — 방 주인(OWNER) 전용. content 1~2,000자. */
@@ -1828,6 +2040,17 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
1828
2040
 
1829
2041
  startTyping(roomId: string): void;
1830
2042
  stopTyping(roomId: string): void;
2043
+
2044
+ /**
2045
+ * 방의 현재 AI 진행 상태를 즉시 반환 (네트워크 호출 없음, v5.3.0).
2046
+ * 진행 표시 이벤트는 transient 라 방 화면 마운트 시 이걸 읽어 버블을 복원한다.
2047
+ */
2048
+ getAssistantProgress(roomId: string): AssistantProgressSnapshot;
2049
+ /**
2050
+ * 구독 중인 모든 방의 진행 스트림 스냅샷을 서버에서 다시 받는다 (v5.1.0).
2051
+ * 재연결·탭 복귀 때 SDK 가 자동 호출하므로 보통은 직접 부를 일이 없다.
2052
+ */
2053
+ resumeAssistantStreams(reason?: string): Promise<void>;
1831
2054
  }
1832
2055
 
1833
2056
  export class WebRTCClient implements SdkEventEmitter<WebRTCClientEvents> {
@@ -2089,6 +2312,24 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2089
2312
  refreshSummary(roomId: string, options?: RefreshSummaryOptions): Promise<RefreshSummaryResult>;
2090
2313
  /** Rolling 누적 요약 조회 — rollup + segment 페이지. 저장된 요약의 단일 진실 소스. */
2091
2314
  getSummary(roomId: string, options?: GetSummaryOptions): Promise<RoomSummaryView>;
2315
+
2316
+ // ── 투표 ──
2317
+ /** 투표 생성. {@code idempotencyKey} 필수 — 재시도에 같은 값을 넘겨야 투표가 두 개 안 생긴다. */
2318
+ createVote(roomId: string, vote: VoteForm, options: CreateVoteOptions): Promise<Vote>;
2319
+ /** 방의 투표 목록(최신순). 집계는 없고 참여자 수만 있다. */
2320
+ getVotes(roomId: string, options?: GetVotesOptions): Promise<VoteList>;
2321
+ /** 투표 단건 + 집계. 마스킹이 호출자마다 다르므로 공유 캐시에 넣지 않는다. */
2322
+ getVote(roomId: string, voteId: string): Promise<Vote>;
2323
+ /** 투표 수정 — 폼 전체를 보낸다. 개설자 + 참여자 0명일 때만. */
2324
+ updateVote(roomId: string, voteId: string, vote: VoteForm): Promise<Vote>;
2325
+ /** 투표 삭제(204). 이미 발행된 카드는 남고 조회가 404 가 된다. */
2326
+ deleteVote(roomId: string, voteId: string): Promise<void>;
2327
+ /** 수동 마감(204). 결과를 보려면 {@link getVote} 를 다시 호출한다. */
2328
+ closeVote(roomId: string, voteId: string): Promise<void>;
2329
+ /** 투표 접수 · 재투표 — 같은 메서드. 응답에 갱신된 집계가 함께 온다. */
2330
+ submitVote(roomId: string, voteId: string, optionIds: string[]): Promise<Vote>;
2331
+ /** 선택지 이미지 업로드. 반환 {@code imageUrl} 을 가공 없이 폼에 넣는다. */
2332
+ uploadVoteImage(roomId: string, file: File | Blob, options?: UploadVoteImageOptions): Promise<VoteImage>;
2092
2333
  /** 방 PM 프롬프트 레이어 조회 — PM_BACKSTAGE 방 전용, 참여자 전체. 미등록 시 404. */
2093
2334
  getRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
2094
2335
  /** 방 PM 프롬프트 레이어 등록/수정 (upsert, 새 버전 자동 기록) — 방 주인(OWNER) 전용. content 1~2,000자. */
@@ -2193,6 +2434,17 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2193
2434
  startTyping(roomId: string): void;
2194
2435
  stopTyping(roomId: string): void;
2195
2436
 
2437
+ /**
2438
+ * 방의 현재 AI 진행 상태를 즉시 반환 (네트워크 호출 없음, v5.3.0).
2439
+ * 진행 표시 이벤트는 transient 라 방 화면 마운트 시 이걸 읽어 버블을 복원한다.
2440
+ */
2441
+ getAssistantProgress(roomId: string): AssistantProgressSnapshot;
2442
+ /**
2443
+ * 구독 중인 모든 방의 진행 스트림 스냅샷을 서버에서 다시 받는다 (v5.1.0).
2444
+ * 재연결·탭 복귀 때 SDK 가 자동 호출하므로 보통은 직접 부를 일이 없다.
2445
+ */
2446
+ resumeAssistantStreams(reason?: string): Promise<void>;
2447
+
2196
2448
  // WebRTC delegations
2197
2449
  initializeIceServers(): Promise<RTCIceServer[]>;
2198
2450
  getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;