@things-factory/ai-assistant 10.1.125 → 10.1.129

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.
Files changed (41) hide show
  1. package/client/components/assistant-chat.ts +122 -68
  2. package/client/components/chat-echo-dedup.ts +2 -1
  3. package/client/components/reply-message.test.ts +15 -0
  4. package/client/components/reply-message.ts +11 -0
  5. package/client/components/roster-identity.test.ts +18 -0
  6. package/client/components/roster-identity.ts +10 -0
  7. package/dist-client/components/assistant-chat.d.ts +7 -0
  8. package/dist-client/components/assistant-chat.js +127 -68
  9. package/dist-client/components/assistant-chat.js.map +1 -1
  10. package/dist-client/components/chat-echo-dedup.d.ts +1 -0
  11. package/dist-client/components/chat-echo-dedup.js +1 -0
  12. package/dist-client/components/chat-echo-dedup.js.map +1 -1
  13. package/dist-client/components/reply-message.d.ts +5 -0
  14. package/dist-client/components/reply-message.js +14 -0
  15. package/dist-client/components/reply-message.js.map +1 -0
  16. package/dist-client/components/reply-message.test.d.ts +1 -0
  17. package/dist-client/components/reply-message.test.js +16 -0
  18. package/dist-client/components/reply-message.test.js.map +1 -0
  19. package/dist-client/components/roster-identity.d.ts +11 -0
  20. package/dist-client/components/roster-identity.js +11 -0
  21. package/dist-client/components/roster-identity.js.map +1 -0
  22. package/dist-client/components/roster-identity.test.d.ts +1 -0
  23. package/dist-client/components/roster-identity.test.js +20 -0
  24. package/dist-client/components/roster-identity.test.js.map +1 -0
  25. package/dist-client/tsconfig.tsbuildinfo +1 -1
  26. package/dist-server/service/chat-session-participant/read-roster.d.ts +9 -0
  27. package/dist-server/service/chat-session-participant/read-roster.js +15 -0
  28. package/dist-server/service/chat-session-participant/read-roster.js.map +1 -0
  29. package/dist-server/service/chat-session-resolver.d.ts +1 -1
  30. package/dist-server/service/chat-session-resolver.js +15 -11
  31. package/dist-server/service/chat-session-resolver.js.map +1 -1
  32. package/dist-server/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +3 -3
  34. package/server/service/chat-session-participant/read-roster.test.ts +18 -0
  35. package/server/service/chat-session-participant/read-roster.ts +14 -0
  36. package/server/service/chat-session-resolver.ts +11 -8
  37. package/translations/en.json +10 -2
  38. package/translations/ja.json +1 -1
  39. package/translations/ko.json +10 -2
  40. package/translations/ms.json +1 -1
  41. package/translations/zh.json +1 -1
@@ -1,5 +1,7 @@
1
1
  var OxAssistantChat_1;
2
2
  import { __decorate, __metadata } from "tslib";
