@vibexnpm/talkx 8.12.0 → 9.1.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/types/index.d.ts CHANGED
@@ -39,37 +39,48 @@ export const ErrorTypes: {
39
39
  readonly CHAT_SUBSCRIPTION_FAILED: 'CHAT_SUBSCRIPTION_FAILED';
40
40
  readonly MEDIA_ACCESS_DENIED: 'MEDIA_ACCESS_DENIED';
41
41
  readonly SCREEN_SHARE_DENIED: 'SCREEN_SHARE_DENIED';
42
- readonly PEER_CONNECTION_FAILED: 'PEER_CONNECTION_FAILED';
43
- readonly ICE_CONNECTION_FAILED: 'ICE_CONNECTION_FAILED';
44
- readonly SIGNALING_FAILED: 'SIGNALING_FAILED';
42
+ readonly MEDIA_CONNECTION_FAILED: 'MEDIA_CONNECTION_FAILED';
45
43
  readonly DEVICE_SWITCH_FAILED: 'DEVICE_SWITCH_FAILED';
46
44
  readonly ENUMERATE_DEVICES_FAILED: 'ENUMERATE_DEVICES_FAILED';
47
45
  readonly CALL_ROOM_NOT_FOUND: 'CALL_ROOM_NOT_FOUND';
46
+ readonly CALL_ALREADY_ACTIVE: 'CALL_ALREADY_ACTIVE';
47
+ readonly NO_ACTIVE_CALL: 'NO_ACTIVE_CALL';
48
48
  readonly INVALID_STATE: 'INVALID_STATE';
49
49
  readonly UNKNOWN_ERROR: 'UNKNOWN_ERROR';
50
50
  };
51
51
  export type ErrorTypeValue = typeof ErrorTypes[keyof typeof ErrorTypes];
52
52
 
53
- export const SignalTypes: {
54
- readonly CALL_OFFER: 'call_offer';
55
- readonly CALL_ANSWER: 'call_answer';
56
- readonly ICE_CANDIDATE: 'ice_candidate';
57
- readonly JOIN_ROOM: 'join_room';
58
- readonly LEAVE_ROOM: 'leave_room';
59
- readonly PEER_JOINED: 'peer_joined';
60
- readonly PEER_LEFT: 'peer_left';
61
- readonly VIDEO_STATE_CHANGED: 'video_state_changed';
62
- readonly AUDIO_STATE_CHANGED: 'audio_state_changed';
63
- readonly SCREEN_SHARE_STARTED: 'screen_share_started';
64
- readonly SCREEN_SHARE_ENDED: 'screen_share_ended';
65
- readonly CALL_REQUEST: 'call_request';
66
- readonly CALL_ACCEPT: 'call_accept';
67
- readonly CALL_REJECT: 'call_reject';
68
- readonly CALL_CANCEL: 'call_cancel';
69
- readonly CALL_END: 'call_end';
70
- readonly CALL_INVITATION: 'call_invitation';
71
- readonly CALL_BUSY: 'call_busy';
53
+ /**
54
+ * 서버 → 클라이언트 통화 이벤트 종류(서버 WebRTCMessageType). 클라이언트는 통화 신호를 보내지 않는다 — 통화 제어는 REST, 미디어는
55
+ * LiveKit(v9.0.0).
56
+ */
57
+ export const CallEventTypes: {
58
+ readonly INVITATION: 'call_invitation';
59
+ readonly ANSWERED: 'call_answered';
60
+ readonly REJECT: 'call_reject';
61
+ readonly CANCEL: 'call_cancel';
62
+ readonly END: 'call_end';
63
+ };
64
+
65
+ /** 통화 종류. */
66
+ export const CallKind: {
67
+ readonly DIRECT: 'DIRECT';
68
+ readonly GROUP: 'GROUP';
69
+ };
70
+ export type CallKindValue = typeof CallKind[keyof typeof CallKind];
71
+
72
+ /**
73
+ * 통화 상태 — ringing(받는 쪽 울림)·connecting(입장·미디어 접속 중)·connected·reconnecting(LiveKit 재접속 또는 «제거됨» 뒤 다시
74
+ * 입장 중)·ended.
75
+ */
76
+ export const CallState: {
77
+ readonly RINGING: 'ringing';
78
+ readonly CONNECTING: 'connecting';
79
+ readonly CONNECTED: 'connected';
80
+ readonly RECONNECTING: 'reconnecting';
81
+ readonly ENDED: 'ended';
72
82
  };
83
+ export type CallStateValue = typeof CallState[keyof typeof CallState];
73
84
 
74
85
  /**
75
86
  * SDK 가 런타임에 export 하는 ChatMessageType 상수.
@@ -573,7 +584,6 @@ export const WebSocketPaths: {
573
584
  readonly CHAT_SEND: string;
574
585
  readonly CHAT_READ: string;
575
586
  readonly CHAT_TYPING: string;
576
- readonly WEBRTC_SIGNAL: string;
577
587
  };
578
588
 
579
589
  export const DefaultConfig: {
@@ -584,7 +594,8 @@ export const DefaultConfig: {
584
594
  readonly heartbeatIncoming: number;
585
595
  readonly heartbeatOutgoing: number;
586
596
  readonly apiTimeout: number;
587
- readonly iceServers: RTCIceServer[];
597
+ /** 받는 쪽 울림을 스스로 끄는 시각(ms) — 서버 울림 상한 60초 + 여유. */
598
+ readonly callRingTimeoutMs: number;
588
599
  readonly logLevel: LogLevelValue;
589
600
  };
590
601
 
@@ -1339,6 +1350,279 @@ export interface ChatFile {
1339
1350
  file: FileMetaData;
1340
1351
  }
1341
1352
 
