@iblai/iblai-js 2.16.0 → 2.17.1

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.
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
- import React__default, { useRef, useEffect, useState, useCallback, createContext, useContext, useMemo, forwardRef, createElement as createElement$1, useLayoutEffect, useReducer, useImperativeHandle, isValidElement, Children, PureComponent, cloneElement, Component, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
3
- import { useDispatch, useSelector } from 'react-redux';
4
- import { selectSessionId, selectActiveChatMessages, useCachedSessionId, chatActions, LOCAL_STORAGE_KEYS, TimeTracker, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, getInitials, useTenantMetadata, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, getTimeAgo, REQUIRED_ACTIONS_FOR_GROUPS, getVisibleAnalyticsTabs, getUserName as getUserName$2, formatRelativeTime as formatRelativeTime$2, ANONYMOUS_USERNAME, combineCSVData, WATCHER_HIDDEN_REPORTS, canViewPlatformAuditLogs, redirectToAuthSpaJoinTenant, redirectToAuthSpa, getAuthSpaJoinUrl, useOS, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, chatInputSliceActions, removeFile, WithFormPermissions, MENTOR_VISIBILITY } from '@iblai/web-utils';
2
+ import React__default, { useRef, useEffect, useMemo, useCallback, useState, createContext, useContext, forwardRef, createElement as createElement$1, useLayoutEffect, useReducer, useImperativeHandle, isValidElement, Children, PureComponent, cloneElement, Component, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
5
3
  import * as dataLayer from '@iblai/data-layer';
6
- import { useCreateSessionIdMutation, useTimeTrackingMutation, useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, useGetNotificationsCountQuery, useLazyGetNotificationsQuery, useMarkAllAsReadMutation, useCreateNotificationPreviewMutation, useSendNotificationMutation, USER_PROGRESS_DATA_POINTS, useResetTemplateMutation, useSendTestTemplateEmailMutation, useUpdateTemplateMutation, useGetTemplateDetailsQuery, USER_PROGRESS_SUMMARY_TYPE, useGetTemplatesQuery, useLazyGetTemplateDetailsQuery, useToggleTemplateMutation, useGetLlmUsageTracesQuery, useGetLlmUsageObservationsQuery, useGetTopicsStatsQuery, useGetUsersStatsQuery, useGetSessionStatsQuery, useGetTopicsDetailsStatsQuery, useGetTranscriptsConversationHeadlineQuery, useGetAccessTimeHeatmapQuery, useGetUserDetailsStatsQuery, useGetAverageRatingQuery, useGetModelCostsQuery, useGetPerUserCostsQuery, useGetTenantCostQuery, useGetAgentCostQuery, useGetTranscriptsMessagesDetailsQuery, useGetTranscriptsMessagesQuery, useGetReportDetailQuery, useGetMentorPublicSettingsQuery, useGetReportsQuery, useCreateReportMutation, useGetContentAnalyticsQuery, useGetContentAnalyticsDetailsQuery, useGetRevenueQuery, useListSubscribersQuery, useGetAuditLogsQuery, useGetPlatformAuditLogsQuery, PLATFORM_AUDIT_EVENT_TYPES, PLATFORM_AUDIT_ACTION_CODES, useGetMentorMemoriesQuery, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetUserEnrolledCoursesQuery, useLazyGetUserAssignedCoursesQuery, useLazyGetUserCredentialsQuery, useLazyGetOverTimeActivityQuery, useLazyGetCatalogSearchQuery, useCreateOrUpdateUserReportedSkillMutation, useCreateOrUpdateUserDesiredSkillMutation, useLazyGetPathwayListQuery, useLazyGetUserAssignedPathwaysQuery, useLazyGetUserEnrolledPathwaysQuery, useLazyGetPathwayCompletionQuery, useLazyGetProgramListQuery, useLazyGetProgramCompletionQuery, useLazyGetUserEnrolledProgramsQuery, useLazyGetAssignedProgramsQuery, useLazyGetUserSkillsPointsQuery, useLazyGetUserReportedSkillsQuery, useLazyGetUserDesiredSkillsQuery, useLazyGetUserCatalogPathwaysQuery, useLazyGetPerLearnerInfoQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useLazyGetPersonnalizedSearchQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetMentorSettingsQuery, useEditMentorMutation, useGetClawMentorConfigQuery, useGetClawInstancesQuery, useGetAgentConfigQuery, useUpdateClawMentorConfigMutation, useCreateClawMentorConfigMutation, useDeleteClawMentorConfigMutation, usePushClawConfigMutation, useCreateClawInstanceMutation, useDeleteClawInstanceMutation, useUpdateAgentConfigMutation, useHealthCheckClawInstanceMutation, useTestConnectivityClawInstanceMutation, useUpdateClawInstanceMutation, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useCreateCheckoutMutation, useGetUserProjectsQuery, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorCategoriesQuery, useCreateMentorMutation, useUpdateUserPlatformMetadataMutation, useGetApplicationStatusQuery, useListOwnApplicationsQuery, useGetApplicationFormQuery, useWithdrawApplicationMutation, useRenewApplicationMutation, useCreateApplicationDraftMutation, useSubmitApplicationMutation, useUpdateApplicationMutation, useUploadApplicationAttachmentMutation, useDeleteApplicationAttachmentMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useGetPromptCategoriesQuery, useCreatePromptMutation, useDeletePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation } from '@iblai/data-layer';
4
+ import { useGetLlmsQuery, useCreateSessionIdMutation, useTimeTrackingMutation, useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useGetVectorDocumentsQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, useGetNotificationsCountQuery, useLazyGetNotificationsQuery, useMarkAllAsReadMutation, useCreateNotificationPreviewMutation, useSendNotificationMutation, USER_PROGRESS_DATA_POINTS, useResetTemplateMutation, useSendTestTemplateEmailMutation, useUpdateTemplateMutation, useGetTemplateDetailsQuery, USER_PROGRESS_SUMMARY_TYPE, useGetTemplatesQuery, useLazyGetTemplateDetailsQuery, useToggleTemplateMutation, useGetLlmUsageTracesQuery, useGetLlmUsageObservationsQuery, useGetTopicsStatsQuery, useGetUsersStatsQuery, useGetSessionStatsQuery, useGetTopicsDetailsStatsQuery, useGetTranscriptsConversationHeadlineQuery, useGetAccessTimeHeatmapQuery, useGetUserDetailsStatsQuery, useGetAverageRatingQuery, useGetModelCostsQuery, useGetPerUserCostsQuery, useGetTenantCostQuery, useGetAgentCostQuery, useGetTranscriptsMessagesDetailsQuery, useGetTranscriptsMessagesQuery, useGetReportDetailQuery, useGetMentorPublicSettingsQuery, useGetReportsQuery, useCreateReportMutation, useGetContentAnalyticsQuery, useGetContentAnalyticsDetailsQuery, useGetRevenueQuery, useListSubscribersQuery, useGetAuditLogsQuery, useGetPlatformAuditLogsQuery, PLATFORM_AUDIT_EVENT_TYPES, PLATFORM_AUDIT_ACTION_CODES, useGetMentorMemoriesQuery, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetUserEnrolledCoursesQuery, useLazyGetUserAssignedCoursesQuery, useLazyGetUserCredentialsQuery, useLazyGetOverTimeActivityQuery, useLazyGetCatalogSearchQuery, useCreateOrUpdateUserReportedSkillMutation, useCreateOrUpdateUserDesiredSkillMutation, useLazyGetPathwayListQuery, useLazyGetUserAssignedPathwaysQuery, useLazyGetUserEnrolledPathwaysQuery, useLazyGetPathwayCompletionQuery, useLazyGetProgramListQuery, useLazyGetProgramCompletionQuery, useLazyGetUserEnrolledProgramsQuery, useLazyGetAssignedProgramsQuery, useLazyGetUserSkillsPointsQuery, useLazyGetUserReportedSkillsQuery, useLazyGetUserDesiredSkillsQuery, useLazyGetUserCatalogPathwaysQuery, useLazyGetPerLearnerInfoQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useLazyGetPersonnalizedSearchQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetMentorSettingsQuery, useEditMentorMutation, useGetClawMentorConfigQuery, useGetClawInstancesQuery, useGetAgentConfigQuery, useUpdateClawMentorConfigMutation, useCreateClawMentorConfigMutation, useDeleteClawMentorConfigMutation, usePushClawConfigMutation, useCreateClawInstanceMutation, useDeleteClawInstanceMutation, useUpdateAgentConfigMutation, useHealthCheckClawInstanceMutation, useTestConnectivityClawInstanceMutation, useUpdateClawInstanceMutation, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useCreateCheckoutMutation, useGetUserProjectsQuery, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorCategoriesQuery, useCreateMentorMutation, useUpdateUserPlatformMetadataMutation, useGetApplicationStatusQuery, useListOwnApplicationsQuery, useGetApplicationFormQuery, useWithdrawApplicationMutation, useRenewApplicationMutation, useCreateApplicationDraftMutation, useSubmitApplicationMutation, useUpdateApplicationMutation, useUploadApplicationAttachmentMutation, useDeleteApplicationAttachmentMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useGetPromptCategoriesQuery, useCreatePromptMutation, useDeletePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation } from '@iblai/data-layer';
5
+ import { useDispatch, useSelector } from 'react-redux';
6
+ import { selectSessionId, selectActiveChatMessages, useCachedSessionId, chatActions, LOCAL_STORAGE_KEYS, TimeTracker, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, getInitials, useTenantMetadata, useUsername, selectStreaming, isLoggedIn, TOOL_NAME_MAP, WRITE_TODOS_TOOL, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, getTimeAgo, REQUIRED_ACTIONS_FOR_GROUPS, getVisibleAnalyticsTabs, getUserName as getUserName$2, formatRelativeTime as formatRelativeTime$2, ANONYMOUS_USERNAME, combineCSVData, WATCHER_HIDDEN_REPORTS, canViewPlatformAuditLogs, redirectToAuthSpaJoinTenant, redirectToAuthSpa, getAuthSpaJoinUrl, useOS, selectNumberOfActiveChatMessages, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, chatInputSliceActions, removeFile, WithFormPermissions, MENTOR_VISIBILITY } from '@iblai/web-utils';
7
7
  import { toast } from 'sonner';
8
8
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
9
9
  import * as ReactDOM from 'react-dom';
@@ -11,13 +11,13 @@ import ReactDOM__default from 'react-dom';
11
11
  import { z } from 'zod';
12
12
  import { RemoveScroll } from 'react-remove-scroll';
13
13
  import { skipToken } from '@reduxjs/toolkit/query';
14
+ import Link$2 from 'next/link';
15
+ import { useParams, useSearchParams, useRouter, usePathname } from 'next/navigation';
14
16
  import * as DialogPrimitive from '@radix-ui/react-dialog';
15
17
  import { createDialogScope } from '@radix-ui/react-dialog';
16
18
  import dynamic from 'next/dynamic';
17
19
  import { NotificationSourceTypeEnum, StateEnum, MentorVisibilityEnum, PromptVisibilityEnum } from '@iblai/iblai-api';
18
- import { useSearchParams, useRouter, usePathname, useParams } from 'next/navigation';
19
20
  import Image$4 from 'next/image';
20
- import Link$2 from 'next/link';
21
21
  import Script from 'next/script';
22
22
 
23
23
  function useIframeMessageHandler({ handlers, allowedOrigins, defaultHandler, }) {
@@ -55,6 +55,218 @@ function useIframeMessageHandler({ handlers, allowedOrigins, defaultHandler, })
55
55
  }, [allowedOrigins]);
56
56
  }
57
57
 
58
+ // Model wire keys whose raw form is not presentable, keyed lowercased and
59
+ // alphanumerics-only.
60
+ const IBL_AI_DISPLAY_NAME = 'ibl.ai';
61
+ const MODEL_DISPLAY_NAME_BY_KEY = {
62
+ iblai: IBL_AI_DISPLAY_NAME,
63
+ };
64
+ /**
65
+ * The label to show for a model when only its wire key is known.
66
+ *
67
+ * The LLM picker gets a `display_name` per model straight from the API, but
68
+ * mentor settings and transcripts persist only `llm_name` — a wire key — so
69
+ * surfaces that read from those have no label to fall back on and would
70
+ * render `iblai`. Most keys are already presentable (`gpt-4.1`), so this maps
71
+ * only the ones that are not and passes everything else through untouched.
72
+ */
73
+ function getLLMModelDisplayName(llmName) {
74
+ var _a;
75
+ const raw = llmName !== null && llmName !== void 0 ? llmName : '';
76
+ const normalized = raw.toLowerCase().replace(/[^a-z0-9]/g, '');
77
+ // ibl.ai keys are the brand followed by an optional variant — `iblai`,
78
+ // `iblai-pro`, `iblai-fast`, `IBL AI Pro`. Rebuild the label the model
79
+ // picker gets from the API (`ibl.ai Pro`) rather than collapsing every
80
+ // variant onto a bare `ibl.ai`, which would disagree with the picker.
81
+ const brand = /^(iblai|ibl)(.*)$/.exec(normalized);
82
+ const leadsWithBrand = brand &&
83
+ (brand[1] === 'iblai' ||
84
+ raw
85
+ .toLowerCase()
86
+ .split(/[^a-z0-9]+/)
87
+ .filter(Boolean)[0] === 'ibl');
88
+ if (leadsWithBrand) {
89
+ const variant = brand[2];
90
+ if (!variant)
91
+ return IBL_AI_DISPLAY_NAME;
92
+ // `ai` is part of the brand when the key spells it out (`ibl-ai-pro`).
93
+ const suffix = variant.startsWith('ai') && brand[1] === 'ibl' ? variant.slice(2) : variant;
94
+ if (!suffix)
95
+ return IBL_AI_DISPLAY_NAME;
96
+ return `${IBL_AI_DISPLAY_NAME} ${suffix.charAt(0).toUpperCase()}${suffix.slice(1)}`;
97
+ }
98
+ return (_a = MODEL_DISPLAY_NAME_BY_KEY[normalized]) !== null && _a !== void 0 ? _a : raw;
99
+ }
100
+
101
+ /** Loose key for matching a model across spellings: lower-case, alphanumerics only. */
102
+ const modelKey = (value) => (value !== null && value !== void 0 ? value : '').toLowerCase().replace(/[^a-z0-9]/g, '');
103
+ /**
104
+ * Model families → the provider keys the catalogue may list them under, for
105
+ * a model the catalogue does not name (a dated or "-latest" wire key, or a
106
+ * model the tenant enabled elsewhere). First key present in the catalogue
107
+ * wins; hosted variants (Azure, Bedrock, Groq) come after the vendor.
108
+ */
109
+ const MODEL_FAMILY_PROVIDERS = [
110
+ [/^(gpt|o[1-9]|chatgpt|davinci|text-embedding)/, ['openai', 'azure_openai', 'azure']],
111
+ [/^claude/, ['anthropic', 'bedrock']],
112
+ [/^(gemini|gemma|palm)/, ['google', 'vertex', 'vertexai', 'gemini']],
113
+ [/^(llama|meta)/, ['llama', 'meta', 'bedrock', 'groq']],
114
+ [/^(mistral|mixtral|codestral)/, ['mistral']],
115
+ [/^deepseek/, ['deepseek']],
116
+ [/^grok/, ['xai', 'grok']],
117
+ [/^(command|aya)/, ['cohere']],
118
+ [/^qwen/, ['qwen', 'alibaba']],
119
+ [/^(nova|titan)/, ['bedrock', 'amazon']],
120
+ ];
121
+ /** Provider keys a model of this family is listed under, in order of preference. */
122
+ function inferProviderKeysFromModel(model) {
123
+ const key = modelKey(model);
124
+ if (!key)
125
+ return [];
126
+ for (const [pattern, providers] of MODEL_FAMILY_PROVIDERS) {
127
+ if (pattern.test(key))
128
+ return providers;
129
+ }
130
+ return [];
131
+ }
132
+ /**
133
+ * A resolver from a provider key (the `llm_provider` mentor settings persist,
134
+ * e.g. `openai`) to the name and logo the backend publishes for it. A second,
135
+ * optional model key finds the provider whose `chat_models` list it, for
136
+ * payloads that name only the model (analytics transcript rows).
137
+ *
138
+ * Provider naming is backend-owned: the mentor-llms catalogue carries each
139
+ * provider's `logo` (and `display_name` where the API supplies one), so this
140
+ * reads the same RTK Query cache entry the LLM tab and model picker fill —
141
+ * matched on `name`. A key the catalogue does not list (the catalogue is
142
+ * still loading, or an on-device provider that has no backend row) resolves
143
+ * to the raw key and no logo, so callers degrade to plain text.
144
+ *
145
+ * Returns a function rather than a value so list renderers (a mentors table,
146
+ * a facet dropdown) can resolve many keys from one subscription.
147
+ */
148
+ function useLlmProviderCatalogue({ org, userId, mentorId }) {
149
+ const skip = !org || !userId || !mentorId;
150
+ const { data: providers } = useGetLlmsQuery({
151
+ org: org !== null && org !== void 0 ? org : '',
152
+ // @ts-expect-error - userId is not in the generated query args but the endpoint scopes by it
153
+ userId: userId !== null && userId !== void 0 ? userId : '',
154
+ mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '',
155
+ }, { skip });
156
+ // Indexed once per catalogue load so list renderers resolve each key in
157
+ // O(1). First row wins on a duplicate name, as `Array.find` would.
158
+ const rowsByName = useMemo(() => {
159
+ const byName = new Map();
160
+ if (!Array.isArray(providers))
161
+ return byName;
162
+ for (const row of providers) {
163
+ if ((row === null || row === void 0 ? void 0 : row.name) && !byName.has(row.name))
164
+ byName.set(row.name, row);
165
+ }
166
+ return byName;
167
+ }, [providers]);
168
+ // Model key → the provider row listing it. Keyed loosely (see `modelKey`):
169
+ // transcripts persist the model as it was displayed ("GPT-5.2") while the
170
+ // catalogue lists the wire key ("gpt-5.2"), so both spellings, and the
171
+ // display name, must find the row.
172
+ const rowsByModel = useMemo(() => {
173
+ var _a;
174
+ const byModel = new Map();
175
+ for (const row of rowsByName.values()) {
176
+ for (const model of (_a = row.chat_models) !== null && _a !== void 0 ? _a : []) {
177
+ for (const candidate of [model === null || model === void 0 ? void 0 : model.llm_name, model === null || model === void 0 ? void 0 : model.display_name]) {
178
+ const key = modelKey(candidate);
179
+ if (key && !byModel.has(key))
180
+ byModel.set(key, row);
181
+ }
182
+ }
183
+ }
184
+ return byModel;
185
+ }, [rowsByName]);
186
+ // Provider rows keyed loosely by name and display name ("OpenAI" → openai).
187
+ const rowsByLooseName = useMemo(() => {
188
+ const byLoose = new Map();
189
+ for (const row of rowsByName.values()) {
190
+ for (const candidate of [row.name, row.display_name]) {
191
+ const key = modelKey(candidate);
192
+ if (key && !byLoose.has(key))
193
+ byLoose.set(key, row);
194
+ }
195
+ }
196
+ return byLoose;
197
+ }, [rowsByName]);
198
+ const rowForModel = useCallback((llmName) => {
199
+ var _a;
200
+ const wanted = modelKey(llmName);
201
+ if (!wanted)
202
+ return undefined;
203
+ const exact = rowsByModel.get(wanted);
204
+ if (exact)
205
+ return exact;
206
+ // A dated or "-latest" wire key ("gpt-5.2-2026-03-01") still names
207
+ // the family the catalogue lists ("gpt-5.2"), and vice versa.
208
+ for (const [listed, row] of rowsByModel) {
209
+ if (listed.startsWith(wanted) || wanted.startsWith(listed))
210
+ return row;
211
+ }
212
+ for (const provider of inferProviderKeysFromModel(llmName)) {
213
+ const row = (_a = rowsByName.get(provider)) !== null && _a !== void 0 ? _a : rowsByLooseName.get(modelKey(provider));
214
+ if (row)
215
+ return row;
216
+ }
217
+ return undefined;
218
+ }, [rowsByModel, rowsByName, rowsByLooseName]);
219
+ return useCallback((llmProvider, llmName) => {
220
+ var _a, _b, _c, _d, _e;
221
+ const key = llmProvider !== null && llmProvider !== void 0 ? llmProvider : '';
222
+ const row = (_c = (_b = (_a = rowsByName.get(key)) !== null && _a !== void 0 ? _a : rowsByName.get(key.toLowerCase())) !== null && _b !== void 0 ? _b : rowsByLooseName.get(modelKey(key))) !== null && _c !== void 0 ? _c : rowForModel(llmName);
223
+ return {
224
+ logo: (row === null || row === void 0 ? void 0 : row.logo) || null,
225
+ displayName: ((_d = row === null || row === void 0 ? void 0 : row.display_name) === null || _d === void 0 ? void 0 : _d.trim()) || ((_e = row === null || row === void 0 ? void 0 : row.name) === null || _e === void 0 ? void 0 : _e.trim()) || key,
226
+ };
227
+ }, [rowsByName, rowsByLooseName, rowForModel]);
228
+ }
229
+ const clean$1 = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || undefined;
230
+ /**
231
+ * The label to show for a model.
232
+ *
233
+ * Settings persist only `llm_name` — a wire key like `iblai-pro` or
234
+ * `claude-haiku-4-5-20251001` — while the model picker reads the catalogue,
235
+ * which carries a `display_name` ("ibl.ai Pro", "Claude Haiku 4.5"). Resolve
236
+ * the label in three steps:
237
+ *
238
+ * 1. `llm_config.display_name` from settings, when the caller has it.
239
+ * 2. The mentor-llms catalogue, matched on `llm_name`. Only queried when step
240
+ * 1 came back empty.
241
+ * 3. `getLLMModelDisplayName`, which tidies the handful of keys whose raw
242
+ * form is unpresentable — used while the catalogue is loading or failed.
243
+ */
244
+ function useLlmDisplayName({ llmName, llmConfig, org, userId, mentorId, }) {
245
+ var _a;
246
+ const fromConfig = clean$1(llmConfig === null || llmConfig === void 0 ? void 0 : llmConfig.display_name);
247
+ const skip = Boolean(fromConfig) || !llmName || !org || !userId || !mentorId;
248
+ const { data: providers } = useGetLlmsQuery({
249
+ org: org !== null && org !== void 0 ? org : '',
250
+ // @ts-expect-error - userId is not in the generated query args but the endpoint scopes by it
251
+ userId: userId !== null && userId !== void 0 ? userId : '',
252
+ mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '',
253
+ }, { skip });
254
+ const fromCatalogue = useMemo(() => {
255
+ var _a;
256
+ if (skip || !llmName || !Array.isArray(providers))
257
+ return undefined;
258
+ const wanted = modelKey(llmName);
259
+ for (const provider of providers) {
260
+ const match = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.find((model) => modelKey(model === null || model === void 0 ? void 0 : model.llm_name) === wanted || modelKey(model === null || model === void 0 ? void 0 : model.display_name) === wanted);
261
+ const label = clean$1(match === null || match === void 0 ? void 0 : match.display_name);
262
+ if (label)
263
+ return label;
264
+ }
265
+ return undefined;
266
+ }, [skip, providers, llmName]);
267
+ return (_a = fromConfig !== null && fromConfig !== void 0 ? fromConfig : fromCatalogue) !== null && _a !== void 0 ? _a : getLLMModelDisplayName(llmName);
268
+ }
269
+
58
270
  var _a;