3
+ import { replyDestination, quotedReply } from './reply-message.js';
4
+ import { rosterInviteState, rosterName } from './roster-identity';
3
5
  /**
4
6
  * <ox-assistant-chat> — AI 도우미 채팅 컴포넌트 (Lit).
5
7
  *
@@ -180,8 +182,8 @@ const DOMAIN_USERS_FOR_MENTION_QUERY = gql `
180
182
  }
181
183
  `;
182
184
  const SAY_TO_ROOM_MUTATION = gql `
183
- mutation SayToRoom($sessionId: String!, $content: String!) {
184
- sayToRoom(sessionId: $sessionId, content: $content)
185
+ mutation SayToRoom($sessionId: String!, $content: String!, $replyToId: String) {
186
+ sayToRoom(sessionId: $sessionId, content: $content, replyToId: $replyToId)
185
187
  }
186
188
  `;
187
189
  const CHAT_MESSAGES_QUERY = gql `
@@ -198,6 +200,7 @@ const CHAT_MESSAGES_QUERY = gql `
198
200
  createdAt
199
201
  redacted
200
202
  creator {
203
+ id
201
204
  name
202
205
  email
203
206
  }
@@ -220,6 +223,7 @@ const CHAT_MESSAGE_SUBSCRIPTION = gql `
220
223
  createdAt
221
224
  redacted
222
225
  creator {
226
+ id
223
227
  name
224
228
  email
225
229
  }
@@ -236,6 +240,7 @@ const CHAT_PARTICIPANTS_QUERY = gql `
236
240
  user {
237
241
  id
238
242
  name
243
+ username
239
244
  }
240
245
  }
241
246
  }
@@ -248,16 +253,6 @@ const TOUCH_PRESENCE_MUTATION = gql `
248
253
  // heartbeat 주기(ms) — 서버 PRESENCE_TTL_MS(75s)의 ~1/2.5. 로컬 online 판정도 동일 TTL.
249
254
  const PRESENCE_HEARTBEAT_MS = 30_000;
250
255
  const PRESENCE_TTL_MS = 75_000;
251
- /** 이름 → roster chip 이니셜 (한글 1자 / 영문 최대 2자). */
252
- function initials(name) {
253
- const n = (name || '?').trim();
254
- if (!n)
255
- return '?';
256
- const parts = n.split(/\s+/);
257
- if (parts.length >= 2)
258
- return (parts[0][0] + parts[1][0]).toUpperCase();
259
- return /[a-zA-Z]/.test(n) ? n.slice(0, 2).toUpperCase() : n.slice(0, 1);
260
- }
261
256
  let OxAssistantChat = class OxAssistantChat extends LitElement {
262
257
  constructor() {
263
258
  super(...arguments);
@@ -316,6 +311,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
316
311
  this.sendBlocked = '';
317
312
  /** C4 presence/roster — 세션 참여자 목록. lastSeenAt 으로 online 판정. */
318
313
  this.participants = [];
314
+ this.rosterLoaded = false;
319
315
  /** roster online 재평가 트리거 — heartbeat tick 마다 갱신(시간 경과 반영). */
320
316
  this._presenceTick = 0;
321
317
  /** 이미 transcript 에 반영된 서버 메시지 id 집합 — 자기 에코·중복 방송 차단. */
@@ -898,6 +894,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
898
894
  font-size: 10px;
899
895
  background: var(--md-sys-color-surface-variant, #f1f5f9);
900
896
  }
897
+ .roster-name { max-width:120px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
901
898
  .roster-chip.offline {
902
899
  opacity: 0.45;
903
900
  }
@@ -2407,6 +2404,21 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
2407
2404
 
2408
2405
  /* ── Input area ───────────────────────────────────── */
2409
2406
  /* border-top 은 .composer 가 담당 (actions-row 가 같은 wrapper 안) */
2407
+ .reply-preview {
2408
+ display: flex; gap: 8px; margin: 4px 14px; padding: 8px 10px;
2409
+ border-left: 3px solid var(--md-sys-color-primary);
2410
+ background: var(--md-sys-color-surface-container, #f1f5f9); border-radius: 6px;
2411
+ }
2412
+ .reply-preview > div { flex: 1; min-width: 0; }
2413
+ .reply-preview strong { display: block; font-size: 12px; }
2414
+ .reply-preview span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
2415
+ .reply-preview button { border: 0; background: transparent; color: inherit; cursor: pointer; }
2416
+ .send-destination {
2417
+ font-size: 11px;
2418
+ color: var(--md-sys-color-on-surface-variant, #475569);
2419
+ align-self: center;
2420
+ max-width: 64px;
2421
+ }
2410
2422
  .input-row {
2411
2423
  display: flex;
2412
2424
  gap: 8px;
@@ -2482,15 +2494,15 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
2482
2494
  --md-icon-size: 15px;
2483
2495
  }
2484
2496
 
2485
- /* ── Send button — ChatGPT 스타일 원형 ↑ (.input-row 안의 button 만) ─── */
2497
+ /* One send control, aligned with the resting input height. */
2486
2498
  .input-row > button {
2487
- width: 32px;
2488
- height: 32px;
2499
+ width: 54px;
2500
+ height: 54px;
2489
2501
  padding: 0;
2490
2502
  background: var(--md-sys-color-primary, #1e293b);
2491
2503
  color: var(--md-sys-color-on-primary, #ffffff);
2492
2504
  border: 0;
2493
- border-radius: 50%;
2505
+ border-radius: 12px;
2494
2506
  cursor: pointer;
2495
2507
  display: flex;
2496
2508
  align-items: center;
@@ -2501,17 +2513,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
2501
2513
  opacity 0.15s;
2502
2514
  flex-shrink: 0;
2503
2515
  align-self: flex-end;
2504
- margin-bottom: 2px;
2505
- }
2506
- /*
2507
- 방에 말하는 자리는 **두 번째 행동**이다 — 이 화면의 첫 행동은 어시스턴트에게 묻는 것이다.
2508
- 같은 무게로 두면 무엇이 기본인지 화면이 말하지 않게 된다(ADR-0070 A15 의 자리 둘).
2509
- */
2510
- .input-row > button.say-to-room {
2511
- background: var(--md-sys-color-surface-container-highest, #e2e8f0);
2512
- color: var(--md-sys-color-on-surface-variant, #475569);
2516
+ margin-bottom: 0;
2513
2517
  }
2514
-
2515
2518
  .input-row > button:disabled {
2516
2519
  background: var(--md-sys-color-surface-container-highest, #e2e8f0);
2517
2520
  color: var(--md-sys-color-outline, #94a3b8);
@@ -2627,6 +2630,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
2627
2630
  this.errorMessage = undefined;
2628
2631
  this.input = '';
2629
2632
  this.participants = [];
2633
+ this.rosterLoaded = false;
2634
+ this.replyingTo = undefined;
2630
2635
  this._seenMessageIds.clear(); // 세션 전환 — 에코 dedup 컨텍스트 리셋
2631
2636
  if (this.sessionId) {
2632
2637
  this.loadHistory();
@@ -2745,18 +2750,19 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
2745
2750
  this.lines = next.lines;
2746
2751
  }
2747
2752
  async _loadParticipants() {
2748
- if (!this.sessionId)
2753
+ const sessionId = this.sessionId;
2754
+ if (!sessionId)
2749
2755
  return;
2750
2756
  try {
2751
- const result = await client.query({
2752
- query: CHAT_PARTICIPANTS_QUERY,
2753
- variables: { sessionId: this.sessionId },
2754
- fetchPolicy: 'network-only'
2755
- });
2757
+ const result = await client.query({ query: CHAT_PARTICIPANTS_QUERY, variables: { sessionId }, fetchPolicy: 'network-only' });
2758
+ if (this.sessionId !== sessionId)
2759
+ return;
2756
2760
  this.participants = result.data?.chatSessionParticipants ?? [];
2761
+ this.rosterLoaded = true;
2757
2762
  }
2758
2763
  catch {
2759
- /* roster 로드 실패는 채팅 본 기능 무영향 — 조용히 무시 */
2764
+ if (this.sessionId === sessionId)
2765
+ this.rosterLoaded = false;
2760
2766
  }
2761
2767
  }
2762
2768
  async _touchPresence() {
@@ -3035,6 +3041,12 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3035
3041
  </div>
3036
3042
  `
3037
3043
  : nothing}
3044
+ ${this.replyingTo ? html `<div class="reply-preview">
3045
+ <div><strong>${i18next.t('ai-assistant.text.reply-to', { name: this.replyingTo.name })}</strong>
3046
+ <span>${this.replyingTo.excerpt}</span></div>
3047
+ <button type="button" aria-label=${i18next.t('ai-assistant.button.cancel-reply')}
3048
+ @click=${() => this.cancelReply()}><md-icon>close</md-icon></button>
3049
+ </div>` : nothing}
3038
3050
  ${this.renderInviteNotice()}
3039
3051
  <div class="input-row">
3040
3052
  <div class="textarea-wrap">
@@ -3071,33 +3083,15 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3071
3083
  `
3072
3084
  : nothing}
3073
3085
  </div>
3074
- ${ /*
3075
- 두 자리다 — 어시스턴트에게 묻는 자리와 **방에 말하는 자리**. 누른 자리가 그 말의 대상을
3076
- 정하고, 서버는 어느 문이 불렸는지로 그 사실을 저장한다(ADR-0070 A15 결정 ①).
3077
-
3078
- **방에 사람이 있을 때만 보인다.** 혼자 쓰는 대화에서 「방에 말하기」는 아무에게도 하지 않는
3079
- 말이고, 누를 일이 없는 단추는 무엇이 기본인지 흐린다. 조건은 참여자 목록과 같다(둘 이상) —
3080
- 둘째 사람은 멘션으로 들어오고, 그 순간 이 자리도 함께 선다.
3081
- */''}
3082
- ${this.sessionId && this.participants.length >= 2
3083
- ? html `
3084
- <button
3085
- class="say-to-room"
3086
- ?disabled=${this.busy || !this.input.trim()}
3087
- @click=${() => this.sayToRoom()}
3088
- title=${i18next.t('ai-assistant.text.say-to-room-tooltip', {
3089
- defaultValue: 'say this to the people here — the assistant does not answer'
3090
- })}>
3091
- <md-icon>forum</md-icon>
3092
- </button>
3093
- `
3094
- : nothing}
3086
+ <span class="send-destination" aria-live="polite">${i18next.t(this.addressesPeople
3087
+ ? 'ai-assistant.text.send-to-people' : 'ai-assistant.text.send-to-ai')}</span>
3095
3088
  ${this.composerWide
3096
3089
  ? html `<span class="send-shortcut">${i18next.t('ai-assistant.text.send-shortcut', { key: sendShortcut(navigator.platform), defaultValue: '{key} to send' })}</span>`
3097
3090
  : nothing}
3098
3091
  <button
3099
3092
  ?disabled=${this.busy || !!this.sendBlocked || (!this.input.trim() && this.attachments.length === 0)}
3100
- @click=${this.send}
3093
+ @click=${() => this.submitMessage()}
3094
+ aria-label=${i18next.t(this.addressesPeople ? 'ai-assistant.text.send-to-people' : 'ai-assistant.text.send-to-ai')}
3101
3095
  title=${this.sendBlocked ||
3102
3096
  (this.composerWide
3103
3097
  ? i18next.t('ai-assistant.text.send-tooltip-wide', { key: sendShortcut(navigator.platform), defaultValue: 'Send ({key})' })
@@ -3116,6 +3110,53 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3116
3110
  * 저장되는 대화가 있을 때만 할 수 있다. 세션이 없는 즉석 질문에는 「방」이 없다 — 그 자리에서
3117
3111
  * 한 말은 누구에게도 남지 않는다.
3118
3112
  */
3113
+ startReply(line) {
3114
+ this.cancelReply();
3115
+ const name = line.role === 'assistant' ? 'AI' : line.senderName || i18next.t('ai-assistant.label.participant');
3116
+ const token = line.role === 'user' ? name : undefined;
3117
+ this.replyingTo = { id: line.id, name, role: line.role, token,
3118
+ excerpt: stripMentionRefids(line.content).replace(/\{userId:[^}]+\}/g, '').replace(/\s+/g, ' ').slice(0, 180) };
3119
+ if (token && line.senderId) {
3120
+ this.pickedUserMentions.set(token, line.senderId);
3121
+ if (!this.input.includes(`@${token}`))
3122
+ this.input = `@${token} ${this.input}`;
3123
+ }
3124
+ this.updateComplete.then(() => this.renderRoot.querySelector('textarea')?.focus());
3125
+ }
3126
+ cancelReply() {
3127
+ const token = this.replyingTo?.token;
3128
+ if (token && this.input.startsWith(`@${token} `))
3129
+ this.input = this.input.slice(token.length + 2);
3130
+ this.replyingTo = undefined;
3131
+ }
3132
+ replyContent(body) {
3133
+ return quotedReply(body, this.replyingTo);
3134
+ }
3135
+ get addressesPeople() {
3136
+ const mentioned = [...this.pickedUserMentions.keys()].some(token => this.input.includes(`@${token}`));
3137
+ return replyDestination(this.replyingTo?.role, mentioned) === 'room';
3138
+ }
3139
+ async submitMessage() {
3140
+ if (this.busy || !this.input.trim() && !this.attachments.length)
3141
+ return;
3142
+ if (!this.addressesPeople)
3143
+ return this.send();
3144
+ if (this.attachments.length) {
3145
+ this.errorMessage = i18next.t('ai-assistant.text.room-attachments-unavailable');
3146
+ return;
3147
+ }
3148
+ if (!this.sessionId && this.prepareSession) {
3149
+ try {
3150
+ await this.prepareSession(id => { this.adoptingSession = true; this.sessionId = id; });
3151
+ await this.updateComplete;
3152
+ }
3153
+ catch (error) {
3154
+ this.errorMessage = error instanceof Error ? error.message : String(error);
3155
+ return;
3156
+ }
3157
+ }
3158
+ return this.sayToRoom();
3159
+ }
3119
3160
  async sayToRoom() {
3120
3161
  const body = this.input.trim();
3121
3162
  if (!body || this.busy)
@@ -3127,15 +3168,20 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3127
3168
  });
3128
3169
  return;
3129
3170
  }
3171
+ const reply = this.replyingTo;
3172
+ const content = this.replyContent(injectMentionUserIds(body, [...this.pickedUserMentions.entries()]
3173
+ .filter(([token]) => body.includes(`@${token}`))
3174
+ .map(([token, userId]) => ({ token, userId }))));
3130
3175
  const localId = `local-${++this._localSeq}-r`;
3131
- this.lines = [...this.lines, { role: 'user', content: body, _localId: localId, senderEmail: this._myEmail }];
3176
+ this.lines = [...this.lines, { role: 'user', content, _localId: localId, senderId: this._myUserId, senderEmail: this._myEmail }];
3132
3177
  this.input = '';
3133
3178
  this.busy = true;
3134
3179
  try {
3135
3180
  const result = await client.mutate({
3136
3181
  mutation: SAY_TO_ROOM_MUTATION,
3137
- variables: { sessionId: this.sessionId, content: body }
3182
+ variables: { sessionId: this.sessionId, content, replyToId: reply?.id }
3138
3183
  });
3184
+ this.replyingTo = undefined;
3139
3185
  /* 방송 에코와 겹치지 않게 서버 id 로 맞춘다 — 보낸 말이 두 줄로 보이지 않게. */
3140
3186
  this._reconcileSentLine(localId, result.data?.sayToRoom ?? undefined, {});
3141
3187
  }
@@ -3166,14 +3212,14 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3166
3212
  .map(([token]) => token);
3167
3213
  if (incoming.length === 0)
3168
3214
  return nothing;
3169
- const myRole = this.participants.find(p => p.user?.id && p.user.id === this._myUserId)?.role;
3170
- const mayInvite = myRole === 'owner' || myRole === 'member';
3215
+ const inviteState = rosterInviteState(this.participants, this._myUserId, this.rosterLoaded);
3216
+ const mayInvite = inviteState === 'allowed';
3171
3217
  const names = incoming.map(token => `@${token}`).join(', ');
3172
3218
  return html `
3173
3219
  <div class="invite-notice">
3174
3220
  <md-icon>person_add</md-icon>
3175
3221
  <span>
3176
- ${mayInvite
3222
+ ${inviteState === 'checking' ? i18next.t('ai-assistant.text.invite-checking') : mayInvite
3177
3223
  ? i18next.t('ai-assistant.text.will-be-invited', {
3178
3224
  names,
3179
3225
  defaultValue: '{names} will join this conversation when you send'
@@ -3289,7 +3335,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3289
3335
  else if (action === 'send') {
3290
3336
  e.preventDefault();
3291
3337
  if (!this.busy)
3292
- this.send();
3338
+ void this.submitMessage();
3293
3339
  }
3294
3340
  }
3295
3341
  /** input 이벤트 — value 즉시 반영 + popup 갱신은 150ms 디바운스 (F-Finder 와 동일).
@@ -3651,12 +3697,12 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3651
3697
  <md-icon>group</md-icon>${online}/${this.participants.length}
3652
3698
  </span>
3653
3699
  ${this.participants.map(p => {
3654
- const name = p.user?.name || p.user?.id || '?';
3700
+ const name = rosterName(p, String(i18next.t('ai-assistant.text.participant-unavailable')));
3655
3701
  const on = this._isOnline(p.lastSeenAt);
3656
3702
  return html `<span
3657
3703
  class="roster-chip ${on ? 'online' : 'offline'}"
3658
3704
  title=${`${name} · ${p.role}${on ? ' · online' : ''}`}>
3659
- <span class="roster-dot"></span>${initials(name)}
3705
+ <span class="roster-dot"></span><span class="roster-name">${name}</span>
3660
3706
  </span>`;
3661
3707
  })}
3662
3708
  </div>
@@ -3781,7 +3827,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3781
3827
  * system : 없음
3782
3828
  */
3783
3829
  renderMessageActions(idx, line, content) {
3784
- if (line.role !== 'assistant')
3830
+ if (line.role === 'system')
3785
3831
  return nothing;
3786
3832
  const isCopied = this.copiedIdx === idx;
3787
3833
  // revert 는 인라인 patch chip (메시지 안) 한 곳에만 — hover 액션에서 제외 (중복 방지).
@@ -3795,13 +3841,17 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
3795
3841
  @click=${() => this.copyMessage(idx, content)}>
3796
3842
  <md-icon>${isCopied ? 'check' : 'content_copy'}</md-icon>
3797
3843
  </button>
3798
- <button
3844
+ <button class="msg-action" ?disabled=${this.busy || (line.role === 'user' && !line.senderId)}
3845
+ title=${i18next.t('ai-assistant.button.reply')}
3846
+ aria-label=${i18next.t('ai-assistant.button.reply')}
3847
+ @click=${() => this.startReply(line)}><md-icon>reply</md-icon></button>
3848
+ ${line.role === 'assistant' ? html `<button
3799
3849
  class="msg-action"
3800
3850
  ?disabled=${this.busy}
3801
3851
  title=${i18next.t('ai-assistant.button.regenerate')}
3802
3852
  @click=${() => this.regenerateAssistant(idx)}>
3803
3853
  <md-icon>refresh</md-icon>
3804
- </button>
3854
+ </button>` : nothing}
3805
3855
  </div>
3806
3856
  `;
3807
3857
  }
@@ -4551,7 +4601,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
4551
4601
  userMentions.push({ token, userId });
4552
4602
  }
4553
4603
  let enrichedContent = injectMentionRefids(promptText, mentions);
4554
- enrichedContent = injectMentionUserIds(enrichedContent, userMentions);
4604
+ enrichedContent = this.replyContent(injectMentionUserIds(enrichedContent, userMentions));
4605
+ this.replyingTo = undefined;
4555
4606
  // optimistic 라인에 임시 핸들 — 방송 에코와의 reconcile 을 index 비의존으로.
4556
4607
  const userLocalId = `local-${++this._localSeq}-u`;
4557
4608
  const asstLocalId = `local-${++this._localSeq}-a`;
@@ -4568,7 +4619,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
4568
4619
  base64: a.base64
4569
4620
  })),
4570
4621
  _localId: userLocalId,
4571
- senderEmail: this._myEmail
4622
+ senderId: this._myUserId, senderEmail: this._myEmail
4572
4623
  }
4573
4624
  ];
4574
4625
  this.input = '';
@@ -4909,6 +4960,10 @@ __decorate([
4909
4960
  state(),
4910
4961
  __metadata("design:type", Array)
4911
4962
  ], OxAssistantChat.prototype, "participants", void 0);
4963
+ __decorate([
4964
+ state(),
4965
+ __metadata("design:type", Object)
4966
+ ], OxAssistantChat.prototype, "rosterLoaded", void 0);
4912
4967
  __decorate([
4913
4968
  state(),
4914
4969
  __metadata("design:type", Object)
@@ -4929,6 +4984,10 @@ __decorate([
4929
4984
  state(),
4930
4985
  __metadata("design:type", Object)
4931
4986
  ], OxAssistantChat.prototype, "mentionOverflow", void 0);
4987
+ __decorate([
4988
+ state(),
4989
+ __metadata("design:type", Object)
4990
+ ], OxAssistantChat.prototype, "replyingTo", void 0);
4932
4991
  __decorate([
4933
4992
  state(),
4934
4993
  __metadata("design:type", Object)