@vibexnpm/talkx 8.13.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
 
@@ -1399,6 +1410,219 @@ export interface PmReportDetail extends PmReport {
1399
1410
  contentLength: number;
1400
1411
  }
1401
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
+
1402
1626
  // ============================================================================
1403
1627
  // Event payloads
1404
1628
  // ============================================================================
@@ -1820,8 +2044,8 @@ export interface TalkFlowClientOptions {
1820
2044
  maxSyncPages?: number;
1821
2045
  /** 갭 복구 페이지 크기. 기본 100. */
1822
2046
  syncPageSize?: number;
1823
- /** WebRTC ICE 서버. */
1824
- iceServers?: RTCIceServer[];
2047
+ /** 받는 쪽 울림을 스스로 끄는 시각(ms). 기본 65000 — 서버 «응답함»·«취소» 가 먼저 오면 그때 끈다. */
2048
+ callRingTimeoutMs?: number;
1825
2049
  /** connect() 시 채팅방 리스트 자동 구독. 기본 true. */
1826
2050
  autoSubscribeRoomList?: boolean;
1827
2051
  /** 로그 레벨. 기본 WARN. */
@@ -2149,7 +2373,7 @@ export interface RetentionCleanupPayload {
2149
2373
  * <p><b>Connection 관련</b>: stateChange, connected, disconnected, reconnecting, reconnected, resumed, subscriptionRequired, connectionError, tokenSet, loggedOut</p>
2150
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>
2151
2375
  * <p><b>Push 관련</b>: pushEnabled, pushFailed, pushNotification</p>
2152
- * <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>
2153
2377
  */
2154
2378
  export interface TalkFlowClientEvents {
2155
2379
  // Connection
@@ -2275,34 +2499,22 @@ export interface TalkFlowClientEvents {
2275
2499
  /** Gmail 묶음 푸시(4통 이상) — 포그라운드. */
2276
2500
  newMailSummary: NewMailSummaryEvent;
2277
2501
 
2278
- // WebRTC (re-emit from WebRTCClient)
2279
- localStreamStarted: unknown;
2280
- localStreamStopped: unknown;
2281
- remoteTrack: unknown;
2282
- screenShareStarted: unknown;
2283
- screenShareEnded: unknown;
2284
- deviceChange: unknown;
2285
- mediaStateChanged: unknown;
2286
- callStarted: unknown;
2287
- callEnded: unknown;
2288
- /** 그룹 통화 재참가가 불가능해 로컬 통화를 종료한 경우. */
2289
- callRecoveryFailed: { roomId: string; error: unknown };
2290
- callRequested: unknown;
2291
- callAccepted: unknown;
2292
- callRejected: unknown;
2293
- callCancelled: unknown;
2294
- callInvitation: unknown;
2295
- callBusy: unknown;
2296
- incomingCall: unknown;
2297
- incomingCallWhileBusy: unknown;
2298
- userJoined: unknown;
2299
- userLeft: unknown;
2300
- participantLeft: unknown;
2301
- participantMediaState: unknown;
2302
- peerConnected: unknown;
2303
- peerDisconnected: unknown;
2304
- peerClosed: unknown;
2305
- 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;
2306
2518
  }
2307
2519
 
2308
2520
  /**
@@ -2368,36 +2580,24 @@ export interface ChatClientEvents {
2368
2580
  }
2369
2581
 
2370
2582
  /**
2371
- * WebRTCClient 가 직접 emit 하는 이벤트 맵.
2583
+ * WebRTCClient 가 직접 emit 하는 이벤트 맵(v9.0.0, LiveKit SFU).
2372
2584
  */
2373
2585
  export interface WebRTCClientEvents {
2374
- localStreamStarted: unknown;
2375
- localStreamStopped: unknown;
2376
- screenShareStarted: unknown;
2377
- screenShareEnded: unknown;
2378
- remoteTrack: unknown;
2379
- peerConnected: unknown;
2380
- peerDisconnected: unknown;
2381
- peerClosed: unknown;
2382
- error: unknown;
2383
- callStarted: unknown;
2384
- callEnded: unknown;
2385
- /** 그룹 통화 재참가가 불가능해 로컬 통화를 종료한 경우. */
2386
- callRecoveryFailed: { roomId: string; error: unknown };
2387
- callRequested: unknown;
2388
- callAccepted: unknown;
2389
- callRejected: unknown;
2390
- callCancelled: unknown;
2391
- callInvitation: unknown;
2392
- callBusy: unknown;
2393
- incomingCall: unknown;
2394
- incomingCallWhileBusy: unknown;
2395
- userJoined: unknown;
2396
- userLeft: unknown;
2397
- participantLeft: unknown;
2398
- participantMediaState: unknown;
2399
- mediaStateChanged: unknown;
2400
- 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;
2401
2601
  }
2402
2602
 
2403
2603
  // ============================================================================
@@ -2526,6 +2726,31 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2526
2726
 
2527
2727
  /** PM 보고서 상세 — 목록 한 줄 + 본문({@code content}). 없거나 볼 수 없으면 404. 반환은 envelope 째. */
2528
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
+
2529
2754
  fetchLinkPreview(url: string): Promise<FetchLinkPreviewResponse | null>;
2530
2755
  sendMessage(roomId: string, data: SendMessageData): Promise<SendMessageResult>;
2531
2756
  /** Optimistic UI 버전 — 응답 전 base/예측 messageIds 동기 반환. */
@@ -2610,13 +2835,190 @@ export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
2610
2835
  resumeAssistantStreams(reason?: string): Promise<void>;
2611
2836
  }
2612
2837
 
2613
- /** POST /api/v1/webrtc/rooms 요청. 생략한 값은 서버 기본값을 사용합니다. */
2614
- export interface CreateCallRoomRequest {
2615
- 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[];
2616
2847
  title?: string;
2617
2848
  maxParticipants?: number;
2618
2849
  }
2619
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
+ */
2620
3022
  export class WebRTCClient implements SdkEventEmitter<WebRTCClientEvents> {
2621
3023
  on<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): () => void;
2622
3024
  once<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): () => void;
@@ -2625,44 +3027,102 @@ export class WebRTCClient implements SdkEventEmitter<WebRTCClientEvents> {
2625
3027
  removeAllListeners(event?: keyof WebRTCClientEvents): void;
2626
3028
  listenerCount(event: keyof WebRTCClientEvents): number;
2627
3029
 
2628
- initializeIceServers(): Promise<RTCIceServer[]>;
2629
- getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;
2630
-
2631
- createCallRoom(data: CreateCallRoomRequest): Promise<unknown>;
2632
- getCallRoom(roomId: string): Promise<unknown>;
2633
- joinCallRoomApi(roomId: string): Promise<unknown>;
2634
- leaveCallRoomApi(roomId: string): Promise<unknown>;
2635
-
2636
- /** 수신 call 구독 활성화 — 내부 WebSocket subscribe 가 async. */
3030
+ /** 통화 알림 수신(개인 큐 구독) — connect() 가 부른다. */
2637
3031
  enableIncomingCalls(): Promise<void>;
2638
3032
  disableIncomingCalls(): void;
2639
3033
  isIncomingCallsEnabled(): boolean;
2640
3034
 
2641
- startCall(options: { targetUserId?: string; roomId?: string; mediaConstraints?: MediaStreamConstraints }): Promise<void>;
2642
- callUser(targetUserId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
2643
- acceptCall(callerId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
2644
- rejectCall(callerId: string): void;
2645
- cancelCall(): void;
2646
- endCall(): void;
2647
-
2648
- toggleVideo(): boolean;
2649
- toggleAudio(): boolean;
2650
- setVideoEnabled(enabled: boolean): void;
2651
- setAudioEnabled(enabled: boolean): void;
2652
- startScreenShare(): Promise<MediaStream>;
2653
- stopScreenShare(): void;
2654
- getLocalStream(): MediaStream | null;
2655
-
2656
- getDevices(): Promise<{ videoInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[] }>;
2657
- switchDevice(deviceId: string, kind: 'video' | 'audio' | 'audiooutput'): Promise<void>;
2658
- startDeviceChangeDetection(): void;
2659
- stopDeviceChangeDetection(): void;
2660
- applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
2661
- getVideoSettings(): MediaTrackSettings | null;
2662
- getAudioSettings(): MediaTrackSettings | null;
2663
-
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>;
2664
3120
  isInCall(): boolean;
2665
- getCurrentRoom(): string | null;
3121
+ getCurrentCall(): CurrentCall | null;
3122
+ getRingingCalls(): RingingCall[];
3123
+
3124
+ setLogLevel(level: LogLevelValue): void;
3125
+ destroy(): void;
2666
3126
  }
2667
3127
 
2668
3128
  /** 푸시 디바이스 응답 (내 디바이스 목록). deviceToken 은 보안상 미포함. */
@@ -3030,6 +3490,31 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
3030
3490
 
3031
3491
  /** PM 보고서 상세 — 목록 한 줄 + 본문({@code content}). 없거나 볼 수 없으면 404. 반환은 envelope 째. */
3032
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
+
3033
3518
  fetchLinkPreview(url: string): Promise<FetchLinkPreviewResponse | null>;
3034
3519
  sendMessage(roomId: string, data: SendMessageData): Promise<SendMessageResult>;
3035
3520
  /** Optimistic UI 버전 — 응답 전 base/예측 messageIds 동기 반환. */
@@ -3109,45 +3594,100 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
3109
3594
  */
3110
3595
  resumeAssistantStreams(reason?: string): Promise<void>;
3111
3596
 
3112
- // WebRTC delegations
3113
- initializeIceServers(): Promise<RTCIceServer[]>;
3114
- getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;
3115
-
3116
- createCallRoom(data: CreateCallRoomRequest): Promise<unknown>;
3117
- getCallRoom(roomId: string): Promise<unknown>;
3118
- joinCallRoomApi(roomId: string): Promise<unknown>;
3119
- leaveCallRoomApi(roomId: string): Promise<unknown>;
3120
-
3121
- /** 수신 call 구독 활성화 — 내부 WebSocket subscribe 가 async. */
3597
+ // 화상통화 delegations (→ client.webrtc)
3598
+ /** 통화 알림 수신(개인 큐 구독) — connect() 가 부른다. */
3122
3599
  enableIncomingCalls(): Promise<void>;
3123
3600
  disableIncomingCalls(): void;
3124
3601
  isIncomingCallsEnabled(): boolean;
3125
3602
 
3126
- startCall(options: { targetUserId?: string; roomId?: string; mediaConstraints?: MediaStreamConstraints }): Promise<void>;
3127
- callUser(targetUserId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
3128
- acceptCall(callerId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
3129
- rejectCall(callerId: string): void;
3130
- cancelCall(): void;
3131
- endCall(): void;
3132
-
3133
- toggleVideo(): boolean;
3134
- toggleAudio(): boolean;
3135
- setVideoEnabled(enabled: boolean): void;
3136
- setAudioEnabled(enabled: boolean): void;
3137
- startScreenShare(): Promise<MediaStream>;
3138
- stopScreenShare(): void;
3139
- getLocalStream(): MediaStream | null;
3140
-
3141
- getDevices(): Promise<{ videoInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[] }>;
3142
- switchDevice(deviceId: string, kind: 'video' | 'audio' | 'audiooutput'): Promise<void>;
3143
- startDeviceChangeDetection(): void;
3144
- stopDeviceChangeDetection(): void;
3145
- applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
3146
- getVideoSettings(): MediaTrackSettings | null;
3147
- getAudioSettings(): MediaTrackSettings | null;
3148
-
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>;
3149
3688
  isInCall(): boolean;
3150
- getCurrentRoom(): string | null;
3689
+ getCurrentCall(): CurrentCall | null;
3690
+ getRingingCalls(): RingingCall[];
3151
3691
 
3152
3692
  // Cleanup
3153
3693
  destroy(): Promise<void>;
@@ -3189,8 +3729,6 @@ export class Logger {
3189
3729
  }
3190
3730
 
3191
3731
  export class ConnectionManager {}
3192
- export class MediaStreamManager {}
3193
- export class PeerConnectionManager {}
3194
3732
 
3195
3733
  /**
3196
3734
  * 방 미독 카운트 계산기 (READ_SYNC_DESIGN §6).