59
271
  const dl$1 = dataLayer;
60
272
  /**
@@ -2264,7 +2476,7 @@ var analyticsAnalyticsTranscriptsStats$3 = {
2264
2476
  labelUsername: "Username:",
2265
2477
  labelAgent: "Agent:",
2266
2478
  labelModel: "Model:",
2267
- labelModelCard: "model:",
2479
+ labelModelCard: "Model:",
2268
2480
  labelUser: "User:",
2269
2481
  roleUser: "User",
2270
2482
  roleAgent: "Agent",
@@ -2306,6 +2518,37 @@ var analyticsAnalyticsTranscriptsStats$3 = {
2306
2518
  avgCostPerConversationDescription: "What a typical conversation costs in model usage.",
2307
2519
  avgRatingDescription: "Average rating users gave their conversations, on a scale of 1 to 5."
2308
2520
  };
2521
+ var analyticsTranscriptTurnDetails$3 = {
2522
+ showDetails: "Show Details",
2523
+ hideDetails: "Hide Details",
2524
+ metadata: "Metadata",
2525
+ requestContext: "Request Context",
2526
+ model: "Model",
2527
+ provider: "Provider",
2528
+ temperature: "Temperature",
2529
+ client: "Client",
2530
+ sourcesBadge: "Documents · {count}",
2531
+ toolsBadge: "Tools · {count}",
2532
+ yes: "Yes",
2533
+ no: "No",
2534
+ redacted: "Redacted",
2535
+ fieldSession: "Session",
2536
+ fieldCallerVerified: "Caller Verified",
2537
+ fieldFiles: "Files",
2538
+ fieldFile: "File",
2539
+ fieldSystemPrompt: "System Prompt",
2540
+ fieldFlow: "Flow",
2541
+ fieldToken: "Token",
2542
+ fieldTenant: "Tenant",
2543
+ fieldPathway: "Pathway",
2544
+ fieldUsername: "Username",
2545
+ fieldName: "Name",
2546
+ fieldCanvas: "Canvas",
2547
+ fieldProactivePrompt: "Proactive Prompt",
2548
+ fieldGrader: "Grader",
2549
+ fieldDatasets: "Datasets",
2550
+ fieldDateTime: "Date & Time"
2551
+ };
2309
2552
  var analyticsAnalyticsUsersStats$3 = {
2310
2553
  usersLoggedInNow: "Users logged in right now",
2311
2554
  usersLoggedInPast30Days: "Users logged in past 30 days",
@@ -3796,7 +4039,8 @@ var historyTabLabels$3 = {
3796
4039
  noResponse: "No response available",
3797
4040
  selectPrompt: "Select a conversation to view details.",
3798
4041
  aiAgent: "AI Agent",
3799
- noConversations: "No conversations found"
4042
+ noConversations: "No conversations found",
4043
+ documentsChip: "Documents · {count}"
3800
4044
  },
3801
4045
  summary: {
3802
4046
  notAvailable: "Summary not available"
@@ -8199,6 +8443,7 @@ var en = {
8199
8443
  analyticsAnalyticsReports: analyticsAnalyticsReports$3,
8200
8444
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$3,
8201
8445
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$3,
8446
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$3,
8202
8447
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$3,
8203
8448
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$3,
8204
8449
  analyticsStatCard: analyticsStatCard$3,
@@ -9422,7 +9667,7 @@ var analyticsAnalyticsTranscriptsStats$2 = {
9422
9667
  labelUsername: "Nom d'utilisateur :",
9423
9668
  labelAgent: "Agent :",
9424
9669
  labelModel: "Modèle :",
9425
- labelModelCard: "modèle :",
9670
+ labelModelCard: "Modèle :",
9426
9671
  labelUser: "Utilisateur :",
9427
9672
  roleUser: "Utilisateur",
9428
9673
  roleAgent: "Agent",
@@ -9464,6 +9709,37 @@ var analyticsAnalyticsTranscriptsStats$2 = {
9464
9709
  avgCostPerConversationDescription: "Coût d’utilisation des modèles pour une conversation type.",
9465
9710
  avgRatingDescription: "Note moyenne attribuée par les utilisateurs à leurs conversations, sur une échelle de 1 à 5."
9466
9711
  };
9712
+ var analyticsTranscriptTurnDetails$2 = {
9713
+ showDetails: "Afficher les détails",
9714
+ hideDetails: "Masquer les détails",
9715
+ metadata: "Métadonnées",
9716
+ requestContext: "Contexte de la requête",
9717
+ model: "Modèle",
9718
+ provider: "Fournisseur",
9719
+ temperature: "Température",
9720
+ client: "Client",
9721
+ sourcesBadge: "Documents · {count}",
9722
+ toolsBadge: "Outils · {count}",
9723
+ yes: "Oui",
9724
+ no: "Non",
9725
+ redacted: "Masqué",
9726
+ fieldSession: "Session",
9727
+ fieldCallerVerified: "Appelant vérifié",
9728
+ fieldFiles: "Fichiers",
9729
+ fieldFile: "Fichier",
9730
+ fieldSystemPrompt: "Prompt système",
9731
+ fieldFlow: "Flux",
9732
+ fieldToken: "Jeton",
9733
+ fieldTenant: "Locataire",
9734
+ fieldPathway: "Parcours",
9735
+ fieldUsername: "Nom d’utilisateur",
9736
+ fieldName: "Nom",
9737
+ fieldCanvas: "Canvas",
9738
+ fieldProactivePrompt: "Prompt proactif",
9739
+ fieldGrader: "Correcteur",
9740
+ fieldDatasets: "Jeux de données",
9741
+ fieldDateTime: "Date et heure"
9742
+ };
9467
9743
  var analyticsAnalyticsUsersStats$2 = {
9468
9744
  usersLoggedInNow: "Utilisateurs connectés en ce moment",
9469
9745
  usersLoggedInPast30Days: "Utilisateurs connectés ces 30 derniers jours",
@@ -11120,7 +11396,8 @@ var historyTabLabels$2 = {
11120
11396
  noResponse: "Aucune réponse disponible",
11121
11397
  selectPrompt: "Sélectionnez une conversation pour afficher les détails.",
11122
11398
  aiAgent: "AI Agent",
11123
- noConversations: "Aucune conversation trouvée"
11399
+ noConversations: "Aucune conversation trouvée",
11400
+ documentsChip: "Documents · {count}"
11124
11401
  },
11125
11402
  summary: {
11126
11403
  notAvailable: "Résumé non disponible"
@@ -15357,6 +15634,7 @@ var fr = {
15357
15634
  analyticsAnalyticsReports: analyticsAnalyticsReports$2,
15358
15635
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$2,
15359
15636
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$2,
15637
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$2,
15360
15638
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$2,
15361
15639
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$2,
15362
15640
  analyticsStatCard: analyticsStatCard$2,
@@ -16580,7 +16858,7 @@ var analyticsAnalyticsTranscriptsStats$1 = {
16580
16858
  labelUsername: "Nombre de usuario:",
16581
16859
  labelAgent: "Agente:",
16582
16860
  labelModel: "Modelo:",
16583
- labelModelCard: "modelo:",
16861
+ labelModelCard: "Modelo:",
16584
16862
  labelUser: "Usuario:",
16585
16863
  roleUser: "Usuario",
16586
16864
  roleAgent: "Agente",
@@ -16622,6 +16900,37 @@ var analyticsAnalyticsTranscriptsStats$1 = {
16622
16900
  avgCostPerConversationDescription: "Lo que cuesta una conversación típica en uso de modelos.",
16623
16901
  avgRatingDescription: "Valoración media que los usuarios dieron a sus conversaciones, en una escala de 1 a 5."
16624
16902
  };
16903
+ var analyticsTranscriptTurnDetails$1 = {
16904
+ showDetails: "Mostrar detalles",
16905
+ hideDetails: "Ocultar detalles",
16906
+ metadata: "Metadatos",
16907
+ requestContext: "Contexto de la solicitud",
16908
+ model: "Modelo",
16909
+ provider: "Proveedor",
16910
+ temperature: "Temperatura",
16911
+ client: "Cliente",
16912
+ sourcesBadge: "Documentos · {count}",
16913
+ toolsBadge: "Herramientas · {count}",
16914
+ yes: "Sí",
16915
+ no: "No",
16916
+ redacted: "Oculto",
16917
+ fieldSession: "Sesión",
16918
+ fieldCallerVerified: "Llamante verificado",
16919
+ fieldFiles: "Archivos",
16920
+ fieldFile: "Archivo",
16921
+ fieldSystemPrompt: "Prompt del sistema",
16922
+ fieldFlow: "Flujo",
16923
+ fieldToken: "Token",
16924
+ fieldTenant: "Inquilino",
16925
+ fieldPathway: "Ruta",
16926
+ fieldUsername: "Nombre de usuario",
16927
+ fieldName: "Nombre",
16928
+ fieldCanvas: "Canvas",
16929
+ fieldProactivePrompt: "Prompt proactivo",
16930
+ fieldGrader: "Calificador",
16931
+ fieldDatasets: "Conjuntos de datos",
16932
+ fieldDateTime: "Fecha y hora"
16933
+ };
16625
16934
  var analyticsAnalyticsUsersStats$1 = {
16626
16935
  usersLoggedInNow: "Usuarios conectados ahora mismo",
16627
16936
  usersLoggedInPast30Days: "Usuarios conectados en los últimos 30 días",
@@ -18278,7 +18587,8 @@ var historyTabLabels$1 = {
18278
18587
  noResponse: "No hay respuesta disponible",
18279
18588
  selectPrompt: "Selecciona una conversación para ver los detalles.",
18280
18589
  aiAgent: "AI Agent",
18281
- noConversations: "No se encontraron conversaciones"
18590
+ noConversations: "No se encontraron conversaciones",
18591
+ documentsChip: "Documentos · {count}"
18282
18592
  },
18283
18593
  summary: {
18284
18594
  notAvailable: "Resumen no disponible"
@@ -22515,6 +22825,7 @@ var es = {
22515
22825
  analyticsAnalyticsReports: analyticsAnalyticsReports$1,
22516
22826
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$1,
22517
22827
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$1,
22828
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$1,
22518
22829
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$1,
22519
22830
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$1,
22520
22831
  analyticsStatCard: analyticsStatCard$1,
@@ -23780,6 +24091,37 @@ var analyticsAnalyticsTranscriptsStats = {
23780
24091
  avgCostPerConversationDescription: "一次典型对话所产生的模型使用成本。",
23781
24092
  avgRatingDescription: "用户对其对话给出的平均评分,满分 5 分。"
23782
24093
  };
24094
+ var analyticsTranscriptTurnDetails = {
24095
+ showDetails: "显示详情",
24096
+ hideDetails: "隐藏详情",
24097
+ metadata: "元数据",
24098
+ requestContext: "请求上下文",
24099
+ model: "模型",
24100
+ provider: "提供商",
24101
+ temperature: "温度",
24102
+ client: "客户端",
24103
+ sourcesBadge: "文档 · {count}",
24104
+ toolsBadge: "工具 · {count}",
24105
+ yes: "是",
24106
+ no: "否",
24107
+ redacted: "已隐藏",
24108
+ fieldSession: "会话",
24109
+ fieldCallerVerified: "调用者已验证",
24110
+ fieldFiles: "文件",
24111
+ fieldFile: "文件",
24112
+ fieldSystemPrompt: "系统提示",
24113
+ fieldFlow: "流程",
24114
+ fieldToken: "令牌",
24115
+ fieldTenant: "租户",
24116
+ fieldPathway: "路径",
24117
+ fieldUsername: "用户名",
24118
+ fieldName: "名称",
24119
+ fieldCanvas: "画布",
24120
+ fieldProactivePrompt: "主动提示",
24121
+ fieldGrader: "评分器",
24122
+ fieldDatasets: "数据集",
24123
+ fieldDateTime: "日期和时间"
24124
+ };
23783
24125
  var analyticsAnalyticsUsersStats = {
23784
24126
  usersLoggedInNow: "当前在线用户",
23785
24127
  usersLoggedInPast30Days: "过去 30 天登录用户",
@@ -25436,7 +25778,8 @@ var historyTabLabels = {
25436
25778
  noResponse: "暂无回复",
25437
25779
  selectPrompt: "选择一条对话以查看详情。",
25438
25780
  aiAgent: "AI Agent",
25439
- noConversations: "未找到对话"
25781
+ noConversations: "未找到对话",
25782
+ documentsChip: "文档 · {count}"
25440
25783
  },
25441
25784
  summary: {
25442
25785
  notAvailable: "摘要不可用"
@@ -29673,6 +30016,7 @@ var zh = {
29673
30016
  analyticsAnalyticsReports: analyticsAnalyticsReports,
29674
30017
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats,
29675
30018
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats,
30019
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails,
29676
30020
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats,
29677
30021
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown,
29678
30022
  analyticsStatCard: analyticsStatCard,
@@ -74459,7 +74803,7 @@ var isArray$f = function isArray(arr) {
74459
74803
  return toStr.call(arr) === '[object Array]';
74460
74804
  };
74461
74805
 
74462
- var isPlainObject$5 = function isPlainObject(obj) {
74806
+ var isPlainObject$6 = function isPlainObject(obj) {
74463
74807
  if (!obj || toStr.call(obj) !== '[object Object]') {
74464
74808
  return false;
74465
74809
  }
@@ -74538,12 +74882,12 @@ var extend$3 = function extend() {
74538
74882
  // Prevent never-ending loop
74539
74883
  if (target !== copy) {
74540
74884
  // Recurse if we're merging plain objects or arrays
74541
- if (deep && copy && (isPlainObject$5(copy) || (copyIsArray = isArray$f(copy)))) {
74885
+ if (deep && copy && (isPlainObject$6(copy) || (copyIsArray = isArray$f(copy)))) {
74542
74886
  if (copyIsArray) {
74543
74887
  copyIsArray = false;
74544
74888
  clone = src && isArray$f(src) ? src : [];
74545
74889
  } else {
74546
- clone = src && isPlainObject$5(src) ? src : {};
74890
+ clone = src && isPlainObject$6(src) ? src : {};
74547
74891
  }
74548
74892
 
74549
74893
  // Never move original objects, clone them
@@ -74564,7 +74908,7 @@ var extend$3 = function extend() {
74564
74908
 
74565
74909
  var extend$2 = /*@__PURE__*/getDefaultExportFromCjs(extend$3);