1353
+ /** PM 보고서 종류 — 지금은 자료조사({@code RESEARCH}) 하나. {@link PmReport.kind} 와 {@link PmReportListParams.kind} 가 같은 값. */
1354
+ export type PmReportKind = 'RESEARCH';
1355
+
1356
+ /** {@link ChatClient.getPmReports} 파라미터 — 보고서 한 페이지. */
1357
+ export interface PmReportListParams {
1358
+ /** 보고서 종류 — 생략하면 {@code RESEARCH}. 종류·방을 바꾸면 커서 없이 처음부터 부른다. */
1359
+ kind?: PmReportKind;
1360
+ /** 방 하나로 거르기. 생략하면 내가 지금 참여 중인 방 전부. 참여하지 않은 방이면 403. */
1361
+ roomId?: string;
1362
+ /** 페이지 크기. 기본 50, max 100. */
1363
+ size?: number;
1364
+ /** 직전 응답의 {@code lastId} 그대로. */
1365
+ lastId?: string;
1366
+ /** 직전 응답의 {@code lastSortValue} 그대로(epoch millis). */
1367
+ lastSortValue?: number | string;
1368
+ }
1369
+
1370
+ /**
1371
+ * PM 보고서 목록 한 줄 — 방에 PM 이 올린 작업 결과 하나.
1372
+ *
1373
+ * <p>보이는 범위는 파일함과 같다 — 참여 중인 방만, 재입장 이전·보관 기간 밖·나만 삭제한 결과는 오지 않는다.</p>
1374
+ */
1375
+ export interface PmReport {
1376
+ /** 보고서 id — 상세({@link ChatClient.getPmReport})에 넘기는 값. 목록 key 로 쓴다. */
1377
+ id: string;
1378
+ /** 결과 메시지 — {@link ChatMessage.messageId} 와 같은 값. 채팅에서 그 메시지로 이동할 때 쓴다. */
1379
+ messageId: string;
1380
+ kind: PmReportKind;
1381
+ /** 제목 — PM 이 작업을 맡긴 요지(120자). 비어 있으면 null. */
1382
+ title: string | null;
1383
+ /** 결과 앞 200자 — 마크다운 원문 그대로. */
1384
+ preview: string;
1385
+ /** 채팅에서 «작업 중 참고한 대화 일부가 수정/삭제되어…» 고지가 붙은 결과인가 — 상세 본문 위에 같은 문구가 붙는다. */
1386
+ contextChanged: boolean;
1387
+ roomId: string;
1388
+ /** 방 이름 — 1:1 은 상대 이름. 찾지 못하면 null. */
1389
+ roomName: string | null;
1390
+ roomType: ChatRoomTypeValue | null;
1391
+ /** 작업을 시킨 사람. */
1392
+ requesterId: string | null;
1393
+ /** 그 사람 닉네임 — 찾지 못하면(탈퇴 등) null. */
1394
+ requesterName: string | null;
1395
+ requesterProfileUrl: string | null;
1396
+ /** 결과를 올린 PM 페르소나 이름 — 찾지 못하면 null. */
1397
+ pmName: string | null;
1398
+ /** 결과가 방에 올라온 시각(epoch millis). */
1399
+ publishedAt: number;
1400
+ }
1401
+
1402
+ /** PM 보고서 상세 — 목록 한 줄({@link PmReport})과 같은 칸 + 본문. */
1403
+ export interface PmReportDetail extends PmReport {
1404
+ /**
1405
+ * 본문(마크다운) — 결과 전문(출처 절 포함). 채팅에서 결과에 붙었던 안내도 같은 문구로 붙는다: 대화 변경 고지(본문 위,
1406
+ * {@code contextChanged} 일 때)·이미지 분석 범위 안내(본문 끝).
1407
+ */
1408
+ content: string;
1409
+ /** 본문 글자 수(코드포인트). */
1410
+ contentLength: number;
1411
+ }
1412
+
1413
+ // ── 예약 보고 (getScheduledReports / createScheduledReport / updateScheduledReport ...) ──
1414
+
1415
+ /** 예약 작업 종류 — 자료조사({@code RESEARCH}) · 방 정리({@code ROOM_DIGEST}). 만든 뒤 못 바꾼다. */
1416
+ export type ScheduledTaskKind = 'RESEARCH' | 'ROOM_DIGEST';
1417
+
1418
+ /** 방 정리 기간 — 예정 시각에서 거슬러 올라가는 길이. */
1419
+ export type ScheduledReportPeriod = 'LAST_24_HOURS' | 'LAST_7_DAYS';
1420
+
1421
+ /** 자료조사 작업 칸 — 지시문으로 웹 조사를 맡기고, PM 이 결과를 전용 방에 보고한다. */
1422
+ export interface ResearchReportSpec {
1423
+ kind: 'RESEARCH';
1424
+ /** 지시문 (1~2,000자). */
1425
+ instruction: string;
1426
+ }
1427
+
1428
+ /** 방 정리 작업 칸 — 원천 방의 지난 기간 대화를 정리해 전용 방에 올린다. */
1429
+ export interface RoomDigestReportSpec {
1430
+ kind: 'ROOM_DIGEST';
1431
+ /** 원천 방 — {@link ChatClient.getScheduledReportSourceRooms} 에서 고른다. 만든 뒤 못 바꾼다. */
1432
+ sourceRoomId: string;
1433
+ period: ScheduledReportPeriod;
1434
+ format: SummarizeFormatValue;
1435
+ /** 특히 볼 내용 (0~500자, 선택). */
1436
+ focus?: string;
1437
+ }
1438
+
1439
+ /** 작업 칸 — {@code kind} 로 가른다. 요청과 응답이 같은 모양이다. */
1440
+ export type ScheduledReportSpec = ResearchReportSpec | RoomDigestReportSpec;
1441
+
1442
+ /** 요일 — 대문자 영어 이름 그대로. */
1443
+ export type ScheduledReportDay = 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY';
1444
+
1445
+ /** 매일({@code DAILY}) · 평일({@code WEEKDAYS}, 월~금 · 공휴일을 빼지 않는다) — 요일을 받지 않는다. */
1446
+ export interface ScheduledReportDailyCadence {
1447
+ type: 'DAILY' | 'WEEKDAYS';
1448
+ /** 보내지 않아도 된다. 요일을 넣으면 400 {@code SCHEDULED_REPORT_CADENCE_INVALID}. */
1449
+ days?: [];
1450
+ /** 시각 {@code HH:mm} — {@code zoneId} 기준. */
1451
+ time: string;
1452
+ /** IANA 시간대({@code Asia/Seoul}). 비우면 만들기는 내 프로필 시간대, 고치기는 지금 시간대. */
1453
+ zoneId?: string;
1454
+ }
1455
+
1456
+ /** 고른 요일마다({@code WEEKLY}). */
1457
+ export interface ScheduledReportWeeklyCadence {
1458
+ type: 'WEEKLY';
1459
+ /** 요일 1~7개, 중복 불가. */
1460
+ days: ScheduledReportDay[];
1461
+ /** 시각 {@code HH:mm} — {@code zoneId} 기준. */
1462
+ time: string;
1463
+ /** IANA 시간대({@code Asia/Seoul}). 비우면 만들기는 내 프로필 시간대, 고치기는 지금 시간대. */
1464
+ zoneId?: string;
1465
+ }
1466
+
1467
+ /** 요청의 주기 — {@code type} 으로 가른다. */
1468
+ export type ScheduledReportCadence = ScheduledReportDailyCadence | ScheduledReportWeeklyCadence;
1469
+
1470
+ /** 응답의 주기 — 요일은 늘 배열이고(매일·평일은 빈 배열, 주간은 월요일부터) 시간대는 늘 있다. 그대로 다시 보낼 수 있다. */
1471
+ export type ScheduledReportCadenceResponse =
1472
+ | (ScheduledReportDailyCadence & { days: []; zoneId: string })
1473
+ | (ScheduledReportWeeklyCadence & { zoneId: string });
1474
+
1475
+ /** 예약 상태 — {@code PAUSED} 면 회차를 만들지 않는다. */
1476
+ export type ScheduledReportStatus = 'ACTIVE' | 'PAUSED';
1477
+
1478
+ /** 예약이 멈춘 사유 — 만든 사람이 멈췄거나({@code USER}), 회차가 더 돌릴 수 없다고 봤다. */
1479
+ export type ScheduledReportPauseReason =
1480
+ | 'USER'
1481
+ | 'OWNER_INACTIVE'
1482
+ | 'OWNER_NOT_IN_ROOM'
1483
+ | 'ROOM_UNAVAILABLE'
1484
+ | 'PM_UNAVAILABLE'
1485
+ | 'SOURCE_UNAVAILABLE'
1486
+ | 'INVALID';
1487
+
1488
+ /** 예약 한 건 — 시각은 UTC ISO-8601 문자열({@code 2026-10-30T00:30:00Z})이다. */
1489
+ export interface ScheduledReport {
1490
+ id: string;
1491
+ title: string;
1492
+ spec: ScheduledReportSpec;
1493
+ cadence: ScheduledReportCadenceResponse;
1494
+ deliveryRoomIds: string[];
1495
+ /** 결과가 올라가는 전용 방 — 방 목록·초대는 이 id 로 기존 방 API 를 쓴다. */
1496
+ homeRoomId: string;
1497
+ status: ScheduledReportStatus;
1498
+ /** 멈췄을 때만 있다. */
1499
+ pauseReason?: ScheduledReportPauseReason;
1500
+ /** 다음 예정 시각. */
1501
+ nextRunAt: string;
1502
+ createdAt: string;
1503
+ updatedAt: string;
1504
+ }
1505
+
1506
+ /** 회차 상태 — {@code PENDING}·{@code CLAIMED}·{@code STARTED}·{@code DELIVERING} 은 진행 중, 나머지는 끝난 상태다. */
1507
+ export type ScheduledReportRunState =
1508
+ | 'PENDING'
1509
+ | 'CLAIMED'
1510
+ | 'STARTED'
1511
+ | 'DELIVERING'
1512
+ | 'DONE'
1513
+ | 'SKIPPED'
1514
+ | 'MISSED'
1515
+ | 'EXPIRED'
1516
+ | 'FAILED';
1517
+
1518
+ /** 회차가 {@code SKIPPED}·{@code FAILED} 로 끝난 사유. */
1519
+ export type ScheduledReportRunEndReason =
1520
+ | 'REPORT_DELETED'
1521
+ | 'REPORT_PAUSED'
1522
+ | 'OWNER_INACTIVE'
1523
+ | 'OWNER_NOT_IN_ROOM'
1524
+ | 'ROOM_UNAVAILABLE'
1525
+ | 'PM_UNAVAILABLE'
1526
+ | 'SOURCE_UNAVAILABLE'
1527
+ | 'NOT_SUBSCRIBED'
1528
+ | 'NO_CREDIT'
1529
+ | 'SOURCE_CHANGED'
1530
+ | 'PUBLISH_EXPIRED'
1531
+ | 'TASK_FAILED'
1532
+ | 'TASK_TIMEOUT'
1533
+ | 'START_FAILED';
1534
+
1535
+ /** 방 정리의 결과 종류 — 보고 · 새 대화 없음 · 실패 안내. */
1536
+ export type ScheduledReportResultKind = 'REPORT' | 'NO_DATA' | 'FAILURE';
1537
+
1538
+ /** 보낼 방 복사 상태. */
1539
+ export type ScheduledReportDeliveryState = 'PENDING' | 'DELIVERED' | 'SKIPPED' | 'EXPIRED';
1540
+
1541
+ /** 보낼 방 하나를 건너뛴 사유. */
1542
+ export type ScheduledReportDeliverySkipReason = 'ROOM_NOT_ELIGIBLE' | 'SOURCE_CHANGED' | 'REMOVED_FROM_REPORT' | 'REPORT_DELETED';
1543
+
1544
+ /** 보낼 방 복사 하나. */
1545
+ export interface ScheduledReportRunDelivery {
1546
+ roomId: string;
1547
+ state: ScheduledReportDeliveryState;
1548
+ /** 복사한 글 — 끝났을 때만 있다. */
1549
+ messageId?: string;
1550
+ /** 건너뛰었을 때만 있다. */
1551
+ skipReason?: ScheduledReportDeliverySkipReason;
1552
+ }
1553
+
1554
+ /** 회차 하나 — 언제 돌았고 어떻게 끝났나. */
1555
+ export interface ScheduledReportRun {
1556
+ id: string;
1557
+ /** 예정 시각 (UTC ISO-8601). */
1558
+ scheduledAt: string;
1559
+ state: ScheduledReportRunState;
1560
+ /** {@code SKIPPED}·{@code FAILED} 일 때만 있다. */
1561
+ endReason?: ScheduledReportRunEndReason;
1562
+ /** 방 정리의 확정 결과 — 자료조사이거나 확정 전이면 없다. */
1563
+ resultKind?: ScheduledReportResultKind;
1564
+ /** 결과 글 — 없으면 아직 안 올렸다. */
1565
+ resultMessageId?: string;
1566
+ deliveries: ScheduledReportRunDelivery[];
1567
+ }
1568
+
1569
+ /** {@link ChatClient.getScheduledReport} 응답 — 예약과 최근 회차(예정 늦은 순, 최대 10개). */
1570
+ export interface ScheduledReportDetail {
1571
+ report: ScheduledReport;
1572
+ recentRuns: ScheduledReportRun[];
1573
+ }
1574
+
1575
+ /** 예약 화면의 방 고르기 목록의 방 하나. */
1576
+ export interface ScheduledReportRoomOption {
1577
+ roomId: string;
1578
+ /** 화면 이름 — 1:1 은 상대 이름이고, 상대를 못 찾으면 없다. */
1579
+ name?: string;
1580
+ roomType?: ChatRoomTypeValue;
1581
+ }
1582
+
1583
+ /**
1584
+ * {@link ChatClient.getScheduledReportDeliveryRooms} 파라미터 — 고치기 화면은 {@code reportId}, 만들기 화면은 {@code kind}.
1585
+ * 방 정리는 원천 방으로만 보내므로 {@code sourceRoomId} 가 필요하다.
1586
+ */
1587
+ export type ScheduledReportDeliveryRoomParams =
1588
+ | { reportId: string }
1589
+ | { kind: 'RESEARCH' }
1590
+ | { kind: 'ROOM_DIGEST'; sourceRoomId: string };
1591
+
1592
+ /** {@link ChatClient.createScheduledReport} 폼. */
1593
+ export interface CreateScheduledReportData {
1594
+ /** 제목 (앞뒤 공백을 뺀 1~50자). 전용 방 이름으로도 쓴다. */
1595
+ title: string;
1596
+ spec: ScheduledReportSpec;
1597
+ cadence: ScheduledReportCadence;
1598
+ /** 결과를 복사할 방 (0~3개) — {@link ChatClient.getScheduledReportDeliveryRooms} 에서 고른다. 비우면 보내지 않는다. */
1599
+ deliveryRoomIds?: string[];
1600
+ }
1601
+
1602
+ /** {@link ChatClient.createScheduledReport} 옵션. */
1603
+ export interface CreateScheduledReportOptions {
1604
+ /**
1605
+ * 🔴 <b>필수.</b> SDK 가 대신 만들지 않는다 — 만들면 그 값이 호출자에게 남지 않아 응답만 유실된
1606
+ * 타임아웃에서 같은 키로 다시 보낼 수 없다(새 키는 예약과 방을 하나 더 만든다).
1607
+ *
1608
+ * <p>폼마다 한 번 만들어 보관하고, 재시도에는 <b>같은 값</b>을 넘긴다. 공백 없는 인쇄 가능 ASCII 1~200자.</p>
1609
+ */
1610
+ idempotencyKey: string;
1611
+ }
1612
+
1613
+ /**
1614
+ * {@link ChatClient.updateScheduledReport} 칸 — 고칠 칸만 보낸다. {@code spec} 은 보내면 칸 전체이고 작업 종류·원천 방은
1615
+ * 지금 값과 같아야 한다. {@code cadence} 는 보내면 주기 전체다. {@code deliveryRoomIds} 는 보내면 목록 전체다(빈 배열 = 보내지 않음).
1616
+ */
1617
+ export interface UpdateScheduledReportData {
1618
+ title?: string;
1619
+ spec?: ScheduledReportSpec;
1620
+ cadence?: ScheduledReportCadence;
1621
+ deliveryRoomIds?: string[];
1622
+ /** {@code PAUSED} = 멈추기 · {@code ACTIVE} = 다시 켜기. */
1623
+ status?: ScheduledReportStatus;
1624
+ }
1625
+
1342
1626
  // ============================================================================
