@iblai/iblai-js 2.18.1 → 2.19.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.
@@ -2121,6 +2121,7 @@ var analyticsAccessTimeHeatmap$3 = {
2121
2121
  tooltipActiveUsers: "{count} active users"
2122
2122
  };
2123
2123
  var analyticsAnalyticsAuditLogStats$3 = {
2124
+ anonymous: "Anonymous",
2124
2125
  scopeAgent: "Agent",
2125
2126
  scopeAriaLabel: "Audit log scope",
2126
2127
  scopePlatform: "Platform",
@@ -2249,6 +2250,7 @@ var analyticsAgentScopeLink$3 = {
2249
2250
  scopeToAgent: "View analytics for {name}"
2250
2251
  };
2251
2252
  var analyticsAnalyticsMemoryStats$3 = {
2253
+ anonymous: "Anonymous",
2252
2254
  noPermission: "You do not have permission to view user memories.",
2253
2255
  scopeLabel: "Memory scope",
2254
2256
  scopeMentor: "This Agent",
@@ -2463,6 +2465,7 @@ var analyticsAnalyticsTopicsStats$3 = {
2463
2465
  topicsRankedFooter: "{shown} of {total} topics · ranked by message volume"
2464
2466
  };
2465
2467
  var analyticsAnalyticsTranscriptsStats$3 = {
2468
+ anonymous: "Anonymous",
2466
2469
  justNow: "Just now",
2467
2470
  minutesAgo: "{count} min ago",
2468
2471
  hoursAgo: "{count} hour{count, plural, one {} other {s}} ago",
@@ -9402,6 +9405,7 @@ var analyticsAccessTimeHeatmap$2 = {
9402
9405
  tooltipActiveUsers: "{count} utilisateurs actifs"
9403
9406
  };
9404
9407
  var analyticsAnalyticsAuditLogStats$2 = {
9408
+ anonymous: "Anonyme",
9405
9409
  scopeAgent: "Agent",
9406
9410
  scopeAriaLabel: "Portée du journal d'audit",
9407
9411
  scopePlatform: "Plateforme",
@@ -9530,6 +9534,7 @@ var analyticsAgentScopeLink$2 = {
9530
9534
  scopeToAgent: "Voir les analyses de {name}"
9531
9535
  };
9532
9536
  var analyticsAnalyticsMemoryStats$2 = {
9537
+ anonymous: "Anonyme",
9533
9538
  noPermission: "Vous n'avez pas l'autorisation de consulter les mémoires des utilisateurs.",
9534
9539
  scopeLabel: "Portée de la mémoire",
9535
9540
  scopeMentor: "Cet agent",
@@ -9744,6 +9749,7 @@ var analyticsAnalyticsTopicsStats$2 = {
9744
9749
  topicsRankedFooter: "{shown} sur {total} sujets · classés par volume de messages"
9745
9750
  };
9746
9751
  var analyticsAnalyticsTranscriptsStats$2 = {
9752
+ anonymous: "Anonyme",
9747
9753
  justNow: "À l'instant",
9748
9754
  minutesAgo: "Il y a {count} min",
9749
9755
  hoursAgo: "Il y a {count} heure{count, plural, one {} other {s}}",
@@ -16683,6 +16689,7 @@ var analyticsAccessTimeHeatmap$1 = {
16683
16689
  tooltipActiveUsers: "{count} usuarios activos"
16684
16690
  };
16685
16691
  var analyticsAnalyticsAuditLogStats$1 = {
16692
+ anonymous: "Anónimo",
16686
16693
  scopeAgent: "Agente",
16687
16694
  scopeAriaLabel: "Alcance del registro de auditoría",
16688
16695
  scopePlatform: "Plataforma",
@@ -16811,6 +16818,7 @@ var analyticsAgentScopeLink$1 = {
16811
16818
  scopeToAgent: "Ver analíticas de {name}"
16812
16819
  };
16813
16820
  var analyticsAnalyticsMemoryStats$1 = {
16821
+ anonymous: "Anónimo",
16814
16822
  noPermission: "No tienes permiso para ver los recuerdos de los usuarios.",
16815
16823
  scopeLabel: "Alcance de la memoria",
16816
16824
  scopeMentor: "Este agente",
@@ -17025,6 +17033,7 @@ var analyticsAnalyticsTopicsStats$1 = {
17025
17033
  topicsRankedFooter: "{shown} de {total} temas · ordenados por volumen de mensajes"
17026
17034
  };
17027
17035
  var analyticsAnalyticsTranscriptsStats$1 = {
17036
+ anonymous: "Anónimo",
17028
17037
  justNow: "Ahora mismo",
17029
17038
  minutesAgo: "Hace {count} min",
17030
17039
  hoursAgo: "Hace {count} hora{count, plural, one {} other {s}}",
@@ -23964,6 +23973,7 @@ var analyticsAccessTimeHeatmap = {
23964
23973
  tooltipActiveUsers: "{count} 位活跃用户"
23965
23974
  };
23966
23975
  var analyticsAnalyticsAuditLogStats = {
23976
+ anonymous: "匿名",
23967
23977
  scopeAgent: "智能体",
23968
23978
  scopeAriaLabel: "审计日志范围",
23969
23979
  scopePlatform: "平台",
@@ -24092,6 +24102,7 @@ var analyticsAgentScopeLink = {
24092
24102
  scopeToAgent: "查看 {name} 的分析"
24093
24103
  };
24094
24104
  var analyticsAnalyticsMemoryStats = {
24105
+ anonymous: "匿名",
24095
24106
  noPermission: "您没有查看用户记忆的权限。",
24096
24107
  scopeLabel: "记忆范围",
24097
24108
  scopeMentor: "当前智能体",
@@ -24306,6 +24317,7 @@ var analyticsAnalyticsTopicsStats = {
24306
24317
  topicsRankedFooter: "共 {total} 个主题,显示 {shown} 个 · 按消息量排序"
24307
24318
  };
24308
24319
  var analyticsAnalyticsTranscriptsStats = {
24320
+ anonymous: "匿名",
24309
24321
  justNow: "刚刚",
24310
24322
  minutesAgo: "{count} 分钟前",
24311
24323
  hoursAgo: "{count} 小时前",
@@ -189734,6 +189746,11 @@ function ToolCallIndicator({ toolCalls, isActive = false, showResults = false, }
189734
189746
  * white circle (the catalogue's artwork, or the bot icon when it has none)
189735
189747
  * beside the model's display name. Read-only; used on admin transcripts to
189736
189748
  * name the model a turn ran on.
189749
+ *
189750
+ * It sets no font size, weight or colour of its own and sizes the logo in
189751
+ * `em`, so it reads as one more fact in whatever row holds it — a `text-xs
189752
+ * text-gray-500` transcript card or the details panel — rather than as a
189753
+ * larger, bolder pill beside them. Pass `className` for the nav bar's look.
189737
189754
  */
189738
189755
  function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
189739
189756
  var _a, _b;
@@ -189754,9 +189771,9 @@ function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
189754
189771
  const label = displayName || providerDetails.displayName;
189755
189772
  if (!label)
189756
189773
  return null;
189757
- return (jsxs("span", { className: cn('inline-flex max-w-full items-center gap-2 text-sm font-medium text-[#646464]', className), title: providerDetails.displayName && providerDetails.displayName !== label
189774
+ return (jsxs("span", { className: cn('inline-flex max-w-full items-center gap-[0.5em] align-middle', className), title: providerDetails.displayName && providerDetails.displayName !== label
189758
189775
  ? `${label} · ${providerDetails.displayName}`
189759
- : label, "data-testid": "llm-model-badge", children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-white", children: providerDetails.logo ? (jsx("img", { src: providerDetails.logo, alt: `${label} model logo`, className: "h-5 w-5 object-contain", height: 32, width: 32, loading: "lazy" })) : (jsx(Bot, { className: "h-4 w-4", "aria-hidden": "true" })) }), jsx("span", { className: "max-w-[220px] truncate", children: label })] }));
189776
+ : label, "data-testid": "llm-model-badge", children: [jsx("span", { className: "flex h-[1.5em] w-[1.5em] flex-shrink-0 items-center justify-center rounded-full bg-white", children: providerDetails.logo ? (jsx("img", { src: providerDetails.logo, alt: `${label} model logo`, className: "h-[1.25em] w-[1.25em] object-contain", height: 32, width: 32, loading: "lazy" })) : (jsx(Bot, { className: "h-[1em] w-[1em]", "aria-hidden": "true" })) }), jsx("span", { className: "max-w-[220px] truncate", children: label })] }));
189760
189777
  }
189761
189778
 
189762
189779
  /** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
@@ -189802,17 +189819,18 @@ function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization
189802
189819
  function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
189803
189820
  const t = useT();
189804
189821
  const [isOpen, setIsOpen] = React__default.useState(false);
189822
+ // The text truncates inside an `inline-flex` box: an `inline-block` with
189823
+ // hidden overflow puts its baseline on its bottom edge, so the value would
189824
+ // ride above the "User:" label beside it.
189805
189825
  if (!username) {
189806
- // `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
189807
- // an inline box takes its width from its own text.
189808
- return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
189826
+ return (jsx("span", { className: cn('inline-flex max-w-full', className), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }));
189809
189827
  }
189810
189828
  return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
189811
189829
  event.stopPropagation();
189812
189830
  setIsOpen(true);
189813
189831
  },
189814
189832
  // Rows that open on Enter/Space would otherwise swallow the key press.
189815
- onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
189833
+ onKeyDown: (event) => event.stopPropagation(), className: cn('inline-flex max-w-full cursor-pointer text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
189816
189834
  }
189817
189835
 
189818
189836
  const AdvancedPagination = ({ currentPage, totalPages, onPageChange, totalCount, itemsPerPage, itemLabel = 'items', variant = 'default', className, }) => {
@@ -232512,7 +232530,8 @@ const nested = (source) => source.user && typeof source.user === 'object' ? sour
232512
232530
  * - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
232513
232531
  * or a username field that turns out to hold an email address.
232514
232532
  * - `username`: `lti_username`, `username`, `student`, `user_username`, the
232515
- * nested `user`'s username, or a bare string/number `user` value.
232533
+ * nested `user`'s username, or a bare string `user` value (a numeric id is
232534
+ * ignored).
232516
232535
  * - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
232517
232536
  * the nested `user`'s name.
232518
232537
  * - `profileUsername`: the first *platform* username (never an LTI claim).
@@ -232531,12 +232550,11 @@ function resolveUserIdentity(source, options = {}) {
232531
232550
  const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
232532
232551
  const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
232533
232552
  const inner = nested(record);
232534
- // A bare `user` value is an id or username; numeric ids are still shown.
232535
- const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
232536
- ? String(record.user)
232537
- : typeof record.user === 'string'
232538
- ? clean(record.user)
232539
- : undefined;
232553
+ // A bare `user` value is a username — unless it is a numeric id, which
232554
+ // names nobody: transcript rows carry `user: 1` beside `username: null`,
232555
+ // and showing "1" for that person is worse than "Anonymous".
232556
+ const bareString = typeof record.user === 'string' ? clean(record.user) : undefined;
232557
+ const bareUser = bareString && !/^\d+$/.test(bareString) ? bareString : undefined;
232540
232558
  let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
232541
232559
  const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
232542
232560
  let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
@@ -233154,19 +233172,22 @@ const useTranscripts = ({ tenantKey, mentorId, usergroupIds, }) => {
233154
233172
  * from the data instead of from a search box.
233155
233173
  *
233156
233174
  * It renders as plain text wherever re-scoping is impossible or pointless: on
233157
- * per-agent pages (no picker, so nothing would visibly change), and on rows
233158
- * whose agent the payload identifies by name alone. Safe inside clickable rows
233159
- * and cards — neither its clicks nor its key presses reach their handlers.
233175
+ * per-agent pages (no picker, so nothing would visibly change), on rows whose
233176
+ * agent the payload identifies by name alone, and on rows of the agent the
233177
+ * page is already scoped to (every row, once the picker names one agent — a
233178
+ * blue link there promised something and delivered nothing). Safe inside
233179
+ * clickable rows and cards — neither its clicks nor its key presses reach
233180
+ * their handlers.
233160
233181
  */
233161
233182
  function AgentScopeLink({ uniqueId, name, fallback = '-', onScoped, className, testId, }) {
233162
233183
  const t = useT();
233163
233184
  const setScopedMentor = useAnalyticsScopeSetter();
233164
233185
  const { scopedMentor } = useAnalyticsScope();
233165
233186
  const display = name || uniqueId || fallback;
233166
- if (!setScopedMentor || !uniqueId) {
233187
+ const isCurrent = !!uniqueId && (scopedMentor === null || scopedMentor === void 0 ? void 0 : scopedMentor.uniqueId) === uniqueId;
233188
+ if (!setScopedMentor || !uniqueId || isCurrent) {
233167
233189
  return (jsx("span", { className: className, "data-testid": testId, children: display }));
233168
233190
  }
233169
- const isCurrent = (scopedMentor === null || scopedMentor === void 0 ? void 0 : scopedMentor.uniqueId) === uniqueId;
233170
233191
  return (jsx("button", { type: "button", onClick: (event) => {
233171
233192
  event.stopPropagation();
233172
233193
  // No `dbId`: these payloads carry the `unique_id` only. AnalyticsLayout
@@ -233176,7 +233197,10 @@ function AgentScopeLink({ uniqueId, name, fallback = '-', onScoped, className, t
233176
233197
  onScoped === null || onScoped === void 0 ? void 0 : onScoped();
233177
233198
  },
233178
233199
  // Rows that open on Enter/Space would otherwise swallow the key press.
233179
- onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', isCurrent && 'font-medium text-blue-600', className), "aria-label": t('analyticsAgentScopeLink.scopeToAgent', { name: display }), "aria-current": isCurrent ? 'true' : undefined, "data-testid": testId, children: display }));
233200
+ onKeyDown: (event) => event.stopPropagation(),
233201
+ // Truncates inside an `inline-flex` box so the name keeps the baseline
233202
+ // of the "Agent:" label beside it (see UserProfileLink).
233203
+ className: cn('inline-flex max-w-full cursor-pointer text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('analyticsAgentScopeLink.scopeToAgent', { name: display }), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }));
233180
233204
  }
233181
233205
 
233182
233206
  /**
@@ -233617,13 +233641,6 @@ const getMessageContent = (content) => {
233617
233641
  }
233618
233642
  return '';
233619
233643
  };
233620
- /**
233621
- * Label for whoever a transcript belongs to, via the shared identity rule.
233622
- * The analytics payload carries an email only on newer deployments, so it
233623
- * wins where present and the display name / username stand in behind it,
233624
- * in the order the caller asks for; "-" when the row names nobody.
233625
- */
233626
- const identifyUser = (source, order) => resolveUserIdentity(source, { order, anonymousLabel: '-' }).label;
233627
233644
  /**
233628
233645
  * The model as the nav bar shows it (logo + display name); hosts may override
233629
233646
  * via `turnDetails.renderModel`. The catalogue is read for the tenant and the
@@ -233668,6 +233685,12 @@ function TranscriptDocumentsButton({ detail, className, }) {
233668
233685
  function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMentorId, usergroupIds, turnDetails, }) {
233669
233686
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
233670
233687
  const t = useT();
233688
+ // Who a row belongs to, by the shared identity rule: the translated
233689
+ // "Anonymous" when the row names nobody (a bare "-" read as missing data).
233690
+ const identifyUser = React__default.useCallback((source, order) => resolveUserIdentity(source, {
233691
+ order,
233692
+ anonymousLabel: t('analyticsAnalyticsTranscriptsStats.anonymous'),
233693
+ }).label, [t]);
233671
233694
  const currentMentorId = useScopedMentorId(selectedMentorId, mentorId);
233672
233695
  const [isModalOpen, setIsModalOpen] = useState(false);
233673
233696
  const searchParams = useSearchParams();
@@ -233817,7 +233840,7 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
233817
233840
  ? t('analyticsAnalyticsTranscriptsStats.sentimentNegative')
233818
233841
  : transcript.sentiment === 'positive'
233819
233842
  ? t('analyticsAnalyticsTranscriptsStats.sentimentPositive')
233820
- : t('analyticsAnalyticsTranscriptsStats.sentimentNeutral'), ' ', t('analyticsAnalyticsTranscriptsStats.userSentiment')] })] }) }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: transcript === null || transcript === void 0 ? void 0 : transcript.username, display: identifyUser(transcript) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: transcript === null || transcript === void 0 ? void 0 : transcript.mentor_unique_id, name: transcript === null || transcript === void 0 ? void 0 : transcript.mentor })] })] }), jsx("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: jsxs("div", { className: "inline-flex flex-wrap items-center gap-1.5", children: [t('analyticsAnalyticsTranscriptsStats.labelModelCard'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.model)
233843
+ : t('analyticsAnalyticsTranscriptsStats.sentimentNeutral'), ' ', t('analyticsAnalyticsTranscriptsStats.userSentiment')] })] }) }), jsxs("div", { className: "flex flex-wrap items-baseline gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: transcript === null || transcript === void 0 ? void 0 : transcript.username, display: identifyUser(transcript) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: transcript === null || transcript === void 0 ? void 0 : transcript.mentor_unique_id, name: transcript === null || transcript === void 0 ? void 0 : transcript.mentor })] })] }), jsx("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: jsxs("div", { className: "inline-flex flex-wrap items-center gap-1.5", children: [t('analyticsAnalyticsTranscriptsStats.labelModelCard'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.model)
233821
233844
  ? ((_c = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _c !== void 0 ? _c : renderModelBadge)({
233822
233845
  model: transcript.model,
233823
233846
  provider: providerOf(transcript),
@@ -236564,10 +236587,7 @@ function AgentAuditLogView({ tenantKey, mentorId, userId, selectedMentorId, curr
236564
236587
  }, numberOfMonths: 2 }) })] }), jsx("div", { className: "min-w-[160px]", children: jsxs(Select$1, { value: actionFilter !== undefined ? String(actionFilter) : 'all', onValueChange: (val) => {
236565
236588
  setActionFilter(val === 'all' ? undefined : Number(val));
236566
236589
  setPage(1);
236567
- }, children: [jsx(SelectTrigger, { className: "w-full font-normal bg-white h-9", children: jsx(SelectValue, { placeholder: t('analyticsAnalyticsAuditLogStats.allActions') }) }), jsx(SelectContent, { children: ACTION_FILTER_OPTIONS.map((opt) => (jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }) })] }) }), jsx("div", { className: "overflow-hidden rounded-md border border-[oklch(0.922_0_0)] bg-white", children: jsx("div", { className: "overflow-x-auto", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex items-center justify-center w-full py-10", children: jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-blue-600 border-t-transparent" }) })) : error && !is403 ? (jsx("div", { className: "py-8 text-center text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.loadError') })) : !((_a = data === null || data === void 0 ? void 0 : data.results) === null || _a === void 0 ? void 0 : _a.length) ? (jsx("div", { className: "h-[120px]", children: jsx(EmptyStats, { title: t('analyticsAnalyticsAuditLogStats.noEntries') }) })) : (jsxs(Table, { className: "min-w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { className: "bg-muted/50 border-b border-[oklch(0.922_0_0)]", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colUser') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colAction') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colTime') })] }) }), jsx(TableBody, { children: data.results.map((entry) => (jsxs(TableRow, { className: "border-b border-[oklch(0.922_0_0)] last:border-0", children: [jsx(TableCell, { className: "p-3 font-medium whitespace-nowrap text-[#646464]", children: jsx(UserProfileLink, { tenantKey: tenantKey, currentSPA: currentSPA, username: entry.actor_username, display: resolveUserIdentity({
236568
- email: entry.actor_email,
236569
- username: entry.actor_username,
236570
- }).label }) }), jsx(TableCell, { className: "min-w-[250px] p-3 text-[#646464]", children: summarizeChanges(entry) }), jsx(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true }) })] }, entry.id))) })] })) }) }) }), jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isLoading })] }));
236590
+ }, children: [jsx(SelectTrigger, { className: "w-full font-normal bg-white h-9", children: jsx(SelectValue, { placeholder: t('analyticsAnalyticsAuditLogStats.allActions') }) }), jsx(SelectContent, { children: ACTION_FILTER_OPTIONS.map((opt) => (jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }) })] }) }), jsx("div", { className: "overflow-hidden rounded-md border border-[oklch(0.922_0_0)] bg-white", children: jsx("div", { className: "overflow-x-auto", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex items-center justify-center w-full py-10", children: jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-blue-600 border-t-transparent" }) })) : error && !is403 ? (jsx("div", { className: "py-8 text-center text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.loadError') })) : !((_a = data === null || data === void 0 ? void 0 : data.results) === null || _a === void 0 ? void 0 : _a.length) ? (jsx("div", { className: "h-[120px]", children: jsx(EmptyStats, { title: t('analyticsAnalyticsAuditLogStats.noEntries') }) })) : (jsxs(Table, { className: "min-w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { className: "bg-muted/50 border-b border-[oklch(0.922_0_0)]", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colUser') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colAction') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colTime') })] }) }), jsx(TableBody, { children: data.results.map((entry) => (jsxs(TableRow, { className: "border-b border-[oklch(0.922_0_0)] last:border-0", children: [jsx(TableCell, { className: "p-3 font-medium whitespace-nowrap text-[#646464]", children: jsx(UserProfileLink, { tenantKey: tenantKey, currentSPA: currentSPA, username: entry.actor_username, display: resolveUserIdentity({ email: entry.actor_email, username: entry.actor_username }, { anonymousLabel: t('analyticsAnalyticsAuditLogStats.anonymous') }).label }) }), jsx(TableCell, { className: "min-w-[250px] p-3 text-[#646464]", children: summarizeChanges(entry) }), jsx(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true }) })] }, entry.id))) })] })) }) }) }), jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isLoading })] }));
236571
236591
  }
236572
236592
  function AnalyticsAuditLogStats({ tenantKey, currentSPA, mentorId, userId, selectedMentorId, defaultScope = 'agent', timezone, canViewPlatformAudit, }) {
236573
236593
  const t = useT();
@@ -236607,14 +236627,14 @@ const formatDate = (date) => date ? date.toISOString().split('T')[0] : undefined
236607
236627
  function isForbiddenError(error) {
236608
236628
  return !!error && 'status' in error && error.status === 403;
236609
236629
  }
236610
- function toRowFromMentorMemory(memory) {
236630
+ function toRowFromMentorMemory(memory, anonymousLabel) {
236611
236631
  var _a, _b, _c, _d, _e, _f;
236612
236632
  return {
236613
236633
  id: memory.id,
236614
236634
  content: memory.content,
236615
236635
  categoryName: (_b = (_a = memory.category) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null,
236616
236636
  categorySlug: (_d = (_c = memory.category) === null || _c === void 0 ? void 0 : _c.slug) !== null && _d !== void 0 ? _d : null,
236617
- learner: resolveUserIdentity(memory).label,
236637
+ learner: resolveUserIdentity(memory, { anonymousLabel }).label,
236618
236638
  username: memory.username,
236619
236639
  email: memory.email || null,
236620
236640
  mentorId: (_e = memory.mentor_id) !== null && _e !== void 0 ? _e : null,
@@ -236624,13 +236644,13 @@ function toRowFromMentorMemory(memory) {
236624
236644
  createdAt: memory.created_at,
236625
236645
  };
236626
236646
  }
236627
- function toRowFromGlobalMemory(memory) {
236647
+ function toRowFromGlobalMemory(memory, anonymousLabel) {
236628
236648
  return {
236629
236649
  id: memory.id,
236630
236650
  content: memory.content,
236631
236651
  categoryName: null,
236632
236652
  categorySlug: null,
236633
- learner: memory.username,
236653
+ learner: resolveUserIdentity(memory, { anonymousLabel }).label,
236634
236654
  username: memory.username,
236635
236655
  email: null,
236636
236656
  mentorId: null,
@@ -236734,14 +236754,18 @@ const useMemory = ({ tenantKey, userId, mentorId, myMemory = false, }) => {
236734
236754
  }), [page, learnerFilter, contentFilter, startDate, endDate]);
236735
236755
  const { data: globalMemories, isLoading: isLoadingGlobal, isFetching: isFetchingGlobal, error: globalError, } = useGetGlobalMemoriesQuery({ org: tenantKey, userId, params: globalParams }, { skip: !hasIdentity || !isGlobalScope });
236736
236756
  const { data: adminCategories } = useGetMemoryCategoriesAdminQuery({ org: tenantKey, mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '' }, { skip: !tenantKey || !hasMentor });
236757
+ const t = useT();
236758
+ // The learner label when a memory names nobody — the same translated word
236759
+ // the other analytics tabs use.
236760
+ const anonymousLabel = t('analyticsAnalyticsMemoryStats.anonymous');
236737
236761
  const rows = useMemo(() => {
236738
236762
  var _a, _b, _c;
236739
236763
  if (isMentorScope)
236740
- return ((_a = mentorList === null || mentorList === void 0 ? void 0 : mentorList.results) !== null && _a !== void 0 ? _a : []).map(toRowFromMentorMemory);
236764
+ return ((_a = mentorList === null || mentorList === void 0 ? void 0 : mentorList.results) !== null && _a !== void 0 ? _a : []).map((memory) => toRowFromMentorMemory(memory, anonymousLabel));
236741
236765
  if (isAllMentorsScope)
236742
- return ((_b = allMentors === null || allMentors === void 0 ? void 0 : allMentors.results) !== null && _b !== void 0 ? _b : []).map(toRowFromMentorMemory);
236743
- return ((_c = globalMemories === null || globalMemories === void 0 ? void 0 : globalMemories.results) !== null && _c !== void 0 ? _c : []).map(toRowFromGlobalMemory);
236744
- }, [isMentorScope, isAllMentorsScope, mentorList, allMentors, globalMemories]);
236766
+ return ((_b = allMentors === null || allMentors === void 0 ? void 0 : allMentors.results) !== null && _b !== void 0 ? _b : []).map((memory) => toRowFromMentorMemory(memory, anonymousLabel));
236767
+ return ((_c = globalMemories === null || globalMemories === void 0 ? void 0 : globalMemories.results) !== null && _c !== void 0 ? _c : []).map((memory) => toRowFromGlobalMemory(memory, anonymousLabel));
236768
+ }, [isMentorScope, isAllMentorsScope, mentorList, allMentors, globalMemories, anonymousLabel]);
236745
236769
  const totalCount = useMemo(() => {
236746
236770
  var _a, _b, _c;
236747
236771
  if (isMentorScope)
@@ -236764,7 +236788,7 @@ const useMemory = ({ tenantKey, userId, mentorId, myMemory = false, }) => {
236764
236788
  let totalMemories = 0;
236765
236789
  const categories = groups.map((group) => {
236766
236790
  group.memories.forEach((memory) => {
236767
- learners.add(resolveUserIdentity(memory).label);
236791
+ learners.add(resolveUserIdentity(memory, { anonymousLabel }).label);
236768
236792
  if (memory.is_auto_generated)
236769
236793
  autoCaptured += 1;
236770
236794
  });
@@ -236781,7 +236805,7 @@ const useMemory = ({ tenantKey, userId, mentorId, myMemory = false, }) => {
236781
236805
  learners: learners.size,
236782
236806
  categories: categories.sort((a, b) => b.count - a.count),
236783
236807
  };
236784
- }, [isMentorScope, mentorGrouped, categoryFilter]);
236808
+ }, [isMentorScope, mentorGrouped, categoryFilter, anonymousLabel]);
236785
236809
  const error = isMentorScope ? mentorListError : isAllMentorsScope ? allMentorsError : globalError;
236786
236810
  const isLoading = isMentorScope
236787
236811
  ? isLoadingMentorList || isLoadingMentorGrouped
@@ -96692,6 +96692,7 @@ var analyticsAccessTimeHeatmap$3 = {
96692
96692
  tooltipActiveUsers: "{count} active users"
96693
96693
  };
96694
96694
  var analyticsAnalyticsAuditLogStats$3 = {
96695
+ anonymous: "Anonymous",
96695
96696
  scopeAgent: "Agent",
96696
96697
  scopeAriaLabel: "Audit log scope",
96697
96698
  scopePlatform: "Platform",
@@ -96820,6 +96821,7 @@ var analyticsAgentScopeLink$3 = {
96820
96821
  scopeToAgent: "View analytics for {name}"
96821
96822
  };
96822
96823
  var analyticsAnalyticsMemoryStats$3 = {
96824
+ anonymous: "Anonymous",
96823
96825
  noPermission: "You do not have permission to view user memories.",
96824
96826
  scopeLabel: "Memory scope",
96825
96827
  scopeMentor: "This Agent",
@@ -97034,6 +97036,7 @@ var analyticsAnalyticsTopicsStats$3 = {
97034
97036
  topicsRankedFooter: "{shown} of {total} topics · ranked by message volume"
97035
97037
  };
97036
97038
  var analyticsAnalyticsTranscriptsStats$3 = {
97039
+ anonymous: "Anonymous",
97037
97040
  justNow: "Just now",
97038
97041
  minutesAgo: "{count} min ago",
97039
97042
  hoursAgo: "{count} hour{count, plural, one {} other {s}} ago",
@@ -103973,6 +103976,7 @@ var analyticsAccessTimeHeatmap$2 = {
103973
103976
  tooltipActiveUsers: "{count} utilisateurs actifs"
103974
103977
  };
103975
103978
  var analyticsAnalyticsAuditLogStats$2 = {
103979
+ anonymous: "Anonyme",
103976
103980
  scopeAgent: "Agent",
103977
103981
  scopeAriaLabel: "Portée du journal d'audit",
103978
103982
  scopePlatform: "Plateforme",
@@ -104101,6 +104105,7 @@ var analyticsAgentScopeLink$2 = {
104101
104105
  scopeToAgent: "Voir les analyses de {name}"
104102
104106
  };
104103
104107
  var analyticsAnalyticsMemoryStats$2 = {
104108
+ anonymous: "Anonyme",
104104
104109
  noPermission: "Vous n'avez pas l'autorisation de consulter les mémoires des utilisateurs.",
104105
104110
  scopeLabel: "Portée de la mémoire",
104106
104111
  scopeMentor: "Cet agent",
@@ -104315,6 +104320,7 @@ var analyticsAnalyticsTopicsStats$2 = {
104315
104320
  topicsRankedFooter: "{shown} sur {total} sujets · classés par volume de messages"
104316
104321
  };
104317
104322
  var analyticsAnalyticsTranscriptsStats$2 = {
104323
+ anonymous: "Anonyme",
104318
104324
  justNow: "À l'instant",
104319
104325
  minutesAgo: "Il y a {count} min",
104320
104326
  hoursAgo: "Il y a {count} heure{count, plural, one {} other {s}}",
@@ -111254,6 +111260,7 @@ var analyticsAccessTimeHeatmap$1 = {
111254
111260
  tooltipActiveUsers: "{count} usuarios activos"
111255
111261
  };
111256
111262
  var analyticsAnalyticsAuditLogStats$1 = {
111263
+ anonymous: "Anónimo",
111257
111264
  scopeAgent: "Agente",
111258
111265
  scopeAriaLabel: "Alcance del registro de auditoría",
111259
111266
  scopePlatform: "Plataforma",
@@ -111382,6 +111389,7 @@ var analyticsAgentScopeLink$1 = {
111382
111389
  scopeToAgent: "Ver analíticas de {name}"
111383
111390
  };
111384
111391
  var analyticsAnalyticsMemoryStats$1 = {
111392
+ anonymous: "Anónimo",
111385
111393
  noPermission: "No tienes permiso para ver los recuerdos de los usuarios.",
111386
111394
  scopeLabel: "Alcance de la memoria",
111387
111395
  scopeMentor: "Este agente",
@@ -111596,6 +111604,7 @@ var analyticsAnalyticsTopicsStats$1 = {
111596
111604
  topicsRankedFooter: "{shown} de {total} temas · ordenados por volumen de mensajes"
111597
111605
  };
111598
111606
  var analyticsAnalyticsTranscriptsStats$1 = {
111607
+ anonymous: "Anónimo",
111599
111608
  justNow: "Ahora mismo",
111600
111609
  minutesAgo: "Hace {count} min",
111601
111610
  hoursAgo: "Hace {count} hora{count, plural, one {} other {s}}",
@@ -118535,6 +118544,7 @@ var analyticsAccessTimeHeatmap = {
118535
118544
  tooltipActiveUsers: "{count} 位活跃用户"
118536
118545
  };
118537
118546
  var analyticsAnalyticsAuditLogStats = {
118547
+ anonymous: "匿名",
118538
118548
  scopeAgent: "智能体",
118539
118549
  scopeAriaLabel: "审计日志范围",
118540
118550
  scopePlatform: "平台",
@@ -118663,6 +118673,7 @@ var analyticsAgentScopeLink = {
118663
118673
  scopeToAgent: "查看 {name} 的分析"
118664
118674
  };
118665
118675
  var analyticsAnalyticsMemoryStats = {
118676
+ anonymous: "匿名",
118666
118677
  noPermission: "您没有查看用户记忆的权限。",
118667
118678
  scopeLabel: "记忆范围",
118668
118679
  scopeMentor: "当前智能体",
@@ -118877,6 +118888,7 @@ var analyticsAnalyticsTopicsStats = {
118877
118888
  topicsRankedFooter: "共 {total} 个主题,显示 {shown} 个 · 按消息量排序"
118878
118889
  };
118879
118890
  var analyticsAnalyticsTranscriptsStats = {
118891
+ anonymous: "匿名",
118880
118892
  justNow: "刚刚",
118881
118893
  minutesAgo: "{count} 分钟前",
118882
118894
  hoursAgo: "{count} 小时前",
@@ -222288,6 +222300,11 @@ function useLlmDisplayName({ llmName, llmConfig, org, userId, mentorId, }) {
222288
222300
  * white circle (the catalogue's artwork, or the bot icon when it has none)
222289
222301
  * beside the model's display name. Read-only; used on admin transcripts to
222290
222302
  * name the model a turn ran on.
222303
+ *
222304
+ * It sets no font size, weight or colour of its own and sizes the logo in
222305
+ * `em`, so it reads as one more fact in whatever row holds it — a `text-xs
222306
+ * text-gray-500` transcript card or the details panel — rather than as a
222307
+ * larger, bolder pill beside them. Pass `className` for the nav bar's look.
222291
222308
  */
222292
222309
  function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
222293
222310
  var _a, _b;
@@ -222308,9 +222325,9 @@ function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
222308
222325
  const label = displayName || providerDetails.displayName;
222309
222326
  if (!label)
222310
222327
  return null;
222311
- return (jsxs("span", { className: cn('inline-flex max-w-full items-center gap-2 text-sm font-medium text-[#646464]', className), title: providerDetails.displayName && providerDetails.displayName !== label
222328
+ return (jsxs("span", { className: cn('inline-flex max-w-full items-center gap-[0.5em] align-middle', className), title: providerDetails.displayName && providerDetails.displayName !== label
222312
222329
  ? `${label} · ${providerDetails.displayName}`
222313
- : label, "data-testid": "llm-model-badge", children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-white", children: providerDetails.logo ? (jsx("img", { src: providerDetails.logo, alt: `${label} model logo`, className: "h-5 w-5 object-contain", height: 32, width: 32, loading: "lazy" })) : (jsx(Bot, { className: "h-4 w-4", "aria-hidden": "true" })) }), jsx("span", { className: "max-w-[220px] truncate", children: label })] }));
222330
+ : label, "data-testid": "llm-model-badge", children: [jsx("span", { className: "flex h-[1.5em] w-[1.5em] flex-shrink-0 items-center justify-center rounded-full bg-white", children: providerDetails.logo ? (jsx("img", { src: providerDetails.logo, alt: `${label} model logo`, className: "h-[1.25em] w-[1.25em] object-contain", height: 32, width: 32, loading: "lazy" })) : (jsx(Bot, { className: "h-[1em] w-[1em]", "aria-hidden": "true" })) }), jsx("span", { className: "max-w-[220px] truncate", children: label })] }));
222314
222331
  }
222315
222332
 
222316
222333
  /** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
@@ -222356,17 +222373,18 @@ function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization
222356
222373
  function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
222357
222374
  const t = useT();
222358
222375
  const [isOpen, setIsOpen] = React__default.useState(false);
222376
+ // The text truncates inside an `inline-flex` box: an `inline-block` with
222377
+ // hidden overflow puts its baseline on its bottom edge, so the value would
222378
+ // ride above the "User:" label beside it.
222359
222379
  if (!username) {
222360
- // `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
222361
- // an inline box takes its width from its own text.
222362
- return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
222380
+ return (jsx("span", { className: cn('inline-flex max-w-full', className), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }));
222363
222381
  }
222364
222382
  return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
222365
222383
  event.stopPropagation();
222366
222384
  setIsOpen(true);
222367
222385
  },
222368
222386
  // Rows that open on Enter/Space would otherwise swallow the key press.
222369
- onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
222387
+ onKeyDown: (event) => event.stopPropagation(), className: cn('inline-flex max-w-full cursor-pointer text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
222370
222388
  }
222371
222389
 
222372
222390
  const AdvancedPagination = ({ currentPage, totalPages, onPageChange, totalCount, itemsPerPage, itemLabel = 'items', variant = 'default', className, }) => {
@@ -259865,7 +259883,8 @@ const nested = (source) => source.user && typeof source.user === 'object' ? sour
259865
259883
  * - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
259866
259884
  * or a username field that turns out to hold an email address.
259867
259885
  * - `username`: `lti_username`, `username`, `student`, `user_username`, the
259868
- * nested `user`'s username, or a bare string/number `user` value.
259886
+ * nested `user`'s username, or a bare string `user` value (a numeric id is
259887
+ * ignored).
259869
259888
  * - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
259870
259889
  * the nested `user`'s name.
259871
259890
  * - `profileUsername`: the first *platform* username (never an LTI claim).
@@ -259884,12 +259903,11 @@ function resolveUserIdentity(source, options = {}) {
259884
259903
  const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
259885
259904
  const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
259886
259905
  const inner = nested(record);
259887
- // A bare `user` value is an id or username; numeric ids are still shown.
259888
- const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
259889
- ? String(record.user)
259890
- : typeof record.user === 'string'
259891
- ? clean(record.user)
259892
- : undefined;
259906
+ // A bare `user` value is a username — unless it is a numeric id, which
259907
+ // names nobody: transcript rows carry `user: 1` beside `username: null`,
259908
+ // and showing "1" for that person is worse than "Anonymous".
259909
+ const bareString = typeof record.user === 'string' ? clean(record.user) : undefined;
259910
+ const bareUser = bareString && !/^\d+$/.test(bareString) ? bareString : undefined;
259893
259911
  let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
259894
259912
  const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
259895
259913
  let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.18.1",
3
+ "version": "2.19.0",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -76,10 +76,10 @@
76
76
  "axios": "1.13.6",
77
77
  "dotenv": "16.6.1",
78
78
  "winston": "3.19.0",
79
+ "@iblai/mcp": "1.17.0",
80
+ "@iblai/web-containers": "1.26.0",
79
81
  "@iblai/data-layer": "1.19.1",
80
- "@iblai/mcp": "1.16.0",
81
- "@iblai/web-utils": "2.4.9",
82
- "@iblai/web-containers": "1.25.1"
82
+ "@iblai/web-utils": "2.5.0"
83
83
  },
84
84
  "peerDependencies": {
85
85
  "@iblai/iblai-api": "4.166.0-ai",