74566
74910
 
74567
- function isPlainObject$4(value) {
74911
+ function isPlainObject$5(value) {
74568
74912
  if (typeof value !== 'object' || value === null) {
74569
74913
  return false;
74570
74914
  }
@@ -75635,7 +75979,7 @@ class Processor extends CallableInstance {
75635
75979
  else if (parameters.length > 0) {
75636
75980
  let [primary, ...rest] = parameters;
75637
75981
  const currentPrimary = attachers[entryIndex][1];
75638
- if (isPlainObject$4(currentPrimary) && isPlainObject$4(primary)) {
75982
+ if (isPlainObject$5(currentPrimary) && isPlainObject$5(primary)) {
75639
75983
  primary = extend$2(true, currentPrimary, primary);
75640
75984
  }
75641
75985
 
@@ -75727,7 +76071,7 @@ function assertUnfrozen(name, frozen) {
75727
76071
  function assertNode(node) {
75728
76072
  // `isPlainObj` unfortunately uses `any` instead of `unknown`.
75729
76073
  // type-coverage:ignore-next-line
75730
- if (!isPlainObject$4(node) || typeof node.type !== 'string') {
76074
+ if (!isPlainObject$5(node) || typeof node.type !== 'string') {
75731
76075
  throw new TypeError('Expected node, got `' + node + '`')
75732
76076
  // Fine.
75733
76077
  }
@@ -136692,7 +137036,7 @@ var objectCtorString = funcToString.call(Object);
136692
137036
  * _.isPlainObject(Object.create(null));
136693
137037
  * // => true
136694
137038
  */
136695
- function isPlainObject$3(value) {
137039
+ function isPlainObject$4(value) {
136696
137040
  if (!isObjectLike$1(value) || baseGetTag$1(value) != objectTag) {
136697
137041
  return false;
136698
137042
  }
@@ -136705,9 +137049,9 @@ function isPlainObject$3(value) {
136705
137049
  funcToString.call(Ctor) == objectCtorString;
136706
137050
  }
136707
137051
 
136708
- var isPlainObject_1 = isPlainObject$3;
137052
+ var isPlainObject_1 = isPlainObject$4;
136709
137053
 
136710
- var isPlainObject$2 = /*@__PURE__*/getDefaultExportFromCjs(isPlainObject_1);
137054
+ var isPlainObject$3 = /*@__PURE__*/getDefaultExportFromCjs(isPlainObject_1);
136711
137055
 
136712
137056
  var baseGetTag = _baseGetTag,
136713
137057
  isObjectLike = isObjectLike_1;
@@ -136924,7 +137268,7 @@ function Shape(_ref2) {
136924
137268
  shape = /*#__PURE__*/cloneElement(option, _objectSpread$i(_objectSpread$i({}, props), getPropsFromShapeOption(option)));
136925
137269
  } else if (isFunction$3(option)) {
136926
137270
  shape = option(props);
136927
- } else if (isPlainObject$2(option) && !isBoolean(option)) {
137271
+ } else if (isPlainObject$3(option) && !isBoolean(option)) {
136928
137272
  var nextProps = propTransformer(option, props);
136929
137273
  shape = /*#__PURE__*/React__default.createElement(ShapeSelector, {
136930
137274
  shapeType: shapeType,
@@ -185485,6 +185829,100 @@ function Markdown({ children, className }) {
185485
185829
  return (jsx("div", { className: className, children: jsx(Markdown$1, { components: components, children: children }) }));
185486
185830
  }
185487
185831
 
185832
+ function FileCard({ fileName, fileType }) {
185833
+ const getFileIcon = (fileName, fileType) => {
185834
+ const type = fileType.toLowerCase();
185835
+ const name = fileName.toLowerCase();
185836
+ const extension = name.split('.').pop() || '';
185837
+ // Check by MIME type first
185838
+ if (type.includes('pdf')) {
185839
+ return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
185840
+ }
185841
+ else if (type.includes('image')) {
185842
+ return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
185843
+ }
185844
+ else if (type.includes('audio')) {
185845
+ return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
185846
+ }
185847
+ else if (type.includes('video')) {
185848
+ return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
185849
+ }
185850
+ else if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
185851
+ return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
185852
+ }
185853
+ else if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
185854
+ return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
185855
+ }
185856
+ else if (type.includes('javascript') ||
185857
+ type.includes('typescript') ||
185858
+ type.includes('html') ||
185859
+ type.includes('css') ||
185860
+ type.includes('json')) {
185861
+ return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
185862
+ }
185863
+ // If MIME type doesn't match, check by extension
185864
+ if (['doc', 'docx', 'txt', 'rtf'].includes(extension)) {
185865
+ return jsx(FileText, { className: "h-5 w-5 text-blue-500" });
185866
+ }
185867
+ else if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'].includes(extension)) {
185868
+ return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
185869
+ }
185870
+ else if (['pdf'].includes(extension)) {
185871
+ return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
185872
+ }
185873
+ else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
185874
+ return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
185875
+ }
185876
+ else if (['mp4', 'webm', 'avi', 'mov'].includes(extension)) {
185877
+ return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
185878
+ }
185879
+ else if (['xls', 'xlsx', 'csv'].includes(extension)) {
185880
+ return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
185881
+ }
185882
+ else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
185883
+ return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
185884
+ }
185885
+ else if (['js', 'ts', 'jsx', 'tsx', 'html', 'css', 'json', 'py', 'java', 'c', 'cpp'].includes(extension)) {
185886
+ return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
185887
+ }
185888
+ // Default icon
185889
+ return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
185890
+ };
185891
+ return (jsxs("div", { className: "mb-2 flex max-w-xs items-center rounded-lg border border-gray-200 bg-white p-3", children: [jsx("div", { className: "mr-3 shrink-0 rounded-lg bg-blue-100 p-2 text-blue-600", children: getFileIcon(fileName, fileType) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "truncate text-sm font-medium text-gray-900", children: fileName }), jsx("p", { className: "truncate text-xs text-gray-500", children: fileType })] })] }));
185892
+ }
185893
+
185894
+ function ImageMessage({ url, fileName, setPreviewImage }) {
185895
+ const [hasError, setHasError] = useState(false);
185896
+ if (hasError || !url) {
185897
+ return jsx(FileCard, { fileName: fileName, fileType: "image/*" });
185898
+ }
185899
+ return (jsx("div", { className: "h-full max-h-64 w-full max-w-96 overflow-hidden rounded-lg", children: jsx("div", { className: "relative", children: jsx("img", { src: url, alt: fileName, className: "h-auto w-full cursor-pointer object-contain p-2", onClick: () => setPreviewImage(url), onError: () => setHasError(true) }) }) }));
185900
+ }
185901
+
185902
+ const openInNewTab = (url) => {
185903
+ window.open(url, '_blank', 'noopener,noreferrer');
185904
+ };
185905
+ /**
185906
+ * Files on an admin transcript, in the chat's own attachment idiom: images
185907
+ * preview like a sent image, everything else is the chat's file card. A card
185908
+ * with a URL opens the file in a new tab (presigned links expire, so nothing
185909
+ * is cached here).
185910
+ */
185911
+ function TranscriptFileCards({ files, className, }) {
185912
+ const shown = files.filter((file) => file === null || file === void 0 ? void 0 : file.name);
185913
+ if (shown.length === 0)
185914
+ return null;
185915
+ return (jsx("div", { className: className !== null && className !== void 0 ? className : 'mt-2 flex flex-wrap items-start gap-2', "data-testid": "transcript-file-cards", children: shown.map((file, index) => {
185916
+ var _a;
185917
+ const contentType = (_a = file.contentType) !== null && _a !== void 0 ? _a : '';
185918
+ if (file.url && contentType.startsWith('image/')) {
185919
+ return (jsx(ImageMessage, { url: file.url, fileName: file.name, setPreviewImage: openInNewTab }, `${file.name}-${index}`));
185920
+ }
185921
+ const card = jsx(FileCard, { fileName: file.name, fileType: contentType || 'file' });
185922
+ return file.url ? (jsx("a", { href: file.url, target: "_blank", rel: "noopener noreferrer", className: "block hover:opacity-90", "aria-label": `Open ${file.name}`, onClick: (event) => event.stopPropagation(), children: card }, `${file.name}-${index}`)) : (jsx("div", { children: card }, `${file.name}-${index}`));
185923
+ }) }));
185924
+ }
185925
+
185488
185926
  /**
185489
185927
  * Whether the dropdown is open, closing on a pointer press outside `ref` or on
185490
185928
  * Escape.
@@ -186090,8 +186528,12 @@ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
186090
186528
  ? (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || fallbackEmail || username
186091
186529
  : t('chatHistoryChatHistoryTab.you');
186092
186530
  return (jsx("div", { className: "space-y-6", children: messages.map((message, index) => {
186093
- var _a, _b;
186094
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium break-all text-gray-700", children: humanLabel }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_b = message.ai) !== null && _b !== void 0 ? _b : '' })] })] })] }, index));
186531
+ var _a, _b, _c;
186532
+ return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium break-all text-gray-700", children: humanLabel }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' }), jsx(TranscriptFileCards, { files: ((_b = message.human_files) !== null && _b !== void 0 ? _b : []).map((file) => ({
186533
+ name: file.name,
186534
+ contentType: file.content_type,
186535
+ url: file.url,
186536
+ })) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_c = message.ai) !== null && _c !== void 0 ? _c : '' })] })] })] }, index));
186095
186537
  }) }));
186096
186538
  }
186097
186539
  /**
@@ -187911,6 +188353,412 @@ function InvitedUsersDialog({ tenant, onClose }) {
187911
188353
  }, children: jsx(DialogPortal, { forceMount: true, children: jsxs(DialogContent, { "aria-label": t('profileInvitedUsers.dialogAriaLabel'), className: "max-w-sm rounded-2xl px-6 py-8 pointer-events-auto", "aria-describedby": "invited-users-dialog", children: [jsx(DialogHeader, { className: "relative flex-row justify-between", children: jsx(DialogTitle, { className: "text-blue-600 text-xl font-semibold", children: t('profileInvitedUsers.title') }) }), jsxs("div", { className: "mt-4 space-y-1", children: [jsx("p", { className: "text-gray-500 text-sm", children: t('profileInvitedUsers.pendingDescription') }), jsx("form", { children: jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('profileInvitedUsers.searchPlaceholder'), className: "mt-2" }) })] }), jsxs("div", { children: [jsx("h2", { className: "text-md text-gray-600 font-semibold mb-4", children: t('profileInvitedUsers.emailHeading') }), invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.map((user) => (jsx("div", { className: "border-b-gray-50 pb-2 mb-4 text-sm text-gray-600 dark:text-gray-400", children: user.email }, user.id))), (invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.length) === 0 && (jsx("p", { className: "text-gray", children: t('profileInvitedUsers.noRecords') }))] })] }) }) }));
187912
188354
  }
187913
188355
 
188356
+ function DocumentSidebar({ isModal = false, sessionId = '', documents, }) {
188357
+ const t = useT();
188358
+ const [isCollapsed, setIsCollapsed] = React.useState(false);
188359
+ const params = useParams();
188360
+ const username = useUsername();
188361
+ const isStreaming = useSelector(selectStreaming);
188362
+ const hasOwnDocuments = documents !== undefined;
188363
+ const { data: sessionDocuments, refetch, status, } = useGetVectorDocumentsQuery({
188364
+ org: params.tenantKey,
188365
+ sessionId: sessionId,
188366
+ // @ts-ignore
188367
+ userId: username,
188368
+ }, {
188369
+ skip: hasOwnDocuments || !params.tenantKey || !sessionId || !username || !isLoggedIn(),
188370
+ });
188371
+ const vectorDocuments = hasOwnDocuments
188372
+ ? (documents !== null && documents !== void 0 ? documents : undefined)
188373
+ : sessionDocuments;
188374
+ React.useEffect(() => {
188375
+ if (!hasOwnDocuments && !isStreaming && isLoggedIn() && status !== 'uninitialized') {
188376
+ refetch();
188377
+ }
188378
+ }, [hasOwnDocuments, isStreaming, refetch]);
188379
+ const toggleCollapse = () => {
188380
+ setIsCollapsed(!isCollapsed);
188381
+ };
188382
+ return (jsx(Fragment$1, { children: (vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length) && vectorDocuments.length > 0 ? (jsx("aside", { className: cn('transition-all duration-300 ease-in-out', isModal
188383
+ ? 'h-full max-h-full w-full overflow-auto'
188384
+ : 'hidden rounded-tl-lg rounded-bl-lg md:hidden lg:block', !isModal && (isCollapsed ? 'w-[40px]' : 'w-[380px]')), style: {
188385
+ backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
188386
+ // In the dialog the parent bounds the height; the rail caps itself.
188387
+ maxHeight: isModal ? undefined : 'calc(100vh - 80px)',
188388
+ }, children: jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "min-h-0 flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
188389
+ var _a, _b;
188390
+ return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
188391
+ }) }) }) }))] }) })) : null }));
188392
+ }
188393
+
188394
+ /**
188395
+ * The chat's "Retrieved Documents" control: an outline button opening the
188396
+ * documents dialog (`DocumentSidebar` in modal mode). Fed by the session's
188397
+ * vector results by default, or by `documents` the host already has — the
188398
+ * admin transcript surfaces pass a turn's or a conversation's documents, so
188399
+ * one component shows retrieved documents everywhere.
188400
+ */
188401
+ function RetrievedDocumentsButton({ sessionId = '', documents, className, label, }) {
188402
+ const t = useT();
188403
+ const [isOpen, setIsOpen] = useState(false);
188404
+ const params = useParams();
188405
+ const username = useUsername();
188406
+ const isStreaming = useSelector(selectStreaming);
188407
+ const hasOwnDocuments = documents !== undefined;
188408
+ const { data: sessionDocuments, refetch, status, } = useGetVectorDocumentsQuery({
188409
+ org: params.tenantKey,
188410
+ sessionId: sessionId,
188411
+ // @ts-ignore
188412
+ userId: username,
188413
+ }, {
188414
+ skip: hasOwnDocuments || !params.tenantKey || !sessionId || !username || !isLoggedIn(),
188415
+ });
188416
+ const vectorDocuments = hasOwnDocuments
188417
+ ? (documents !== null && documents !== void 0 ? documents : undefined)
188418
+ : sessionDocuments;
188419
+ useEffect(() => {
188420
+ if (!hasOwnDocuments && !isStreaming && isLoggedIn() && status !== 'uninitialized') {
188421
+ refetch();
188422
+ }
188423
+ }, [hasOwnDocuments, isStreaming, refetch]);
188424
+ if (!(vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length)) {
188425
+ return null;
188426
+ }
188427
+ return (jsxs(Fragment$1, { children: [jsxs(Button$1, { variant: "outline", size: "sm", className: cn('flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50', className), onClick: (event) => {
188428
+ // Safe inside clickable transcript rows: the row must not react.
188429
+ event.stopPropagation();
188430
+ setIsOpen(true);
188431
+ }, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
188432
+ }
188433
+
188434
+ var COLLAPSIBLE_NAME = "Collapsible";
188435
+ var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
188436
+ var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
188437
+ var Collapsible$1 = React.forwardRef(
188438
+ (props, forwardedRef) => {
188439
+ const {
188440
+ __scopeCollapsible,
188441
+ open: openProp,
188442
+ defaultOpen,
188443
+ disabled,
188444
+ onOpenChange,
188445
+ ...collapsibleProps
188446
+ } = props;
188447
+ const [open, setOpen] = useControllableState$2({
188448
+ prop: openProp,
188449
+ defaultProp: defaultOpen ?? false,
188450
+ onChange: onOpenChange,
188451
+ caller: COLLAPSIBLE_NAME
188452
+ });
188453
+ return /* @__PURE__ */ jsx(
188454
+ CollapsibleProvider,
188455
+ {
188456
+ scope: __scopeCollapsible,
188457
+ disabled,
188458
+ contentId: useId$1(),
188459
+ open,
188460
+ onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
188461
+ children: /* @__PURE__ */ jsx(
188462
+ Primitive$4.div,
188463
+ {
188464
+ "data-state": getState$1(open),
188465
+ "data-disabled": disabled ? "" : void 0,
188466
+ ...collapsibleProps,
188467
+ ref: forwardedRef
188468
+ }
188469
+ )
188470
+ }
188471
+ );
188472
+ }
188473
+ );
188474
+ Collapsible$1.displayName = COLLAPSIBLE_NAME;
188475
+ var TRIGGER_NAME$1 = "CollapsibleTrigger";
188476
+ var CollapsibleTrigger$1 = React.forwardRef(
188477
+ (props, forwardedRef) => {
188478
+ const { __scopeCollapsible, ...triggerProps } = props;
188479
+ const context = useCollapsibleContext(TRIGGER_NAME$1, __scopeCollapsible);
188480
+ return /* @__PURE__ */ jsx(
188481
+ Primitive$4.button,
188482
+ {
188483
+ type: "button",
188484
+ "aria-controls": context.contentId,
188485
+ "aria-expanded": context.open || false,
188486
+ "data-state": getState$1(context.open),
188487
+ "data-disabled": context.disabled ? "" : void 0,
188488
+ disabled: context.disabled,
188489
+ ...triggerProps,
188490
+ ref: forwardedRef,
188491
+ onClick: composeEventHandlers$2(props.onClick, context.onOpenToggle)
188492
+ }
188493
+ );
188494
+ }
188495
+ );
188496
+ CollapsibleTrigger$1.displayName = TRIGGER_NAME$1;
188497
+ var CONTENT_NAME$1 = "CollapsibleContent";
188498
+ var CollapsibleContent$1 = React.forwardRef(
188499
+ (props, forwardedRef) => {
188500
+ const { forceMount, ...contentProps } = props;
188501
+ const context = useCollapsibleContext(CONTENT_NAME$1, props.__scopeCollapsible);
188502
+ return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
188503
+ }
188504
+ );
188505
+ CollapsibleContent$1.displayName = CONTENT_NAME$1;
188506
+ var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
188507
+ const { __scopeCollapsible, present, children, ...contentProps } = props;
188508
+ const context = useCollapsibleContext(CONTENT_NAME$1, __scopeCollapsible);
188509
+ const [isPresent, setIsPresent] = React.useState(present);
188510
+ const ref = React.useRef(null);
188511
+ const composedRefs = useComposedRefs$1(forwardedRef, ref);
188512
+ const heightRef = React.useRef(0);
188513
+ const height = heightRef.current;
188514
+ const widthRef = React.useRef(0);
188515
+ const width = widthRef.current;
188516
+ const isOpen = context.open || isPresent;
188517
+ const isMountAnimationPreventedRef = React.useRef(isOpen);
188518
+ const originalStylesRef = React.useRef(void 0);
188519
+ React.useEffect(() => {
188520
+ const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
188521
+ return () => cancelAnimationFrame(rAF);
188522
+ }, []);
188523
+ useLayoutEffect2$1(() => {
188524
+ const node = ref.current;
188525
+ if (node) {
188526
+ originalStylesRef.current = originalStylesRef.current || {
188527
+ transitionDuration: node.style.transitionDuration,
188528
+ animationName: node.style.animationName
188529
+ };
188530
+ node.style.transitionDuration = "0s";
188531
+ node.style.animationName = "none";
188532
+ const rect = node.getBoundingClientRect();
188533
+ heightRef.current = rect.height;
188534
+ widthRef.current = rect.width;
188535
+ if (!isMountAnimationPreventedRef.current) {
188536
+ node.style.transitionDuration = originalStylesRef.current.transitionDuration;
188537
+ node.style.animationName = originalStylesRef.current.animationName;
188538
+ }
188539
+ setIsPresent(present);
188540
+ }
188541
+ }, [context.open, present]);
188542
+ return /* @__PURE__ */ jsx(
188543
+ Primitive$4.div,
188544
+ {
188545
+ "data-state": getState$1(context.open),
188546
+ "data-disabled": context.disabled ? "" : void 0,
188547
+ id: context.contentId,
188548
+ hidden: !isOpen,
188549
+ ...contentProps,
188550
+ ref: composedRefs,
188551
+ style: {
188552
+ [`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
188553
+ [`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
188554
+ ...props.style
188555
+ },
188556
+ children: isOpen && children
188557
+ }
188558
+ );
188559
+ });
188560
+ function getState$1(open) {
188561
+ return open ? "open" : "closed";
188562
+ }
188563
+ var Root$3 = Collapsible$1;
188564
+
188565
+ const Collapsible = Root$3;
188566
+ const CollapsibleTrigger = CollapsibleTrigger$1;
188567
+ const CollapsibleContent = CollapsibleContent$1;
188568
+
188569
+ function getFriendlyToolName(toolName) {
188570
+ if (TOOL_NAME_MAP === null || TOOL_NAME_MAP === void 0 ? void 0 : TOOL_NAME_MAP[toolName])
188571
+ return TOOL_NAME_MAP[toolName];
188572
+ const words = toolName.replace(/_/g, ' ').trim();
188573
+ return words.charAt(0).toUpperCase() + words.slice(1);
188574
+ }
188575
+ // Extract a short human-readable query from tool input or log
188576
+ function getQueryLabel(toolCall) {
188577
+ var _a, _b, _c, _d, _e;
188578
+ // Prefer structured input
188579
+ if (toolCall.input && typeof toolCall.input === 'object') {
188580
+ const val = (_c = (_b = (_a = toolCall.input.query) !== null && _a !== void 0 ? _a : toolCall.input.q) !== null && _b !== void 0 ? _b : toolCall.input.input) !== null && _c !== void 0 ? _c : toolCall.input.text;
188581
+ if (val && typeof val === 'string')
188582
+ return val;
188583
+ // Fallback: first string value in input
188584
+ const firstStr = Object.values(toolCall.input).find((v) => typeof v === 'string');
188585
+ if (firstStr)
188586
+ return firstStr;
188587
+ }
188588
+ // Fallback: parse the "… with `{...}`" snippet of the log string. Located
188589
+ // with index scans rather than a lazy regex, which backtracks
188590
+ // polynomially on adversarial logs (CodeQL js/polynomial-redos).
188591
+ if (toolCall.log) {
188592
+ const snippet = extractLogPayload(toolCall.log);
188593
+ if (snippet) {
188594
+ try {
188595
+ const parsed = JSON.parse(snippet.replace(/'/g, '"'));
188596
+ const val = (_e = (_d = parsed.query) !== null && _d !== void 0 ? _d : parsed.q) !== null && _e !== void 0 ? _e : parsed.input;
188597
+ if (val)
188598
+ return val;
188599
+ }
188600
+ catch (_f) {
188601
+ // Not every log snippet is JSON (apostrophes inside the payload break
188602
+ // the quote swap above). That is expected input, not an error — and
188603
+ // this runs inside a memo comparator on every streaming tick, so
188604
+ // logging here flooded the console on phones.
188605
+ }
188606
+ }
188607
+ }
188608
+ return null;
188609
+ }
188610
+ /** The `{...}` inside the first "with `{...}`" of a tool log, or null. */
188611
+ function extractLogPayload(log) {
188612
+ let from = 0;
188613
+ while (from < log.length) {
188614
+ const open = log.indexOf('`{', from);
188615
+ if (open === -1)
188616
+ return null;
188617
+ // Must follow the word "with" plus at least one whitespace character.
188618
+ let i = open - 1;
188619
+ let sawSpace = false;
188620
+ while (i >= 0 && /\s/.test(log[i])) {
188621
+ sawSpace = true;
188622
+ i -= 1;
188623
+ }
188624
+ if (sawSpace && log.slice(Math.max(0, i - 3), i + 1) === 'with') {
188625
+ const close = log.indexOf('}`', open + 1);
188626
+ if (close === -1)
188627
+ return null;
188628
+ return log.slice(open + 1, close + 1);
188629
+ }
188630
+ from = open + 1;
188631
+ }
188632
+ return null;
188633
+ }
188634
+ function formatResult(result) {
188635
+ return result.replace(/\n{3,}/g, '\n\n').trim();
188636
+ }
188637
+
188638
+ const TOOL_ICONS = {
188639
+ web_search_call: Globe,
188640
+ vector_search: Search,
188641
+ code_executor: Code$1,
188642
+ file_reader: FileText,
188643
+ wikipedia: BookOpen,
188644
+ };
188645
+ function getToolIcon(toolName) {
188646
+ return (toolName && TOOL_ICONS[toolName]) || Wrench;
188647
+ }
188648
+ /**
188649
+ * Collapsible record of the tools a turn used — the "Used N tools" line of
188650
+ * the chat bubble.
188651
+ *
188652
+ * The header always reads as the completed record ("Used N tools") and the
188653
+ * individual rows never animate. Only while this row is the live phase does the
188654
+ * header sprout dots — at which point the working line has stood down, so there
188655
+ * is still exactly one animated "agent is working" signal on screen.
188656
+ */
188657
+ function ToolCallIndicator({ toolCalls, isActive = false, showResults = false, }) {
188658
+ const [isOpen, setIsOpen] = useState(false);
188659
+ // `write_todos` has a dedicated renderer (`AgentTodoList`), so it must never
188660
+ // appear as a generic tool card nor be counted in "Used N tools".
188661
+ const visibleToolCalls = (toolCalls !== null && toolCalls !== void 0 ? toolCalls : []).filter((tc) => (tc === null || tc === void 0 ? void 0 : tc.name) !== WRITE_TODOS_TOOL);
188662
+ if (visibleToolCalls.length === 0) {
188663
+ return null;
188664
+ }
188665
+ const uniqueToolCount = new Set(visibleToolCalls.map((tc) => tc.name)).size;
188666
+ const headerLabel = `Used ${uniqueToolCount} tool${uniqueToolCount === 1 ? '' : 's'}`;
188667
+ return (jsxs(Collapsible, { open: isOpen, onOpenChange: setIsOpen, className: "mb-2", children: [jsxs(CollapsibleTrigger, { className: "flex cursor-pointer items-center gap-1 pt-1 text-xs text-gray-600 transition-colors hover:text-gray-800", children: [jsx(ChevronRight, { className: cn('h-3 w-3 transition-transform duration-200', isOpen && 'rotate-90') }), jsx(Wrench, { className: "h-3 w-3" }), jsx("span", { children: headerLabel }), isActive && (jsxs("span", { className: "inline-flex gap-0.5", "aria-hidden": "true", children: [jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:0ms] motion-reduce:animate-none" }), jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:150ms] motion-reduce:animate-none" }), jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:300ms] motion-reduce:animate-none" })] }))] }), jsx(CollapsibleContent, { className: "pb-2 pl-4", children: jsx("div", { className: "space-y-2.5 border-l-2 border-gray-300 pt-2.5 pl-3 text-xs leading-relaxed text-gray-600", children: visibleToolCalls.map((toolCall, index) => {
188668
+ var _a;
188669
+ const query = getQueryLabel(toolCall);
188670
+ const Icon = getToolIcon(toolCall === null || toolCall === void 0 ? void 0 : toolCall.name);
188671
+ const result = showResults && (toolCall === null || toolCall === void 0 ? void 0 : toolCall.result) ? formatResult(toolCall.result) : null;
188672
+ return (jsxs("div", { children: [jsxs("div", { className: "flex items-center gap-1.5 text-gray-700", children: [jsx(Icon, { className: "h-3 w-3 shrink-0 text-gray-500" }), jsx("span", { className: "font-medium", children: getFriendlyToolName((_a = toolCall === null || toolCall === void 0 ? void 0 : toolCall.name) !== null && _a !== void 0 ? _a : '') })] }), query && (jsx("div", { className: "mt-0.5 ml-[18px] text-gray-600", children: jsx(Markdown, { className: "prose prose-xs max-w-none [&_*]:text-xs [&_*]:text-gray-600 [&_p]:m-0", children: query }) })), result && (jsx("div", { className: "mt-1 ml-[18px] max-h-48 overflow-y-auto rounded-md border border-gray-200 bg-white px-2 py-1.5 leading-relaxed whitespace-pre-wrap text-gray-600", "data-testid": "tool-call-result", children: result }))] }, (toolCall === null || toolCall === void 0 ? void 0 : toolCall.id) || index));
188673
+ }) }) })] }));
188674
+ }
188675
+
188676
+ /**
188677
+ * A model the way the nav bar shows the mentor's: the provider logo in a
188678
+ * white circle (the catalogue's artwork, or the bot icon when it has none)
188679
+ * beside the model's display name. Read-only; used on admin transcripts to
188680
+ * name the model a turn ran on.
188681
+ */
188682
+ function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
188683
+ var _a, _b;
188684
+ const params = useParams();
188685
+ const username = useUsername();
188686
+ const org = (_a = tenantKey !== null && tenantKey !== void 0 ? tenantKey : params === null || params === void 0 ? void 0 : params.tenantKey) !== null && _a !== void 0 ? _a : null;
188687
+ const mentor = (_b = mentorId !== null && mentorId !== void 0 ? mentorId : params === null || params === void 0 ? void 0 : params.mentorId) !== null && _b !== void 0 ? _b : null;
188688
+ const resolveLlmProvider = useLlmProviderCatalogue({ org, userId: username, mentorId: mentor });
188689
+ // With no provider key (transcript rows carry only the model), the
188690
+ // catalogue row that lists this model supplies the logo and name.
188691
+ const providerDetails = resolveLlmProvider(provider, model);
188692
+ const displayName = useLlmDisplayName({
188693
+ llmName: model,
188694
+ org,
188695
+ userId: username,
188696
+ mentorId: mentor,
188697
+ });
188698
+ const label = displayName || providerDetails.displayName;
188699
+ if (!label)
188700
+ return null;
188701
+ 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
188702
+ ? `${label} · ${providerDetails.displayName}`
188703
+ : 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 })] }));
188704
+ }
188705
+
188706
+ /** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
188707
+ const GRADEBOOK_SPAS = ['lms', 'skills'];
188708
+ /**
188709
+ * The customization `Profile` is opened with: whatever the host passed, with
188710
+ * the Gradebook tab defaulted from the SPA it runs in. An explicit
188711
+ * `showGradebookTab` always wins, so a host can force it either way.
188712
+ */
188713
+ function profileCustomization(customization, currentSPA) {
188714
+ var _a;
188715
+ return {
188716
+ ...customization,
188717
+ showGradebookTab: (_a = customization === null || customization === void 0 ? void 0 : customization.showGradebookTab) !== null && _a !== void 0 ? _a : GRADEBOOK_SPAS.includes((currentSPA !== null && currentSPA !== void 0 ? currentSPA : '').trim().toLowerCase()),
188718
+ };
188719
+ }
188720
+ /**
188721
+ * The shared `Profile` viewer in a dialog — the same modal the tenant
188722
+ * Management tab opens when a user row is clicked. Controlled variant for
188723
+ * hosts that trigger it from a whole table row.
188724
+ *
188725
+ * Radix portals the dialog into `document.body`, but a React portal still
188726
+ * bubbles its events through the React tree — so without the guard below, a
188727
+ * click *inside* the open profile would reach the row this dialog is rendered
188728
+ * from and fire its `onClick` (e.g. opening the enrollment progress modal
188729
+ * behind the profile). The wrapper is `display: contents`, so it adds nothing
188730
+ * to the host's layout.
188731
+ */
188732
+ function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization, currentSPA, }) {
188733
+ const t = useT();
188734
+ if (!isOpen)
188735
+ return null;
188736
+ return (jsx("span", { className: "contents", onClick: (event) => event.stopPropagation(), onMouseDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose, customization: profileCustomization(customization, currentSPA), currentSPA: currentSPA })] }) }) }));
188737
+ }
188738
+ /**
188739
+ * Clickable user identity that opens the shared `Profile` viewer in a dialog
188740
+ * — the same modal the tenant Management tab opens when a user row is
188741
+ * clicked. `Profile` drops into preview (read-only) mode on its own for anyone
188742
+ * but the signed-in user. Used by the spend-cap tables and every analytics
188743
+ * surface that names a user; safe inside clickable rows and cards, since
188744
+ * neither its clicks nor its key presses reach the host's handlers.
188745
+ */
188746
+ function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
188747
+ const t = useT();
188748
+ const [isOpen, setIsOpen] = React__default.useState(false);
188749
+ if (!username) {
188750
+ // `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
188751
+ // an inline box takes its width from its own text.
188752
+ return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
188753
+ }
188754
+ return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
188755
+ event.stopPropagation();
188756
+ setIsOpen(true);
188757
+ },
188758
+ // Rows that open on Enter/Space would otherwise swallow the key press.
188759
+ 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 })] }));
188760
+ }
188761
+
187914
188762
  const AdvancedPagination = ({ currentPage, totalPages, onPageChange, totalCount, itemsPerPage, itemLabel = 'items', variant = 'default', className, }) => {
187915
188763
  const t = useT();
187916
188764
  if (totalPages <= 1) {
@@ -189821,141 +190669,6 @@ function trimTrailingSlashes(value) {
189821
190669
  return end === value.length ? value : value.slice(0, end);
189822
190670
  }
189823
190671
 
189824
- var COLLAPSIBLE_NAME = "Collapsible";
189825
- var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
189826
- var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
189827
- var Collapsible$1 = React.forwardRef(
189828
- (props, forwardedRef) => {
189829
- const {
189830
- __scopeCollapsible,
189831
- open: openProp,
189832
- defaultOpen,
189833
- disabled,
189834
- onOpenChange,
189835
- ...collapsibleProps
189836
- } = props;
189837
- const [open, setOpen] = useControllableState$2({
189838
- prop: openProp,
189839
- defaultProp: defaultOpen ?? false,
189840
- onChange: onOpenChange,
189841
- caller: COLLAPSIBLE_NAME
189842
- });
189843
- return /* @__PURE__ */ jsx(
189844
- CollapsibleProvider,
189845
- {
189846
- scope: __scopeCollapsible,
189847
- disabled,
189848
- contentId: useId$1(),
189849
- open,
189850
- onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
189851
- children: /* @__PURE__ */ jsx(
189852
- Primitive$4.div,
189853
- {
189854
- "data-state": getState$1(open),
189855
- "data-disabled": disabled ? "" : void 0,
189856
- ...collapsibleProps,
189857
- ref: forwardedRef
189858
- }
189859
- )
189860
- }
189861
- );
189862
- }
189863
- );
189864
- Collapsible$1.displayName = COLLAPSIBLE_NAME;
189865
- var TRIGGER_NAME$1 = "CollapsibleTrigger";
189866
- var CollapsibleTrigger$1 = React.forwardRef(
189867
- (props, forwardedRef) => {
189868
- const { __scopeCollapsible, ...triggerProps } = props;
189869
- const context = useCollapsibleContext(TRIGGER_NAME$1, __scopeCollapsible);
189870
- return /* @__PURE__ */ jsx(
189871
- Primitive$4.button,
189872
- {
189873
- type: "button",
189874
- "aria-controls": context.contentId,
189875
- "aria-expanded": context.open || false,
189876
- "data-state": getState$1(context.open),
189877
- "data-disabled": context.disabled ? "" : void 0,
189878
- disabled: context.disabled,
189879
- ...triggerProps,
189880
- ref: forwardedRef,
189881
- onClick: composeEventHandlers$2(props.onClick, context.onOpenToggle)
189882
- }
189883
- );
189884
- }
189885
- );
189886
- CollapsibleTrigger$1.displayName = TRIGGER_NAME$1;
189887
- var CONTENT_NAME$1 = "CollapsibleContent";
189888
- var CollapsibleContent$1 = React.forwardRef(
189889
- (props, forwardedRef) => {
189890
- const { forceMount, ...contentProps } = props;
189891
- const context = useCollapsibleContext(CONTENT_NAME$1, props.__scopeCollapsible);
189892
- return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
189893
- }
189894
- );
189895
- CollapsibleContent$1.displayName = CONTENT_NAME$1;
189896
- var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
189897
- const { __scopeCollapsible, present, children, ...contentProps } = props;
189898
- const context = useCollapsibleContext(CONTENT_NAME$1, __scopeCollapsible);
189899
- const [isPresent, setIsPresent] = React.useState(present);
189900
- const ref = React.useRef(null);
189901
- const composedRefs = useComposedRefs$1(forwardedRef, ref);
189902
- const heightRef = React.useRef(0);
189903
- const height = heightRef.current;
189904
- const widthRef = React.useRef(0);
189905
- const width = widthRef.current;
189906
- const isOpen = context.open || isPresent;
189907
- const isMountAnimationPreventedRef = React.useRef(isOpen);
189908
- const originalStylesRef = React.useRef(void 0);
189909
- React.useEffect(() => {
189910
- const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
189911
- return () => cancelAnimationFrame(rAF);
189912
- }, []);
189913
- useLayoutEffect2$1(() => {
189914
- const node = ref.current;
189915
- if (node) {
189916
- originalStylesRef.current = originalStylesRef.current || {
189917
- transitionDuration: node.style.transitionDuration,
189918
- animationName: node.style.animationName
189919
- };
189920
- node.style.transitionDuration = "0s";
189921
- node.style.animationName = "none";
189922
- const rect = node.getBoundingClientRect();
189923
- heightRef.current = rect.height;
189924
- widthRef.current = rect.width;
189925
- if (!isMountAnimationPreventedRef.current) {
189926
- node.style.transitionDuration = originalStylesRef.current.transitionDuration;
189927
- node.style.animationName = originalStylesRef.current.animationName;
189928
- }
189929
- setIsPresent(present);
189930
- }
189931
- }, [context.open, present]);
189932
- return /* @__PURE__ */ jsx(
189933
- Primitive$4.div,
189934
- {
189935
- "data-state": getState$1(context.open),
189936
- "data-disabled": context.disabled ? "" : void 0,
189937
- id: context.contentId,
189938
- hidden: !isOpen,
189939
- ...contentProps,
189940
- ref: composedRefs,
189941
- style: {
189942
- [`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
189943
- [`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
189944
- ...props.style
189945
- },
189946
- children: isOpen && children
189947
- }
189948
- );
189949
- });
189950
- function getState$1(open) {
189951
- return open ? "open" : "closed";
189952
- }
189953
- var Root$3 = Collapsible$1;
189954
-
189955
- const Collapsible = Root$3;
189956
- const CollapsibleTrigger = CollapsibleTrigger$1;
189957
- const CollapsibleContent = CollapsibleContent$1;
189958
-
189959
190672
  // Pure logic behind the application form (questions) editor. No React here so
189960
190673
  // it stays unit testable. The backend validates schemas on save and remains
189961
190674
  // authoritative; these helpers only shape the editor's local state.
@@ -194258,62 +194971,6 @@ function SpendLimitsSection({ tenantKey, labels: labelsOverride }) {
194258
194971
  return (jsxs(Fragment$1, { children: [jsxs("div", { "data-testid": "spend-limits-workspace-section", className: "rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800", children: [jsx("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: jsxs("div", { className: "space-y-0.5", children: [jsx("h3", { className: "text-[16px] font-medium leading-6 text-[#101828] dark:text-gray-100", children: labels.tenantSections.workspace.title }), jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: labels.tenantSections.workspace.description })] }) }), jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: isTenantCapLoading ? (jsx("div", { className: "flex items-center justify-center py-8", "data-testid": "tenant-cap-loading", children: jsx(Spinner, {}) })) : isTenantForbidden ? (jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-10 text-center", "data-testid": "spend-limits-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.denied.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.denied.description })] })) : (jsxs("div", { className: "space-y-4", children: [!tenantCap && (monthlySpend != null || totalSpend != null) && (jsxs("div", { className: "space-y-1", "data-testid": "workspace-actual-spend-stats", children: [jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [monthlySpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentThisMonth }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-month", children: formatUsd$2(String(monthlySpend)) })] })), totalSpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentAllTime }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-total", children: formatUsd$2(String(totalSpend)) })] }))] }), jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: labels.tenantSections.noCapStats.hint })] })), jsx(SpendCapForm, { labels: labels, cap: tenantCap, isSaving: isSavingTenantCap, isDeleting: isDeletingTenantCap, onSave: saveTenantCap, onDelete: () => setIsDeleteOpen(true), testId: "tenant-cap" })] })) })] }), jsx(Dialog, { open: isDeleteOpen, onOpenChange: () => setIsDeleteOpen(false), children: jsxs(DialogContent, { "aria-describedby": "tenant-cap-delete-description", "data-testid": "tenant-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "tenant-cap-delete-description", children: jsx("p", { className: "text-sm text-[#646464]", children: labels.deleteModal.description }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setIsDeleteOpen(false), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeletingTenantCap, "data-testid": "tenant-cap-delete-confirm", children: isDeletingTenantCap ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
194259
194972
  }
194260
194973
 
194261
- /** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
194262
- const GRADEBOOK_SPAS = ['lms', 'skills'];
194263
- /**
194264
- * The customization `Profile` is opened with: whatever the host passed, with
194265
- * the Gradebook tab defaulted from the SPA it runs in. An explicit
194266
- * `showGradebookTab` always wins, so a host can force it either way.
194267
- */
194268
- function profileCustomization(customization, currentSPA) {
194269
- var _a;
194270
- return {
194271
- ...customization,
194272
- showGradebookTab: (_a = customization === null || customization === void 0 ? void 0 : customization.showGradebookTab) !== null && _a !== void 0 ? _a : GRADEBOOK_SPAS.includes((currentSPA !== null && currentSPA !== void 0 ? currentSPA : '').trim().toLowerCase()),
194273
- };
194274
- }
194275
- /**
194276
- * The shared `Profile` viewer in a dialog — the same modal the tenant
194277
- * Management tab opens when a user row is clicked. Controlled variant for
194278
- * hosts that trigger it from a whole table row.
194279
- *
194280
- * Radix portals the dialog into `document.body`, but a React portal still
194281
- * bubbles its events through the React tree — so without the guard below, a
194282
- * click *inside* the open profile would reach the row this dialog is rendered
194283
- * from and fire its `onClick` (e.g. opening the enrollment progress modal
194284
- * behind the profile). The wrapper is `display: contents`, so it adds nothing
194285
- * to the host's layout.
194286
- */
194287
- function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization, currentSPA, }) {
194288
- const t = useT();
194289
- if (!isOpen)
194290
- return null;
194291
- return (jsx("span", { className: "contents", onClick: (event) => event.stopPropagation(), onMouseDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose, customization: profileCustomization(customization, currentSPA), currentSPA: currentSPA })] }) }) }));
194292
- }
194293
- /**
194294
- * Clickable user identity that opens the shared `Profile` viewer in a dialog
194295
- * — the same modal the tenant Management tab opens when a user row is
194296
- * clicked. `Profile` drops into preview (read-only) mode on its own for anyone
194297
- * but the signed-in user. Used by the spend-cap tables and every analytics
194298
- * surface that names a user; safe inside clickable rows and cards, since
194299
- * neither its clicks nor its key presses reach the host's handlers.
194300
- */
194301
- function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
194302
- const t = useT();
194303
- const [isOpen, setIsOpen] = React__default.useState(false);
194304
- if (!username) {
194305
- // `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
194306
- // an inline box takes its width from its own text.
194307
- return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
194308
- }
194309
- return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
194310
- event.stopPropagation();
194311
- setIsOpen(true);
194312
- },
194313
- // Rows that open on Enter/Space would otherwise swallow the key press.
194314
- 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 })] }));
194315
- }
194316
-
194317
194974
  /**
194318
194975
  * Add/edit dialog for one per-user spend limit, mirroring the grader
194319
194976
  * criterion modal: create mode starts with a searchable platform-user picker
@@ -214574,7 +215231,7 @@ function inputRulesPlugin(props) {
214574
215231
  function getType(value) {
214575
215232
  return Object.prototype.toString.call(value).slice(8, -1);
214576
215233
  }
214577
- function isPlainObject$1(value) {
215234
+ function isPlainObject$2(value) {
214578
215235
  if (getType(value) !== "Object") {
214579
215236
  return false;
214580
215237
  }
@@ -214584,9 +215241,9 @@ function isPlainObject$1(value) {
214584
215241
  // src/utilities/mergeDeep.ts
214585
215242
  function mergeDeep(target, source) {
214586
215243
  const output = { ...target };
214587
- if (isPlainObject$1(target) && isPlainObject$1(source)) {
215244
+ if (isPlainObject$2(target) && isPlainObject$2(source)) {
214588
215245
  Object.keys(source).forEach((key) => {
214589
- if (isPlainObject$1(source[key]) && isPlainObject$1(target[key])) {
215246
+ if (isPlainObject$2(source[key]) && isPlainObject$2(target[key])) {
214590
215247
  output[key] = mergeDeep(target[key], source[key]);
214591
215248
  } else {
214592
215249
  output[key] = source[key];
@@ -231411,6 +232068,501 @@ function TranscriptFiles({ files }) {
231411
232068
  }) }));
231412
232069
  }
231413
232070
 
232071
+ const asArray = (value) => (Array.isArray(value) ? value : []);
232072
+ const hasEntries = (value) => !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0;
232073
+ /** Rollup counts as the conversation-list endpoint reports them: numbers, numeric strings or `null`. */
232074
+ const toRollupCount = (value) => {
232075
+ const count = typeof value === 'string' ? Number(value) : (value !== null && value !== void 0 ? value : 0);
232076
+ return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0;
232077
+ };
232078
+ /**
232079
+ * Sum the retrieved documents and tool calls over a conversation's turns, for
232080
+ * surfaces (the admin chat-history list) that carry the turns inline but have
232081
+ * no server-side rollup fields.
232082
+ */
232083
+ function summarizeTranscriptTurns(turns) {
232084
+ return asArray(turns).reduce((rollup, turn) => ({
232085
+ documentsCount: rollup.documentsCount + asArray(turn === null || turn === void 0 ? void 0 : turn.documents).length,
232086
+ toolCallsCount: rollup.toolCallsCount + asArray(turn === null || turn === void 0 ? void 0 : turn.tool_calls).length,
232087
+ }), { documentsCount: 0, toolCallsCount: 0 });
232088
+ }
232089
+ /** A transcript's retrieved documents in the shape the chat's documents dialog lists. */
232090
+ function toRetrievedDocuments(documents) {
232091
+ return documents.map((doc) => {
232092
+ var _a, _b, _c, _d;
232093
+ return ({
232094
+ title: (_b = (_a = doc.title) !== null && _a !== void 0 ? _a : doc.source) !== null && _b !== void 0 ? _b : null,
232095
+ snippet: (_c = doc.snippet) !== null && _c !== void 0 ? _c : null,
232096
+ source: (_d = doc.source) !== null && _d !== void 0 ? _d : null,
232097
+ score: typeof doc.score === 'number' ? doc.score : null,
232098
+ });
232099
+ });
232100
+ }
232101
+ /**
232102
+ * Every document a conversation retrieved, across its turns, exactly as the
232103
+ * backend reports them — a source retrieved again on a later turn appears
232104
+ * again, so the count matches the backend's per-conversation totals. Comes
232105
+ * from the admin history payload itself, so it needs no per-user session
232106
+ * lookup.
232107
+ */
232108
+ function conversationDocuments(turns) {
232109
+ return toRetrievedDocuments(asArray(turns).flatMap((turn) => asArray(turn === null || turn === void 0 ? void 0 : turn.documents)));
232110
+ }
232111
+ const asText = (value) => {
232112
+ if (value === null || value === undefined)
232113
+ return '';
232114
+ if (typeof value === 'string')
232115
+ return value;
232116
+ if (typeof value === 'number' || typeof value === 'boolean')
232117
+ return String(value);
232118
+ try {
232119
+ return JSON.stringify(value, null, 2);
232120
+ }
232121
+ catch (_a) {
232122
+ return String(value);
232123
+ }
232124
+ };
232125
+ /** A transcript's tool calls in the shape the chat's "Used N tools" record renders. */
232126
+ function toToolCallInfos(toolCalls) {
232127
+ return toolCalls.map((call, index) => {
232128
+ var _a;
232129
+ return ({
232130
+ id: `transcript-tool-${index}`,
232131
+ name: (_a = call.name) !== null && _a !== void 0 ? _a : '',
232132
+ input: call.input && typeof call.input === 'object' && !Array.isArray(call.input)
232133
+ ? call.input
232134
+ : typeof call.input === 'string'
232135
+ ? { input: call.input }
232136
+ : undefined,
232137
+ log: '',
232138
+ result: asText(call.output),
232139
+ });
232140
+ });
232141
+ }
232142
+ /** Whether a turn carries anything worth a details panel. */
232143
+ function hasTranscriptTurnDetails(turn) {
232144
+ if (!turn)
232145
+ return false;
232146
+ return (asArray(turn.documents).length > 0 ||
232147
+ asArray(turn.tool_calls).length > 0 ||
232148
+ hasEntries(turn.metadata) ||
232149
+ hasEntries(turn.request_context));
232150
+ }
232151
+ const isPlainObject$1 = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
232152
+ /** Nothing worth a row: null, blank strings, empty arrays and empty objects. */
232153
+ const isPresent = (value) => {
232154
+ if (value === null || value === undefined)
232155
+ return false;
232156
+ if (typeof value === 'string')
232157
+ return value.trim() !== '';
232158
+ if (Array.isArray(value))
232159
+ return value.length > 0;
232160
+ if (isPlainObject$1(value))
232161
+ return Object.keys(value).length > 0;
232162
+ return true;
232163
+ };
232164
+ const FIELD_ABBREVIATIONS$1 = new Set([
232165
+ 'ai',
232166
+ 'api',
232167
+ 'css',
232168
+ 'csv',
232169
+ 'html',
232170
+ 'http',
232171
+ 'https',
232172
+ 'id',
232173
+ 'ids',
232174
+ 'ip',
232175
+ 'json',
232176
+ 'llm',
232177
+ 'lti',
232178
+ 'mcp',
232179
+ 'mime',
232180
+ 'pdf',
232181
+ 'rag',
232182
+ 'sso',
232183
+ 'stt',
232184
+ 'tts',
232185
+ 'ui',
232186
+ 'uri',
232187
+ 'url',
232188
+ 'urls',
232189
+ 'uuid',
232190
+ ]);
232191
+ /**
232192
+ * "file_references" → "File References", "llmModel" → "LLM Model". Backend
232193
+ * keys are never shown raw: every unknown key goes through this, and the
232194
+ * common ones get a translated label on top (see `useFieldLabels`).
232195
+ */
232196
+ function humanizeFieldName(key) {
232197
+ const words = key
232198
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
232199
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
232200
+ .toLowerCase()
232201
+ .split(/[^a-z0-9]+/)
232202
+ .filter(Boolean);
232203
+ if (words.length === 0)
232204
+ return key;
232205
+ return words
232206
+ .map((word) => FIELD_ABBREVIATIONS$1.has(word)
232207
+ ? word.toUpperCase()
232208
+ : word.charAt(0).toUpperCase() + word.slice(1))
232209
+ .join(' ');
232210
+ }
232211
+ /** Translated labels for the keys the backend is known to send. */
232212
+ function useFieldLabels() {
232213
+ const t = useT();
232214
+ const known = {
232215
+ llm_model: t('analyticsTranscriptTurnDetails.model'),
232216
+ llm_provider: t('analyticsTranscriptTurnDetails.provider'),
232217
+ llm_temperature: t('analyticsTranscriptTurnDetails.temperature'),
232218
+ model: t('analyticsTranscriptTurnDetails.model'),
232219
+ provider: t('analyticsTranscriptTurnDetails.provider'),
232220
+ temperature: t('analyticsTranscriptTurnDetails.temperature'),
232221
+ client: t('analyticsTranscriptTurnDetails.client'),
232222
+ session_id: t('analyticsTranscriptTurnDetails.fieldSession'),
232223
+ // The product's own names: artifacts are "Canvas" in the chat, proactivity
232224
+ // is the "Proactive Prompt", grading is the "Grader" tab, document filters
232225
+ // are "Datasets".
232226
+ enable_artifacts: t('analyticsTranscriptTurnDetails.fieldCanvas'),
232227
+ artifact: t('analyticsTranscriptTurnDetails.fieldCanvas'),
232228
+ artifacts: t('analyticsTranscriptTurnDetails.fieldCanvas'),
232229
+ is_proactive: t('analyticsTranscriptTurnDetails.fieldProactivePrompt'),
232230
+ grading_context: t('analyticsTranscriptTurnDetails.fieldGrader'),
232231
+ document_filter: t('analyticsTranscriptTurnDetails.fieldDatasets'),
232232
+ current_datetime: t('analyticsTranscriptTurnDetails.fieldDateTime'),
232233
+ caller_verified: t('analyticsTranscriptTurnDetails.fieldCallerVerified'),
232234
+ file_references: t('analyticsTranscriptTurnDetails.fieldFiles'),
232235
+ file_id: t('analyticsTranscriptTurnDetails.fieldFile'),
232236
+ system_prompt: t('analyticsTranscriptTurnDetails.fieldSystemPrompt'),
232237
+ flow: t('analyticsTranscriptTurnDetails.fieldFlow'),
232238
+ token: t('analyticsTranscriptTurnDetails.fieldToken'),
232239
+ tenant: t('analyticsTranscriptTurnDetails.fieldTenant'),
232240
+ pathway: t('analyticsTranscriptTurnDetails.fieldPathway'),
232241
+ username: t('analyticsTranscriptTurnDetails.fieldUsername'),
232242
+ name: t('analyticsTranscriptTurnDetails.fieldName'),
232243
+ };
232244
+ return (key) => { var _a; return (_a = known[key]) !== null && _a !== void 0 ? _a : humanizeFieldName(key); };
232245
+ }
232246
+ const fileName = (value) => {
232247
+ var _a, _b, _c, _d;
232248
+ const name = (_b = (_a = value.file_name) !== null && _a !== void 0 ? _a : value.filename) !== null && _b !== void 0 ? _b : value.name;
232249
+ if (typeof name === 'string' && name.trim())
232250
+ return name.trim();
232251
+ const key = (_d = (_c = value.file_key) !== null && _c !== void 0 ? _c : value.key) !== null && _d !== void 0 ? _d : value.path;
232252
+ if (typeof key === 'string' && key.trim())
232253
+ return key.trim().split('/').pop() || key.trim();
232254
+ return undefined;
232255
+ };
232256
+ /** Backend payloads name files in a few shapes; anything with a name plus a file-ish fact counts. */
232257
+ function isFileReference(value) {
232258
+ if (!isPlainObject$1(value) || !fileName(value))
232259
+ return false;
232260
+ return ['file_id', 'file_key', 'file_size', 'content_type', 'mime_type', 'url', 'size'].some((key) => key in value && value[key] !== null && value[key] !== undefined);
232261
+ }
232262
+ const attachmentUrl = (name, attachments) => { var _a; return (_a = attachments === null || attachments === void 0 ? void 0 : attachments.find((file) => (file === null || file === void 0 ? void 0 : file.name) === name && !!file.url)) === null || _a === void 0 ? void 0 : _a.url; };
232263
+ /** Normalise a file reference; the turn's attachments lend their presigned `url` to a same-named file. */
232264
+ function toFileReference(value, attachments) {
232265
+ var _a, _b, _c, _d, _e, _f;
232266
+ const name = (_a = fileName(value)) !== null && _a !== void 0 ? _a : '';
232267
+ const contentType = (_c = (_b = value.content_type) !== null && _b !== void 0 ? _b : value.mime_type) !== null && _c !== void 0 ? _c : value.type;
232268
+ const size = (_d = value.file_size) !== null && _d !== void 0 ? _d : value.size;
232269
+ const own = (_f = (_e = value.url) !== null && _e !== void 0 ? _e : value.download_url) !== null && _f !== void 0 ? _f : value.href;
232270
+ const numericSize = typeof size === 'number'
232271
+ ? size
232272
+ : typeof size === 'string' && size.trim() && Number.isFinite(Number(size))
232273
+ ? Number(size)
232274
+ : undefined;
232275
+ return {
232276
+ name,
232277
+ contentType: typeof contentType === 'string' ? contentType : undefined,
232278
+ size: numericSize,
232279
+ url: typeof own === 'string' && own ? own : attachmentUrl(name, attachments),
232280
+ };
232281
+ }
232282
+ const formatFileSize = (bytes) => {
232283
+ if (bytes === undefined || !Number.isFinite(bytes) || bytes < 0)
232284
+ return '';
232285
+ if (bytes < 1024)
232286
+ return `${bytes} B`;
232287
+ if (bytes < 1024 * 1024)
232288
+ return `${(bytes / 1024).toFixed(1)} KB`;
232289
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
232290
+ };
232291
+ const REDACTED_PATTERN = /^\[?(redacted|hidden|masked|removed)\]?$|^\*{3,}$/i;
232292
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
232293
+ /** Past this depth nested objects flatten to "key: value" text rather than nesting further. */
232294
+ const MAX_NESTING = 4;
232295
+ const formatPrimitive = (value) => {
232296
+ if (value === null || value === undefined)
232297
+ return '';
232298
+ if (typeof value === 'string')
232299
+ return value;
232300
+ if (typeof value === 'number')
232301
+ return Number.isInteger(value) ? String(value) : String(value);
232302
+ return String(value);
232303
+ };
232304
+ /** Deeply nested leftovers as readable "Key: value" text — never JSON. */
232305
+ function compactText(value, labelFor) {
232306
+ if (Array.isArray(value)) {
232307
+ return value
232308
+ .filter(isPresent)
232309
+ .map((item) => compactText(item, labelFor))
232310
+ .join('; ');
232311
+ }
232312
+ if (isPlainObject$1(value)) {
232313
+ return Object.entries(value)
232314
+ .filter(([, v]) => isPresent(v))
232315
+ .map(([k, v]) => `${labelFor(k)}: ${compactText(v, labelFor)}`)
232316
+ .join(', ');
232317
+ }
232318
+ return formatPrimitive(value);
232319
+ }
232320
+ function BooleanValue({ value }) {
232321
+ const t = useT();
232322
+ return (jsx("span", { className: cn('inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', value ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-600'), children: value ? t('analyticsTranscriptTurnDetails.yes') : t('analyticsTranscriptTurnDetails.no') }));
232323
+ }
232324
+ function StringValue({ value }) {
232325
+ const t = useT();
232326
+ const text = value.trim();
232327
+ if (REDACTED_PATTERN.test(text)) {
232328
+ return (jsx("span", { className: "text-gray-400 italic", children: t('analyticsTranscriptTurnDetails.redacted') }));
232329
+ }
232330
+ if (UUID_PATTERN.test(text)) {
232331
+ return jsx("span", { className: "font-mono text-xs break-all text-gray-700", children: text });
232332
+ }
232333
+ return (jsx("span", { className: "block max-h-48 overflow-y-auto break-words whitespace-pre-line", children: text }));
232334
+ }
232335
+ const DetailCtx = React__default.createContext({ labelFor: humanizeFieldName });
232336
+ function DetailValue({ value, depth, labelFor, }) {
232337
+ var _a, _b;
232338
+ const ctx = React__default.useContext(DetailCtx);
232339
+ if (typeof value === 'boolean')
232340
+ return jsx(BooleanValue, { value: value });
232341
+ if (typeof value === 'number')
232342
+ return jsx("span", { className: "tabular-nums", children: formatPrimitive(value) });
232343
+ if (typeof value === 'string')
232344
+ return jsx(StringValue, { value: value });
232345
+ if (depth >= MAX_NESTING) {
232346
+ return jsx("span", { className: "break-words", children: compactText(value, labelFor) });
232347
+ }
232348
+ if (isFileReference(value)) {
232349
+ const files = [toFileReference(value, ctx.attachments)];
232350
+ return jsx(Fragment$1, { children: ((_a = ctx.renderFiles) !== null && _a !== void 0 ? _a : defaultRenderFiles)(files) });
232351
+ }
232352
+ if (Array.isArray(value)) {
232353
+ const items = value.filter(isPresent);
232354
+ if (items.length === 0)
232355
+ return null;
232356
+ if (items.every(isFileReference)) {
232357
+ const files = items.map((item) => toFileReference(item, ctx.attachments));
232358
+ return jsx(Fragment$1, { children: ((_b = ctx.renderFiles) !== null && _b !== void 0 ? _b : defaultRenderFiles)(files) });
232359
+ }
232360
+ const allPrimitive = items.every((item) => !isPlainObject$1(item) && !Array.isArray(item));
232361
+ if (allPrimitive) {
232362
+ return (jsx("span", { className: "flex flex-wrap gap-1", children: items.map((item, index) => (jsx(Badge, { variant: "secondary", className: "max-w-full font-normal break-all whitespace-normal", children: typeof item === 'boolean' ? jsx(BooleanValue, { value: item }) : formatPrimitive(item) }, index))) }));
232363
+ }
232364
+ return (jsx("ol", { className: "space-y-2", children: items.map((item, index) => (jsxs("li", { className: "flex gap-2", children: [jsxs("span", { className: "mt-1 text-xs font-medium text-gray-400 tabular-nums", children: [index + 1, "."] }), jsx("div", { className: "min-w-0 flex-1 rounded-md border border-gray-200 px-3 py-1", children: isPlainObject$1(item) ? (jsx(DetailRows, { entries: item, depth: depth + 1, labelFor: labelFor })) : (jsx(DetailValue, { value: item, depth: depth + 1, labelFor: labelFor })) })] }, index))) }));
232365
+ }
232366
+ if (isPlainObject$1(value)) {
232367
+ return (jsx("div", { className: "border-l-2 border-gray-200 pl-3", children: jsx(DetailRows, { entries: value, depth: depth + 1, labelFor: labelFor }) }));
232368
+ }
232369
+ return jsx("span", { children: formatPrimitive(value) });
232370
+ }
232371
+ /**
232372
+ * Label/value rows for a metadata-style object. Keys are humanized (never
232373
+ * shown raw), blanks are skipped, booleans read Yes/No, lists become chips or
232374
+ * numbered cards, files become the chat's file cards and nested objects
232375
+ * indent — the same read-only detail style as the audit-log sheet, so nothing
232376
+ * here ever looks like a JSON dump.
232377
+ */
232378
+ function DetailRows({ entries, depth = 0, labelFor, }) {
232379
+ const rows = Object.entries(entries).filter(([, value]) => isPresent(value));
232380
+ if (rows.length === 0)
232381
+ return null;
232382
+ const top = depth === 0;
232383
+ return (jsx("dl", { className: cn(top ? 'divide-y divide-gray-100' : 'space-y-1'), children: rows.map(([key, value]) => (jsxs("div", { className: cn('grid gap-x-3', top
232384
+ ? 'grid-cols-[minmax(0,7rem)_minmax(0,1fr)] py-2'
232385
+ : 'grid-cols-[minmax(0,6rem)_minmax(0,1fr)] py-0.5'), children: [jsx("dt", { className: cn('break-words text-gray-500', top ? 'text-xs font-medium tracking-wide uppercase' : 'text-xs font-medium'), children: labelFor(key) }), jsx("dd", { className: "min-w-0 text-sm text-gray-800", children: jsx(DetailValue, { value: value, depth: depth, labelFor: labelFor }) })] }, key))) }));
232386
+ }
232387
+ function SectionHeading({ icon, children }) {
232388
+ return (jsxs("h4", { className: "mb-2 flex items-center gap-1.5 text-xs font-semibold tracking-wide text-gray-700 uppercase", children: [icon, children] }));
232389
+ }
232390
+ const MODEL_KEYS = new Set(['llm_model', 'llm_provider']);
232391
+ function MetadataSection({ metadata, renderModel, }) {
232392
+ var _a, _b;
232393
+ const t = useT();
232394
+ const labelFor = useFieldLabels();
232395
+ const rest = Object.fromEntries(Object.entries(metadata).filter(([key]) => !MODEL_KEYS.has(key)));
232396
+ const model = ((_a = metadata.llm_model) !== null && _a !== void 0 ? _a : '').trim() || undefined;
232397
+ const provider = ((_b = metadata.llm_provider) !== null && _b !== void 0 ? _b : '').trim() || undefined;
232398
+ const hasModel = !!(model || provider);
232399
+ return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children: (renderModel !== null && renderModel !== void 0 ? renderModel : defaultRenderModel)({ model, provider }) })), jsx(DetailRows, { entries: rest, labelFor: labelFor })] }));
232400
+ }
232401
+ function RequestContextSection({ requestContext }) {
232402
+ const t = useT();
232403
+ const labelFor = useFieldLabels();
232404
+ return (jsxs("section", { "data-testid": "transcript-turn-request-context", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.requestContext') }), jsx(DetailRows, { entries: requestContext, labelFor: labelFor })] }));
232405
+ }
232406
+ /**
232407
+ * The panel's defaults are the chat's own components, so admins see exactly
232408
+ * what the conversation shows: the "Retrieved Documents" dialog button, the
232409
+ * "Used N tools" record (with results), the nav-bar model badge and the
232410
+ * attachment file cards.
232411
+ */
232412
+ const defaultRenderDocuments = (documents) => (jsx(RetrievedDocumentsButton, { documents: toRetrievedDocuments(documents) }));
232413
+ const defaultRenderToolCalls = (toolCalls) => (jsx(ToolCallIndicator, { toolCalls: toToolCallInfos(toolCalls), showResults: true }));
232414
+ const defaultRenderModel = ({ model, provider }) => (jsx(LlmModelBadge, { model: model, provider: provider }));
232415
+ const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }));
232416
+ /**
232417
+ * Collapsible "Sources / Tools / Metadata" panel for one AI turn of an admin
232418
+ * transcript (Perplexity/Claude-style citations). Driven by the extended
232419
+ * per-turn context the backend attaches on admin surfaces — the analytics
232420
+ * `messages/details/` endpoint and the admin `chat-history/` list — and
232421
+ * renders nothing at all when every field is null or empty, so it is safe to
232422
+ * drop under every AI bubble.
232423
+ */
232424
+ function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, }) {
232425
+ const t = useT();
232426
+ const labelFor = useFieldLabels();
232427
+ const [open, setOpen] = React__default.useState(defaultOpen);
232428
+ const attachments = React__default.useMemo(() => [...asArray(turn === null || turn === void 0 ? void 0 : turn.human_files), ...asArray(turn === null || turn === void 0 ? void 0 : turn.ai_files)], [turn === null || turn === void 0 ? void 0 : turn.human_files, turn === null || turn === void 0 ? void 0 : turn.ai_files]);
232429
+ const detailContext = React__default.useMemo(() => ({ labelFor, attachments, renderFiles }), [labelFor, attachments, renderFiles]);
232430
+ if (!hasTranscriptTurnDetails(turn))
232431
+ return null;
232432
+ const documents = asArray(turn === null || turn === void 0 ? void 0 : turn.documents);
232433
+ const toolCalls = asArray(turn === null || turn === void 0 ? void 0 : turn.tool_calls);
232434
+ const metadata = hasEntries(turn === null || turn === void 0 ? void 0 : turn.metadata) ? turn === null || turn === void 0 ? void 0 : turn.metadata : null;
232435
+ const requestContext = hasEntries(turn === null || turn === void 0 ? void 0 : turn.request_context) ? turn === null || turn === void 0 ? void 0 : turn.request_context : null;
232436
+ return (jsx(DetailCtx.Provider, { value: detailContext, children: jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: cn('mt-2', className), "data-testid": "transcript-turn-details", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(Button$1, { type: "button", variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", "aria-expanded": open, children: [jsx(ChevronDown, { className: cn('h-4 w-4 transition-transform', open && 'rotate-180'), "aria-hidden": "true" }), open
232437
+ ? t('analyticsTranscriptTurnDetails.hideDetails')
232438
+ : t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && jsx(MetadataSection, { metadata: metadata, renderModel: renderModel }), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
232439
+ }
232440
+ /**
232441
+ * "Documents · N" / "Tools · N" chips for a conversation row, from the
232442
+ * conversation-list rollup fields (or a client-side `summarizeTranscriptTurns`).
232443
+ * A chip is hidden when its count is zero; nothing renders when both are.
232444
+ */
232445
+ function TranscriptRollupBadges({ documentsCount, toolCallsCount, className, }) {
232446
+ const t = useT();
232447
+ const documents = toRollupCount(documentsCount);
232448
+ const tools = toRollupCount(toolCallsCount);
232449
+ if (documents === 0 && tools === 0)
232450
+ return null;
232451
+ return (jsxs("span", { className: cn('inline-flex flex-wrap items-center gap-1.5', className), "data-testid": "transcript-rollup-badges", children: [documents > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-blue-50 font-normal text-blue-700", "data-testid": "transcript-sources-badge", children: [jsx(FileText, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.sourcesBadge', { count: documents })] })), tools > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-amber-50 font-normal text-amber-800", "data-testid": "transcript-tools-badge", children: [jsx(Wrench, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.toolsBadge', { count: tools })] }))] }));
232452
+ }
232453
+
232454
+ /**
232455
+ * One place that decides how a user is shown.
232456
+ *
232457
+ * Backend payloads name their user in a dozen ways — `email` / `lti_email` /
232458
+ * `user_email`, `username` / `student` / `lti_username`, `name` /
232459
+ * `full_name` / `user_full_name`, or a nested `user` object — and most of
232460
+ * them can be empty, `null`, or a placeholder like "None". Every surface used
232461
+ * to pick through those on its own. `resolveUserIdentity()` takes whatever
232462
+ * the payload is and returns what to show, what to open a profile on, and
232463
+ * the avatar initial, so the rule lives here and can be tweaked once.
232464
+ *
232465
+ * Default rule: email → full name → username → the anonymous label.
232466
+ */
232467
+ const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
232468
+ const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
232469
+ /** Values backends emit instead of "nothing". Compared case-insensitively. */
232470
+ const PLACEHOLDERS = new Set([
232471
+ '',
232472
+ 'none',
232473
+ 'null',
232474
+ 'undefined',
232475
+ 'n/a',
232476
+ 'na',
232477
+ '-',
232478
+ 'anonymous',
232479
+ 'anonymoususer',
232480
+ ]);
232481
+ /**
232482
+ * `local@domain.tld` with no whitespace, checked with a single pass rather than
232483
+ * a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
232484
+ * adversarial input).
232485
+ */
232486
+ const isEmailLike = (value) => {
232487
+ const text = value.trim();
232488
+ if (!text || /\s/.test(text))
232489
+ return false;
232490
+ const at = text.indexOf('@');
232491
+ if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
232492
+ return false;
232493
+ const domain = text.slice(at + 1);
232494
+ const dot = domain.indexOf('.');
232495
+ // A dot somewhere inside the domain, with something after the last one.
232496
+ return dot > 0 && !domain.endsWith('.');
232497
+ };
232498
+ /** Trimmed string, or undefined for blanks, non-strings and placeholders. */
232499
+ const clean = (value) => {
232500
+ if (typeof value !== 'string')
232501
+ return undefined;
232502
+ const trimmed = value.trim();
232503
+ return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
232504
+ };
232505
+ const firstOf = (...candidates) => {
232506
+ for (const candidate of candidates) {
232507
+ const value = clean(candidate);
232508
+ if (value)
232509
+ return value;
232510
+ }
232511
+ return undefined;
232512
+ };
232513
+ const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
232514
+ /**
232515
+ * Resolve how a user should be displayed from any payload that names one.
232516
+ *
232517
+ * - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
232518
+ * or a username field that turns out to hold an email address.
232519
+ * - `username`: `lti_username`, `username`, `student`, `user_username`, the
232520
+ * nested `user`'s username, or a bare string/number `user` value.
232521
+ * - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
232522
+ * the nested `user`'s name.
232523
+ * - `profileUsername`: the first *platform* username (never an LTI claim).
232524
+ *
232525
+ * A string source is treated as an email when it looks like one, else as a
232526
+ * username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
232527
+ */
232528
+ function resolveUserIdentity(source, options = {}) {
232529
+ var _a;
232530
+ const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
232531
+ const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
232532
+ const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
232533
+ 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;
232540
+ 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
+ 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
+ let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
232543
+ const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
232544
+ const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
232545
+ // A bare string that looks like an email is an email, and so is a
232546
+ // "username" field that actually carries one.
232547
+ if (!email && bareUser && isEmailLike(bareUser))
232548
+ email = bareUser;
232549
+ if (!email && username && isEmailLike(username)) {
232550
+ email = username;
232551
+ username = undefined;
232552
+ }
232553
+ const facts = { email, username, name };
232554
+ const label = order.map((field) => facts[field]).find(Boolean);
232555
+ return {
232556
+ label: label !== null && label !== void 0 ? label : anonymousLabel,
232557
+ email,
232558
+ username,
232559
+ name,
232560
+ profileUsername: platformUsername,
232561
+ initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
232562
+ isAnonymous: !label,
232563
+ };
232564
+ }
232565
+
231414
232566
  // Utility function to format relative time
231415
232567
  const formatRelativeTime = (dateString, t) => {
231416
232568
  const date = new Date(dateString);
@@ -231450,13 +232602,50 @@ const getMessageContent = (content) => {
231450
232602
  return '';
231451
232603
  };
231452
232604
  /**
231453
- * Label for whoever a transcript belongs to. The analytics payload carries an
231454
- * email only on newer deployments, so it wins where present and the display
231455
- * name / username stand in behind it.
232605
+ * Label for whoever a transcript belongs to, via the shared identity rule.
232606
+ * The analytics payload carries an email only on newer deployments, so it
232607
+ * wins where present and the display name / username stand in behind it,
232608
+ * in the order the caller asks for; "-" when the row names nobody.
231456
232609
  */
231457
- const identifyUser = (...candidates) => candidates.find((candidate) => !!candidate) || '-';
231458
- function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMentorId, usergroupIds, }) {
231459
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
232610
+ const identifyUser = (source, order) => resolveUserIdentity(source, { order, anonymousLabel: '-' }).label;
232611
+ /** The model as the nav bar shows it (logo + display name); hosts may override via `turnDetails.renderModel`. */
232612
+ const renderModelBadge = ({ model, provider }) => (jsx(LlmModelBadge, { model: model, provider: provider }));
232613
+ /** Newer deployments name the provider on rows and summaries; older ones only the model. */
232614
+ const providerOf = (source) => {
232615
+ const value = source === null || source === void 0 ? void 0 : source.llm_provider;
232616
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
232617
+ };
232618
+ /**
232619
+ * The provider the transcript's turns ran on — the same `metadata.llm_provider`
232620
+ * the per-turn details show — so the header badge resolves its logo exactly
232621
+ * like the metadata badge does, whatever spelling the summary's `model` uses.
232622
+ */
232623
+ const providerOfTurns = (messages) => {
232624
+ var _a;
232625
+ if (!Array.isArray(messages))
232626
+ return undefined;
232627
+ for (const message of messages) {
232628
+ const provider = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.llm_provider;
232629
+ if (typeof provider === 'string' && provider.trim())
232630
+ return provider.trim();
232631
+ }
232632
+ return undefined;
232633
+ };
232634
+ /**
232635
+ * Everything the transcript retrieved, as the chat offers it — from the
232636
+ * detail turns when they carry documents, else the session's own lookup.
232637
+ */
232638
+ function TranscriptDocumentsButton({ detail, className, }) {
232639
+ var _a;
232640
+ const turns = Array.isArray(detail === null || detail === void 0 ? void 0 : detail.messages) ? detail === null || detail === void 0 ? void 0 : detail.messages : [];
232641
+ const documents = conversationDocuments(turns);
232642
+ const sessionId = ((_a = detail === null || detail === void 0 ? void 0 : detail.summary) === null || _a === void 0 ? void 0 : _a.session) ? String(detail.summary.session) : '';
232643
+ if (documents.length === 0 && !sessionId)
232644
+ return null;
232645
+ return (jsx(RetrievedDocumentsButton, { sessionId: sessionId, documents: documents.length > 0 ? documents : undefined, className: className }));
232646
+ }
232647
+ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMentorId, usergroupIds, turnDetails, }) {
232648
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
231460
232649
  const t = useT();
231461
232650
  const currentMentorId = useScopedMentorId(selectedMentorId, mentorId);
231462
232651
  const [isModalOpen, setIsModalOpen] = useState(false);
@@ -231565,16 +232754,21 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
231565
232754
  };
231566
232755
  // Transcript Detail Component (reusable for both desktop and mobile)
231567
232756
  const TranscriptDetail = ({ className = '' }) => {
231568
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
232757
+ var _a, _b, _c, _d, _e, _f, _g, _h;
231569
232758
  return (jsx("div", { className: cn('h-full flex flex-col', className), children: !selectedTranscript ? (jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx(ArrowLeft, { className: "h-6 w-6 mx-auto mb-2" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.selectConversation') })] }) })) : isLoadingTranscriptsMessagesDetailsData ? (
231570
232759
  /* Loading state */
231571
232760
  jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-3", "aria-hidden": "true" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.loadingTranscript') })] }) })) : isErrorTranscriptsMessagesDetailsData || !transcriptsMessagesDetailsData ? (
231572
232761
  /* Error or no data state */
231573
232762
  jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx(X$1, { className: "h-6 w-6 mx-auto mb-2 text-red-400" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noTranscriptDetails') }), jsx("p", { className: "text-sm mt-1", children: t('analyticsAnalyticsTranscriptsStats.unableToLoadTranscript') })] }) })) : (
231574
232763
  /* Data loaded successfully */
231575
- jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex-shrink-0", children: [jsx("h2", { className: "text-lg font-semibold mb-2", children: t('analyticsAnalyticsTranscriptsStats.conversationTranscript') }), jsxs("div", { className: "text-sm text-gray-500", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelFullName'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_a = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _a === void 0 ? void 0 : _a.username, display: identifyUser((_b = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _b === void 0 ? void 0 : _b.email, (_c = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _c === void 0 ? void 0 : _c.name) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUsername'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_d = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _d === void 0 ? void 0 : _d.username, display: identifyUser((_e = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _e === void 0 ? void 0 : _e.email, (_f = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _f === void 0 ? void 0 : _f.username) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: (_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _g === void 0 ? void 0 : _g.mentor_unique_id, name: (_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _h === void 0 ? void 0 : _h.mentor })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_j = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _j === void 0 ? void 0 : _j.model) || '-'] })] })] }), jsx("div", { className: "flex-1 overflow-y-auto space-y-4 min-h-0", children: ((_k = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _k === void 0 ? void 0 : _k.length) > 0 ? ((_l = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _l === void 0 ? void 0 : _l.map((message, index) => {
232764
+ jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex-shrink-0", children: [jsx("h2", { className: "text-lg font-semibold mb-2", children: t('analyticsAnalyticsTranscriptsStats.conversationTranscript') }), jsxs("div", { className: "text-sm text-gray-500", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_a = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _a === void 0 ? void 0 : _a.username, display: identifyUser(transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: (_b = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _b === void 0 ? void 0 : _b.mentor_unique_id, name: (_c = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _c === void 0 ? void 0 : _c.mentor })] }), jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_d = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _d === void 0 ? void 0 : _d.model)
232765
+ ? ((_e = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _e !== void 0 ? _e : renderModelBadge)({
232766
+ model: transcriptsMessagesDetailsData.summary.model,
232767
+ provider: (_f = providerOf(transcriptsMessagesDetailsData.summary)) !== null && _f !== void 0 ? _f : providerOfTurns(transcriptsMessagesDetailsData.messages),
232768
+ })
232769
+ : '-'] }), jsx(TranscriptDocumentsButton, { detail: transcriptsMessagesDetailsData, className: "mt-2" })] })] }), jsx("div", { className: "flex-1 overflow-y-auto space-y-4 min-h-0", children: ((_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _g === void 0 ? void 0 : _g.length) > 0 ? ((_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _h === void 0 ? void 0 : _h.map((message, index) => {
231576
232770
  var _a, _b;
231577
- return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-blue-100 ml-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] }, `${index}-human`)), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-white mr-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files })] }, `${index}-ai`))] }, index));
232771
+ return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-blue-100 ml-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] }, `${index}-human`)), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-white mr-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files }), jsx(TranscriptTurnDetails, { turn: message, ...turnDetails })] }, `${index}-ai`))] }, index));
231578
232772
  })) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) }));