1343
1627
  // Event payloads
1344
1628
  // ============================================================================
@@ -1760,8 +2044,8 @@ export interface TalkFlowClientOptions {
1760
2044
  maxSyncPages?: number;
1761
2045
  /** 갭 복구 페이지 크기. 기본 100. */
1762
2046
  syncPageSize?: number;
1763
- /** WebRTC ICE 서버. */
1764
- iceServers?: RTCIceServer[];
2047
+ /** 받는 쪽 울림을 스스로 끄는 시각(ms). 기본 65000 — 서버 «응답함»·«취소» 가 먼저 오면 그때 끈다. */
2048
+ callRingTimeoutMs?: number;
1765
2049
  /** connect() 시 채팅방 리스트 자동 구독. 기본 true. */
1766
2050
  autoSubscribeRoomList?: boolean;
1767
2051
  /** 로그 레벨. 기본 WARN. */
@@ -2089,7 +2373,7 @@ export interface RetentionCleanupPayload {
2089
2373
  * <p><b>Connection 관련</b>: stateChange, connected, disconnected, reconnecting, reconnected, resumed, subscriptionRequired, connectionError, tokenSet, loggedOut</p>
2090
2374
  * <p><b>Chat 관련 (ChatClient 이벤트를 이 이름으로 re-emit)</b>: chatMessage, newChatMessage, messageUpdated, messageDeleted, reactionChanged, linkPreviewAttached, messageRead, typing, assistantProgress, memberJoined, memberLeft, roomSubscribed, roomUnsubscribed, roomListSubscribed, roomListUnsubscribed, roomListUpdate, roomListMessage, roomListCreated, roomListJoined, roomListLeft, roomListSelfLeft, roomListRoomUpdated, aiUnavailable, retentionCleanup</p>
2091
2375
  * <p><b>Push 관련</b>: pushEnabled, pushFailed, pushNotification</p>
2092
- * <p><b>WebRTC 관련</b>: localStreamStarted, localStreamStopped, remoteTrack, screenShareStarted, screenShareEnded, deviceChange, mediaStateChanged, callStarted, callEnded, callRequested, callAccepted, callRejected, callCancelled, callInvitation, callBusy, incomingCall, incomingCallWhileBusy, userJoined, userLeft, participantLeft, participantMediaState, peerConnected, peerDisconnected, peerClosed, webrtcError</p>
2376
+ * <p><b>화상통화 관련(WebRTCClient 이벤트를 같은 이름으로 re-emit)</b>: incomingCall, ringStopped, callStateChanged, callEnded, callRejected, participantJoined, participantLeft, trackSubscribed, trackUnsubscribed, localTrackPublished, localTrackUnpublished, participantMediaChanged, callRecoveryFailed, callError</p>
2093
2377
  */
2094
2378
  export interface TalkFlowClientEvents {
2095
2379
  // Connection
@@ -2215,34 +2499,22 @@ export interface TalkFlowClientEvents {
2215
2499
  /** Gmail 묶음 푸시(4통 이상) — 포그라운드. */
2216
2500
  newMailSummary: NewMailSummaryEvent;
2217
2501
 
2218
- // WebRTC (re-emit from WebRTCClient)
2219
- localStreamStarted: unknown;
2220
- localStreamStopped: unknown;
2221
- remoteTrack: unknown;
2222
- screenShareStarted: unknown;
2223
- screenShareEnded: unknown;
2224
- deviceChange: unknown;
2225
- mediaStateChanged: unknown;
2226
- callStarted: unknown;
2227
- callEnded: unknown;
2228
- /** 그룹 통화 재참가가 불가능해 로컬 통화를 종료한 경우. */
2229
- callRecoveryFailed: { roomId: string; error: unknown };
2230
- callRequested: unknown;
2231
- callAccepted: unknown;
2232
- callRejected: unknown;
2233
- callCancelled: unknown;
2234
- callInvitation: unknown;
2235
- callBusy: unknown;
2236
- incomingCall: unknown;
2237
- incomingCallWhileBusy: unknown;
2238
- userJoined: unknown;
2239
- userLeft: unknown;
2240
- participantLeft: unknown;
2241
- participantMediaState: unknown;
2242
- peerConnected: unknown;
2243
- peerDisconnected: unknown;
2244
- peerClosed: unknown;
2245
- webrtcError: unknown;
2502
+ // 화상통화 (re-emit from WebRTCClient)
2503
+ incomingCall: IncomingCallEvent;
2504
+ ringStopped: RingStoppedEvent;
2505
+ callStateChanged: CallStateChangedEvent;
2506
+ callEnded: CallEndedEvent;
2507
+ callRejected: CallRejectedEvent;
2508
+ participantJoined: CallParticipantEvent;
2509
+ participantLeft: CallParticipantEvent;
2510
+ trackSubscribed: CallTrackEvent;
2511
+ trackUnsubscribed: CallTrackEvent;
2512
+ localTrackPublished: CallLocalTrackEvent;
2513
+ localTrackUnpublished: CallLocalTrackEvent;
2514
+ participantMediaChanged: CallParticipantMediaChangedEvent;
2515
+ /** LiveKit 재접속 실패 — 통화는 이 기기에서 끝난다(나가기는 보내지 않았다, 끊김 유예 안에 joinCall 로 돌아올 수 있다). */
2516
+ callRecoveryFailed: CallRecoveryFailedEvent;
2517
+ callError: CallErrorEvent;
2246
2518
  }
2247
2519
 
2248
2520
  /**
@@ -2308,36 +2580,24 @@ export interface ChatClientEvents {
2308
2580
  }
2309
2581
 
2310
2582
  /**
2311
- * WebRTCClient 가 직접 emit 하는 이벤트 맵.
2583
+ * WebRTCClient 가 직접 emit 하는 이벤트 맵(v9.0.0, LiveKit SFU).
2312
2584
  */
2313
2585
  export interface WebRTCClientEvents {
2314
- localStreamStarted: unknown;
2315
- localStreamStopped: unknown;
2316
- screenShareStarted: unknown;
2317
- screenShareEnded: unknown;
2318
- remoteTrack: unknown;
2319
- peerConnected: unknown;
2320
- peerDisconnected: unknown;
2321
- peerClosed: unknown;
2322
- error: unknown;
2323
- callStarted: unknown;
2324
- callEnded: unknown;
2325
- /** 그룹 통화 재참가가 불가능해 로컬 통화를 종료한 경우. */
2326
- callRecoveryFailed: { roomId: string; error: unknown };
2327
- callRequested: unknown;
2328
- callAccepted: unknown;
2329
- callRejected: unknown;
2330
- callCancelled: unknown;
2331
- callInvitation: unknown;
2332
- callBusy: unknown;
2333
- incomingCall: unknown;
2334
- incomingCallWhileBusy: unknown;
2335
- userJoined: unknown;
2336
- userLeft: unknown;
2337
- participantLeft: unknown;
2338
- participantMediaState: unknown;
2339
- mediaStateChanged: unknown;
2340
- deviceChange: unknown;
2586
+ incomingCall: IncomingCallEvent;
2587
+ ringStopped: RingStoppedEvent;
2588
+ callStateChanged: CallStateChangedEvent;
2589
+ callEnded: CallEndedEvent;
2590
+ callRejected: CallRejectedEvent;
2591
+ participantJoined: CallParticipantEvent;
2592
+ participantLeft: CallParticipantEvent;
2593
+ trackSubscribed: CallTrackEvent;
2594
+ trackUnsubscribed: CallTrackEvent;
2595
+ localTrackPublished: CallLocalTrackEvent;
2596
+ localTrackUnpublished: CallLocalTrackEvent;
2597
+ participantMediaChanged: CallParticipantMediaChangedEvent;
2598
+ /** LiveKit 재접속 실패 — 통화는 이 기기에서 끝난다(나가기는 보내지 않았다, 끊김 유예 안에 joinCall 로 돌아올 수 있다). */
2599
+ callRecoveryFailed: CallRecoveryFailedEvent;
2600
+ callError: CallErrorEvent;
2341
2601
  }
2342
2602
 
2343
2603
  // ============================================================================
@@ -2455,6 +2715,42 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2455
2715
  * 다음 페이지는 응답의 {@code lastId}·{@code lastSortValue} 를 그대로 넘긴다.
2456
2716
  */
2457
2717
  getFiles(params?: ChatFileListParams): Promise<ApiEnvelope<CursorPageResponse<ChatFile>>>;
2718
+
2719
+ /**
2720
+ * PM 보고서 목록 — 내가 지금 참여 중인 방들에 PM 이 올린 작업 결과(지금은 자료조사), 최신순 커서 페이징. 반환은
2721
+ * envelope 째({@code result.data.content}). 보이는 범위는 파일함과 같다. 다음 페이지는 응답의 {@code lastId}·
2722
+ * {@code lastSortValue} 를 그대로 넘긴다. 끝은 {@code hasNext} 로만 판단한다 — 드물게 짧거나 빈 페이지가
2723
+ * {@code hasNext: true} 와 함께 온다.
2724
+ */
2725
+ getPmReports(params?: PmReportListParams): Promise<ApiEnvelope<CursorPageResponse<PmReport>>>;
2726
+
2727
+ /** PM 보고서 상세 — 목록 한 줄 + 본문({@code content}). 없거나 볼 수 없으면 404. 반환은 envelope 째. */
2728
+ getPmReport(reportId: string): Promise<ApiEnvelope<PmReportDetail>>;
2729
+
2730
+ /** 내가 만든 예약 목록(만든 순). */
2731
+ getScheduledReports(): Promise<ScheduledReport[]>;
2732
+
2733
+ /** 예약 하나와 최근 회차(예정 늦은 순, 최대 10개). 없거나 남의 예약이면 404. */
2734
+ getScheduledReport(reportId: string): Promise<ScheduledReportDetail>;
2735
+
2736
+ /** 지금 새로 만들 수 있는 작업 종류. 키에 PM 권한이 없거나 일시정지 중인 종류는 빠진다 — 빈 배열이면 만들기를 막는다. */
2737
+ getScheduledReportKinds(): Promise<ScheduledTaskKind[]>;
2738
+
2739
+ /** 방 정리의 원천 방 고르기 — 내가 지금 참여 중이고 메시지 보관 기간이 없는 방. */
2740
+ getScheduledReportSourceRooms(): Promise<ScheduledReportRoomOption[]>;
2741
+
2742
+ /** 보낼 방 고르기 — 만들기·고치기와 같은 판정으로 거른 방. */
2743
+ getScheduledReportDeliveryRooms(params: ScheduledReportDeliveryRoomParams): Promise<ScheduledReportRoomOption[]>;
2744
+
2745
+ /** 예약 만들기 — 전용 방을 만들고 예약을 저장한다. {@code idempotencyKey} 필수 — 재시도에 같은 값을 넘겨야 예약과 방이 두 개 안 생긴다. */
2746
+ createScheduledReport(report: CreateScheduledReportData, options: CreateScheduledReportOptions): Promise<ScheduledReport>;
2747
+
2748
+ /** 예약 고치기·멈추기·다시 켜기 — 고칠 칸만 보낸다. 바꾼 예약을 돌려준다. */
2749
+ updateScheduledReport(reportId: string, changes: UpdateScheduledReportData): Promise<ScheduledReport>;
2750
+
2751
+ /** 예약 지우기 — 예약만 지우고 전용 방은 남긴다. */
2752
+ deleteScheduledReport(reportId: string): Promise<void>;
2753
+
2458
2754
  fetchLinkPreview(url: string): Promise<FetchLinkPreviewResponse | null>;
2459
2755
  sendMessage(roomId: string, data: SendMessageData): Promise<SendMessageResult>;
2460
2756
  /** Optimistic UI 버전 — 응답 전 base/예측 messageIds 동기 반환. */
@@ -2539,13 +2835,190 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2539
2835
  resumeAssistantStreams(reason?: string): Promise<void>;
2540
2836
  }
2541
2837
 
2542
- /** POST /api/v1/webrtc/rooms 요청. 생략한 값은 서버 기본값을 사용합니다. */
2543
- export interface CreateCallRoomRequest {
2544
- invitedUsers?: string[];
2838
+ /** 미디어 선택 — 생략하면 카메라·마이크 둘 다 켠다. */
2839
+ export interface CallMediaOptions {
2840
+ video?: boolean;
2841
+ audio?: boolean;
2842
+ }
2843
+
2844
+ /** 그룹 통화 열기. 생략한 값은 서버 기본값을 쓴다. */
2845
+ export interface CreateGroupCallOptions extends CallMediaOptions {
2846
+ invitedUsers: string[];
2545
2847
  title?: string;
2546
2848
  maxParticipants?: number;
2547
2849
  }
2548
2850
 
2851
+ /**
2852
+ * 통화가 끝난 사유 — 서버 사유(ended_by_host·cancelled·declined·busy·no_answer·participant_left·connection_lost·media_room_closed·
2853
+ * empty·expired), 이 기기의 사유(left 나감·replaced 같은 사용자가 다른 기기로 들어옴·removed·room_deleted·room_closed·failed),
2854
+ * 또는 받지 않고 끝난 울림의 사유({@link RingStopReason} 중 accepted 를 뺀 것).
2855
+ */
2856
+ export type CallEndReason =
2857
+ | 'ended_by_host' | 'cancelled' | 'declined' | 'busy' | 'no_answer' | 'participant_left' | 'connection_lost'
2858
+ | 'media_room_closed' | 'empty' | 'expired'
2859
+ | 'left' | 'replaced' | 'removed' | 'room_deleted' | 'room_closed' | 'failed'
2860
+ | 'rejected' | 'answered_elsewhere' | 'timeout' | 'stale' | 'ended'
2861
+ | (string & {});
2862
+
2863
+ /**
2864
+ * 울림이 멈춘 사유 — accepted(이 기기에서 받음)·rejected(이 기기에서 거절)·answered_elsewhere(같은 사용자의 다른 기기가 받았거나
2865
+ * 그룹 통화를 거절함)·timeout(울림 상한)·stale(다시 물으니 더는 울리지 않음)·ended(통화가 없어졌거나 이 기기의 로그인 신원이 끝남)
2866
+ * 또는 서버 사유(취소·종료).
2867
+ * accepted 가 아니면 같은 사유로 callStateChanged(ended)·callEnded 도 온다.
2868
+ */
2869
+ export type RingStopReason =
2870
+ | 'accepted' | 'rejected' | 'answered_elsewhere' | 'timeout' | 'stale' | 'ended'
2871
+ | 'cancelled' | 'no_answer' | 'declined' | 'busy' | 'ended_by_host' | 'empty' | 'expired' | 'media_room_closed'
2872
+ | (string & {});
2873
+
2874
+ /** 트랙 출처. */
2875
+ export type CallTrackSource = 'camera' | 'microphone' | 'screen' | 'screen_audio' | 'unknown';
2876
+
2877
+ /** 울림 시작. 이 리스너 안에서 바로 거절·받으면 그 통화의 `callStateChanged`(ringing) 은 오지 않는다. */
2878
+ export interface IncomingCallEvent {
2879
+ callRoomId: string;
2880
+ kind: CallKindValue | null;
2881
+ callerId: string | null;
2882
+ callerName: string | null;
2883
+ callerProfileUrl: string | null;
2884
+ title: string | null;
2885
+ }
2886
+
2887
+ export interface RingStoppedEvent {
2888
+ callRoomId: string;
2889
+ reason: RingStopReason;
2890
+ }
2891
+
2892
+ export interface CallStateChangedEvent {
2893
+ callRoomId: string;
2894
+ state: CallStateValue;
2895
+ /** ended 이면 끝난 사유, reconnecting 이면 removed(서버가 연결을 뺐다)일 수 있다. */
2896
+ reason?: CallEndReason;
2897
+ }
2898
+
2899
+ export interface CallEndedEvent {
2900
+ callRoomId: string;
2901
+ reason: CallEndReason;
2902
+ }
2903
+
2904
+ /** 방장에게 — 초대 대상의 거절(declined·busy)·무응답(no_answer). 1:1 이면 통화도 끝난다(callEnded). */
2905
+ export interface CallRejectedEvent {
2906
+ callRoomId: string;
2907
+ userId: string | null;
2908
+ reason: string | null;
2909
+ }
2910
+
2911
+ export interface CallParticipantEvent {
2912
+ callRoomId: string;
2913
+ userId: string;
2914
+ }
2915
+
2916
+ /**
2917
+ * 화면에 붙이는 미디어 트랙 — livekit-client 의 Track 가운데 SDK 가 약속하는 부분이다(실제 값은 RemoteTrack·LocalTrack). livekit-client
2918
+ * 타입을 그대로 내보내지 않는다 — 그 판에 묶이지 않게.
2919
+ */
2920
+ export interface CallMediaTrack {
2921
+ /** 'audio' · 'video' */
2922
+ readonly kind: string;
2923
+ readonly mediaStreamTrack: MediaStreamTrack;
2924
+ /** 새 &lt;video&gt;/&lt;audio&gt; 요소를 만들어 붙이고 돌려준다. */
2925
+ attach(): HTMLMediaElement;
2926
+ /** 주어진 요소에 붙인다. */
2927
+ attach(element: HTMLMediaElement): HTMLMediaElement;
2928
+ /** 붙인 요소 전부에서 떼고 그 요소들을 돌려준다. */
2929
+ detach(): HTMLMediaElement[];
2930
+ /** 주어진 요소에서 뗀다. */
2931
+ detach(element: HTMLMediaElement): HTMLMediaElement;
2932
+ }
2933
+
2934
+ export interface CallTrackEvent {
2935
+ callRoomId: string;
2936
+ userId: string;
2937
+ source: CallTrackSource;
2938
+ /** 상대의 트랙 — {@code track.attach()} 로 화면에 붙인다. */
2939
+ track: CallMediaTrack;
2940
+ }
2941
+
2942
+ /**
2943
+ * 내 트랙 게시·내림 — 카메라·마이크의 게시는 그 장치의 지금 트랙만 온다(재접속 중 다시 켜 새 트랙이 생기면 늦게 다시 게시되는 옛
2944
+ * 트랙은 오지 않는다). 게시가 온 트랙은 내려질 때(그 사이 새 트랙으로 바뀌었어도) 짝으로 내림이 온다.
2945
+ */
2946
+ export interface CallLocalTrackEvent {
2947
+ callRoomId: string;
2948
+ source: CallTrackSource;
2949
+ /** 내 트랙 — 미리보기용. */
2950
+ track: CallMediaTrack;
2951
+ }
2952
+
2953
+ /** 참가자 미디어 상태 — 내 것이면 userId 는 null. */
2954
+ export interface CallParticipantMedia {
2955
+ userId: string | null;
2956
+ camera: boolean;
2957
+ microphone: boolean;
2958
+ screen: boolean;
2959
+ }
2960
+
2961
+ export interface CallParticipantMediaChangedEvent extends CallParticipantMedia {
2962
+ callRoomId: string;
2963
+ }
2964
+
2965
+ export interface CallRecoveryFailedEvent {
2966
+ callRoomId: string;
2967
+ reason: string;
2968
+ }
2969
+
2970
+ export interface CallErrorEvent {
2971
+ callRoomId: string;
2972
+ code: ErrorTypeValue | string;
2973
+ message: string;
2974
+ error?: unknown;
2975
+ }
2976
+
2977
+ export interface CurrentCall {
2978
+ /** 방을 만드는 중(callUser·createGroupCall 이 서버 응답을 기다리는 동안)이면 null. */
2979
+ callRoomId: string | null;
2980
+ kind: CallKindValue | null;
2981
+ state: CallStateValue | null;
2982
+ participants: CallParticipantMedia[];
2983
+ localMedia: { camera: boolean; microphone: boolean; screen: boolean };
2984
+ }
2985
+
2986
+ export interface RingingCall {
2987
+ callRoomId: string;
2988
+ kind: CallKindValue | null;
2989
+ callerId: string | null;
2990
+ title: string | null;
2991
+ }
2992
+
2993
+ /** GET /api/v1/webrtc/rooms/{id}/participation */
2994
+ export interface CallParticipation {
2995
+ callRoomId: string;
2996
+ participating: boolean;
2997
+ /** 초대 대상의 울림 상태. 방장·초대 밖이면 null. */
2998
+ ring: 'RINGING' | 'ANSWERED' | 'DECLINED' | 'BUSY' | 'NO_ANSWER' | (string & {}) | null;
2999
+ }
3000
+
3001
+ /** GET /api/v1/webrtc/rooms/{id} */
3002
+ export interface CallRoomInfo {
3003
+ callRoomId: string;
3004
+ kind: CallKindValue;
3005
+ status: 'CREATED' | 'ACTIVE' | 'ENDED' | 'EXPIRED' | (string & {});
3006
+ version: number;
3007
+ hostUserId: string;
3008
+ invitedUsers: string[];
3009
+ title: string | null;
3010
+ /** ISO-8601 */
3011
+ createdAt: string;
3012
+ /** ISO-8601 */
3013
+ expiresAt: string;
3014
+ maxParticipants: number | null;
3015
+ projectId: string;
3016
+ }
3017
+
3018
+ /**
3019
+ * 화상통화 클라이언트(v9.0.0) — 통화 제어는 서버 REST, 미디어는 LiveKit(SFU), 서버 알림은 STOMP. 클라이언트는 통화 신호를 보내지
3020
+ * 않는다. 한 번에 한 통화만 미디어에 붙는다.
3021
+ */
2549
3022
  export class WebRTCClient implements SdkEventEmitter<WebRTCClientEvents> {
2550
3023
  on<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): () => void;
2551
3024
  once<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): () => void;
@@ -2554,44 +3027,102 @@ export class WebRTCClient implements SdkEventEmitter<WebRTCClientEvents> {
2554
3027
  removeAllListeners(event?: keyof WebRTCClientEvents): void;
2555
3028
  listenerCount(event: keyof WebRTCClientEvents): number;
2556
3029
 
2557
- initializeIceServers(): Promise<RTCIceServer[]>;
2558
- getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;
2559
-
2560
- createCallRoom(data: CreateCallRoomRequest): Promise<unknown>;
2561
- getCallRoom(roomId: string): Promise<unknown>;
2562
- joinCallRoomApi(roomId: string): Promise<unknown>;
2563
- leaveCallRoomApi(roomId: string): Promise<unknown>;
2564
-
2565
- /** 수신 call 구독 활성화 — 내부 WebSocket subscribe 가 async. */
3030
+ /** 통화 알림 수신(개인 큐 구독) — connect() 가 부른다. */
2566
3031
  enableIncomingCalls(): Promise<void>;
2567
3032
  disableIncomingCalls(): void;
2568
3033
  isIncomingCallsEnabled(): boolean;
2569
3034
 
2570
- startCall(options: { targetUserId?: string; roomId?: string; mediaConstraints?: MediaStreamConstraints }): Promise<void>;
2571
- callUser(targetUserId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
2572
- acceptCall(callerId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
2573
- rejectCall(callerId: string): void;
2574
- cancelCall(): void;
2575
- endCall(): void;
2576
-
2577
- toggleVideo(): boolean;
2578
- toggleAudio(): boolean;
2579
- setVideoEnabled(enabled: boolean): void;
2580
- setAudioEnabled(enabled: boolean): void;
2581
- startScreenShare(): Promise<MediaStream>;
2582
- stopScreenShare(): void;
2583
- getLocalStream(): MediaStream | null;
2584
-
2585
- getDevices(): Promise<{ videoInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[] }>;
2586
- switchDevice(deviceId: string, kind: 'video' | 'audio' | 'audiooutput'): Promise<void>;
2587
- startDeviceChangeDetection(): void;
2588
- stopDeviceChangeDetection(): void;
2589
- applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
2590
- getVideoSettings(): MediaTrackSettings | null;
2591
- getAudioSettings(): MediaTrackSettings | null;
2592
-
3035
+ /**
3036
+ * 1:1 통화를 건다 — 방 만들기 → 입장 → 미디어 접속. 반환: callRoomId. 방을 만드는 중에 이 기기의 로그인 신원이 끝나거나(로그아웃·
3037
+ * 전환) destroy 되면 `NO_ACTIVE_CALL` 로 끝난다. destroy 뒤에는 `INVALID_STATE`.
3038
+ */
3039
+ callUser(targetUserId: string, media?: CallMediaOptions): Promise<string>;
3040
+ /** 그룹 통화를 연다. 반환: callRoomId. 끝나는 경우는 callUser 와 같다. */
3041
+ createGroupCall(options: CreateGroupCallOptions): Promise<string>;
3042
+ /**
3043
+ * 울리는 통화를 받는다 — 이 기기 울림을 끄고 입장한다. 입장 중에 통화가 끝나면(상대 취소 등) 던지지 않고 callEnded 로 알린다.
3044
+ */
3045
+ acceptCall(callRoomId: string, media?: CallMediaOptions): Promise<string>;
3046
+ /** 그룹 통화에 들어가거나 다시 들어간다 — 나간 뒤·재접속 실패 뒤에도 통화가 이어지면 다시 들어갈 수 있다. */
3047
+ joinCall(callRoomId: string, media?: CallMediaOptions): Promise<string>;
3048
+ /** 울리는 통화를 거절한다. 기본 DECLINED. callRoomId 가 없으면 요청 없이 던진다. */
3049
+ rejectCall(callRoomId: string, options?: { reason?: 'DECLINED' | 'BUSY' }): Promise<void>;
3050
+ /** 건 통화를 취소한다(방장, 상대 입장 전). 생략하면 지금 통화. 방을 만드는 중이면 만든 뒤 취소한다. */
3051
+ cancelCall(callRoomId?: string): Promise<void>;
3052
+ /**
3053
+ * 지금 통화에서 나간다 — 이 기기의 미디어 끊기를 시작하고 같은 틱에 서버에 나가기를 보낸다(끊기를 기다리지 않는다 — 누른 순간의
3054
+ * 신원으로 나간다). 서버 요청이 실패하면 던지지만 통화는 이미 끝났다(callEnded left). 방을 만드는 중이면 만든 뒤 끝내고 취소를
3055
+ * 보낸다 — 나가기·취소·모두 끝내기 중 처음 누른 것이 정한다. 울리던 초대를 받고 입장 전에 나가면 서버 초대를 거절(DECLINED)로
3056
+ * 정리한다. 로그인 신원이 바뀌어 재시도를 멈추면 `INVALID_STATE`.
3057
+ */
3058
+ leaveCall(): Promise<void>;
3059
+ /** 모두의 통화를 끝낸다(방장). */
3060
+ endCallForAll(): Promise<void>;
3061
+
3062
+ /**
3063
+ * 카메라를 켜고 끈다 — 통화가 있으면 언제든. 미디어가 붙기 전(방을 만드는 중·입장 중·미디어 서버 접속 중)에 부르면 의도만
3064
+ * 기록하고 바로 끝나며, 붙으면 그 의도로 켜고 끈다. 붙은 뒤에는 장치가 그 상태가 되면 끝난다(켜기는 권한 응답 뒤) — 그 사이 다시
3065
+ * 부르면 마지막 의도로 맞추고 앞선 호출은 그대로 끝난다. 끄기·한 번 켠 장치 다시 켜기(음소거 풀기)는 다른 장치의 권한 창을 기다리지
3066
+ * 않는다(처음 켜기만 권한 창을 하나씩 띄우려 줄에 서고, 줄에서 기다리는 사이 끄면 바로 끝난다). 그 의도가 실패하면(권한
3067
+ * 거부) `MEDIA_ACCESS_DENIED` — 통화가 기억하는 켜짐·꺼짐은 실제 상태로 돌아가고(앞선 누름도 실패했으면 그것이 아니라) 다시
3068
+ * 부르면 다시 시도한다. 통화가 끝나면 기다리던 호출도 끝난다. 통화가 없으면 `NO_ACTIVE_CALL`. 켜 둔 장치가 빠져 LiveKit 이 다시
3069
+ * 열지 못하면(스스로 음소거하거나 재접속 뒤 다시 올리지 못하면) 그 장치를 끈 것으로 보고 `callError`(`MEDIA_ACCESS_DENIED`)로 알린다
3070
+ * — 통화도 꺼짐으로 기억해 다시 입장해도 저절로 켜지지 않는다. 꺼진 카메라는 장치를 쥐지 않고, 게시 없이 열린 채 남은 트랙(재접속
3071
+ * 뒤 다시 열고 게시만 못 함)은 끄면(늦게 다시 열려도) 멈추고 다시 켜면 새 트랙으로 바뀐다. 재접속의 다시 게시가 옛 트랙을 다시 여는
3072
+ * 사이 다시 켜 새 트랙이 생기면 옛 트랙은 늦게 열리거나 게시돼도 멈추고 내린다(장치마다 게시는 하나, 꺼짐이면 보내는 트랙 없음).
3073
+ * 통화가 끝나면 그 연결이 연 카메라·마이크를 모두 그 자리에서 멈춘다(게시를 기다리는 중이어도, 권한 응답이 통화가 끝난 뒤에 와도).
3074
+ */
3075
+ setCameraEnabled(enabled: boolean): Promise<void>;
3076
+ /** 마이크를 켜고 끈다 — `setCameraEnabled` 와 같은 규칙. 장치가 빠져 끝난 마이크는 켤 때 다시 연다(못 열면 그 켜기가 실패한다). */
3077
+ setMicrophoneEnabled(enabled: boolean): Promise<void>;
3078
+ /**
3079
+ * 화면 공유 — 미디어 접속이 끝난 뒤에만(공유 창은 사용자 조작 안에서만 열려 미룰 수 없다). 그 전이면 `INVALID_STATE`, 거부는
3080
+ * `SCREEN_SHARE_DENIED`. 마지막 누름대로 맞춘다 — 켜져 있으면 마지막에 고른 화면 하나만 게시된다. 공유 중이 아니면 공유 창을 바로
3081
+ * 열고 고른 화면(함께 고른 소리까지)이 게시되면 끝나며, 공유 창이 떠 있는 동안 다시 부르면 그 창의 결과를 같이 기다린다(창은 하나, 공유 중이면 새 창을
3082
+ * 열지 않는다). 공유 창을 취소하거나 게시가 실패하면 그 사이 대체된 누름은 그대로 끝나고 마지막 누름만 거부된다(게시를 시도한 쪽이
3083
+ * 앞선 누름·재접속 뒤 정리여도 — 기다리는 누름이 없으면 `callError`, 이미 게시된 영상·소리도 내린다 — 끈 화면의 늦은 실패는 알리지 않는다). 재접속 중에는 SDK 가
3084
+ * 게시하지 않는다 — 공유 중이던 화면이면 바로 끝나고, 새로 고른 화면은 다시 붙은 뒤(LiveKit 의 다시 게시가 끝난 뒤) 게시하고
3085
+ * 끝난다(다른 트랙의 다시 게시 실패는 화면 공유를 끄지 않는다). 재접속 중에 다시
3086
+ * 고르면 다시 게시된 옛 화면은 내린다. 통화가 끝나면 기다리지 않고 `INVALID_STATE` 로 끝난다(화면 공유는 다시 입장한 연결로 넘어가지
3087
+ * 않는다).
3088
+ */
3089
+ startScreenShare(): Promise<void>;
3090
+ /**
3091
+ * 화면 공유를 멈춘다 — `startScreenShare` 와 같은 규칙. 고른 화면을 그 자리에서 멈추고(공유 표시가 바로 꺼진다) 게시를 내린 뒤
3092
+ * 끝난다. 공유 창이 떠 있어도 바로 끝나고 그 뒤 고른 화면은 게시하지 않는다. 재접속 중에 멈춰도 다시 게시된 화면까지 내리고, LiveKit
3093
+ * 이 다시 게시하는 중이어도, 게시가 아직 끝나지 않았어도(협상·재접속 대기) 기다리지 않는다 — 늦게 붙은 화면은 바로 내린다.
3094
+ */
3095
+ stopScreenShare(): Promise<void>;
3096
+ /** 장치 목록 — 통화 전에도 부를 수 있다. */
3097
+ getDevices(kind?: MediaDeviceKind): Promise<MediaDeviceInfo[]>;
3098
+ /**
3099
+ * 장치를 바꾼다 — 통화가 있으면 언제든. 미디어가 붙기 전이면 기억했다가 붙을 때 그 장치로, 다시 입장해도 그 장치로 붙는다. 카메라·
3100
+ * 마이크는 그 장치의 켜고 끄기·앞선 바꾸기와 부른 순서대로 하나씩 옮긴다 — 켜는 중(권한 창·획득)이면 켜기가 끝난 뒤 옮기고 끝나며,
3101
+ * 켜는 중에 끄고 고르면 끈 뒤 옮기며, 옮기는 중에 끈 카메라는 옮긴 뒤 꺼진다(마이크 음소거는 기다리지 않는다). 켜져 있는 장치를 못
3102
+ * 바꾸면(같은 장치를 다시 골라도 다시 연다) `DEVICE_SWITCH_FAILED`(또는 false) — 쓰던 장치로 한 번 다시 열고 기억도 지금 쓰는 장치가
3103
+ * 된다(앞선 선택도 실패했으면 그것이 아니라 — 쓰던 장치로도 못 돌아가거나 쓰던 장치를 모르면 그 장치를 끈 뒤 `callError` 로 알린다 — 바로 다시 켜면 다시 연다, 끄는 사이
3104
+ * 다시 켜거나 끄면 그 누름이 이기고 알리지 않는다). 꺼진
3105
+ * 카메라·음소거한 마이크는 바꿀 때 열지 않고 켤 때 연다 — 그때 못 열면 그 켜기가 `MEDIA_ACCESS_DENIED`(`cause` 에 원래 오류)로
3106
+ * 실패한다. 스피커도 부른 순서대로 하나씩 바꾼다 — 상대 소리 요소마다 바꾸고 모두 끝난 뒤 판정해, 일부만 바뀌면 바뀐 요소를 쓰던
3107
+ * 출력으로 되돌린 뒤 실패한다(되돌리지도 못하면 그 `DEVICE_SWITCH_FAILED` 에 `outputMismatch: true` — 일부 소리가 고른 출력에 남았고
3108
+ * 기억은 쓰던 출력이다, 다시 고르면 모두 맞춘다). 전체 재접속 중(다시 붙었어도 LiveKit 이 다시 게시하는 중이면 그 끝까지)에 고른
3109
+ * 카메라·마이크는 그 끝을 기다려 옮기고 그때 끝난다(그동안 옮기면 다시 게시가 옛 장치로 열려 기억과 실제가 어긋난다 — 기다리는 동안
3110
+ * 그 장치의 켜고 끄기는 막지 않고, 기다리는 사이 다시 고르면 마지막에 고른 장치로만 옮기며, 스피커는 기다리지 않는다). 장치를 여는 중·다시 맞추기를 기다리는 중에 통화가 끝나면 기다리지 않고
3111
+ * true 로 끝난다(고른 장치는 기억한다). LiveKit 이 스스로 장치를 옮기면(빠진 장치 대신 다른 장치로 다시 열기·기기 변화로 입력·출력
3112
+ * 전환, 꺼진 카메라·음소거한 마이크에 적어 둔 다음 켜기 장치 — 꺼진 채 바꾼 장치는 재접속 뒤에도 그 장치) 고른 장치 기억도 따라간다 — 다시 입장해도 없는 장치로 붙지 않는다(바꾸는 중이면 그
3113
+ * 바꾸기의 결과가 기억이다, 고른 장치가 없으면 기본 장치 그대로).
3114
+ */
3115
+ switchDevice(kind: MediaDeviceKind, deviceId: string): Promise<boolean>;
3116
+
3117
+ getCallRoom(callRoomId: string): Promise<CallRoomInfo>;
3118
+ /** 내 참가 상태 — ring 이 RINGING 이면 아직 울리는 중. */
3119
+ getParticipation(callRoomId: string): Promise<CallParticipation>;
2593
3120
  isInCall(): boolean;
2594
- getCurrentRoom(): string | null;
3121
+ getCurrentCall(): CurrentCall | null;
3122
+ getRingingCalls(): RingingCall[];
3123
+
3124
+ setLogLevel(level: LogLevelValue): void;
3125
+ destroy(): void;
2595
3126
  }
2596
3127
 
2597
3128
  /** 푸시 디바이스 응답 (내 디바이스 목록). deviceToken 은 보안상 미포함. */
@@ -2948,6 +3479,42 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2948
3479
  * 다음 페이지는 응답의 {@code lastId}·{@code lastSortValue} 를 그대로 넘긴다.
2949
3480
  */
2950
3481
  getFiles(params?: ChatFileListParams): Promise<ApiEnvelope<CursorPageResponse<ChatFile>>>;
3482
+
3483
+ /**
3484
+ * PM 보고서 목록 — 내가 지금 참여 중인 방들에 PM 이 올린 작업 결과(지금은 자료조사), 최신순 커서 페이징. 반환은
3485
+ * envelope 째({@code result.data.content}). 보이는 범위는 파일함과 같다. 다음 페이지는 응답의 {@code lastId}·
3486
+ * {@code lastSortValue} 를 그대로 넘긴다. 끝은 {@code hasNext} 로만 판단한다 — 드물게 짧거나 빈 페이지가
3487
+ * {@code hasNext: true} 와 함께 온다.
3488
+ */
3489
+ getPmReports(params?: PmReportListParams): Promise<ApiEnvelope<CursorPageResponse<PmReport>>>;
3490
+
3491
+ /** PM 보고서 상세 — 목록 한 줄 + 본문({@code content}). 없거나 볼 수 없으면 404. 반환은 envelope 째. */
3492
+ getPmReport(reportId: string): Promise<ApiEnvelope<PmReportDetail>>;
3493
+
3494
+ /** 내가 만든 예약 목록(만든 순). */
3495
+ getScheduledReports(): Promise<ScheduledReport[]>;
3496
+
3497
+ /** 예약 하나와 최근 회차(예정 늦은 순, 최대 10개). 없거나 남의 예약이면 404. */
3498
+ getScheduledReport(reportId: string): Promise<ScheduledReportDetail>;
3499
+
3500
+ /** 지금 새로 만들 수 있는 작업 종류. 키에 PM 권한이 없거나 일시정지 중인 종류는 빠진다 — 빈 배열이면 만들기를 막는다. */
3501
+ getScheduledReportKinds(): Promise<ScheduledTaskKind[]>;
3502
+
3503
+ /** 방 정리의 원천 방 고르기 — 내가 지금 참여 중이고 메시지 보관 기간이 없는 방. */
3504
+ getScheduledReportSourceRooms(): Promise<ScheduledReportRoomOption[]>;
3505
+
3506
+ /** 보낼 방 고르기 — 만들기·고치기와 같은 판정으로 거른 방. */
3507
+ getScheduledReportDeliveryRooms(params: ScheduledReportDeliveryRoomParams): Promise<ScheduledReportRoomOption[]>;
3508
+
3509
+ /** 예약 만들기 — 전용 방을 만들고 예약을 저장한다. {@code idempotencyKey} 필수 — 재시도에 같은 값을 넘겨야 예약과 방이 두 개 안 생긴다. */
3510
+ createScheduledReport(report: CreateScheduledReportData, options: CreateScheduledReportOptions): Promise<ScheduledReport>;
3511
+
3512
+ /** 예약 고치기·멈추기·다시 켜기 — 고칠 칸만 보낸다. 바꾼 예약을 돌려준다. */
3513
+ updateScheduledReport(reportId: string, changes: UpdateScheduledReportData): Promise<ScheduledReport>;
3514
+
3515
+ /** 예약 지우기 — 예약만 지우고 전용 방은 남긴다. */
3516
+ deleteScheduledReport(reportId: string): Promise<void>;
3517
+
2951
3518
  fetchLinkPreview(url: string): Promise<FetchLinkPreviewResponse | null>;
2952
3519
  sendMessage(roomId: string, data: SendMessageData): Promise<SendMessageResult>;
2953
3520
  /** Optimistic UI 버전 — 응답 전 base/예측 messageIds 동기 반환. */
@@ -3027,45 +3594,100 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
3027
3594
  */
3028
3595
  resumeAssistantStreams(reason?: string): Promise<void>;
3029
3596
 
3030
- // WebRTC delegations
3031
- initializeIceServers(): Promise<RTCIceServer[]>;
3032
- getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;
3033
-
3034
- createCallRoom(data: CreateCallRoomRequest): Promise<unknown>;
3035
- getCallRoom(roomId: string): Promise<unknown>;
3036
- joinCallRoomApi(roomId: string): Promise<unknown>;
3037
- leaveCallRoomApi(roomId: string): Promise<unknown>;
3038
-
3039
- /** 수신 call 구독 활성화 — 내부 WebSocket subscribe 가 async. */
3597
+ // 화상통화 delegations (→ client.webrtc)
3598
+ /** 통화 알림 수신(개인 큐 구독) — connect() 가 부른다. */
3040
3599
  enableIncomingCalls(): Promise<void>;
3041
3600
  disableIncomingCalls(): void;
3042
3601
  isIncomingCallsEnabled(): boolean;
3043
3602
 
3044
- startCall(options: { targetUserId?: string; roomId?: string; mediaConstraints?: MediaStreamConstraints }): Promise<void>;
3045
- callUser(targetUserId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
3046
- acceptCall(callerId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
3047
- rejectCall(callerId: string): void;
3048
- cancelCall(): void;
3049
- endCall(): void;
3050
-
3051
- toggleVideo(): boolean;
3052
- toggleAudio(): boolean;
3053
- setVideoEnabled(enabled: boolean): void;
3054
- setAudioEnabled(enabled: boolean): void;
3055
- startScreenShare(): Promise<MediaStream>;
3056
- stopScreenShare(): void;
3057
- getLocalStream(): MediaStream | null;
3058
-
3059
- getDevices(): Promise<{ videoInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[] }>;
3060
- switchDevice(deviceId: string, kind: 'video' | 'audio' | 'audiooutput'): Promise<void>;
3061
- startDeviceChangeDetection(): void;
3062
- stopDeviceChangeDetection(): void;
3063
- applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
3064
- getVideoSettings(): MediaTrackSettings | null;
3065
- getAudioSettings(): MediaTrackSettings | null;
3066
-
3603
+ /**
3604
+ * 1:1 통화를 건다 — 방 만들기 → 입장 → 미디어 접속. 반환: callRoomId. 방을 만드는 중에 이 기기의 로그인 신원이 끝나거나(로그아웃·
3605
+ * 전환) destroy 되면 `NO_ACTIVE_CALL` 로 끝난다. destroy 뒤에는 `INVALID_STATE`.
3606
+ */
3607
+ callUser(targetUserId: string, media?: CallMediaOptions): Promise<string>;
3608
+ /** 그룹 통화를 연다. 반환: callRoomId. 끝나는 경우는 callUser 와 같다. */
3609
+ createGroupCall(options: CreateGroupCallOptions): Promise<string>;
3610
+ /**
3611
+ * 울리는 통화를 받는다 — 이 기기 울림을 끄고 입장한다. 입장 중에 통화가 끝나면(상대 취소 등) 던지지 않고 callEnded 로 알린다.
3612
+ */
3613
+ acceptCall(callRoomId: string, media?: CallMediaOptions): Promise<string>;
3614
+ /** 그룹 통화에 들어가거나 다시 들어간다 — 나간 뒤·재접속 실패 뒤에도 통화가 이어지면 다시 들어갈 수 있다. */
3615
+ joinCall(callRoomId: string, media?: CallMediaOptions): Promise<string>;
3616
+ /** 울리는 통화를 거절한다. 기본 DECLINED. callRoomId 가 없으면 요청 없이 던진다. */
3617
+ rejectCall(callRoomId: string, options?: { reason?: 'DECLINED' | 'BUSY' }): Promise<void>;
3618
+ /** 건 통화를 취소한다(방장, 상대 입장 전). 생략하면 지금 통화. 방을 만드는 중이면 만든 뒤 취소한다. */
3619
+ cancelCall(callRoomId?: string): Promise<void>;
3620
+ /**
3621
+ * 지금 통화에서 나간다 — 이 기기의 미디어 끊기를 시작하고 같은 틱에 서버에 나가기를 보낸다(끊기를 기다리지 않는다 — 누른 순간의
3622
+ * 신원으로 나간다). 서버 요청이 실패하면 던지지만 통화는 이미 끝났다(callEnded left). 방을 만드는 중이면 만든 뒤 끝내고 취소를
3623
+ * 보낸다 — 나가기·취소·모두 끝내기 중 처음 누른 것이 정한다. 울리던 초대를 받고 입장 전에 나가면 서버 초대를 거절(DECLINED)로
3624
+ * 정리한다. 로그인 신원이 바뀌어 재시도를 멈추면 `INVALID_STATE`.
3625
+ */
3626
+ leaveCall(): Promise<void>;
3627
+ /** 모두의 통화를 끝낸다(방장). */
3628
+ endCallForAll(): Promise<void>;
3629
+
3630
+ /**
3631
+ * 카메라를 켜고 끈다 — 통화가 있으면 언제든. 미디어가 붙기 전(방을 만드는 중·입장 중·미디어 서버 접속 중)에 부르면 의도만
3632
+ * 기록하고 바로 끝나며, 붙으면 그 의도로 켜고 끈다. 붙은 뒤에는 장치가 그 상태가 되면 끝난다(켜기는 권한 응답 뒤) — 그 사이 다시
3633
+ * 부르면 마지막 의도로 맞추고 앞선 호출은 그대로 끝난다. 끄기·한 번 켠 장치 다시 켜기(음소거 풀기)는 다른 장치의 권한 창을 기다리지
3634
+ * 않는다(처음 켜기만 권한 창을 하나씩 띄우려 줄에 서고, 줄에서 기다리는 사이 끄면 바로 끝난다). 그 의도가 실패하면(권한
3635
+ * 거부) `MEDIA_ACCESS_DENIED` — 통화가 기억하는 켜짐·꺼짐은 실제 상태로 돌아가고(앞선 누름도 실패했으면 그것이 아니라) 다시
3636
+ * 부르면 다시 시도한다. 통화가 끝나면 기다리던 호출도 끝난다. 통화가 없으면 `NO_ACTIVE_CALL`. 켜 둔 장치가 빠져 LiveKit 이 다시
3637
+ * 열지 못하면(스스로 음소거하거나 재접속 뒤 다시 올리지 못하면) 그 장치를 끈 것으로 보고 `callError`(`MEDIA_ACCESS_DENIED`)로 알린다
3638
+ * — 통화도 꺼짐으로 기억해 다시 입장해도 저절로 켜지지 않는다. 꺼진 카메라는 장치를 쥐지 않고, 게시 없이 열린 채 남은 트랙(재접속
3639
+ * 뒤 다시 열고 게시만 못 함)은 끄면(늦게 다시 열려도) 멈추고 다시 켜면 새 트랙으로 바뀐다. 재접속의 다시 게시가 옛 트랙을 다시 여는
3640
+ * 사이 다시 켜 새 트랙이 생기면 옛 트랙은 늦게 열리거나 게시돼도 멈추고 내린다(장치마다 게시는 하나, 꺼짐이면 보내는 트랙 없음).
3641
+ * 통화가 끝나면 그 연결이 연 카메라·마이크를 모두 그 자리에서 멈춘다(게시를 기다리는 중이어도, 권한 응답이 통화가 끝난 뒤에 와도).
3642
+ */
3643
+ setCameraEnabled(enabled: boolean): Promise<void>;
3644
+ /** 마이크를 켜고 끈다 — `setCameraEnabled` 와 같은 규칙. 장치가 빠져 끝난 마이크는 켤 때 다시 연다(못 열면 그 켜기가 실패한다). */
3645
+ setMicrophoneEnabled(enabled: boolean): Promise<void>;
3646
+ /**
3647
+ * 화면 공유 — 미디어 접속이 끝난 뒤에만(공유 창은 사용자 조작 안에서만 열려 미룰 수 없다). 그 전이면 `INVALID_STATE`, 거부는
3648
+ * `SCREEN_SHARE_DENIED`. 마지막 누름대로 맞춘다 — 켜져 있으면 마지막에 고른 화면 하나만 게시된다. 공유 중이 아니면 공유 창을 바로
3649
+ * 열고 고른 화면(함께 고른 소리까지)이 게시되면 끝나며, 공유 창이 떠 있는 동안 다시 부르면 그 창의 결과를 같이 기다린다(창은 하나, 공유 중이면 새 창을
3650
+ * 열지 않는다). 공유 창을 취소하거나 게시가 실패하면 그 사이 대체된 누름은 그대로 끝나고 마지막 누름만 거부된다(게시를 시도한 쪽이
3651
+ * 앞선 누름·재접속 뒤 정리여도 — 기다리는 누름이 없으면 `callError`, 이미 게시된 영상·소리도 내린다 — 끈 화면의 늦은 실패는 알리지 않는다). 재접속 중에는 SDK 가
3652
+ * 게시하지 않는다 — 공유 중이던 화면이면 바로 끝나고, 새로 고른 화면은 다시 붙은 뒤(LiveKit 의 다시 게시가 끝난 뒤) 게시하고
3653
+ * 끝난다(다른 트랙의 다시 게시 실패는 화면 공유를 끄지 않는다). 재접속 중에 다시
3654
+ * 고르면 다시 게시된 옛 화면은 내린다. 통화가 끝나면 기다리지 않고 `INVALID_STATE` 로 끝난다(화면 공유는 다시 입장한 연결로 넘어가지
3655
+ * 않는다).
3656
+ */
3657
+ startScreenShare(): Promise<void>;
3658
+ /**
3659
+ * 화면 공유를 멈춘다 — `startScreenShare` 와 같은 규칙. 고른 화면을 그 자리에서 멈추고(공유 표시가 바로 꺼진다) 게시를 내린 뒤
3660
+ * 끝난다. 공유 창이 떠 있어도 바로 끝나고 그 뒤 고른 화면은 게시하지 않는다. 재접속 중에 멈춰도 다시 게시된 화면까지 내리고, LiveKit
3661
+ * 이 다시 게시하는 중이어도, 게시가 아직 끝나지 않았어도(협상·재접속 대기) 기다리지 않는다 — 늦게 붙은 화면은 바로 내린다.
3662
+ */
3663
+ stopScreenShare(): Promise<void>;
3664
+ /** 장치 목록 — 통화 전에도 부를 수 있다. */
3665
+ getDevices(kind?: MediaDeviceKind): Promise<MediaDeviceInfo[]>;
3666
+ /**
3667
+ * 장치를 바꾼다 — 통화가 있으면 언제든. 미디어가 붙기 전이면 기억했다가 붙을 때 그 장치로, 다시 입장해도 그 장치로 붙는다. 카메라·
3668
+ * 마이크는 그 장치의 켜고 끄기·앞선 바꾸기와 부른 순서대로 하나씩 옮긴다 — 켜는 중(권한 창·획득)이면 켜기가 끝난 뒤 옮기고 끝나며,
3669
+ * 켜는 중에 끄고 고르면 끈 뒤 옮기며, 옮기는 중에 끈 카메라는 옮긴 뒤 꺼진다(마이크 음소거는 기다리지 않는다). 켜져 있는 장치를 못
3670
+ * 바꾸면(같은 장치를 다시 골라도 다시 연다) `DEVICE_SWITCH_FAILED`(또는 false) — 쓰던 장치로 한 번 다시 열고 기억도 지금 쓰는 장치가
3671
+ * 된다(앞선 선택도 실패했으면 그것이 아니라 — 쓰던 장치로도 못 돌아가거나 쓰던 장치를 모르면 그 장치를 끈 뒤 `callError` 로 알린다 — 바로 다시 켜면 다시 연다, 끄는 사이
3672
+ * 다시 켜거나 끄면 그 누름이 이기고 알리지 않는다). 꺼진
3673
+ * 카메라·음소거한 마이크는 바꿀 때 열지 않고 켤 때 연다 — 그때 못 열면 그 켜기가 `MEDIA_ACCESS_DENIED`(`cause` 에 원래 오류)로
3674
+ * 실패한다. 스피커도 부른 순서대로 하나씩 바꾼다 — 상대 소리 요소마다 바꾸고 모두 끝난 뒤 판정해, 일부만 바뀌면 바뀐 요소를 쓰던
3675
+ * 출력으로 되돌린 뒤 실패한다(되돌리지도 못하면 그 `DEVICE_SWITCH_FAILED` 에 `outputMismatch: true` — 일부 소리가 고른 출력에 남았고
3676
+ * 기억은 쓰던 출력이다, 다시 고르면 모두 맞춘다). 전체 재접속 중(다시 붙었어도 LiveKit 이 다시 게시하는 중이면 그 끝까지)에 고른
3677
+ * 카메라·마이크는 그 끝을 기다려 옮기고 그때 끝난다(그동안 옮기면 다시 게시가 옛 장치로 열려 기억과 실제가 어긋난다 — 기다리는 동안
3678
+ * 그 장치의 켜고 끄기는 막지 않고, 기다리는 사이 다시 고르면 마지막에 고른 장치로만 옮기며, 스피커는 기다리지 않는다). 장치를 여는 중·다시 맞추기를 기다리는 중에 통화가 끝나면 기다리지 않고
3679
+ * true 로 끝난다(고른 장치는 기억한다). LiveKit 이 스스로 장치를 옮기면(빠진 장치 대신 다른 장치로 다시 열기·기기 변화로 입력·출력
3680
+ * 전환, 꺼진 카메라·음소거한 마이크에 적어 둔 다음 켜기 장치 — 꺼진 채 바꾼 장치는 재접속 뒤에도 그 장치) 고른 장치 기억도 따라간다 — 다시 입장해도 없는 장치로 붙지 않는다(바꾸는 중이면 그
3681
+ * 바꾸기의 결과가 기억이다, 고른 장치가 없으면 기본 장치 그대로).
3682
+ */
3683
+ switchDevice(kind: MediaDeviceKind, deviceId: string): Promise<boolean>;
3684
+
3685
+ getCallRoom(callRoomId: string): Promise<CallRoomInfo>;
3686
+ /** 내 참가 상태 — ring 이 RINGING 이면 아직 울리는 중. */
3687
+ getParticipation(callRoomId: string): Promise<CallParticipation>;
3067
3688
  isInCall(): boolean;
3068
- getCurrentRoom(): string | null;
3689
+ getCurrentCall(): CurrentCall | null;
3690
+ getRingingCalls(): RingingCall[];
3069
3691
 
3070
3692
  // Cleanup
3071
3693
  destroy(): Promise<void>;
@@ -3107,8 +3729,6 @@ export class Logger {
3107
3729
  }
3108
3730
 
3109
3731
  export class ConnectionManager {}
3110
- export class MediaStreamManager {}
3111
- export class PeerConnectionManager {}
3112
3732
 
3113
3733
  /**
3114
3734
  * 방 미독 카운트 계산기 (READ_SYNC_DESIGN §6).