231579
232773
  };
231580
232774
  return (jsxs("div", { className: "space-y-8", children: [jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-6", children: [jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgMessagesPerConversation'), value: ((_a = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_messages_per_conversation) === null || _a === void 0 ? void 0 : _a.value) || 0, percentage: ((_b = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_messages_per_conversation) === null || _b === void 0 ? void 0 : _b.percentage_change) ||
@@ -231584,11 +232778,11 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
231584
232778
  jsx("div", { className: "space-y-4", children: Array.from({ length: 3 }).map((_, index) => (jsxs(Card, { className: "p-4 animate-pulse", children: [jsx("div", { className: "mb-3", children: jsx("div", { className: "h-4 bg-gray-200 rounded w-3/4" }) }), jsxs("div", { className: "flex flex-wrap gap-2 mb-3", children: [jsx("div", { className: "h-6 bg-gray-200 rounded w-16" }), jsx("div", { className: "h-6 bg-gray-200 rounded w-20" })] }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 mb-2", children: [jsx("div", { className: "h-4 bg-gray-200 rounded w-24" }), jsx("div", { className: "h-4 bg-gray-200 rounded w-20" })] }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2", children: [jsx("div", { className: "h-4 bg-gray-200 rounded w-20" }), jsx("div", { className: "h-4 bg-gray-200 rounded w-24" })] })] }, index))) })) : (transcriptsMessagesData === null || transcriptsMessagesData === void 0 ? void 0 : transcriptsMessagesData.results) && transcriptsMessagesData.results.length > 0 ? (
231585
232779
  // Results
231586
232780
  transcriptsMessagesData.results.map((transcript) => {
231587
- var _a, _b;
232781
+ var _a, _b, _c;
231588
232782
  return (jsxs(Card, { className: cn(`p-4 hover:shadow-md transition-shadow cursor-pointer border ${selectedTranscript === transcript.session
231589
232783
  ? 'border-blue-500 bg-blue-50'
231590
- : 'border-gray-200'}`), onClick: () => handleTranscriptSelect(transcript.session), children: [jsx("div", { className: "mb-3", children: jsx("p", { className: "text-gray-800", children: transcript.first_user_message }) }), jsx("div", { className: "flex flex-wrap gap-2 mb-3", children: ((_a = transcript === null || transcript === void 0 ? void 0 : transcript.topics) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
231591
- ((_b = transcript.topics) === null || _b === void 0 ? void 0 : _b.map((topic, index) => (jsx("span", { className: "bg-gray-100 text-gray-600 px-3 py-1 rounded-[5px] text-xs", children: topic }, index)))) }), jsx("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 text-sm mb-2", children: jsxs("div", { className: cn('flex items-center gap-1', transcript.sentiment === 'negative'
232784
+ : 'border-gray-200'}`), onClick: () => handleTranscriptSelect(transcript.session), children: [jsx("div", { className: "mb-3", children: jsx("p", { className: "text-gray-800", children: transcript.first_user_message }) }), jsxs("div", { className: "flex flex-wrap gap-2 mb-3", children: [((_a = transcript === null || transcript === void 0 ? void 0 : transcript.topics) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
232785
+ ((_b = transcript.topics) === null || _b === void 0 ? void 0 : _b.map((topic, index) => (jsx("span", { className: "bg-gray-100 text-gray-600 px-3 py-1 rounded-[5px] text-xs", children: topic }, index)))), jsx(TranscriptRollupBadges, { documentsCount: transcript.documents_count, toolCallsCount: transcript.tool_calls_count })] }), jsx("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 text-sm mb-2", children: jsxs("div", { className: cn('flex items-center gap-1', transcript.sentiment === 'negative'
231592
232786
  ? 'text-blue-600'
231593
232787
  : transcript.sentiment === 'positive'
231594
232788
  ? 'text-green-600'
@@ -231596,7 +232790,12 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
231596
232790
  ? t('analyticsAnalyticsTranscriptsStats.sentimentNegative')
231597
232791
  : transcript.sentiment === 'positive'
231598
232792
  ? t('analyticsAnalyticsTranscriptsStats.sentimentPositive')
231599
- : 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 === null || transcript === void 0 ? void 0 : transcript.email, transcript === null || transcript === void 0 ? void 0 : transcript.name, transcript === null || transcript === void 0 ? void 0 : transcript.username) })] }), 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", { children: [t('analyticsAnalyticsTranscriptsStats.labelModelCard'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.model) || '-'] }) }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500", children: [jsxs("div", { children: [transcript.message_count, " ", t('analyticsAnalyticsTranscriptsStats.messages')] }), jsx("div", { children: "\u2022" }), jsxs("div", { children: ["$", Number((transcript === null || transcript === void 0 ? void 0 : transcript.cost) || 0).toFixed(2), ' ', t('analyticsAnalyticsTranscriptsStats.estimatedCost')] }), (transcript === null || transcript === void 0 ? void 0 : transcript.created_at) && (jsxs(Fragment$1, { children: [jsx("div", { children: "\u2022" }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.created'), ' ', formatRelativeTime(transcript.created_at, t)] })] }))] })] }, transcript.session));
232793
+ : 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)
232794
+ ? ((_c = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _c !== void 0 ? _c : renderModelBadge)({
232795
+ model: transcript.model,
232796
+ provider: providerOf(transcript),
232797
+ })
232798
+ : '-'] }) }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500", children: [jsxs("div", { children: [transcript.message_count, " ", t('analyticsAnalyticsTranscriptsStats.messages')] }), jsx("div", { children: "\u2022" }), jsxs("div", { children: ["$", Number((transcript === null || transcript === void 0 ? void 0 : transcript.cost) || 0).toFixed(2), ' ', t('analyticsAnalyticsTranscriptsStats.estimatedCost')] }), (transcript === null || transcript === void 0 ? void 0 : transcript.created_at) && (jsxs(Fragment$1, { children: [jsx("div", { children: "\u2022" }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.created'), ' ', formatRelativeTime(transcript.created_at, t)] })] }))] })] }, transcript.session));
231600
232799
  })) : (
231601
232800
  // Empty state
231602
232801
  jsx("div", { className: "text-center py-12", children: jsx("p", { className: "text-gray-500", children: filters.topicSearch || filters.userSearch || filters.rating
@@ -231632,9 +232831,14 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
231632
232831
  /* Error or no data state */
231633
232832
  jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx(X$1, { className: "h-6 w-6 mx-auto mb-2 text-red-400" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noTranscriptDetails') }), jsx("p", { className: "text-sm mt-1", children: t('analyticsAnalyticsTranscriptsStats.unableToLoadTranscript') })] }) })) : (
231634
232833
  /* Data loaded successfully */
231635
- jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "mb-4", children: jsxs("div", { className: "text-sm text-gray-500 space-y-1", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelFullName'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _g === void 0 ? void 0 : _g.username, display: identifyUser((_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _h === void 0 ? void 0 : _h.email, (_j = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _j === void 0 ? void 0 : _j.name) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUsername'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_k = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _k === void 0 ? void 0 : _k.username, display: identifyUser((_l = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _l === void 0 ? void 0 : _l.email, (_m = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _m === void 0 ? void 0 : _m.username) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: (_o = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _o === void 0 ? void 0 : _o.mentor_unique_id, name: (_p = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _p === void 0 ? void 0 : _p.mentor })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_q = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _q === void 0 ? void 0 : _q.model) || '-'] })] }) }), jsx("div", { className: "space-y-4 pb-4", children: ((_r = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _r === void 0 ? void 0 : _r.length) > 0 ? ((_s = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _s === void 0 ? void 0 : _s.map((message, index) => {
232834
+ jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "mb-4", children: jsxs("div", { className: "text-sm text-gray-500 space-y-1", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _g === void 0 ? void 0 : _g.username, display: identifyUser(transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: (_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _h === void 0 ? void 0 : _h.mentor_unique_id, name: (_j = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _j === void 0 ? void 0 : _j.mentor })] }), jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_k = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _k === void 0 ? void 0 : _k.model)
232835
+ ? ((_l = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _l !== void 0 ? _l : renderModelBadge)({
232836
+ model: transcriptsMessagesDetailsData.summary.model,
232837
+ provider: (_m = providerOf(transcriptsMessagesDetailsData.summary)) !== null && _m !== void 0 ? _m : providerOfTurns(transcriptsMessagesDetailsData.messages),
232838
+ })
232839
+ : '-'] }), jsx(TranscriptDocumentsButton, { detail: transcriptsMessagesDetailsData, className: "mt-2" })] }) }), jsx("div", { className: "space-y-4 pb-4", children: ((_o = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _o === void 0 ? void 0 : _o.length) > 0 ? ((_p = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _p === void 0 ? void 0 : _p.map((message, index) => {
231636
232840
  var _a, _b;
231637
- return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-blue-100 ml-4", children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] })), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-white mr-4 border", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files })] }))] }, index));
232841
+ return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-blue-100 ml-4", children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] })), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-white mr-4 border", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files }), jsx(TranscriptTurnDetails, { turn: message, ...turnDetails })] }))] }, index));
231638
232842
  })) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) })] }) }))] }));
231639
232843
  }
231640
232844
 
@@ -240511,64 +241715,6 @@ const AutoResizeTextarea = ({ allowEmptySubmit = false, value, onChange, onSubmi
240511
241715
  disabled, placeholder: placeholder, style: combinedStyle, className: `flex max-h-32 min-h-[36px] w-full resize-none rounded-2xl border-0 border-gray-200 bg-transparent px-4 py-2 pt-2 pb-1 text-base ring-offset-white placeholder:text-gray-400 focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 ${className}`, ...props }));
240512
241716
  };
240513
241717
 
240514
- function DocumentSidebar({ isModal = false, sessionId }) {
240515
- const t = useT();
240516
- const [isCollapsed, setIsCollapsed] = React.useState(false);
240517
- const params = useParams();
240518
- const username = useUsername();
240519
- const isStreaming = useSelector(selectStreaming);
240520
- const { data: vectorDocuments, refetch, status, } = useGetVectorDocumentsQuery({
240521
- org: params.tenantKey,
240522
- sessionId: sessionId,
240523
- // @ts-ignore
240524
- userId: username,
240525
- }, {
240526
- skip: !params.tenantKey || !sessionId || !username || !isLoggedIn(),
240527
- });
240528
- React.useEffect(() => {
240529
- if (!isStreaming && isLoggedIn() && status !== 'uninitialized') {
240530
- refetch();
240531
- }
240532
- }, [isStreaming, refetch]);
240533
- const toggleCollapse = () => {
240534
- setIsCollapsed(!isCollapsed);
240535
- };
240536
- return (jsx(Fragment$1, { children: (vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length) && vectorDocuments.length > 0 ? (jsx("aside", { className: cn('transition-all duration-300 ease-in-out', isModal
240537
- ? 'max-h-full w-full overflow-auto'
240538
- : 'hidden rounded-tl-lg rounded-bl-lg md:hidden lg:block', !isModal && (isCollapsed ? 'w-[40px]' : 'w-[380px]')), style: {
240539
- backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
240540
- maxHeight: 'calc(100vh - 80px)',
240541
- }, children: jsxs("div", { className: "flex h-full flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
240542
- var _a, _b;
240543
- return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
240544
- }) }) }) }))] }) })) : null }));
240545
- }
240546
-
240547
- function RetrievedDocumentsButton({ sessionId }) {
240548
- const t = useT();
240549
- const [isOpen, setIsOpen] = useState(false);
240550
- const params = useParams();
240551
- const username = useUsername();
240552
- const isStreaming = useSelector(selectStreaming);
240553
- const { data: vectorDocuments, refetch, status, } = useGetVectorDocumentsQuery({
240554
- org: params.tenantKey,
240555
- sessionId: sessionId,
240556
- // @ts-ignore
240557
- userId: username,
240558
- }, {
240559
- skip: !params.tenantKey || !sessionId || !username || !isLoggedIn(),
240560
- });
240561
- useEffect(() => {
240562
- if (!isStreaming && isLoggedIn() && status !== 'uninitialized') {
240563
- refetch();
240564
- }
240565
- }, [isStreaming, refetch]);
240566
- if (!(vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length)) {
240567
- return null;
240568
- }
240569
- return (jsxs(Fragment$1, { children: [jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", onClick: () => setIsOpen(true), children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "flex-1 overflow-hidden", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId }) })] }) })] }));
240570
- }
240571
-
240572
241718
  const PAGE_SIZE = 10;
240573
241719
  const MemoryMenu = ({ onClose, mentorId, tenantKey, username }) => {
240574
241720
  const t = useT();
@@ -246092,5 +247238,5 @@ var trainOrDeleteModal = /*#__PURE__*/Object.freeze({
246092
247238
  TrainOrDeleteModal: TrainOrDeleteModal
246093
247239
  });
246094
247240
 
246095
- export { ACCESS_COURSE_LABEL, ALL_CATEGORIES, AccessTimeHeatmap, AccessiblePaginate, AddMentorToProjectModal, AddSkillDialog, Admin, AdvancedTab, AgentConfigPrompts, AgentScopeLink, AgentSkills, AlertsTab, AnalyticsAgentPicker, AnalyticsAreaChart, AnalyticsAuditLogStats, AnalyticsBarChart, AnalyticsControlBar, AnalyticsCourseDetail, AnalyticsCourses, AnalyticsFinancialStats, AnalyticsGlobalFiltersProvider, AnalyticsLayout, AnalyticsLlmUsageStats, AnalyticsMemoryStats, AnalyticsMonetizationStats, AnalyticsNotice, AnalyticsOverview, AnalyticsPlatformAuditLog, AnalyticsProgramDetail, AnalyticsPrograms, AnalyticsReportDownload, AnalyticsReports, AnalyticsScopeProvider, AnalyticsSettingsProvider, AnalyticsTopicsStats, AnalyticsTraces, AnalyticsTranscriptsStats, AnalyticsUsersStats, AppleRestrictionModal, ApplyWizard, AuditLogChangesView, AuditLogJsonView, BUY_NOW_LABEL, BillingTab, CATALOGS, CHART_ORDINAL_RAMP, CHART_SERIES_COLORS, CHART_SINGLE_SERIES_COLOR, CREATE_AGENT_LABELS, CUA_DRIVER_REPO_URL, CUA_DRIVER_TAURI_COMMANDS, CUA_DRIVER_TAURI_EVENTS, CardInfoIcon, CategorizedDropdownMenu, ChartCardWrapper, ChartFiltersProvider, ChartLoading, ChartTooltipCard, ChatContext, ChatPrivacyToggle, ChatProvider, CompanyDialog, ConnectorManagementDialog, CopyButtonIcon, CourseAccessGuard, CourseCardSkeleton, CourseContentLoading, CourseOutline, CourseOutlineContext, CourseOutlineDrawer, CreateAgentForm, CreateAgentModal, CreateProjectModal, CreateWorkflowModal, CredentialBox, CredentialMiniBoxSkeleton, CredentialsList, CreditBalance, CustomDateRangePicker, CustomTooltip, DEFAULT_COWORK_REQUIRED_SIZE_GB, DEFAULT_LOCALE, DefaultEmptyBox, DeleteProjectModal, DeleteWorkflowModal, DiscoverContentCard, DiscoverFacetsFilter, DiscoverFilterDrawer, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENROLL_NOW_COURSE_STARTING_SOON_LABEL, ENROLL_NOW_LABEL, EditAlertDialog, EducationBox, EducationDialog, EducationTab, EdxIframeContext, EmptyStats, ExperienceBox, ExperienceDialog, ExperienceTab, FacetFilterContext, Footer, GradebookTab, GroupsFilterDropdown, INVITATION_ONLY_LABEL, InstitutionDialog, IntegrationsTab, InviteUserContent, InviteUserDialog, InvitedUsersDialog, LOCAL_LLM_CHANGED_EVENT, LOCAL_MODELS, Loader, LocalLLMTab, LocalModelsContent, LoginButton, MACOS_PERMISSIONS_COMMANDS, MEMORY_PAGE_SIZE, Markdown, MemoryAdminTab, MinimumMentorAlert, MonetizationTab, NotificationDisplay, NotificationDropdown, ONBOARDING_FIELD_TYPES, ONBOARDING_SECTORS, OnboardingField, OnboardingShell, OnboardingWizard, PLATFORM_AUDIT_DEEP_PAGING_THRESHOLD, PLATFORM_AUDIT_PAGE_SIZE, PaywallModal, PlacementExam, PlacementExamsDropdown, PlatformAuditLogDetailSheet, PlatformNavbar, PlatformNavbarSearch, Profile, ProfileTimeChart, ProgressDots, ProjectActionButtons, ProjectFilesModal, ProjectInstructionsModal, ProjectLandingPage, ProjectMentorsList, ProjectsPage, REQUEST_ACCESS_COURSE_STARTING_SOON_LABEL, REQUEST_ACCESS_LABEL, RankedBars, RenameProjectModal, ResumeBox, ResumeTab, RichTextEditor, SERVICE_COLOR_ORDER, SUPPORTED_LOCALES, SandboxConfig, SearchableMultiSelect, SendNotificationDialog, SignupButton, SkeletonActivityStatBox, SkeletonAddSkillsLoading, SkeletonCreatePathwaySearchList, SkeletonDiscoverFilterBox, SkeletonEducationBox, SkeletonMultiplier, SkeletonPathwayBox, SkeletonProfileInfoCard, SkeletonSkillBox, SkillBox, SkillDetailModal, SkillLeaderboardChart, SkillsBox, SkillsList, SlashSkillPicker, SourceChip, Spinner, StatCard, StepHeader, SummerNoteTextEditor, TAURI_COMMANDS, TAURI_EVENTS, TRACES_PAGE_SIZE, TenantSwitcher, TimeFilter, TimeTrackingProvider, TimedExam, ToolDialogs, TopBanner, TrendSparkline, USER_ONBOARDING_FORM_SLUG, USER_ONBOARDING_FORM_VERSION, USER_ONBOARDING_SUBMISSION_SLUG, UpgradePackageModal, UserAvatar, UserOnboardingWizard, Version, WebContainersI18nProvider, WorkflowSidebar, addBookmarksTab, areChartFiltersEqual, availablePlacementExams, buildOnboardingAgentPath, buildUserOnboardingFormMetadata, buildUserOnboardingSubmissionMetadata, coerceOnboardingAgent, coerceOnboardingAgents, coerceUserOnboardingForm, colorForRank, colorForSeries, emptyUserOnboardingForm, findLastResumeBlock, findOnboardingAgent, findSequentialParent, flattenVerticalBlocks, formatAuditTimestamp, formatCompactNumber, formatLatencySeconds, formatTimeZoneLabel, getCatalog, getFirstAvailableUnit, getLocalLLMModel, getLocalLLMToolSupport, getNextUnitIframe, getOrg, getParentBlockById, getParentsInfosFromSublessonId, getPreviousUnitIframe, getRandomCourseImage, getSectorById, getTenant, getUnitToIframe, getUserId, getUserName, granularityForFilter, hasOnboardingContent, humanizeAuditToken, inBrowserPrint, inIframe, initialCuaDriverInstallState, initialModelDownloadState, isCoworkEnabled, isLlmUsageForbidden, isLlmUsageUnavailable, isLocalLLMEnabled, isPlatformNavbarCreditBalanceVisible, isSlashCommandToken, isTauriApp, isValidLocale, isValidTimeZone, llmUsageDateArgs, components as markdownComponents, modelSupportsCowork, onboardingPrimaryButtonClass, onboardingSecondaryButtonClass, readUserOnboardingForm, readUserOnboardingSubmission, readUserOnboardingSubmissions, resolveAuditTimeZone, resolveCreateAgentLabels, sanitizeCss, setCoworkEnabled, setLocalLLMEnabled, setLocalLLMModel, setLocalLLMToolSupport, smallestCoworkModel, useAnalyticsGlobalFilters, useAnalyticsScope, useAnalyticsSettings, useAuditLog, useCatalogSearch, useChartFilters, useChatPrivacy, useChatState, useCourseDetail, useCourseMetadata, useCourses, useCuaDriver, useDiscover, useEdxIframe, useFinancial, useIframeMessageHandler, useLlmTraces, useLlmUsage, useLocalStorage, useMemory, useModelDownload, useMonetization, useOverview, usePlatformAuditLabels, usePlatformAuditLog, useProfileActivityStats, useProfileCredentials, useProfilePathways, useProfilePrograms, useProfileSkills, useProfileTimeSpent, usePrograms, useReports, useScopedMentorDbId, useScopedMentorId, useSelectedLocalModel, useSlashSkillPicker, useSlashSkills, useT, useTauri, useTimeTracking, useTopics, useTranscripts, useUserCourses, useUserMetadata, useUsers, useWebContainersI18n, useWebContainersLocale };
247241
+ export { ACCESS_COURSE_LABEL, ALL_CATEGORIES, AccessTimeHeatmap, AccessiblePaginate, AddMentorToProjectModal, AddSkillDialog, Admin, AdvancedTab, AgentConfigPrompts, AgentScopeLink, AgentSkills, AlertsTab, AnalyticsAgentPicker, AnalyticsAreaChart, AnalyticsAuditLogStats, AnalyticsBarChart, AnalyticsControlBar, AnalyticsCourseDetail, AnalyticsCourses, AnalyticsFinancialStats, AnalyticsGlobalFiltersProvider, AnalyticsLayout, AnalyticsLlmUsageStats, AnalyticsMemoryStats, AnalyticsMonetizationStats, AnalyticsNotice, AnalyticsOverview, AnalyticsPlatformAuditLog, AnalyticsProgramDetail, AnalyticsPrograms, AnalyticsReportDownload, AnalyticsReports, AnalyticsScopeProvider, AnalyticsSettingsProvider, AnalyticsTopicsStats, AnalyticsTraces, AnalyticsTranscriptsStats, AnalyticsUsersStats, AppleRestrictionModal, ApplyWizard, AuditLogChangesView, AuditLogJsonView, BUY_NOW_LABEL, BillingTab, CATALOGS, CHART_ORDINAL_RAMP, CHART_SERIES_COLORS, CHART_SINGLE_SERIES_COLOR, CREATE_AGENT_LABELS, CUA_DRIVER_REPO_URL, CUA_DRIVER_TAURI_COMMANDS, CUA_DRIVER_TAURI_EVENTS, CardInfoIcon, CategorizedDropdownMenu, ChartCardWrapper, ChartFiltersProvider, ChartLoading, ChartTooltipCard, ChatContext, ChatPrivacyToggle, ChatProvider, CompanyDialog, ConnectorManagementDialog, CopyButtonIcon, CourseAccessGuard, CourseCardSkeleton, CourseContentLoading, CourseOutline, CourseOutlineContext, CourseOutlineDrawer, CreateAgentForm, CreateAgentModal, CreateProjectModal, CreateWorkflowModal, CredentialBox, CredentialMiniBoxSkeleton, CredentialsList, CreditBalance, CustomDateRangePicker, CustomTooltip, DEFAULT_COWORK_REQUIRED_SIZE_GB, DEFAULT_LOCALE, DEFAULT_USER_IDENTITY_ORDER, DefaultEmptyBox, DeleteProjectModal, DeleteWorkflowModal, DiscoverContentCard, DiscoverFacetsFilter, DiscoverFilterDrawer, DocumentSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENROLL_NOW_COURSE_STARTING_SOON_LABEL, ENROLL_NOW_LABEL, EditAlertDialog, EducationBox, EducationDialog, EducationTab, EdxIframeContext, EmptyStats, ExperienceBox, ExperienceDialog, ExperienceTab, FacetFilterContext, Footer, GradebookTab, GroupsFilterDropdown, INVITATION_ONLY_LABEL, InstitutionDialog, IntegrationsTab, InviteUserContent, InviteUserDialog, InvitedUsersDialog, LOCAL_LLM_CHANGED_EVENT, LOCAL_MODELS, LlmModelBadge, Loader, LocalLLMTab, LocalModelsContent, LoginButton, MACOS_PERMISSIONS_COMMANDS, MEMORY_PAGE_SIZE, Markdown, MemoryAdminTab, MinimumMentorAlert, MonetizationTab, NotificationDisplay, NotificationDropdown, ONBOARDING_FIELD_TYPES, ONBOARDING_SECTORS, OnboardingField, OnboardingShell, OnboardingWizard, PLATFORM_AUDIT_DEEP_PAGING_THRESHOLD, PLATFORM_AUDIT_PAGE_SIZE, PaywallModal, PlacementExam, PlacementExamsDropdown, PlatformAuditLogDetailSheet, PlatformNavbar, PlatformNavbarSearch, Profile, ProfileTimeChart, ProgressDots, ProjectActionButtons, ProjectFilesModal, ProjectInstructionsModal, ProjectLandingPage, ProjectMentorsList, ProjectsPage, REQUEST_ACCESS_COURSE_STARTING_SOON_LABEL, REQUEST_ACCESS_LABEL, RankedBars, RenameProjectModal, ResumeBox, ResumeTab, RetrievedDocumentsButton, RichTextEditor, SERVICE_COLOR_ORDER, SUPPORTED_LOCALES, SandboxConfig, SearchableMultiSelect, SendNotificationDialog, SignupButton, SkeletonActivityStatBox, SkeletonAddSkillsLoading, SkeletonCreatePathwaySearchList, SkeletonDiscoverFilterBox, SkeletonEducationBox, SkeletonMultiplier, SkeletonPathwayBox, SkeletonProfileInfoCard, SkeletonSkillBox, SkillBox, SkillDetailModal, SkillLeaderboardChart, SkillsBox, SkillsList, SlashSkillPicker, SourceChip, Spinner, StatCard, StepHeader, SummerNoteTextEditor, TAURI_COMMANDS, TAURI_EVENTS, TRACES_PAGE_SIZE, TenantSwitcher, TimeFilter, TimeTrackingProvider, TimedExam, ToolCallIndicator, ToolDialogs, TopBanner, TranscriptFileCards, TranscriptRollupBadges, TranscriptTurnDetails, TrendSparkline, USER_ONBOARDING_FORM_SLUG, USER_ONBOARDING_FORM_VERSION, USER_ONBOARDING_SUBMISSION_SLUG, UpgradePackageModal, UserAvatar, UserOnboardingWizard, UserProfileDialog, UserProfileLink, Version, WebContainersI18nProvider, WorkflowSidebar, addBookmarksTab, areChartFiltersEqual, availablePlacementExams, buildOnboardingAgentPath, buildUserOnboardingFormMetadata, buildUserOnboardingSubmissionMetadata, coerceOnboardingAgent, coerceOnboardingAgents, coerceUserOnboardingForm, colorForRank, colorForSeries, conversationDocuments, emptyUserOnboardingForm, findLastResumeBlock, findOnboardingAgent, findSequentialParent, flattenVerticalBlocks, formatAuditTimestamp, formatCompactNumber, formatFileSize, formatLatencySeconds, formatResult, formatTimeZoneLabel, getCatalog, getFirstAvailableUnit, getFriendlyToolName, getLLMModelDisplayName, getLocalLLMModel, getLocalLLMToolSupport, getNextUnitIframe, getOrg, getParentBlockById, getParentsInfosFromSublessonId, getPreviousUnitIframe, getQueryLabel, getRandomCourseImage, getSectorById, getTenant, getUnitToIframe, getUserId, getUserName, granularityForFilter, hasOnboardingContent, humanizeAuditToken, humanizeFieldName, inBrowserPrint, inIframe, inferProviderKeysFromModel, initialCuaDriverInstallState, initialModelDownloadState, isCoworkEnabled, isEmailLike, isFileReference, isLlmUsageForbidden, isLlmUsageUnavailable, isLocalLLMEnabled, isPlatformNavbarCreditBalanceVisible, isSlashCommandToken, isTauriApp, isValidLocale, isValidTimeZone, llmUsageDateArgs, components as markdownComponents, modelSupportsCowork, onboardingPrimaryButtonClass, onboardingSecondaryButtonClass, readUserOnboardingForm, readUserOnboardingSubmission, readUserOnboardingSubmissions, resolveAuditTimeZone, resolveCreateAgentLabels, resolveUserIdentity, sanitizeCss, setCoworkEnabled, setLocalLLMEnabled, setLocalLLMModel, setLocalLLMToolSupport, smallestCoworkModel, summarizeTranscriptTurns, toFileReference, toRetrievedDocuments, toToolCallInfos, useAnalyticsGlobalFilters, useAnalyticsScope, useAnalyticsSettings, useAuditLog, useCatalogSearch, useChartFilters, useChatPrivacy, useChatState, useCourseDetail, useCourseMetadata, useCourses, useCuaDriver, useDiscover, useEdxIframe, useFinancial, useIframeMessageHandler, useLlmDisplayName, useLlmProviderCatalogue, useLlmTraces, useLlmUsage, useLocalStorage, useMemory, useModelDownload, useMonetization, useOverview, usePlatformAuditLabels, usePlatformAuditLog, useProfileActivityStats, useProfileCredentials, useProfilePathways, useProfilePrograms, useProfileSkills, useProfileTimeSpent, usePrograms, useReports, useScopedMentorDbId, useScopedMentorId, useSelectedLocalModel, useSlashSkillPicker, useSlashSkills, useT, useTauri, useTimeTracking, useTopics, useTranscripts, useUserCourses, useUserMetadata, useUsers, useWebContainersI18n, useWebContainersLocale };
246096
247242
  //# sourceMappingURL=index.esm.js.map