@iblai/iblai-js 2.4.3 → 2.4.5

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.
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useReducer, useRef, useImperativeHandle, useCallback, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
4
4
  import * as dataLayer from '@iblai/data-layer';
5
- import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, 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, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, 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, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useGetAiSearchMentorsQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useStarMentorMutation, useUnstarMentorMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetAgentSkillsQuery, useGetMentorSkillAssignmentsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, resolveEffectiveAgentSkills, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useDeleteMentorMemoryMutation, useUpdateMentorMemoryMutation, useCreateMentorMemoryMutation, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, isBaseAgentMentor, useGetGradeResultQuery, useOverrideGradeResultMutation, useGetChatHistoryFilterQuery, useListGradeResultsQuery, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetMentorPublicSettingsQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
5
+ import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, 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, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, 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, 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, useGetFinancialStatsQuery, useGetMentorsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useGetAiSearchMentorsQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useStarMentorMutation, useUnstarMentorMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useDeleteMentorMemoryMutation, useUpdateMentorMemoryMutation, useCreateMentorMemoryMutation, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, isBaseAgentMentor, useGetGradeResultQuery, useOverrideGradeResultMutation, useGetChatHistoryFilterQuery, useListGradeResultsQuery, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetSpendCapStatusQuery, useGetMentorPublicSettingsQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
6
6
  import { getInitials, useTenantMetadata, useCurrentTenant, selectRbacPermissions, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, 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, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
7
7
  import { useDispatch, useSelector } from 'react-redux';
8
8
  import { toast } from 'sonner';
@@ -98333,7 +98333,13 @@ var chatIndex$3 = {
98333
98333
  confirmScreenShareTitle: "Confirm Screen Sharing",
98334
98334
  confirmScreenShareDescription: "Would you like to start a screen sharing with your agent?",
98335
98335
  cancel: "Cancel",
98336
- confirm: "Confirm"
98336
+ confirm: "Confirm",
98337
+ spendCapLimitDay: "You've reached today's usage limit for this assistant.",
98338
+ spendCapLimitWeek: "You've reached this week's usage limit for this assistant.",
98339
+ spendCapLimitMonth: "You've reached this month's usage limit for this assistant.",
98340
+ spendCapLimitYear: "You've reached this year's usage limit for this assistant.",
98341
+ spendCapLimitGeneric: "You've reached the usage limit for this assistant.",
98342
+ spendCapContactAdmin: "Please try again after the limit resets, or contact your administrator."
98337
98343
  };
98338
98344
  var chatInputForm$3 = {
98339
98345
  uploadingOneFile: "Uploading 1 file...",
@@ -98536,6 +98542,8 @@ var clawSandboxAgentSkills$3 = {
98536
98542
  fieldCategoryPlaceholder: "e.g. web, code, data",
98537
98543
  privateToAgent: "Only This Agent",
98538
98544
  privateToAgentHint: "Private skills are available to this agent only and take precedence over platform skills with the same slug.",
98545
+ privateToOtherAgentHint: "This skill is private to another agent, so this setting cannot be changed here.",
98546
+ privateToOtherAgentTooltip: "This skill is private to another agent and cannot be added here.",
98539
98547
  instructionHint: "The playbook the agent follows when the skill is used. Edits apply immediately, including in conversations already in progress.",
98540
98548
  filesHint: "Optional resources the agent can use with this skill. References and scripts are text files; assets are uploaded files.",
98541
98549
  filesAfterCreateHint: "You can attach resources to this skill after creating it (reopen it with Edit).",
@@ -98775,6 +98783,92 @@ var componentsProactiveSettingsSection$3 = {
98775
98783
  editAgentPromptAriaLabel: "Edit agent prompt",
98776
98784
  periodicAgentsHelp: "Optional. Select agents to include in the proactive outreach rotation."
98777
98785
  };
98786
+ var componentsTemplateActionsSection$3 = {
98787
+ title: "Test & Reset",
98788
+ description: "Preview the template in your inbox, or discard this platform's customizations.",
98789
+ testEmailLabel: "Send a test email",
98790
+ testEmailHelper: "Sends one rendering of this template to the address you enter.",
98791
+ testEmailPlaceholder: "you@example.com",
98792
+ sendTest: "Send test email",
98793
+ sendingTest: "Sending...",
98794
+ testSuccessTitle: "Test email sent",
98795
+ testSuccessDescription: "Sent to {email}.",
98796
+ testErrorTitle: "Could not send test email",
98797
+ testErrorDescription: "Please check the address and try again.",
98798
+ resetLabel: "Reset to default",
98799
+ resetHelper: "Discards this platform's copy and restores the default template.",
98800
+ reset: "Reset to default",
98801
+ resetting: "Resetting...",
98802
+ resetConfirmTitle: "Reset this template?",
98803
+ resetConfirmDescription: "This platform's customizations will be discarded and the default template restored. This cannot be undone.",
98804
+ resetConfirmCancel: "Cancel",
98805
+ resetConfirmAction: "Reset template",
98806
+ resetSuccessTitle: "Template reset",
98807
+ resetSuccessDescription: "The platform default has been restored.",
98808
+ resetErrorTitle: "Could not reset template",
98809
+ resetErrorDescription: "Please try again."
98810
+ };
98811
+ var componentsTemplateVariablesHelper$3 = {
98812
+ title: "Variables you can use",
98813
+ copyHint: "Click a variable to copy it, then paste it into the subject or body.",
98814
+ insertHint: "Click a variable to insert it.",
98815
+ copiedToast: "Copied {token} to your clipboard.",
98816
+ copyUnavailableToast: "Copy {token} manually — clipboard access is unavailable.",
98817
+ copyAriaLabel: "Copy the {name} variable",
98818
+ insertAriaLabel: "Insert the {name} variable"
98819
+ };
98820
+ var componentsUserProgressSettingsSection$3 = {
98821
+ scheduleTitle: "Schedule & Reporting Period",
98822
+ scheduleDescription: "Choose how often the summary is sent and how far back it looks.",
98823
+ frequencyLabel: "Frequency",
98824
+ selectFrequencyAriaLabel: "Select frequency",
98825
+ selectFrequencyPlaceholder: "Select frequency",
98826
+ daily: "Daily",
98827
+ weekly: "Weekly",
98828
+ monthly: "Monthly",
98829
+ custom: "Custom",
98830
+ cadenceDaily: "Sends every day at the time below.",
98831
+ cadenceWeekly: "Sends every Monday at the time below.",
98832
+ cadenceMonthly: "Sends on the 1st of each month at the time below.",
98833
+ cadenceCustom: "Sends every {days} days at the time below.",
98834
+ cadenceCustomUnset: "Set a custom interval to choose how often the summary is sent.",
98835
+ executionTimeLabel: "Send Time",
98836
+ executionTimeRequired: "Specify the time the summary should be sent.",
98837
+ timezoneLabel: "Timezone",
98838
+ selectTimezoneAriaLabel: "Select timezone",
98839
+ selectTimezonePlaceholder: "Select timezone",
98840
+ useLocalTimezone: "Use local timezone ({timezone})",
98841
+ customIntervalLabel: "Custom Interval (days)",
98842
+ customIntervalRequired: "Set how many days should pass between sends.",
98843
+ dayRangeError: "Enter a whole number of days between 1 and 365.",
98844
+ lookbackLabel: "Reporting Window (days)",
98845
+ lookbackHelp: "How far back each summary looks. This is separate from how often the email is sent — a weekly email can still report on the last 30 days.",
98846
+ lookbackRequired: "Set the reporting window in days.",
98847
+ dataPointsLabel: "Included Data Points",
98848
+ dataPointsHelp: "The email only shows sections that are enabled and have data for that learner.",
98849
+ dataPointCoursesProgressed: "Courses progressed",
98850
+ dataPointCoursesCompleted: "Courses completed",
98851
+ dataPointCredentialsEarned: "Credentials earned",
98852
+ dataPointTimeSpent: "Time spent",
98853
+ dataPointActiveDays: "Active days",
98854
+ dataPointMentorsAccessed: "AI agent activity",
98855
+ noDataPointsWarning: "No data points selected — the summary will have no content to show."
98856
+ };
98857
+ var componentsWatcherEmailSection$3 = {
98858
+ title: "Watcher Email",
98859
+ description: "Sent to a learner's watchers, such as a parent or manager.",
98860
+ fallbackNotice: "Watchers currently receive the learner email. Customize it to send them a different message.",
98861
+ customize: "Customize watcher email",
98862
+ useLearnerEmail: "Use the learner email",
98863
+ subjectLabel: "Watcher Email Subject",
98864
+ subjectHelper: "Supports the same variables as the learner email, plus learner_name.",
98865
+ subjectFallbackHelper: "Leave empty and watchers receive the learner email subject.",
98866
+ subjectPlaceholder: "Enter the watcher email subject...",
98867
+ bodyLabel: "Watcher Email Body",
98868
+ bodyHelper: "Supports the same variables as the learner email, plus learner_name.",
98869
+ bodyFallbackHelper: "Leave empty and watchers receive the learner email body.",
98870
+ bodyPlaceholder: "Enter the watcher email body..."
98871
+ };
98778
98872
  var courseContentAccessiblePaginate$3 = {
98779
98873
  pagination: "Pagination"
98780
98874
  };
@@ -99977,7 +100071,14 @@ var notificationsEditAlertDialog$3 = {
99977
100071
  toastErrorDescription: "Failed to update template. Please try again.",
99978
100072
  cancel: "Cancel",
99979
100073
  saving: "Saving...",
99980
- saveChanges: "Save Changes"
100074
+ saveChanges: "Save Changes",
100075
+ learnerEmailSubjectLabel: "Learner Email Subject",
100076
+ learnerEmailSubjectHelper: "Required. Supports template variables — see the list below the body.",
100077
+ learnerEmailBodyLabel: "Learner Email Body",
100078
+ learnerEmailBodyHelper: "Required. Greet with \"Hey,\" rather than a username — usernames are often auto-generated.",
100079
+ noChangesToast: "No changes to save.",
100080
+ toastValidationTitle: "Check the highlighted fields",
100081
+ toastValidationDescription: "Some values were rejected. Review the fields marked in red."
99981
100082
  };
99982
100083
  var notificationsNotificationDisplay$3 = {
99983
100084
  notificationsAriaLabel: "Notifications",
@@ -100260,7 +100361,8 @@ var profileBilling$3 = {
100260
100361
  addCredits: "Add Credits",
100261
100362
  manageBilling: "Manage Billing",
100262
100363
  availableLabel: "Available",
100263
- creditsRemaining: "Credits remaining",
100364
+ creditsUsdEquivalent: " {amount}",
100365
+ creditsConversionHint: "{credits} credits = $1",
100264
100366
  creditsUnit: "{amount} Credits",
100265
100367
  consumedLabel: "Consumed",
100266
100368
  usedThisPeriod: "Used this period",
@@ -100272,10 +100374,17 @@ var profileBilling$3 = {
100272
100374
  disabled: "Disabled",
100273
100375
  manageUsage: "Manage Usage",
100274
100376
  thresholdLabel: "Threshold",
100377
+ thresholdHint: "When your available credits fall below this amount, a top-up starts automatically",
100275
100378
  rechargeAmountLabel: "Recharge amount",
100379
+ rechargeAmountHint: "Charged to your payment method and added to your credits with each top-up",
100276
100380
  spendingLimitLabel: "Spending limit",
100381
+ spendingLimitHint: "The most auto recharge can charge per billing period",
100277
100382
  unlimited: "Unlimited",
100278
- autoRechargeDisabledMessage: "Auto recharge is currently disabled. Enable it to keep your balance topped up without interruption."
100383
+ autoRechargeDisabledMessage: "Auto recharge is currently disabled. Enable it to keep your balance topped up without interruption.",
100384
+ billingTabsAriaLabel: "Billing sections",
100385
+ tabPlanAndCredits: "Plan & Credits",
100386
+ tabSpendLimits: "Spend Limits",
100387
+ tabAgentLimits: "Agent Limits"
100279
100388
  };
100280
100389
  var profileCatalogInviteModal$3 = {
100281
100390
  title: "Catalog Invite",
@@ -101219,6 +101328,156 @@ var skillsProfileSkillsBox$3 = {
101219
101328
  desired: "Desired",
101220
101329
  noDesiredSkills: "You don't have any desired skills yet."
101221
101330
  };
101331
+ var spendCapUsage$3 = {
101332
+ scopeTenant: "Workspace",
101333
+ scopeAgent: "This agent",
101334
+ scopeUserAgent: "Your usage on this agent",
101335
+ intervalDay: "today",
101336
+ intervalWeek: "this week",
101337
+ intervalMonth: "this month",
101338
+ intervalYear: "this year",
101339
+ percentUsed: "{percent}% used",
101340
+ blockedBanner: "The usage limit has been reached. New chats and training are paused until the period resets.",
101341
+ warningBanner: "Usage is nearing its limit."
101342
+ };
101343
+ var spendCapsTabLabels$3 = {
101344
+ header: {
101345
+ title: "Billing",
101346
+ description: "Set spend limits for this agent and its users, and see how much has been used."
101347
+ },
101348
+ capability: {
101349
+ title: "Spend Limit",
101350
+ description: "Put a ceiling on AI spend. While this limit is on, everything it covers counts toward the amount below — and when the limit is reached, you choose whether new requests are blocked or admins are simply alerted.",
101351
+ offHint: "Turn it on to start tracking and enforcing this limit. Your setup here is kept safe if you later turn it off."
101352
+ },
101353
+ subTabs: {
101354
+ agent: "This Agent",
101355
+ users: "Per User"
101356
+ },
101357
+ tenantSections: {
101358
+ workspace: {
101359
+ title: "Workspace Spend Limit",
101360
+ description: "One limit covering everything all agents and users spend in this workspace. Per-agent and per-user limits live in each agent’s settings under Billing."
101361
+ },
101362
+ noCapStats: {
101363
+ spentThisMonth: "Spent This Month",
101364
+ spentAllTime: "Spent All Time",
101365
+ hint: "Across all agents and users in this workspace"
101366
+ },
101367
+ agents: {
101368
+ title: "Agent Spend Limits",
101369
+ description: "Agents with a spend limit configured in this workspace. Open one to manage its limit and its per-user limits.",
101370
+ filter: {
101371
+ label: "Search Agents",
101372
+ placeholder: "Search Agents…",
101373
+ typeToSearch: "Type at least 2 characters to search",
101374
+ searching: "Searching agents…",
101375
+ noResults: "No agents found",
101376
+ clear: "Clear Agent Filter"
101377
+ },
101378
+ table: {
101379
+ agentColumn: "Agent",
101380
+ limitColumn: "Limit",
101381
+ spentColumn: "Spent",
101382
+ remainingColumn: "Remaining",
101383
+ statusColumn: "Status"
101384
+ },
101385
+ emptyState: "No agent spend limits yet. Search for an agent above to set one.",
101386
+ emptyFiltered: "No spend limit for this agent yet.",
101387
+ setLimitButton: "Set Spend Limit",
101388
+ manageAria: "Manage spend limits for {name}",
101389
+ statusToggleAria: "Toggle spend limit for {name}"
101390
+ }
101391
+ },
101392
+ form: {
101393
+ limitLabel: "Spend Limit (USD)",
101394
+ limitPlaceholder: "e.g. 250.00",
101395
+ limitError: "Enter an amount greater than 0",
101396
+ intervalLabel: "Resets Every",
101397
+ intervalOptions: {
101398
+ day: "Day",
101399
+ week: "Week",
101400
+ month: "Month",
101401
+ year: "Year"
101402
+ },
101403
+ enforcementLabel: "When the Limit Is Reached",
101404
+ enforcementOptions: {
101405
+ block: "Block Requests",
101406
+ alertOnly: "Alert Only"
101407
+ },
101408
+ enforcementHelp: {
101409
+ block: "New chat and training requests are refused until the period resets.",
101410
+ alertOnly: "Nothing is blocked — admins are notified when spend nears the limit."
101411
+ },
101412
+ thresholdsLabel: "Alert At (% of Limit)",
101413
+ thresholdsPlaceholder: "e.g. 80, 95",
101414
+ thresholdsHelp: "Comma-separated percentages at which admins get a near-limit alert.",
101415
+ thresholdsError: "Use comma-separated numbers between 1 and 100",
101416
+ noCapHint: "No spend limit configured yet — set an amount and save to create one.",
101417
+ save: "Save",
101418
+ saving: "Saving…",
101419
+ remove: "Delete Limit",
101420
+ removing: "Deleting…",
101421
+ cancel: "Cancel"
101422
+ },
101423
+ usage: {
101424
+ title: "Spend Limit Usage",
101425
+ periodTitles: {
101426
+ day: "Daily Usage",
101427
+ week: "Weekly Usage",
101428
+ month: "Monthly Usage",
101429
+ year: "Yearly Usage"
101430
+ },
101431
+ spent: "Spent: {amount}",
101432
+ remaining: "Remaining: {amount}",
101433
+ percentUsed: "{percent}% of limit used",
101434
+ exceededBadge: "Limit Reached",
101435
+ alertOnlyBadge: "Alert Only",
101436
+ disabledBadge: "Off"
101437
+ },
101438
+ users: {
101439
+ sectionTitle: "Per-User Limits",
101440
+ sectionDescription: "Limit what specific users can spend on this agent.",
101441
+ addButton: "Add User Limit",
101442
+ addTitle: "New User Limit",
101443
+ editTitle: "Edit User Limit",
101444
+ cancel: "Cancel",
101445
+ emptyState: "No per-user limits yet. Add one to limit a specific user on this agent.",
101446
+ edit: "Edit",
101447
+ "delete": "Delete",
101448
+ table: {
101449
+ userColumn: "User",
101450
+ limitColumn: "Limit",
101451
+ statusColumn: "Status"
101452
+ },
101453
+ actionsAria: "Actions for {username}",
101454
+ statusToggleAria: "Toggle spend limit for {username}",
101455
+ picker: {
101456
+ label: "User",
101457
+ placeholder: "Search Users…",
101458
+ typeToSearch: "Type at least 2 characters to search",
101459
+ searching: "Searching users…",
101460
+ noResults: "No users found",
101461
+ clear: "Change User"
101462
+ }
101463
+ },
101464
+ denied: {
101465
+ title: "No Access to Spend Limits",
101466
+ description: "You don't have permission to view spend limits here. Ask an administrator for access."
101467
+ },
101468
+ deleteModal: {
101469
+ title: "Delete Spend Limit",
101470
+ description: "Spending will no longer be limited or tracked against this limit.",
101471
+ cancel: "Cancel",
101472
+ confirm: "Delete"
101473
+ },
101474
+ toasts: {
101475
+ saved: "Spend limit saved",
101476
+ saveError: "Couldn't save the spend limit",
101477
+ deleted: "Spend limit deleted",
101478
+ deleteError: "Couldn't delete the spend limit"
101479
+ }
101480
+ };
101222
101481
  var tabsAlertstab$3 = {
101223
101482
  searchAlertsPlaceholder: "Search alerts",
101224
101483
  searchAlertsAriaLabel: "Search alerts",
@@ -103127,6 +103386,10 @@ var en = {
103127
103386
  componentsPolicyAssignmentSection: componentsPolicyAssignmentSection$3,
103128
103387
  componentsPolicyConfigDialog: componentsPolicyConfigDialog$3,
103129
103388
  componentsProactiveSettingsSection: componentsProactiveSettingsSection$3,
103389
+ componentsTemplateActionsSection: componentsTemplateActionsSection$3,
103390
+ componentsTemplateVariablesHelper: componentsTemplateVariablesHelper$3,
103391
+ componentsUserProgressSettingsSection: componentsUserProgressSettingsSection$3,
103392
+ componentsWatcherEmailSection: componentsWatcherEmailSection$3,
103130
103393
  courseContentAccessiblePaginate: courseContentAccessiblePaginate$3,
103131
103394
  courseContentCourseContentLayout: courseContentCourseContentLayout$3,
103132
103395
  courseContentDiscoverFacetsFilter: courseContentDiscoverFacetsFilter$3,
@@ -103242,6 +103505,8 @@ var en = {
103242
103505
  skillsProfileResumeBox: skillsProfileResumeBox$3,
103243
103506
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$3,
103244
103507
  skillsProfileSkillsBox: skillsProfileSkillsBox$3,
103508
+ spendCapUsage: spendCapUsage$3,
103509
+ spendCapsTabLabels: spendCapsTabLabels$3,
103245
103510
  tabsAlertstab: tabsAlertstab$3,
103246
103511
  tabsDisclaimersTab: tabsDisclaimersTab$3,
103247
103512
  tabsEmbedTab: tabsEmbedTab$3,
@@ -104972,7 +105237,13 @@ var chatIndex$2 = {
104972
105237
  confirmScreenShareTitle: "Confirmer le partage d'écran",
104973
105238
  confirmScreenShareDescription: "Souhaitez-vous démarrer un partage d'écran avec votre agent ?",
104974
105239
  cancel: "Annuler",
104975
- confirm: "Confirmer"
105240
+ confirm: "Confirmer",
105241
+ spendCapLimitDay: "Vous avez atteint la limite d'utilisation du jour pour cet assistant.",
105242
+ spendCapLimitWeek: "Vous avez atteint la limite d'utilisation de la semaine pour cet assistant.",
105243
+ spendCapLimitMonth: "Vous avez atteint la limite d'utilisation du mois pour cet assistant.",
105244
+ spendCapLimitYear: "Vous avez atteint la limite d'utilisation de l'année pour cet assistant.",
105245
+ spendCapLimitGeneric: "Vous avez atteint la limite d'utilisation de cet assistant.",
105246
+ spendCapContactAdmin: "Réessayez après la réinitialisation de la limite ou contactez votre administrateur."
104976
105247
  };
104977
105248
  var chatInputForm$2 = {
104978
105249
  uploadingOneFile: "Téléversement de 1 fichier...",
@@ -105175,6 +105446,8 @@ var clawSandboxAgentSkills$2 = {
105175
105446
  fieldCategoryPlaceholder: "p. ex. web, code, données",
105176
105447
  privateToAgent: "Cet agent uniquement",
105177
105448
  privateToAgentHint: "Les compétences privées ne sont disponibles que pour cet agent et priment sur les compétences de la plateforme portant le même slug.",
105449
+ privateToOtherAgentHint: "Cette compétence est privée d'un autre agent, ce réglage ne peut donc pas être modifié ici.",
105450
+ privateToOtherAgentTooltip: "Cette compétence est privée d'un autre agent et ne peut pas être ajoutée ici.",
105178
105451
  instructionHint: "Le guide que l'agent suit lorsque la compétence est utilisée. Les modifications s'appliquent immédiatement, y compris dans les conversations en cours.",
105179
105452
  filesHint: "Ressources facultatives que l'agent peut utiliser avec cette compétence. Les références et les scripts sont des fichiers texte ; les assets sont des fichiers téléversés.",
105180
105453
  filesAfterCreateHint: "Vous pouvez joindre des ressources à cette compétence après sa création (rouvrez-la avec Modifier).",
@@ -105414,6 +105687,92 @@ var componentsProactiveSettingsSection$2 = {
105414
105687
  editAgentPromptAriaLabel: "Modifier le prompt de l'agent",
105415
105688
  periodicAgentsHelp: "Facultatif. Sélectionnez les agents à inclure dans la rotation de contact proactif."
105416
105689
  };
105690
+ var componentsTemplateActionsSection$2 = {
105691
+ title: "Tester et réinitialiser",
105692
+ description: "Prévisualisez le modèle dans votre boîte de réception ou annulez les personnalisations de cette plateforme.",
105693
+ testEmailLabel: "Envoyer un e-mail de test",
105694
+ testEmailHelper: "Envoie un rendu de ce modèle à l'adresse que vous saisissez.",
105695
+ testEmailPlaceholder: "vous@exemple.com",
105696
+ sendTest: "Envoyer l'e-mail de test",
105697
+ sendingTest: "Envoi...",
105698
+ testSuccessTitle: "E-mail de test envoyé",
105699
+ testSuccessDescription: "Envoyé à {email}.",
105700
+ testErrorTitle: "Impossible d'envoyer l'e-mail de test",
105701
+ testErrorDescription: "Vérifiez l'adresse et réessayez.",
105702
+ resetLabel: "Réinitialiser par défaut",
105703
+ resetHelper: "Supprime la copie de cette plateforme et restaure le modèle par défaut.",
105704
+ reset: "Réinitialiser par défaut",
105705
+ resetting: "Réinitialisation...",
105706
+ resetConfirmTitle: "Réinitialiser ce modèle ?",
105707
+ resetConfirmDescription: "Les personnalisations de cette plateforme seront supprimées et le modèle par défaut restauré. Cette action est irréversible.",
105708
+ resetConfirmCancel: "Annuler",
105709
+ resetConfirmAction: "Réinitialiser le modèle",
105710
+ resetSuccessTitle: "Modèle réinitialisé",
105711
+ resetSuccessDescription: "Le modèle par défaut de la plateforme a été restauré.",
105712
+ resetErrorTitle: "Impossible de réinitialiser le modèle",
105713
+ resetErrorDescription: "Veuillez réessayer."
105714
+ };
105715
+ var componentsTemplateVariablesHelper$2 = {
105716
+ title: "Variables disponibles",
105717
+ copyHint: "Cliquez sur une variable pour la copier, puis collez-la dans l’objet ou le corps.",
105718
+ insertHint: "Cliquez sur une variable pour l’insérer.",
105719
+ copiedToast: "{token} copié dans le presse-papiers.",
105720
+ copyUnavailableToast: "Copiez {token} manuellement : le presse-papiers est indisponible.",
105721
+ copyAriaLabel: "Copier la variable {name}",
105722
+ insertAriaLabel: "Insérer la variable {name}"
105723
+ };
105724
+ var componentsUserProgressSettingsSection$2 = {
105725
+ scheduleTitle: "Planification et période de rapport",
105726
+ scheduleDescription: "Choisissez la fréquence d’envoi du résumé et la période qu’il couvre.",
105727
+ frequencyLabel: "Fréquence",
105728
+ selectFrequencyAriaLabel: "Sélectionner la fréquence",
105729
+ selectFrequencyPlaceholder: "Sélectionner la fréquence",
105730
+ daily: "Quotidienne",
105731
+ weekly: "Hebdomadaire",
105732
+ monthly: "Mensuelle",
105733
+ custom: "Personnalisée",
105734
+ cadenceDaily: "Envoyé chaque jour à l’heure indiquée ci-dessous.",
105735
+ cadenceWeekly: "Envoyé chaque lundi à l’heure indiquée ci-dessous.",
105736
+ cadenceMonthly: "Envoyé le 1er de chaque mois à l’heure indiquée ci-dessous.",
105737
+ cadenceCustom: "Envoyé tous les {days} jours à l’heure indiquée ci-dessous.",
105738
+ cadenceCustomUnset: "Définissez un intervalle personnalisé pour choisir la fréquence d’envoi du résumé.",
105739
+ executionTimeLabel: "Heure d’envoi",
105740
+ executionTimeRequired: "Indiquez l’heure à laquelle le résumé doit être envoyé.",
105741
+ timezoneLabel: "Fuseau horaire",
105742
+ selectTimezoneAriaLabel: "Sélectionner le fuseau horaire",
105743
+ selectTimezonePlaceholder: "Sélectionner le fuseau horaire",
105744
+ useLocalTimezone: "Utiliser le fuseau horaire local ({timezone})",
105745
+ customIntervalLabel: "Intervalle personnalisé (jours)",
105746
+ customIntervalRequired: "Indiquez le nombre de jours entre deux envois.",
105747
+ dayRangeError: "Saisissez un nombre entier de jours compris entre 1 et 365.",
105748
+ lookbackLabel: "Période de rapport (jours)",
105749
+ lookbackHelp: "La période couverte par chaque résumé. Elle est indépendante de la fréquence d’envoi : un e-mail hebdomadaire peut couvrir les 30 derniers jours.",
105750
+ lookbackRequired: "Indiquez la période de rapport en jours.",
105751
+ dataPointsLabel: "Données incluses",
105752
+ dataPointsHelp: "L’e-mail n’affiche que les sections activées et pour lesquelles l’apprenant a des données.",
105753
+ dataPointCoursesProgressed: "Cours en progression",
105754
+ dataPointCoursesCompleted: "Cours terminés",
105755
+ dataPointCredentialsEarned: "Certifications obtenues",
105756
+ dataPointTimeSpent: "Temps passé",
105757
+ dataPointActiveDays: "Jours actifs",
105758
+ dataPointMentorsAccessed: "Activité des agents IA",
105759
+ noDataPointsWarning: "Aucune donnée sélectionnée : le résumé n’aura aucun contenu à afficher."
105760
+ };
105761
+ var componentsWatcherEmailSection$2 = {
105762
+ title: "E-mail des observateurs",
105763
+ description: "Envoyé aux observateurs de l’apprenant, par exemple un parent ou un responsable.",
105764
+ fallbackNotice: "Les observateurs reçoivent actuellement l’e-mail de l’apprenant. Personnalisez-le pour leur envoyer un message différent.",
105765
+ customize: "Personnaliser l’e-mail des observateurs",
105766
+ useLearnerEmail: "Utiliser l’e-mail de l’apprenant",
105767
+ subjectLabel: "Objet de l’e-mail des observateurs",
105768
+ subjectHelper: "Prend en charge les mêmes variables que l’e-mail de l’apprenant, plus learner_name.",
105769
+ subjectFallbackHelper: "Laissez vide et les observateurs recevront l’objet de l’e-mail de l’apprenant.",
105770
+ subjectPlaceholder: "Saisissez l’objet de l’e-mail des observateurs...",
105771
+ bodyLabel: "Corps de l’e-mail des observateurs",
105772
+ bodyHelper: "Prend en charge les mêmes variables que l’e-mail de l’apprenant, plus learner_name.",
105773
+ bodyFallbackHelper: "Laissez vide et les observateurs recevront le corps de l’e-mail de l’apprenant.",
105774
+ bodyPlaceholder: "Saisissez le corps de l’e-mail des observateurs..."
105775
+ };
105417
105776
  var courseContentAccessiblePaginate$2 = {
105418
105777
  pagination: "Pagination"
105419
105778
  };
@@ -106616,7 +106975,14 @@ var notificationsEditAlertDialog$2 = {
106616
106975
  toastErrorDescription: "Échec de la mise à jour du modèle. Veuillez réessayer.",
106617
106976
  cancel: "Annuler",
106618
106977
  saving: "Enregistrement...",
106619
- saveChanges: "Enregistrer les modifications"
106978
+ saveChanges: "Enregistrer les modifications",
106979
+ learnerEmailSubjectLabel: "Objet de l’e-mail de l’apprenant",
106980
+ learnerEmailSubjectHelper: "Obligatoire. Prend en charge les variables de modèle : voir la liste sous le corps.",
106981
+ learnerEmailBodyLabel: "Corps de l’e-mail de l’apprenant",
106982
+ learnerEmailBodyHelper: "Obligatoire. Utilisez « Bonjour, » plutôt que le nom d’utilisateur : ceux-ci sont souvent générés automatiquement.",
106983
+ noChangesToast: "Aucune modification à enregistrer.",
106984
+ toastValidationTitle: "Vérifiez les champs signalés",
106985
+ toastValidationDescription: "Certaines valeurs ont été rejetées. Vérifiez les champs marqués en rouge."
106620
106986
  };
106621
106987
  var notificationsNotificationDisplay$2 = {
106622
106988
  notificationsAriaLabel: "Notifications",
@@ -106899,7 +107265,8 @@ var profileBilling$2 = {
106899
107265
  addCredits: "Ajouter des crédits",
106900
107266
  manageBilling: "Gérer la facturation",
106901
107267
  availableLabel: "Disponibles",
106902
- creditsRemaining: "Crédits restants",
107268
+ creditsUsdEquivalent: " {amount}",
107269
+ creditsConversionHint: "{credits} crédits = 1 $",
106903
107270
  creditsUnit: "{amount} Crédits",
106904
107271
  consumedLabel: "Consommés",
106905
107272
  usedThisPeriod: "Utilisés cette période",
@@ -106911,10 +107278,17 @@ var profileBilling$2 = {
106911
107278
  disabled: "Désactivé",
106912
107279
  manageUsage: "Gérer l'utilisation",
106913
107280
  thresholdLabel: "Seuil",
107281
+ thresholdHint: "Quand vos crédits disponibles passent sous ce montant, une recharge démarre automatiquement",
106914
107282
  rechargeAmountLabel: "Montant de rechargement",
107283
+ rechargeAmountHint: "Débité de votre moyen de paiement et ajouté à vos crédits à chaque recharge",
106915
107284
  spendingLimitLabel: "Limite de dépenses",
107285
+ spendingLimitHint: "Le maximum que la recharge automatique peut débiter par période de facturation",
106916
107286
  unlimited: "Illimité",
106917
- autoRechargeDisabledMessage: "Le rechargement automatique est actuellement désactivé. Activez-le pour maintenir votre solde sans interruption."
107287
+ autoRechargeDisabledMessage: "Le rechargement automatique est actuellement désactivé. Activez-le pour maintenir votre solde sans interruption.",
107288
+ billingTabsAriaLabel: "Sections de facturation",
107289
+ tabPlanAndCredits: "Forfait et crédits",
107290
+ tabSpendLimits: "Limites de dépenses",
107291
+ tabAgentLimits: "Limites par agent"
106918
107292
  };
106919
107293
  var profileCatalogInviteModal$2 = {
106920
107294
  title: "Invitation au catalogue",
@@ -107858,6 +108232,156 @@ var skillsProfileSkillsBox$2 = {
107858
108232
  desired: "Souhaitées",
107859
108233
  noDesiredSkills: "Vous n'avez pas encore de compétences souhaitées."
107860
108234
  };
108235
+ var spendCapUsage$2 = {
108236
+ scopeTenant: "Espace de travail",
108237
+ scopeAgent: "Cet agent",
108238
+ scopeUserAgent: "Votre utilisation sur cet agent",
108239
+ intervalDay: "aujourd'hui",
108240
+ intervalWeek: "cette semaine",
108241
+ intervalMonth: "ce mois-ci",
108242
+ intervalYear: "cette année",
108243
+ percentUsed: "{percent}% utilisé",
108244
+ blockedBanner: "La limite d'utilisation est atteinte. Les nouveaux chats et l'entraînement sont suspendus jusqu'à la réinitialisation de la période.",
108245
+ warningBanner: "L'utilisation approche de sa limite."
108246
+ };
108247
+ var spendCapsTabLabels$2 = {
108248
+ header: {
108249
+ title: "Facturation",
108250
+ description: "Définissez des limites de dépenses pour cet agent et ses utilisateurs, et consultez la consommation."
108251
+ },
108252
+ capability: {
108253
+ title: "Limite de dépenses",
108254
+ description: "Fixez un plafond aux dépenses d'IA. Tant que cette limite est activée, tout ce qu'elle couvre compte dans le montant ci-dessous — et lorsque la limite est atteinte, vous choisissez de bloquer les nouvelles requêtes ou de simplement alerter les administrateurs.",
108255
+ offHint: "Activez-la pour commencer à suivre et appliquer cette limite. Votre configuration est conservée si vous la désactivez plus tard."
108256
+ },
108257
+ subTabs: {
108258
+ agent: "Cet agent",
108259
+ users: "Par utilisateur"
108260
+ },
108261
+ tenantSections: {
108262
+ workspace: {
108263
+ title: "Limite de dépenses de l'espace de travail",
108264
+ description: "Une seule limite couvrant tout ce que dépensent les agents et utilisateurs de cet espace de travail. Les limites par agent et par utilisateur se gèrent dans les réglages de chaque agent, sous Facturation."
108265
+ },
108266
+ noCapStats: {
108267
+ spentThisMonth: "Dépensé ce mois-ci",
108268
+ spentAllTime: "Dépensé au total",
108269
+ hint: "Sur l'ensemble des agents et utilisateurs de cet espace de travail"
108270
+ },
108271
+ agents: {
108272
+ title: "Limites de dépenses par agent",
108273
+ description: "Agents avec une limite de dépenses configurée dans cet espace de travail. Ouvrez-en un pour gérer sa limite et ses limites par utilisateur.",
108274
+ filter: {
108275
+ label: "Rechercher des agents",
108276
+ placeholder: "Rechercher des agents…",
108277
+ typeToSearch: "Saisissez au moins 2 caractères pour rechercher",
108278
+ searching: "Recherche d'agents…",
108279
+ noResults: "Aucun agent trouvé",
108280
+ clear: "Effacer le filtre d'agent"
108281
+ },
108282
+ table: {
108283
+ agentColumn: "Agent",
108284
+ limitColumn: "Limite",
108285
+ spentColumn: "Dépensé",
108286
+ remainingColumn: "Restant",
108287
+ statusColumn: "Statut"
108288
+ },
108289
+ emptyState: "Aucune limite de dépenses par agent pour l'instant. Recherchez un agent ci-dessus pour en définir une.",
108290
+ emptyFiltered: "Cet agent n'a pas encore de limite de dépenses.",
108291
+ setLimitButton: "Définir une limite de dépenses",
108292
+ manageAria: "Gérer les limites de dépenses de {name}",
108293
+ statusToggleAria: "Activer ou désactiver la limite de dépenses de {name}"
108294
+ }
108295
+ },
108296
+ form: {
108297
+ limitLabel: "Limite de dépenses (USD)",
108298
+ limitPlaceholder: "p. ex. 250.00",
108299
+ limitError: "Saisissez un montant supérieur à 0",
108300
+ intervalLabel: "Réinitialisation chaque",
108301
+ intervalOptions: {
108302
+ day: "Jour",
108303
+ week: "Semaine",
108304
+ month: "Mois",
108305
+ year: "Année"
108306
+ },
108307
+ enforcementLabel: "Quand la limite est atteinte",
108308
+ enforcementOptions: {
108309
+ block: "Bloquer les requêtes",
108310
+ alertOnly: "Alerte uniquement"
108311
+ },
108312
+ enforcementHelp: {
108313
+ block: "Les nouvelles requêtes de chat et d'entraînement sont refusées jusqu'à la réinitialisation de la période.",
108314
+ alertOnly: "Rien n'est bloqué : les administrateurs sont notifiés lorsque les dépenses approchent de la limite."
108315
+ },
108316
+ thresholdsLabel: "Alerter à (% de la limite)",
108317
+ thresholdsPlaceholder: "p. ex. 80, 95",
108318
+ thresholdsHelp: "Pourcentages séparés par des virgules auxquels les administrateurs reçoivent une alerte d'approche de la limite.",
108319
+ thresholdsError: "Utilisez des nombres séparés par des virgules entre 1 et 100",
108320
+ noCapHint: "Aucune limite de dépenses configurée pour l'instant — définissez un montant et enregistrez pour en créer une.",
108321
+ save: "Enregistrer",
108322
+ saving: "Enregistrement…",
108323
+ remove: "Supprimer la limite",
108324
+ removing: "Suppression…",
108325
+ cancel: "Annuler"
108326
+ },
108327
+ usage: {
108328
+ title: "Utilisation de la limite de dépenses",
108329
+ periodTitles: {
108330
+ day: "Utilisation quotidienne",
108331
+ week: "Utilisation hebdomadaire",
108332
+ month: "Utilisation mensuelle",
108333
+ year: "Utilisation annuelle"
108334
+ },
108335
+ spent: "Dépensé : {amount}",
108336
+ remaining: "Restant : {amount}",
108337
+ percentUsed: "{percent}% de la limite utilisée",
108338
+ exceededBadge: "Limite atteinte",
108339
+ alertOnlyBadge: "Alerte uniquement",
108340
+ disabledBadge: "Désactivé"
108341
+ },
108342
+ users: {
108343
+ sectionTitle: "Limites par utilisateur",
108344
+ sectionDescription: "Limitez ce que certains utilisateurs peuvent dépenser sur cet agent.",
108345
+ addButton: "Ajouter une limite utilisateur",
108346
+ addTitle: "Nouvelle limite utilisateur",
108347
+ editTitle: "Modifier la limite utilisateur",
108348
+ cancel: "Annuler",
108349
+ emptyState: "Aucune limite par utilisateur pour l'instant. Ajoutez-en une pour limiter un utilisateur précis sur cet agent.",
108350
+ edit: "Modifier",
108351
+ "delete": "Supprimer",
108352
+ table: {
108353
+ userColumn: "Utilisateur",
108354
+ limitColumn: "Limite",
108355
+ statusColumn: "Statut"
108356
+ },
108357
+ actionsAria: "Actions pour {username}",
108358
+ statusToggleAria: "Activer ou désactiver la limite de dépenses de {username}",
108359
+ picker: {
108360
+ label: "Utilisateur",
108361
+ placeholder: "Rechercher des utilisateurs…",
108362
+ typeToSearch: "Saisissez au moins 2 caractères pour rechercher",
108363
+ searching: "Recherche d'utilisateurs…",
108364
+ noResults: "Aucun utilisateur trouvé",
108365
+ clear: "Changer d'utilisateur"
108366
+ }
108367
+ },
108368
+ denied: {
108369
+ title: "Pas d'accès aux limites de dépenses",
108370
+ description: "Vous n'avez pas la permission de voir les limites de dépenses ici. Demandez l'accès à un administrateur."
108371
+ },
108372
+ deleteModal: {
108373
+ title: "Supprimer la limite de dépenses",
108374
+ description: "Les dépenses ne seront plus limitées ni suivies par cette limite.",
108375
+ cancel: "Annuler",
108376
+ confirm: "Supprimer"
108377
+ },
108378
+ toasts: {
108379
+ saved: "Limite de dépenses enregistrée",
108380
+ saveError: "Impossible d'enregistrer la limite de dépenses",
108381
+ deleted: "Limite de dépenses supprimée",
108382
+ deleteError: "Impossible de supprimer la limite de dépenses"
108383
+ }
108384
+ };
107861
108385
  var tabsAlertstab$2 = {
107862
108386
  searchAlertsPlaceholder: "Rechercher des alertes",
107863
108387
  searchAlertsAriaLabel: "Rechercher des alertes",
@@ -109601,6 +110125,10 @@ var fr = {
109601
110125
  componentsPolicyAssignmentSection: componentsPolicyAssignmentSection$2,
109602
110126
  componentsPolicyConfigDialog: componentsPolicyConfigDialog$2,
109603
110127
  componentsProactiveSettingsSection: componentsProactiveSettingsSection$2,
110128
+ componentsTemplateActionsSection: componentsTemplateActionsSection$2,
110129
+ componentsTemplateVariablesHelper: componentsTemplateVariablesHelper$2,
110130
+ componentsUserProgressSettingsSection: componentsUserProgressSettingsSection$2,
110131
+ componentsWatcherEmailSection: componentsWatcherEmailSection$2,
109604
110132
  courseContentAccessiblePaginate: courseContentAccessiblePaginate$2,
109605
110133
  courseContentCourseContentLayout: courseContentCourseContentLayout$2,
109606
110134
  courseContentDiscoverFacetsFilter: courseContentDiscoverFacetsFilter$2,
@@ -109716,6 +110244,8 @@ var fr = {
109716
110244
  skillsProfileResumeBox: skillsProfileResumeBox$2,
109717
110245
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$2,
109718
110246
  skillsProfileSkillsBox: skillsProfileSkillsBox$2,
110247
+ spendCapUsage: spendCapUsage$2,
110248
+ spendCapsTabLabels: spendCapsTabLabels$2,
109719
110249
  tabsAlertstab: tabsAlertstab$2,
109720
110250
  tabsDisclaimersTab: tabsDisclaimersTab$2,
109721
110251
  tabsEmbedTab: tabsEmbedTab$2,
@@ -111445,7 +111975,13 @@ var chatIndex$1 = {
111445
111975
  confirmScreenShareTitle: "Confirmar uso compartido de pantalla",
111446
111976
  confirmScreenShareDescription: "¿Deseas iniciar un uso compartido de pantalla con tu agente?",
111447
111977
  cancel: "Cancelar",
111448
- confirm: "Confirmar"
111978
+ confirm: "Confirmar",
111979
+ spendCapLimitDay: "Has alcanzado el límite de uso de hoy para este asistente.",
111980
+ spendCapLimitWeek: "Has alcanzado el límite de uso de esta semana para este asistente.",
111981
+ spendCapLimitMonth: "Has alcanzado el límite de uso de este mes para este asistente.",
111982
+ spendCapLimitYear: "Has alcanzado el límite de uso de este año para este asistente.",
111983
+ spendCapLimitGeneric: "Has alcanzado el límite de uso de este asistente.",
111984
+ spendCapContactAdmin: "Vuelve a intentarlo cuando el límite se reinicie o contacta con tu administrador."
111449
111985
  };
111450
111986
  var chatInputForm$1 = {
111451
111987
  uploadingOneFile: "Subiendo 1 archivo...",
@@ -111648,6 +112184,8 @@ var clawSandboxAgentSkills$1 = {
111648
112184
  fieldCategoryPlaceholder: "p. ej. web, código, datos",
111649
112185
  privateToAgent: "Solo este agente",
111650
112186
  privateToAgentHint: "Las habilidades privadas solo están disponibles para este agente y tienen prioridad sobre las habilidades de la plataforma con el mismo slug.",
112187
+ privateToOtherAgentHint: "Esta habilidad es privada de otro agente, por lo que este ajuste no se puede cambiar aquí.",
112188
+ privateToOtherAgentTooltip: "Esta habilidad es privada de otro agente y no se puede añadir aquí.",
111651
112189
  instructionHint: "La guía que sigue el agente cuando se usa la habilidad. Los cambios se aplican de inmediato, incluso en conversaciones en curso.",
111652
112190
  filesHint: "Recursos opcionales que el agente puede usar con esta habilidad. Las referencias y los scripts son archivos de texto; los assets son archivos subidos.",
111653
112191
  filesAfterCreateHint: "Puedes adjuntar recursos a esta habilidad después de crearla (vuelve a abrirla con Editar).",
@@ -111887,6 +112425,92 @@ var componentsProactiveSettingsSection$1 = {
111887
112425
  editAgentPromptAriaLabel: "Editar prompt del agente",
111888
112426
  periodicAgentsHelp: "Opcional. Seleccione los agentes a incluir en la rotación de contacto proactivo."
111889
112427
  };
112428
+ var componentsTemplateActionsSection$1 = {
112429
+ title: "Probar y restablecer",
112430
+ description: "Previsualice la plantilla en su bandeja de entrada o descarte las personalizaciones de esta plataforma.",
112431
+ testEmailLabel: "Enviar un correo de prueba",
112432
+ testEmailHelper: "Envía una versión de esta plantilla a la dirección que indique.",
112433
+ testEmailPlaceholder: "usted@ejemplo.com",
112434
+ sendTest: "Enviar correo de prueba",
112435
+ sendingTest: "Enviando...",
112436
+ testSuccessTitle: "Correo de prueba enviado",
112437
+ testSuccessDescription: "Enviado a {email}.",
112438
+ testErrorTitle: "No se pudo enviar el correo de prueba",
112439
+ testErrorDescription: "Verifique la dirección e inténtelo de nuevo.",
112440
+ resetLabel: "Restablecer al valor predeterminado",
112441
+ resetHelper: "Descarta la copia de esta plataforma y restaura la plantilla predeterminada.",
112442
+ reset: "Restablecer al valor predeterminado",
112443
+ resetting: "Restableciendo...",
112444
+ resetConfirmTitle: "¿Restablecer esta plantilla?",
112445
+ resetConfirmDescription: "Las personalizaciones de esta plataforma se descartarán y se restaurará la plantilla predeterminada. Esta acción no se puede deshacer.",
112446
+ resetConfirmCancel: "Cancelar",
112447
+ resetConfirmAction: "Restablecer plantilla",
112448
+ resetSuccessTitle: "Plantilla restablecida",
112449
+ resetSuccessDescription: "Se restauró el valor predeterminado de la plataforma.",
112450
+ resetErrorTitle: "No se pudo restablecer la plantilla",
112451
+ resetErrorDescription: "Inténtelo de nuevo."
112452
+ };
112453
+ var componentsTemplateVariablesHelper$1 = {
112454
+ title: "Variables que puede usar",
112455
+ copyHint: "Haga clic en una variable para copiarla y péguela en el asunto o el cuerpo.",
112456
+ insertHint: "Haga clic en una variable para insertarla.",
112457
+ copiedToast: "Se copió {token} al portapapeles.",
112458
+ copyUnavailableToast: "Copie {token} manualmente: el portapapeles no está disponible.",
112459
+ copyAriaLabel: "Copiar la variable {name}",
112460
+ insertAriaLabel: "Insertar la variable {name}"
112461
+ };
112462
+ var componentsUserProgressSettingsSection$1 = {
112463
+ scheduleTitle: "Programación y período del informe",
112464
+ scheduleDescription: "Elija con qué frecuencia se envía el resumen y qué período abarca.",
112465
+ frequencyLabel: "Frecuencia",
112466
+ selectFrequencyAriaLabel: "Seleccionar frecuencia",
112467
+ selectFrequencyPlaceholder: "Seleccionar frecuencia",
112468
+ daily: "Diaria",
112469
+ weekly: "Semanal",
112470
+ monthly: "Mensual",
112471
+ custom: "Personalizada",
112472
+ cadenceDaily: "Se envía todos los días a la hora indicada abajo.",
112473
+ cadenceWeekly: "Se envía todos los lunes a la hora indicada abajo.",
112474
+ cadenceMonthly: "Se envía el día 1 de cada mes a la hora indicada abajo.",
112475
+ cadenceCustom: "Se envía cada {days} días a la hora indicada abajo.",
112476
+ cadenceCustomUnset: "Defina un intervalo personalizado para elegir con qué frecuencia se envía el resumen.",
112477
+ executionTimeLabel: "Hora de envío",
112478
+ executionTimeRequired: "Indique la hora a la que debe enviarse el resumen.",
112479
+ timezoneLabel: "Zona horaria",
112480
+ selectTimezoneAriaLabel: "Seleccionar zona horaria",
112481
+ selectTimezonePlaceholder: "Seleccionar zona horaria",
112482
+ useLocalTimezone: "Usar la zona horaria local ({timezone})",
112483
+ customIntervalLabel: "Intervalo personalizado (días)",
112484
+ customIntervalRequired: "Indique cuántos días deben pasar entre envíos.",
112485
+ dayRangeError: "Introduzca un número entero de días entre 1 y 365.",
112486
+ lookbackLabel: "Período del informe (días)",
112487
+ lookbackHelp: "Cuánto tiempo hacia atrás abarca cada resumen. Es independiente de la frecuencia de envío: un correo semanal puede informar sobre los últimos 30 días.",
112488
+ lookbackRequired: "Indique el período del informe en días.",
112489
+ dataPointsLabel: "Datos incluidos",
112490
+ dataPointsHelp: "El correo solo muestra las secciones que están activadas y tienen datos para ese estudiante.",
112491
+ dataPointCoursesProgressed: "Cursos en progreso",
112492
+ dataPointCoursesCompleted: "Cursos completados",
112493
+ dataPointCredentialsEarned: "Credenciales obtenidas",
112494
+ dataPointTimeSpent: "Tiempo dedicado",
112495
+ dataPointActiveDays: "Días activos",
112496
+ dataPointMentorsAccessed: "Actividad de agentes de IA",
112497
+ noDataPointsWarning: "No se seleccionó ningún dato: el resumen no tendrá contenido que mostrar."
112498
+ };
112499
+ var componentsWatcherEmailSection$1 = {
112500
+ title: "Correo para observadores",
112501
+ description: "Se envía a los observadores del estudiante, como un padre, una madre o un supervisor.",
112502
+ fallbackNotice: "Actualmente los observadores reciben el correo del estudiante. Personalícelo para enviarles un mensaje distinto.",
112503
+ customize: "Personalizar el correo para observadores",
112504
+ useLearnerEmail: "Usar el correo del estudiante",
112505
+ subjectLabel: "Asunto del correo para observadores",
112506
+ subjectHelper: "Admite las mismas variables que el correo del estudiante, más learner_name.",
112507
+ subjectFallbackHelper: "Si lo deja vacío, los observadores reciben el asunto del correo del estudiante.",
112508
+ subjectPlaceholder: "Escriba el asunto del correo para observadores...",
112509
+ bodyLabel: "Cuerpo del correo para observadores",
112510
+ bodyHelper: "Admite las mismas variables que el correo del estudiante, más learner_name.",
112511
+ bodyFallbackHelper: "Si lo deja vacío, los observadores reciben el cuerpo del correo del estudiante.",
112512
+ bodyPlaceholder: "Escriba el cuerpo del correo para observadores..."
112513
+ };
111890
112514
  var courseContentAccessiblePaginate$1 = {
111891
112515
  pagination: "Paginación"
111892
112516
  };
@@ -113089,7 +113713,14 @@ var notificationsEditAlertDialog$1 = {
113089
113713
  toastErrorDescription: "No se pudo actualizar la plantilla. Por favor, inténtelo de nuevo.",
113090
113714
  cancel: "Cancelar",
113091
113715
  saving: "Guardando...",
113092
- saveChanges: "Guardar cambios"
113716
+ saveChanges: "Guardar cambios",
113717
+ learnerEmailSubjectLabel: "Asunto del correo del estudiante",
113718
+ learnerEmailSubjectHelper: "Obligatorio. Admite variables de plantilla: consulte la lista debajo del cuerpo.",
113719
+ learnerEmailBodyLabel: "Cuerpo del correo del estudiante",
113720
+ learnerEmailBodyHelper: "Obligatorio. Salude con «Hola,» en lugar de usar el nombre de usuario: suelen generarse automáticamente.",
113721
+ noChangesToast: "No hay cambios que guardar.",
113722
+ toastValidationTitle: "Revise los campos resaltados",
113723
+ toastValidationDescription: "Se rechazaron algunos valores. Revise los campos marcados en rojo."
113093
113724
  };
113094
113725
  var notificationsNotificationDisplay$1 = {
113095
113726
  notificationsAriaLabel: "Notificaciones",
@@ -113372,7 +114003,8 @@ var profileBilling$1 = {
113372
114003
  addCredits: "Agregar créditos",
113373
114004
  manageBilling: "Gestionar facturación",
113374
114005
  availableLabel: "Disponibles",
113375
- creditsRemaining: "Créditos restantes",
114006
+ creditsUsdEquivalent: " {amount}",
114007
+ creditsConversionHint: "{credits} créditos = $1",
113376
114008
  creditsUnit: "{amount} Créditos",
113377
114009
  consumedLabel: "Consumidos",
113378
114010
  usedThisPeriod: "Usados este período",
@@ -113384,10 +114016,17 @@ var profileBilling$1 = {
113384
114016
  disabled: "Deshabilitado",
113385
114017
  manageUsage: "Gestionar uso",
113386
114018
  thresholdLabel: "Umbral",
114019
+ thresholdHint: "Cuando tus créditos disponibles bajan de este importe, se inicia una recarga automática",
113387
114020
  rechargeAmountLabel: "Monto de recarga",
114021
+ rechargeAmountHint: "Se cobra a tu método de pago y se añade a tus créditos en cada recarga",
113388
114022
  spendingLimitLabel: "Límite de gasto",
114023
+ spendingLimitHint: "Lo máximo que la recarga automática puede cobrar por periodo de facturación",
113389
114024
  unlimited: "Ilimitado",
113390
- autoRechargeDisabledMessage: "La recarga automática está deshabilitada. Actívala para mantener tu saldo sin interrupciones."
114025
+ autoRechargeDisabledMessage: "La recarga automática está deshabilitada. Actívala para mantener tu saldo sin interrupciones.",
114026
+ billingTabsAriaLabel: "Secciones de facturación",
114027
+ tabPlanAndCredits: "Plan y créditos",
114028
+ tabSpendLimits: "Límites de gasto",
114029
+ tabAgentLimits: "Límites por agente"
113391
114030
  };
113392
114031
  var profileCatalogInviteModal$1 = {
113393
114032
  title: "Invitación al catálogo",
@@ -114331,6 +114970,156 @@ var skillsProfileSkillsBox$1 = {
114331
114970
  desired: "Deseadas",
114332
114971
  noDesiredSkills: "Todavía no tienes habilidades deseadas."
114333
114972
  };
114973
+ var spendCapUsage$1 = {
114974
+ scopeTenant: "Espacio de trabajo",
114975
+ scopeAgent: "Este agente",
114976
+ scopeUserAgent: "Tu uso en este agente",
114977
+ intervalDay: "hoy",
114978
+ intervalWeek: "esta semana",
114979
+ intervalMonth: "este mes",
114980
+ intervalYear: "este año",
114981
+ percentUsed: "{percent}% usado",
114982
+ blockedBanner: "Se ha alcanzado el límite de uso. Los nuevos chats y el entrenamiento quedan en pausa hasta que el periodo se reinicie.",
114983
+ warningBanner: "El uso se acerca a su límite."
114984
+ };
114985
+ var spendCapsTabLabels$1 = {
114986
+ header: {
114987
+ title: "Facturación",
114988
+ description: "Define límites de gasto para este agente y sus usuarios, y consulta cuánto se ha usado."
114989
+ },
114990
+ capability: {
114991
+ title: "Límite de gasto",
114992
+ description: "Pon un techo al gasto en IA. Mientras este límite esté activado, todo lo que cubre cuenta para el importe de abajo; y cuando se alcanza, tú decides si se bloquean las nuevas solicitudes o simplemente se avisa a los administradores.",
114993
+ offHint: "Actívalo para empezar a hacer seguimiento y aplicar este límite. Tu configuración se conserva si luego lo desactivas."
114994
+ },
114995
+ subTabs: {
114996
+ agent: "Este agente",
114997
+ users: "Por usuario"
114998
+ },
114999
+ tenantSections: {
115000
+ workspace: {
115001
+ title: "Límite de gasto del espacio de trabajo",
115002
+ description: "Un único límite que cubre todo lo que gastan los agentes y usuarios de este espacio de trabajo. Los límites por agente y por usuario se gestionan en la configuración de cada agente, en Facturación."
115003
+ },
115004
+ noCapStats: {
115005
+ spentThisMonth: "Gastado este mes",
115006
+ spentAllTime: "Gastado en total",
115007
+ hint: "En todos los agentes y usuarios de este espacio de trabajo"
115008
+ },
115009
+ agents: {
115010
+ title: "Límites de gasto por agente",
115011
+ description: "Agentes con un límite de gasto configurado en este espacio de trabajo. Abre uno para gestionar su límite y sus límites por usuario.",
115012
+ filter: {
115013
+ label: "Buscar agentes",
115014
+ placeholder: "Buscar agentes…",
115015
+ typeToSearch: "Escribe al menos 2 caracteres para buscar",
115016
+ searching: "Buscando agentes…",
115017
+ noResults: "No se encontraron agentes",
115018
+ clear: "Quitar el filtro de agente"
115019
+ },
115020
+ table: {
115021
+ agentColumn: "Agente",
115022
+ limitColumn: "Límite",
115023
+ spentColumn: "Gastado",
115024
+ remainingColumn: "Restante",
115025
+ statusColumn: "Estado"
115026
+ },
115027
+ emptyState: "Aún no hay límites de gasto por agente. Busca un agente arriba para configurar uno.",
115028
+ emptyFiltered: "Este agente aún no tiene límite de gasto.",
115029
+ setLimitButton: "Configurar límite de gasto",
115030
+ manageAria: "Gestionar los límites de gasto de {name}",
115031
+ statusToggleAria: "Activar o desactivar el límite de gasto de {name}"
115032
+ }
115033
+ },
115034
+ form: {
115035
+ limitLabel: "Límite de gasto (USD)",
115036
+ limitPlaceholder: "p. ej. 250.00",
115037
+ limitError: "Introduce un importe mayor que 0",
115038
+ intervalLabel: "Se reinicia cada",
115039
+ intervalOptions: {
115040
+ day: "Día",
115041
+ week: "Semana",
115042
+ month: "Mes",
115043
+ year: "Año"
115044
+ },
115045
+ enforcementLabel: "Al alcanzar el límite",
115046
+ enforcementOptions: {
115047
+ block: "Bloquear solicitudes",
115048
+ alertOnly: "Solo alertar"
115049
+ },
115050
+ enforcementHelp: {
115051
+ block: "Las nuevas solicitudes de chat y entrenamiento se rechazan hasta que el periodo se reinicie.",
115052
+ alertOnly: "No se bloquea nada: se notifica a los administradores cuando el gasto se acerca al límite."
115053
+ },
115054
+ thresholdsLabel: "Alertar al (% del límite)",
115055
+ thresholdsPlaceholder: "p. ej. 80, 95",
115056
+ thresholdsHelp: "Porcentajes separados por comas en los que los administradores reciben una alerta de proximidad al límite.",
115057
+ thresholdsError: "Usa números separados por comas entre 1 y 100",
115058
+ noCapHint: "Aún no hay límite de gasto configurado: define un importe y guarda para crearlo.",
115059
+ save: "Guardar",
115060
+ saving: "Guardando…",
115061
+ remove: "Eliminar límite",
115062
+ removing: "Eliminando…",
115063
+ cancel: "Cancelar"
115064
+ },
115065
+ usage: {
115066
+ title: "Uso del límite de gasto",
115067
+ periodTitles: {
115068
+ day: "Uso diario",
115069
+ week: "Uso semanal",
115070
+ month: "Uso mensual",
115071
+ year: "Uso anual"
115072
+ },
115073
+ spent: "Gastado: {amount}",
115074
+ remaining: "Restante: {amount}",
115075
+ percentUsed: "{percent}% del límite usado",
115076
+ exceededBadge: "Límite alcanzado",
115077
+ alertOnlyBadge: "Solo alerta",
115078
+ disabledBadge: "Desactivado"
115079
+ },
115080
+ users: {
115081
+ sectionTitle: "Límites por usuario",
115082
+ sectionDescription: "Limita cuánto pueden gastar usuarios concretos en este agente.",
115083
+ addButton: "Añadir límite de usuario",
115084
+ addTitle: "Nuevo límite de usuario",
115085
+ editTitle: "Editar límite de usuario",
115086
+ cancel: "Cancelar",
115087
+ emptyState: "Aún no hay límites por usuario. Añade uno para limitar a un usuario concreto en este agente.",
115088
+ edit: "Editar",
115089
+ "delete": "Eliminar",
115090
+ table: {
115091
+ userColumn: "Usuario",
115092
+ limitColumn: "Límite",
115093
+ statusColumn: "Estado"
115094
+ },
115095
+ actionsAria: "Acciones para {username}",
115096
+ statusToggleAria: "Activar o desactivar el límite de gasto de {username}",
115097
+ picker: {
115098
+ label: "Usuario",
115099
+ placeholder: "Buscar usuarios…",
115100
+ typeToSearch: "Escribe al menos 2 caracteres para buscar",
115101
+ searching: "Buscando usuarios…",
115102
+ noResults: "No se encontraron usuarios",
115103
+ clear: "Cambiar de usuario"
115104
+ }
115105
+ },
115106
+ denied: {
115107
+ title: "Sin acceso a los límites de gasto",
115108
+ description: "No tienes permiso para ver los límites de gasto aquí. Pide acceso a un administrador."
115109
+ },
115110
+ deleteModal: {
115111
+ title: "Eliminar límite de gasto",
115112
+ description: "El gasto dejará de estar limitado y de contabilizarse contra este límite.",
115113
+ cancel: "Cancelar",
115114
+ confirm: "Eliminar"
115115
+ },
115116
+ toasts: {
115117
+ saved: "Límite de gasto guardado",
115118
+ saveError: "No se pudo guardar el límite de gasto",
115119
+ deleted: "Límite de gasto eliminado",
115120
+ deleteError: "No se pudo eliminar el límite de gasto"
115121
+ }
115122
+ };
114334
115123
  var tabsAlertstab$1 = {
114335
115124
  searchAlertsPlaceholder: "Buscar alertas",
114336
115125
  searchAlertsAriaLabel: "Buscar alertas",
@@ -116074,6 +116863,10 @@ var es = {
116074
116863
  componentsPolicyAssignmentSection: componentsPolicyAssignmentSection$1,
116075
116864
  componentsPolicyConfigDialog: componentsPolicyConfigDialog$1,
116076
116865
  componentsProactiveSettingsSection: componentsProactiveSettingsSection$1,
116866
+ componentsTemplateActionsSection: componentsTemplateActionsSection$1,
116867
+ componentsTemplateVariablesHelper: componentsTemplateVariablesHelper$1,
116868
+ componentsUserProgressSettingsSection: componentsUserProgressSettingsSection$1,
116869
+ componentsWatcherEmailSection: componentsWatcherEmailSection$1,
116077
116870
  courseContentAccessiblePaginate: courseContentAccessiblePaginate$1,
116078
116871
  courseContentCourseContentLayout: courseContentCourseContentLayout$1,
116079
116872
  courseContentDiscoverFacetsFilter: courseContentDiscoverFacetsFilter$1,
@@ -116189,6 +116982,8 @@ var es = {
116189
116982
  skillsProfileResumeBox: skillsProfileResumeBox$1,
116190
116983
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$1,
116191
116984
  skillsProfileSkillsBox: skillsProfileSkillsBox$1,
116985
+ spendCapUsage: spendCapUsage$1,
116986
+ spendCapsTabLabels: spendCapsTabLabels$1,
116192
116987
  tabsAlertstab: tabsAlertstab$1,
116193
116988
  tabsDisclaimersTab: tabsDisclaimersTab$1,
116194
116989
  tabsEmbedTab: tabsEmbedTab$1,
@@ -117918,7 +118713,13 @@ var chatIndex = {
117918
118713
  confirmScreenShareTitle: "确认屏幕共享",
117919
118714
  confirmScreenShareDescription: "您要与您的助手开始屏幕共享吗?",
117920
118715
  cancel: "取消",
117921
- confirm: "确认"
118716
+ confirm: "确认",
118717
+ spendCapLimitDay: "您已达到此助手今天的使用限额。",
118718
+ spendCapLimitWeek: "您已达到此助手本周的使用限额。",
118719
+ spendCapLimitMonth: "您已达到此助手本月的使用限额。",
118720
+ spendCapLimitYear: "您已达到此助手今年的使用限额。",
118721
+ spendCapLimitGeneric: "您已达到此助手的使用限额。",
118722
+ spendCapContactAdmin: "请在限额重置后重试,或联系您的管理员。"
117922
118723
  };
117923
118724
  var chatInputForm = {
117924
118725
  uploadingOneFile: "正在上传 1 个文件...",
@@ -118121,6 +118922,8 @@ var clawSandboxAgentSkills = {
118121
118922
  fieldCategoryPlaceholder: "例如 web、代码、数据",
118122
118923
  privateToAgent: "仅此智能体",
118123
118924
  privateToAgentHint: "私有技能仅对此智能体可用,并优先于具有相同 slug 的平台技能。",
118925
+ privateToOtherAgentHint: "该技能为其他智能体私有,因此无法在此更改该设置。",
118926
+ privateToOtherAgentTooltip: "该技能为其他智能体私有,无法在此添加。",
118124
118927
  instructionHint: "使用该技能时智能体遵循的操作指南。修改会立即生效,包括正在进行的对话。",
118125
118928
  filesHint: "智能体在使用此技能时可参考的可选资源。参考资料和脚本是文本文件;资产是上传的文件。",
118126
118929
  filesAfterCreateHint: "创建技能后即可附加资源(通过“编辑”重新打开)。",
@@ -118360,6 +119163,92 @@ var componentsProactiveSettingsSection = {
118360
119163
  editAgentPromptAriaLabel: "编辑代理提示",
118361
119164
  periodicAgentsHelp: "可选。选择要包含在主动外联轮换中的代理。"
118362
119165
  };
119166
+ var componentsTemplateActionsSection = {
119167
+ title: "测试与重置",
119168
+ description: "在您的收件箱中预览模板,或放弃本平台的自定义内容。",
119169
+ testEmailLabel: "发送测试邮件",
119170
+ testEmailHelper: "将此模板的一次渲染结果发送至您输入的地址。",
119171
+ testEmailPlaceholder: "you@example.com",
119172
+ sendTest: "发送测试邮件",
119173
+ sendingTest: "发送中...",
119174
+ testSuccessTitle: "测试邮件已发送",
119175
+ testSuccessDescription: "已发送至 {email}。",
119176
+ testErrorTitle: "无法发送测试邮件",
119177
+ testErrorDescription: "请检查地址后重试。",
119178
+ resetLabel: "重置为默认值",
119179
+ resetHelper: "放弃本平台的副本并恢复默认模板。",
119180
+ reset: "重置为默认值",
119181
+ resetting: "重置中...",
119182
+ resetConfirmTitle: "要重置此模板吗?",
119183
+ resetConfirmDescription: "本平台的自定义内容将被放弃并恢复默认模板。此操作无法撤销。",
119184
+ resetConfirmCancel: "取消",
119185
+ resetConfirmAction: "重置模板",
119186
+ resetSuccessTitle: "模板已重置",
119187
+ resetSuccessDescription: "已恢复平台默认设置。",
119188
+ resetErrorTitle: "无法重置模板",
119189
+ resetErrorDescription: "请重试。"
119190
+ };
119191
+ var componentsTemplateVariablesHelper = {
119192
+ title: "可用变量",
119193
+ copyHint: "点击变量即可复制,然后粘贴到主题或正文中。",
119194
+ insertHint: "点击变量即可插入。",
119195
+ copiedToast: "已将 {token} 复制到剪贴板。",
119196
+ copyUnavailableToast: "请手动复制 {token}:剪贴板不可用。",
119197
+ copyAriaLabel: "复制变量 {name}",
119198
+ insertAriaLabel: "插入变量 {name}"
119199
+ };
119200
+ var componentsUserProgressSettingsSection = {
119201
+ scheduleTitle: "发送计划与统计周期",
119202
+ scheduleDescription: "选择摘要的发送频率及其涵盖的时间范围。",
119203
+ frequencyLabel: "频率",
119204
+ selectFrequencyAriaLabel: "选择频率",
119205
+ selectFrequencyPlaceholder: "选择频率",
119206
+ daily: "每天",
119207
+ weekly: "每周",
119208
+ monthly: "每月",
119209
+ custom: "自定义",
119210
+ cadenceDaily: "每天在下方设定的时间发送。",
119211
+ cadenceWeekly: "每周一在下方设定的时间发送。",
119212
+ cadenceMonthly: "每月 1 日在下方设定的时间发送。",
119213
+ cadenceCustom: "每 {days} 天在下方设定的时间发送。",
119214
+ cadenceCustomUnset: "设置自定义间隔以确定摘要的发送频率。",
119215
+ executionTimeLabel: "发送时间",
119216
+ executionTimeRequired: "请指定摘要的发送时间。",
119217
+ timezoneLabel: "时区",
119218
+ selectTimezoneAriaLabel: "选择时区",
119219
+ selectTimezonePlaceholder: "选择时区",
119220
+ useLocalTimezone: "使用本地时区({timezone})",
119221
+ customIntervalLabel: "自定义间隔(天)",
119222
+ customIntervalRequired: "请设置两次发送之间相隔的天数。",
119223
+ dayRangeError: "请输入 1 到 365 之间的整数天数。",
119224
+ lookbackLabel: "统计周期(天)",
119225
+ lookbackHelp: "每份摘要回溯统计的时间范围。它与发送频率相互独立——每周发送的邮件也可以统计最近 30 天的数据。",
119226
+ lookbackRequired: "请设置以天为单位的统计周期。",
119227
+ dataPointsLabel: "包含的数据项",
119228
+ dataPointsHelp: "邮件仅显示已启用且该学习者有相应数据的板块。",
119229
+ dataPointCoursesProgressed: "课程进展",
119230
+ dataPointCoursesCompleted: "已完成课程",
119231
+ dataPointCredentialsEarned: "已获得证书",
119232
+ dataPointTimeSpent: "学习时长",
119233
+ dataPointActiveDays: "活跃天数",
119234
+ dataPointMentorsAccessed: "AI 代理活动",
119235
+ noDataPointsWarning: "未选择任何数据项——摘要将没有内容可显示。"
119236
+ };
119237
+ var componentsWatcherEmailSection = {
119238
+ title: "关注者邮件",
119239
+ description: "发送给学习者的关注者,例如家长或主管。",
119240
+ fallbackNotice: "关注者目前收到的是学习者邮件。自定义后可向他们发送不同的内容。",
119241
+ customize: "自定义关注者邮件",
119242
+ useLearnerEmail: "使用学习者邮件",
119243
+ subjectLabel: "关注者邮件主题",
119244
+ subjectHelper: "支持与学习者邮件相同的变量,另加 learner_name。",
119245
+ subjectFallbackHelper: "留空则关注者收到学习者邮件的主题。",
119246
+ subjectPlaceholder: "请输入关注者邮件主题...",
119247
+ bodyLabel: "关注者邮件正文",
119248
+ bodyHelper: "支持与学习者邮件相同的变量,另加 learner_name。",
119249
+ bodyFallbackHelper: "留空则关注者收到学习者邮件的正文。",
119250
+ bodyPlaceholder: "请输入关注者邮件正文..."
119251
+ };
118363
119252
  var courseContentAccessiblePaginate = {
118364
119253
  pagination: "分页"
118365
119254
  };
@@ -119562,7 +120451,14 @@ var notificationsEditAlertDialog = {
119562
120451
  toastErrorDescription: "更新模板失败,请重试。",
119563
120452
  cancel: "取消",
119564
120453
  saving: "保存中...",
119565
- saveChanges: "保存更改"
120454
+ saveChanges: "保存更改",
120455
+ learnerEmailSubjectLabel: "学习者邮件主题",
120456
+ learnerEmailSubjectHelper: "必填。支持模板变量——请参阅正文下方的列表。",
120457
+ learnerEmailBodyLabel: "学习者邮件正文",
120458
+ learnerEmailBodyHelper: "必填。请使用“您好,”问候,而不要使用用户名——用户名通常是自动生成的。",
120459
+ noChangesToast: "没有需要保存的更改。",
120460
+ toastValidationTitle: "请检查高亮显示的字段",
120461
+ toastValidationDescription: "部分值被拒绝。请检查标记为红色的字段。"
119566
120462
  };
119567
120463
  var notificationsNotificationDisplay = {
119568
120464
  notificationsAriaLabel: "通知",
@@ -119845,7 +120741,8 @@ var profileBilling = {
119845
120741
  addCredits: "添加积分",
119846
120742
  manageBilling: "管理账单",
119847
120743
  availableLabel: "可用",
119848
- creditsRemaining: "剩余积分",
120744
+ creditsUsdEquivalent: "约合 {amount}",
120745
+ creditsConversionHint: "{credits} 积分 = $1",
119849
120746
  creditsUnit: "{amount} 积分",
119850
120747
  consumedLabel: "已消耗",
119851
120748
  usedThisPeriod: "本期已使用",
@@ -119857,10 +120754,17 @@ var profileBilling = {
119857
120754
  disabled: "已禁用",
119858
120755
  manageUsage: "管理用量",
119859
120756
  thresholdLabel: "阈值",
120757
+ thresholdHint: "当可用积分低于此金额时,将自动开始充值",
119860
120758
  rechargeAmountLabel: "充值金额",
120759
+ rechargeAmountHint: "每次充值时从您的付款方式扣款,并添加到您的积分中",
119861
120760
  spendingLimitLabel: "消费限额",
120761
+ spendingLimitHint: "每个账单周期内自动充值可扣款的上限",
119862
120762
  unlimited: "无限制",
119863
- autoRechargeDisabledMessage: "自动充值当前已禁用。启用后可在不中断服务的情况下保持余额充足。"
120763
+ autoRechargeDisabledMessage: "自动充值当前已禁用。启用后可在不中断服务的情况下保持余额充足。",
120764
+ billingTabsAriaLabel: "账单板块",
120765
+ tabPlanAndCredits: "套餐与积分",
120766
+ tabSpendLimits: "支出限额",
120767
+ tabAgentLimits: "智能体限额"
119864
120768
  };
119865
120769
  var profileCatalogInviteModal = {
119866
120770
  title: "目录邀请",
@@ -120804,6 +121708,156 @@ var skillsProfileSkillsBox = {
120804
121708
  desired: "期望",
120805
121709
  noDesiredSkills: "您还没有任何期望的技能。"
120806
121710
  };
121711
+ var spendCapUsage = {
121712
+ scopeTenant: "工作区",
121713
+ scopeAgent: "此智能体",
121714
+ scopeUserAgent: "您在此智能体上的用量",
121715
+ intervalDay: "今天",
121716
+ intervalWeek: "本周",
121717
+ intervalMonth: "本月",
121718
+ intervalYear: "今年",
121719
+ percentUsed: "已使用 {percent}%",
121720
+ blockedBanner: "已达到使用限额。在周期重置前,新的聊天和训练将暂停。",
121721
+ warningBanner: "用量即将达到限额。"
121722
+ };
121723
+ var spendCapsTabLabels = {
121724
+ header: {
121725
+ title: "账单",
121726
+ description: "为此智能体及其用户设置支出限额,并查看已用金额。"
121727
+ },
121728
+ capability: {
121729
+ title: "支出限额",
121730
+ description: "为 AI 支出设置上限。此限额开启期间,其覆盖的所有支出都会计入下方金额;达到限额后,由您决定是阻止新请求,还是仅提醒管理员。",
121731
+ offHint: "开启后即开始统计并执行此限额。之后关闭也会保留您在此的配置。"
121732
+ },
121733
+ subTabs: {
121734
+ agent: "此智能体",
121735
+ users: "按用户"
121736
+ },
121737
+ tenantSections: {
121738
+ workspace: {
121739
+ title: "工作区支出限额",
121740
+ description: "一个覆盖此工作区内所有智能体和用户全部支出的限额。按智能体和按用户的限额请在各智能体设置的“账单”中管理。"
121741
+ },
121742
+ noCapStats: {
121743
+ spentThisMonth: "本月已支出",
121744
+ spentAllTime: "累计已支出",
121745
+ hint: "涵盖此工作区内的所有智能体和用户"
121746
+ },
121747
+ agents: {
121748
+ title: "智能体支出限额",
121749
+ description: "此工作区中已配置支出限额的智能体。打开任意一个即可管理其限额及按用户限额。",
121750
+ filter: {
121751
+ label: "搜索智能体",
121752
+ placeholder: "搜索智能体…",
121753
+ typeToSearch: "请输入至少 2 个字符进行搜索",
121754
+ searching: "正在搜索智能体…",
121755
+ noResults: "未找到智能体",
121756
+ clear: "清除智能体筛选"
121757
+ },
121758
+ table: {
121759
+ agentColumn: "智能体",
121760
+ limitColumn: "限额",
121761
+ spentColumn: "已支出",
121762
+ remainingColumn: "剩余",
121763
+ statusColumn: "状态"
121764
+ },
121765
+ emptyState: "尚无智能体支出限额。请在上方搜索智能体进行设置。",
121766
+ emptyFiltered: "此智能体尚未设置支出限额。",
121767
+ setLimitButton: "设置支出限额",
121768
+ manageAria: "管理 {name} 的支出限额",
121769
+ statusToggleAria: "开启或关闭 {name} 的支出限额"
121770
+ }
121771
+ },
121772
+ form: {
121773
+ limitLabel: "支出限额(美元)",
121774
+ limitPlaceholder: "例如 250.00",
121775
+ limitError: "请输入大于 0 的金额",
121776
+ intervalLabel: "重置周期",
121777
+ intervalOptions: {
121778
+ day: "每天",
121779
+ week: "每周",
121780
+ month: "每月",
121781
+ year: "每年"
121782
+ },
121783
+ enforcementLabel: "达到限额时",
121784
+ enforcementOptions: {
121785
+ block: "阻止请求",
121786
+ alertOnly: "仅提醒"
121787
+ },
121788
+ enforcementHelp: {
121789
+ block: "在周期重置前,新的聊天和训练请求将被拒绝。",
121790
+ alertOnly: "不会阻止任何请求——当支出接近限额时通知管理员。"
121791
+ },
121792
+ thresholdsLabel: "提醒阈值(限额百分比)",
121793
+ thresholdsPlaceholder: "例如 80, 95",
121794
+ thresholdsHelp: "以逗号分隔的百分比,达到时管理员会收到接近限额的提醒。",
121795
+ thresholdsError: "请使用 1 到 100 之间、以逗号分隔的数字",
121796
+ noCapHint: "尚未配置支出限额——设置金额并保存即可创建。",
121797
+ save: "保存",
121798
+ saving: "保存中…",
121799
+ remove: "删除限额",
121800
+ removing: "删除中…",
121801
+ cancel: "取消"
121802
+ },
121803
+ usage: {
121804
+ title: "支出限额使用情况",
121805
+ periodTitles: {
121806
+ day: "每日用量",
121807
+ week: "每周用量",
121808
+ month: "每月用量",
121809
+ year: "每年用量"
121810
+ },
121811
+ spent: "已用:{amount}",
121812
+ remaining: "剩余:{amount}",
121813
+ percentUsed: "已使用限额的 {percent}%",
121814
+ exceededBadge: "已达限额",
121815
+ alertOnlyBadge: "仅提醒",
121816
+ disabledBadge: "已关闭"
121817
+ },
121818
+ users: {
121819
+ sectionTitle: "按用户限额",
121820
+ sectionDescription: "限制特定用户在此智能体上的支出。",
121821
+ addButton: "添加用户限额",
121822
+ addTitle: "新建用户限额",
121823
+ editTitle: "编辑用户限额",
121824
+ cancel: "取消",
121825
+ emptyState: "尚无按用户设置的限额。添加一个以限制此智能体上的特定用户。",
121826
+ edit: "编辑",
121827
+ "delete": "删除",
121828
+ table: {
121829
+ userColumn: "用户",
121830
+ limitColumn: "限额",
121831
+ statusColumn: "状态"
121832
+ },
121833
+ actionsAria: "{username} 的操作",
121834
+ statusToggleAria: "开启或关闭 {username} 的支出限额",
121835
+ picker: {
121836
+ label: "用户",
121837
+ placeholder: "搜索用户…",
121838
+ typeToSearch: "请输入至少 2 个字符进行搜索",
121839
+ searching: "正在搜索用户…",
121840
+ noResults: "未找到用户",
121841
+ clear: "更换用户"
121842
+ }
121843
+ },
121844
+ denied: {
121845
+ title: "无权访问支出限额",
121846
+ description: "您没有权限在此查看支出限额。请向管理员申请访问权限。"
121847
+ },
121848
+ deleteModal: {
121849
+ title: "删除支出限额",
121850
+ description: "支出将不再受此限额限制或统计。",
121851
+ cancel: "取消",
121852
+ confirm: "删除"
121853
+ },
121854
+ toasts: {
121855
+ saved: "支出限额已保存",
121856
+ saveError: "无法保存支出限额",
121857
+ deleted: "支出限额已删除",
121858
+ deleteError: "无法删除支出限额"
121859
+ }
121860
+ };
120807
121861
  var tabsAlertstab = {
120808
121862
  searchAlertsPlaceholder: "搜索提醒",
120809
121863
  searchAlertsAriaLabel: "搜索提醒",
@@ -122547,6 +123601,10 @@ var zh = {
122547
123601
  componentsPolicyAssignmentSection: componentsPolicyAssignmentSection,
122548
123602
  componentsPolicyConfigDialog: componentsPolicyConfigDialog,
122549
123603
  componentsProactiveSettingsSection: componentsProactiveSettingsSection,
123604
+ componentsTemplateActionsSection: componentsTemplateActionsSection,
123605
+ componentsTemplateVariablesHelper: componentsTemplateVariablesHelper,
123606
+ componentsUserProgressSettingsSection: componentsUserProgressSettingsSection,
123607
+ componentsWatcherEmailSection: componentsWatcherEmailSection,
122550
123608
  courseContentAccessiblePaginate: courseContentAccessiblePaginate,
122551
123609
  courseContentCourseContentLayout: courseContentCourseContentLayout,
122552
123610
  courseContentDiscoverFacetsFilter: courseContentDiscoverFacetsFilter,
@@ -122662,6 +123720,8 @@ var zh = {
122662
123720
  skillsProfileResumeBox: skillsProfileResumeBox,
122663
123721
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal,
122664
123722
  skillsProfileSkillsBox: skillsProfileSkillsBox,
123723
+ spendCapUsage: spendCapUsage,
123724
+ spendCapsTabLabels: spendCapsTabLabels,
122665
123725
  tabsAlertstab: tabsAlertstab,
122666
123726
  tabsDisclaimersTab: tabsDisclaimersTab,
122667
123727
  tabsEmbedTab: tabsEmbedTab,
@@ -188323,6 +189383,1002 @@ function AddCreditsModal({ isOpen, onClose, tenant }) {
188323
189383
  return (jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: jsxs(DialogContent, { className: "max-w-md", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('profileAddCreditsModal.title') }), jsx(DialogDescription, { children: t('profileAddCreditsModal.description') })] }), jsxs("form", { onSubmit: handleSubmit, className: "space-y-6", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "credit-amount", className: "text-sm font-medium", children: t('profileAddCreditsModal.amountLabel') }), jsxs("div", { className: "relative", children: [jsx(DollarSign, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" }), jsx(Input, { id: "credit-amount", type: "number", min: "0.01", step: "0.01", placeholder: "50.00", value: amount, onChange: (e) => setAmount(e.target.value), className: "pl-9" })] }), jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400", children: t('profileAddCreditsModal.amountHint') })] }), jsxs("div", { className: "flex items-start gap-2 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-100 dark:border-blue-800", children: [jsx(CircleAlert, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), jsx("p", { className: "text-xs text-blue-700 dark:text-blue-300", children: t('profileAddCreditsModal.paymentMethodNotice') })] }), jsxs(DialogFooter, { className: "gap-2 sm:gap-0", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: handleClose, children: t('profileAddCreditsModal.cancel') }), jsx(Button$1, { type: "submit", disabled: isLoading, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: isLoading ? (jsxs(Fragment$1, { children: [jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('profileAddCreditsModal.processing')] })) : (t('profileAddCreditsModal.addCredits')) })] })] })] }) }));
188324
189384
  }
188325
189385
 
189386
+ /**
189387
+ * The info icon that sits on every analytics card and, on hover or keyboard
189388
+ * focus, explains what the card measures. The description doubles as the
189389
+ * accessible name so screen readers get it without needing the hover.
189390
+ *
189391
+ * Uses the tooltip primitives directly rather than the shared `TooltipContent`
189392
+ * because analytics wants a light popover, while `TooltipContent` is the dark
189393
+ * `bg-primary` style the rest of the app relies on.
189394
+ */
189395
+ function CardInfoIcon({ title, description, className }) {
189396
+ return (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { type: "button", "aria-label": description, className: cn('shrink-0 cursor-help rounded p-1 text-[#898989] transition-colors hover:bg-gray-100 hover:text-[#38A1E5] focus-visible:ring-2 focus-visible:ring-[#38A1E5] focus-visible:outline-none', className), children: jsx(Info$3, { className: "h-4 w-4", "aria-hidden": "true" }) }) }), jsx(Portal$2, { children: jsxs(Content2$3, { "data-slot": "tooltip-content", side: "top", align: "center", sideOffset: 6, collisionPadding: 12, className: "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 z-50 max-w-[260px] rounded-lg border border-[oklch(0.922_0_0)] bg-white p-3 shadow-lg", children: [title && (jsx("div", { "data-slot": "tooltip-title", className: "mb-1 text-sm font-semibold text-[#484848]", children: title })), jsx("p", { "data-slot": "tooltip-description", className: "text-sm leading-relaxed text-[#646464]", children: description })] }) })] }));
189397
+ }
189398
+
189399
+ /**
189400
+ * Shared utilities for tab label systems.
189401
+ *
189402
+ * Every tab exports a `resolve*Labels(override?)` function that deep-merges
189403
+ * a partial override into the default AGENT labels. This file provides the
189404
+ * DeepPartial type and deepMerge implementation they all share.
189405
+ */
189406
+ function deepMerge(base, override) {
189407
+ const result = { ...base };
189408
+ const overrideBag = override;
189409
+ for (const key of Object.keys(overrideBag)) {
189410
+ const overrideValue = overrideBag[key];
189411
+ if (overrideValue === undefined)
189412
+ continue;
189413
+ const baseValue = base[key];
189414
+ if (baseValue &&
189415
+ typeof baseValue === 'object' &&
189416
+ !Array.isArray(baseValue) &&
189417
+ overrideValue &&
189418
+ typeof overrideValue === 'object' &&
189419
+ !Array.isArray(overrideValue)) {
189420
+ result[key] = deepMerge(baseValue, overrideValue);
189421
+ }
189422
+ else {
189423
+ result[key] = overrideValue;
189424
+ }
189425
+ }
189426
+ return result;
189427
+ }
189428
+
189429
+ const AGENT_SPEND_CAPS_TAB_LABELS = {
189430
+ header: {
189431
+ title: 'Billing',
189432
+ description: 'Set spend limits for this agent and its users, and see how much has been used.',
189433
+ },
189434
+ capability: {
189435
+ title: 'Spend Limit',
189436
+ description: 'Put a ceiling on AI spend. While this limit is on, everything it covers counts toward the amount below — and when the limit is reached, you choose whether new requests are blocked or admins are simply alerted.',
189437
+ offHint: 'Turn it on to start tracking and enforcing this limit. Your setup here is kept safe if you later turn it off.',
189438
+ },
189439
+ subTabs: {
189440
+ agent: 'This Agent',
189441
+ users: 'Per User',
189442
+ },
189443
+ tenantSections: {
189444
+ workspace: {
189445
+ title: 'Workspace Spend Limit',
189446
+ description: 'One limit covering everything all agents and users spend in this workspace. Per-agent and per-user limits live in each agent’s settings under Billing.',
189447
+ },
189448
+ noCapStats: {
189449
+ spentThisMonth: 'Spent This Month',
189450
+ spentAllTime: 'Spent All Time',
189451
+ hint: 'Across all agents and users in this workspace',
189452
+ },
189453
+ agents: {
189454
+ title: 'Agent Spend Limits',
189455
+ description: 'Agents with a spend limit configured in this workspace. Open one to manage its limit and its per-user limits.',
189456
+ filter: {
189457
+ label: 'Search Agents',
189458
+ placeholder: 'Search Agents…',
189459
+ typeToSearch: 'Type at least 2 characters to search',
189460
+ searching: 'Searching agents…',
189461
+ noResults: 'No agents found',
189462
+ clear: 'Clear Agent Filter',
189463
+ },
189464
+ table: {
189465
+ agentColumn: 'Agent',
189466
+ limitColumn: 'Limit',
189467
+ spentColumn: 'Spent',
189468
+ remainingColumn: 'Remaining',
189469
+ statusColumn: 'Status',
189470
+ },
189471
+ emptyState: 'No agent spend limits yet. Search for an agent above to set one.',
189472
+ emptyFiltered: 'No spend limit for this agent yet.',
189473
+ setLimitButton: 'Set Spend Limit',
189474
+ manageAria: (name) => `Manage spend limits for ${name}`,
189475
+ statusToggleAria: (name) => `Toggle spend limit for ${name}`,
189476
+ },
189477
+ },
189478
+ form: {
189479
+ limitLabel: 'Spend Limit (USD)',
189480
+ limitPlaceholder: 'e.g. 250.00',
189481
+ limitError: 'Enter an amount greater than 0',
189482
+ intervalLabel: 'Resets Every',
189483
+ intervalOptions: {
189484
+ day: 'Day',
189485
+ week: 'Week',
189486
+ month: 'Month',
189487
+ year: 'Year',
189488
+ },
189489
+ enforcementLabel: 'When the Limit Is Reached',
189490
+ enforcementOptions: {
189491
+ block: 'Block Requests',
189492
+ alertOnly: 'Alert Only',
189493
+ },
189494
+ enforcementHelp: {
189495
+ block: 'New chat and training requests are refused until the period resets.',
189496
+ alertOnly: 'Nothing is blocked — admins are notified when spend nears the limit.',
189497
+ },
189498
+ thresholdsLabel: 'Alert At (% of Limit)',
189499
+ thresholdsPlaceholder: 'e.g. 80, 95',
189500
+ thresholdsHelp: 'Comma-separated percentages at which admins get a near-limit alert.',
189501
+ thresholdsError: 'Use comma-separated numbers between 1 and 100',
189502
+ noCapHint: 'No spend limit configured yet — set an amount and save to create one.',
189503
+ save: 'Save',
189504
+ saving: 'Saving…',
189505
+ cancel: 'Cancel',
189506
+ remove: 'Delete Limit',
189507
+ removing: 'Deleting…',
189508
+ },
189509
+ usage: {
189510
+ title: 'Spend Limit Usage',
189511
+ periodTitles: {
189512
+ day: 'Daily Usage',
189513
+ week: 'Weekly Usage',
189514
+ month: 'Monthly Usage',
189515
+ year: 'Yearly Usage',
189516
+ },
189517
+ spent: 'Spent: {amount}',
189518
+ remaining: 'Remaining: {amount}',
189519
+ percentUsed: '{percent}% of limit used',
189520
+ exceededBadge: 'Limit Reached',
189521
+ alertOnlyBadge: 'Alert Only',
189522
+ disabledBadge: 'Off',
189523
+ },
189524
+ users: {
189525
+ sectionTitle: 'Per-User Limits',
189526
+ sectionDescription: 'Limit what specific users can spend on this agent.',
189527
+ addButton: 'Add User Limit',
189528
+ addTitle: 'New User Limit',
189529
+ editTitle: 'Edit User Limit',
189530
+ cancel: 'Cancel',
189531
+ emptyState: 'No per-user limits yet. Add one to limit a specific user on this agent.',
189532
+ edit: 'Edit',
189533
+ delete: 'Delete',
189534
+ table: {
189535
+ userColumn: 'User',
189536
+ limitColumn: 'Limit',
189537
+ statusColumn: 'Status',
189538
+ },
189539
+ actionsAria: (username) => `Actions for ${username}`,
189540
+ statusToggleAria: (username) => `Toggle spend limit for ${username}`,
189541
+ picker: {
189542
+ label: 'User',
189543
+ placeholder: 'Search Users…',
189544
+ typeToSearch: 'Type at least 2 characters to search',
189545
+ searching: 'Searching users…',
189546
+ noResults: 'No users found',
189547
+ clear: 'Change User',
189548
+ },
189549
+ },
189550
+ denied: {
189551
+ title: 'No Access to Spend Limits',
189552
+ description: "You don't have permission to view spend limits here. Ask an administrator for access.",
189553
+ },
189554
+ deleteModal: {
189555
+ title: 'Delete Spend Limit',
189556
+ description: 'Spending will no longer be limited or tracked against this limit.',
189557
+ cancel: 'Cancel',
189558
+ confirm: 'Delete',
189559
+ },
189560
+ toasts: {
189561
+ saved: 'Spend limit saved',
189562
+ saveError: "Couldn't save the spend limit",
189563
+ deleted: 'Spend limit deleted',
189564
+ deleteError: "Couldn't delete the spend limit",
189565
+ },
189566
+ };
189567
+ function buildSpendCapsTabLabels(t) {
189568
+ return {
189569
+ header: {
189570
+ title: t('spendCapsTabLabels.header.title'),
189571
+ description: t('spendCapsTabLabels.header.description'),
189572
+ },
189573
+ capability: {
189574
+ title: t('spendCapsTabLabels.capability.title'),
189575
+ description: t('spendCapsTabLabels.capability.description'),
189576
+ offHint: t('spendCapsTabLabels.capability.offHint'),
189577
+ },
189578
+ subTabs: {
189579
+ agent: t('spendCapsTabLabels.subTabs.agent'),
189580
+ users: t('spendCapsTabLabels.subTabs.users'),
189581
+ },
189582
+ tenantSections: {
189583
+ workspace: {
189584
+ title: t('spendCapsTabLabels.tenantSections.workspace.title'),
189585
+ description: t('spendCapsTabLabels.tenantSections.workspace.description'),
189586
+ },
189587
+ noCapStats: {
189588
+ spentThisMonth: t('spendCapsTabLabels.tenantSections.noCapStats.spentThisMonth'),
189589
+ spentAllTime: t('spendCapsTabLabels.tenantSections.noCapStats.spentAllTime'),
189590
+ hint: t('spendCapsTabLabels.tenantSections.noCapStats.hint'),
189591
+ },
189592
+ agents: {
189593
+ title: t('spendCapsTabLabels.tenantSections.agents.title'),
189594
+ description: t('spendCapsTabLabels.tenantSections.agents.description'),
189595
+ filter: {
189596
+ label: t('spendCapsTabLabels.tenantSections.agents.filter.label'),
189597
+ placeholder: t('spendCapsTabLabels.tenantSections.agents.filter.placeholder'),
189598
+ typeToSearch: t('spendCapsTabLabels.tenantSections.agents.filter.typeToSearch'),
189599
+ searching: t('spendCapsTabLabels.tenantSections.agents.filter.searching'),
189600
+ noResults: t('spendCapsTabLabels.tenantSections.agents.filter.noResults'),
189601
+ clear: t('spendCapsTabLabels.tenantSections.agents.filter.clear'),
189602
+ },
189603
+ table: {
189604
+ agentColumn: t('spendCapsTabLabels.tenantSections.agents.table.agentColumn'),
189605
+ limitColumn: t('spendCapsTabLabels.tenantSections.agents.table.limitColumn'),
189606
+ spentColumn: t('spendCapsTabLabels.tenantSections.agents.table.spentColumn'),
189607
+ remainingColumn: t('spendCapsTabLabels.tenantSections.agents.table.remainingColumn'),
189608
+ statusColumn: t('spendCapsTabLabels.tenantSections.agents.table.statusColumn'),
189609
+ },
189610
+ emptyState: t('spendCapsTabLabels.tenantSections.agents.emptyState'),
189611
+ emptyFiltered: t('spendCapsTabLabels.tenantSections.agents.emptyFiltered'),
189612
+ setLimitButton: t('spendCapsTabLabels.tenantSections.agents.setLimitButton'),
189613
+ manageAria: (name) => t('spendCapsTabLabels.tenantSections.agents.manageAria', { name }),
189614
+ statusToggleAria: (name) => t('spendCapsTabLabels.tenantSections.agents.statusToggleAria', { name }),
189615
+ },
189616
+ },
189617
+ form: {
189618
+ limitLabel: t('spendCapsTabLabels.form.limitLabel'),
189619
+ limitPlaceholder: t('spendCapsTabLabels.form.limitPlaceholder'),
189620
+ limitError: t('spendCapsTabLabels.form.limitError'),
189621
+ intervalLabel: t('spendCapsTabLabels.form.intervalLabel'),
189622
+ intervalOptions: {
189623
+ day: t('spendCapsTabLabels.form.intervalOptions.day'),
189624
+ week: t('spendCapsTabLabels.form.intervalOptions.week'),
189625
+ month: t('spendCapsTabLabels.form.intervalOptions.month'),
189626
+ year: t('spendCapsTabLabels.form.intervalOptions.year'),
189627
+ },
189628
+ enforcementLabel: t('spendCapsTabLabels.form.enforcementLabel'),
189629
+ enforcementOptions: {
189630
+ block: t('spendCapsTabLabels.form.enforcementOptions.block'),
189631
+ alertOnly: t('spendCapsTabLabels.form.enforcementOptions.alertOnly'),
189632
+ },
189633
+ enforcementHelp: {
189634
+ block: t('spendCapsTabLabels.form.enforcementHelp.block'),
189635
+ alertOnly: t('spendCapsTabLabels.form.enforcementHelp.alertOnly'),
189636
+ },
189637
+ thresholdsLabel: t('spendCapsTabLabels.form.thresholdsLabel'),
189638
+ thresholdsPlaceholder: t('spendCapsTabLabels.form.thresholdsPlaceholder'),
189639
+ thresholdsHelp: t('spendCapsTabLabels.form.thresholdsHelp'),
189640
+ thresholdsError: t('spendCapsTabLabels.form.thresholdsError'),
189641
+ noCapHint: t('spendCapsTabLabels.form.noCapHint'),
189642
+ save: t('spendCapsTabLabels.form.save'),
189643
+ saving: t('spendCapsTabLabels.form.saving'),
189644
+ cancel: t('spendCapsTabLabels.form.cancel'),
189645
+ remove: t('spendCapsTabLabels.form.remove'),
189646
+ removing: t('spendCapsTabLabels.form.removing'),
189647
+ },
189648
+ usage: {
189649
+ title: t('spendCapsTabLabels.usage.title'),
189650
+ periodTitles: {
189651
+ day: t('spendCapsTabLabels.usage.periodTitles.day'),
189652
+ week: t('spendCapsTabLabels.usage.periodTitles.week'),
189653
+ month: t('spendCapsTabLabels.usage.periodTitles.month'),
189654
+ year: t('spendCapsTabLabels.usage.periodTitles.year'),
189655
+ },
189656
+ spent: t('spendCapsTabLabels.usage.spent'),
189657
+ remaining: t('spendCapsTabLabels.usage.remaining'),
189658
+ percentUsed: t('spendCapsTabLabels.usage.percentUsed'),
189659
+ exceededBadge: t('spendCapsTabLabels.usage.exceededBadge'),
189660
+ alertOnlyBadge: t('spendCapsTabLabels.usage.alertOnlyBadge'),
189661
+ disabledBadge: t('spendCapsTabLabels.usage.disabledBadge'),
189662
+ },
189663
+ users: {
189664
+ sectionTitle: t('spendCapsTabLabels.users.sectionTitle'),
189665
+ sectionDescription: t('spendCapsTabLabels.users.sectionDescription'),
189666
+ addButton: t('spendCapsTabLabels.users.addButton'),
189667
+ addTitle: t('spendCapsTabLabels.users.addTitle'),
189668
+ editTitle: t('spendCapsTabLabels.users.editTitle'),
189669
+ cancel: t('spendCapsTabLabels.users.cancel'),
189670
+ emptyState: t('spendCapsTabLabels.users.emptyState'),
189671
+ edit: t('spendCapsTabLabels.users.edit'),
189672
+ delete: t('spendCapsTabLabels.users.delete'),
189673
+ table: {
189674
+ userColumn: t('spendCapsTabLabels.users.table.userColumn'),
189675
+ limitColumn: t('spendCapsTabLabels.users.table.limitColumn'),
189676
+ statusColumn: t('spendCapsTabLabels.users.table.statusColumn'),
189677
+ },
189678
+ actionsAria: (username) => t('spendCapsTabLabels.users.actionsAria', { username }),
189679
+ statusToggleAria: (username) => t('spendCapsTabLabels.users.statusToggleAria', { username }),
189680
+ picker: {
189681
+ label: t('spendCapsTabLabels.users.picker.label'),
189682
+ placeholder: t('spendCapsTabLabels.users.picker.placeholder'),
189683
+ typeToSearch: t('spendCapsTabLabels.users.picker.typeToSearch'),
189684
+ searching: t('spendCapsTabLabels.users.picker.searching'),
189685
+ noResults: t('spendCapsTabLabels.users.picker.noResults'),
189686
+ clear: t('spendCapsTabLabels.users.picker.clear'),
189687
+ },
189688
+ },
189689
+ denied: {
189690
+ title: t('spendCapsTabLabels.denied.title'),
189691
+ description: t('spendCapsTabLabels.denied.description'),
189692
+ },
189693
+ deleteModal: {
189694
+ title: t('spendCapsTabLabels.deleteModal.title'),
189695
+ description: t('spendCapsTabLabels.deleteModal.description'),
189696
+ cancel: t('spendCapsTabLabels.deleteModal.cancel'),
189697
+ confirm: t('spendCapsTabLabels.deleteModal.confirm'),
189698
+ },
189699
+ toasts: {
189700
+ saved: t('spendCapsTabLabels.toasts.saved'),
189701
+ saveError: t('spendCapsTabLabels.toasts.saveError'),
189702
+ deleted: t('spendCapsTabLabels.toasts.deleted'),
189703
+ deleteError: t('spendCapsTabLabels.toasts.deleteError'),
189704
+ },
189705
+ };
189706
+ }
189707
+ function resolveSpendCapsTabLabels(override, t) {
189708
+ const base = t ? buildSpendCapsTabLabels(t) : AGENT_SPEND_CAPS_TAB_LABELS;
189709
+ if (!override)
189710
+ return base;
189711
+ return deepMerge(base, override);
189712
+ }
189713
+
189714
+ const CustomSwitch = React.forwardRef(({ className, checked, onCheckedChange, ...props }, ref) => (jsx(Root$c, { checked: checked, onCheckedChange: onCheckedChange, className: cn('peer focus-visible:ring-ring focus-visible:ring-offset-background inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-blue-500 data-[state=unchecked]:bg-gray-200', className), ...props, ref: ref, children: jsx(Thumb, { className: cn('pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0') }) })));
189715
+ CustomSwitch.displayName = Root$c.displayName;
189716
+
189717
+ /**
189718
+ * Reusable header used at the top of every capability-gated agent-settings
189719
+ * tab. It surfaces the capability's on/off toggle (previously buried in the
189720
+ * Settings → Capabilities list) right where the feature is configured, with a
189721
+ * naive-friendly explanation.
189722
+ *
189723
+ * The configuration UI below is always rendered so admins can preview what
189724
+ * they'll get, but it's grayed out and made inert (`pointer-events-none`,
189725
+ * `aria-hidden`) until the capability is switched on.
189726
+ */
189727
+ function CapabilityGate({ title, description, enabled, onToggle, disabled = false, ariaLabel, offHint, toggleTestId, children, }) {
189728
+ return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/60 p-4", "data-testid": "capability-gate-header", children: [jsx("div", { className: "min-w-0", children: jsxs("p", { className: "text-xs leading-relaxed text-gray-600", children: [description, !enabled && offHint && (jsxs("span", { className: "font-medium text-gray-500", "data-testid": "capability-gate-off-hint", children: [' ', offHint] }))] }) }), jsx(CustomSwitch, { checked: enabled, onCheckedChange: onToggle, disabled: disabled, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : title, "aria-checked": enabled, "data-testid": toggleTestId, className: "mt-0.5 flex-shrink-0" })] }), jsx("div", { className: cn('transition-opacity', !enabled && 'pointer-events-none select-none opacity-50'), "aria-hidden": !enabled, inert: !enabled, "data-testid": "capability-gate-content", "data-enabled": enabled, children: children })] }));
189729
+ }
189730
+
189731
+ /**
189732
+ * Pure helpers for the spend-caps tab. The backend stores alert thresholds as
189733
+ * fractions of the limit (0.8 = 80%) while the form edits them as a
189734
+ * comma-separated percentage list, and dollar fields travel as decimal strings.
189735
+ */
189736
+ /** Format a backend decimal string as a dollar amount, e.g. "30.500000" → "$30.50". */
189737
+ function formatUsd$1(value) {
189738
+ const parsed = Number(value);
189739
+ if (value == null || value === '' || Number.isNaN(parsed))
189740
+ return '$0.00';
189741
+ return `$${parsed.toFixed(2)}`;
189742
+ }
189743
+ /** Fractions → editable percent list: [0.8, 0.95] → "80, 95". */
189744
+ function thresholdsToInput(thresholds) {
189745
+ if (!Array.isArray(thresholds))
189746
+ return '';
189747
+ return thresholds
189748
+ .filter((value) => typeof value === 'number' && !Number.isNaN(value))
189749
+ .map((value) => {
189750
+ const percent = value * 100;
189751
+ return Number.isInteger(percent) ? String(percent) : percent.toFixed(1);
189752
+ })
189753
+ .join(', ');
189754
+ }
189755
+ /**
189756
+ * Percent list → fractions: "80, 95" → [0.8, 0.95]. Empty input is valid and
189757
+ * means "keep the backend default". Returns null when any entry is not a
189758
+ * number in (0, 100].
189759
+ */
189760
+ function parseThresholdsInput(input) {
189761
+ const trimmed = input.trim();
189762
+ if (trimmed === '')
189763
+ return [];
189764
+ const parts = trimmed.split(',').map((part) => part.trim());
189765
+ const fractions = [];
189766
+ for (const part of parts) {
189767
+ if (part === '')
189768
+ return null;
189769
+ const percent = Number(part);
189770
+ if (Number.isNaN(percent) || percent <= 0 || percent > 100)
189771
+ return null;
189772
+ fractions.push(percent / 100);
189773
+ }
189774
+ return fractions;
189775
+ }
189776
+ /** A valid limit is any number strictly greater than 0. */
189777
+ function isValidLimit(input) {
189778
+ const parsed = Number(input);
189779
+ return input.trim() !== '' && !Number.isNaN(parsed) && parsed > 0;
189780
+ }
189781
+ /** Percent of the limit already spent, clamped to [0, 100] for progress bars. */
189782
+ function percentUsed(currentSpend, maxCost) {
189783
+ const spent = Number(currentSpend);
189784
+ const limit = Number(maxCost);
189785
+ if (Number.isNaN(spent) || Number.isNaN(limit) || limit <= 0)
189786
+ return 0;
189787
+ return Math.min(100, Math.max(0, Math.round((spent / limit) * 100)));
189788
+ }
189789
+
189790
+ /**
189791
+ * Editor for one spend cap of any scope. Counter fields
189792
+ * (current spend / remaining / exceeded) are read-only on the backend and
189793
+ * only rendered, never submitted.
189794
+ */
189795
+ function SpendCapForm({ labels, cap, isSaving, isDeleting, onSave, onDelete, onCancel, showUsage = true, testId, }) {
189796
+ const [limit, setLimit] = React__default.useState('');
189797
+ const [interval, setInterval] = React__default.useState('month');
189798
+ const [enforcement, setEnforcement] = React__default.useState('block');
189799
+ const [thresholds, setThresholds] = React__default.useState('80, 95');
189800
+ const [enabled, setEnabled] = React__default.useState(true);
189801
+ // Rehydrate the form whenever the server copy changes.
189802
+ React__default.useEffect(() => {
189803
+ var _a, _b, _c;
189804
+ setLimit(cap ? String(Number(cap.max_cost_usd)) : '');
189805
+ setInterval((_a = cap === null || cap === void 0 ? void 0 : cap.interval_type) !== null && _a !== void 0 ? _a : 'month');
189806
+ setEnforcement((_b = cap === null || cap === void 0 ? void 0 : cap.enforcement) !== null && _b !== void 0 ? _b : 'block');
189807
+ setThresholds(cap ? thresholdsToInput(cap.alert_thresholds) : '80, 95');
189808
+ setEnabled((_c = cap === null || cap === void 0 ? void 0 : cap.enabled) !== null && _c !== void 0 ? _c : true);
189809
+ }, [cap === null || cap === void 0 ? void 0 : cap.id, cap === null || cap === void 0 ? void 0 : cap.updated_at]); // eslint-disable-line react-hooks/exhaustive-deps
189810
+ const limitTouched = limit.trim() !== '';
189811
+ const limitValid = isValidLimit(limit);
189812
+ const parsedThresholds = parseThresholdsInput(thresholds);
189813
+ const thresholdsValid = parsedThresholds !== null;
189814
+ const canSave = limitValid && thresholdsValid && !isSaving && !isDeleting;
189815
+ const handleSave = () => {
189816
+ const formData = {
189817
+ interval_type: interval,
189818
+ max_cost_usd: Number(limit).toFixed(2),
189819
+ enforcement,
189820
+ enabled,
189821
+ };
189822
+ if (parsedThresholds && parsedThresholds.length > 0) {
189823
+ formData.alert_thresholds = parsedThresholds;
189824
+ }
189825
+ onSave(formData);
189826
+ };
189827
+ const usagePercent = cap ? percentUsed(cap.current_spend_usd, cap.max_cost_usd) : 0;
189828
+ const barColor = (cap === null || cap === void 0 ? void 0 : cap.is_exceeded)
189829
+ ? 'bg-red-500'
189830
+ : usagePercent >= 80
189831
+ ? 'bg-amber-500'
189832
+ : 'bg-blue-500';
189833
+ return (jsxs("div", { className: "space-y-4", "data-testid": `${testId}-form`, children: [!cap && (jsx("p", { className: "text-xs text-gray-500", "data-testid": `${testId}-no-cap-hint`, children: labels.form.noCapHint })), jsx(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: enabled, onToggle: setEnabled, disabled: isSaving || isDeleting, ariaLabel: labels.capability.title, toggleTestId: `${testId}-enabled-switch`, children: jsxs("div", { className: "space-y-4", children: [cap && showUsage && (jsxs("div", { className: "space-y-2 rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": `${testId}-usage`, children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("p", { className: "text-xs font-medium text-gray-700", children: labels.usage.periodTitles[cap.interval_type] }), jsxs("div", { className: "flex items-center gap-1.5", children: [!cap.enabled && (jsx("span", { className: "rounded-full bg-gray-200 px-2 py-0.5 text-[11px] font-medium text-gray-700", "data-testid": `${testId}-disabled-badge`, children: labels.usage.disabledBadge })), cap.enforcement === 'alert_only' && (jsx("span", { className: "rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium text-amber-800", "data-testid": `${testId}-alert-only-badge`, children: labels.usage.alertOnlyBadge })), cap.is_exceeded && (jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-[11px] font-medium text-red-700", "data-testid": `${testId}-exceeded-badge`, children: labels.usage.exceededBadge }))] })] }), jsx("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-200", role: "progressbar", "aria-valuenow": usagePercent, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": labels.usage.title, children: jsx("div", { className: `h-full rounded-full transition-all ${barColor}`, style: { width: `${usagePercent}%` } }) }), jsxs("div", { className: "flex items-center justify-between text-xs text-gray-600", children: [jsx("span", { "data-testid": `${testId}-spent`, children: labels.usage.spent.replace('{amount}', formatUsd$1(cap.current_spend_usd)) }), jsx("span", { "data-testid": `${testId}-percent`, children: labels.usage.percentUsed.replace('{percent}', String(usagePercent)) }), jsx("span", { "data-testid": `${testId}-remaining`, children: labels.usage.remaining.replace('{amount}', formatUsd$1(cap.remaining_usd)) })] })] })), jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.form.limitLabel }), jsx(Input, { className: "focus-visible:border-blue-500 focus-visible:ring-1 focus-visible:ring-blue-500", value: limit, onChange: (e) => setLimit(e.target.value), placeholder: labels.form.limitPlaceholder, inputMode: "decimal", "data-testid": `${testId}-limit-input` }), limitTouched && !limitValid && (jsx("p", { className: "text-xs text-red-500", "data-testid": `${testId}-limit-error`, children: labels.form.limitError }))] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.form.intervalLabel }), jsxs(Select$1, { value: interval, onValueChange: (value) => setInterval(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": `${testId}-interval-select`, children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "day", children: labels.form.intervalOptions.day }), jsx(SelectItem, { value: "week", children: labels.form.intervalOptions.week }), jsx(SelectItem, { value: "month", children: labels.form.intervalOptions.month }), jsx(SelectItem, { value: "year", children: labels.form.intervalOptions.year })] })] })] })] }), jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.form.enforcementLabel }), jsxs(Select$1, { value: enforcement, onValueChange: (value) => setEnforcement(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": `${testId}-enforcement-select`, children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "block", children: labels.form.enforcementOptions.block }), jsx(SelectItem, { value: "alert_only", children: labels.form.enforcementOptions.alertOnly })] })] }), jsx("p", { className: "text-xs text-gray-500", children: enforcement === 'block'
189834
+ ? labels.form.enforcementHelp.block
189835
+ : labels.form.enforcementHelp.alertOnly })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.form.thresholdsLabel }), jsx(Input, { className: "focus-visible:border-blue-500 focus-visible:ring-1 focus-visible:ring-blue-500", value: thresholds, onChange: (e) => setThresholds(e.target.value), placeholder: labels.form.thresholdsPlaceholder, "data-testid": `${testId}-thresholds-input` }), thresholdsValid ? (jsx("p", { className: "text-xs text-gray-500", children: labels.form.thresholdsHelp })) : (jsx("p", { className: "text-xs text-red-500", "data-testid": `${testId}-thresholds-error`, children: labels.form.thresholdsError }))] })] })] }) }), jsxs("div", { className: "flex items-center justify-end gap-2", children: [onCancel && (jsx("button", { type: "button", onClick: onCancel, disabled: isSaving || isDeleting, "data-testid": `${testId}-cancel-button`, className: "inline-flex items-center rounded-md border border-gray-200 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-50", children: labels.form.cancel })), cap && onDelete && (jsx("button", { type: "button", onClick: onDelete, disabled: isSaving || isDeleting, "data-testid": `${testId}-delete-button`, className: "inline-flex items-center rounded-md border border-red-200 px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-50", children: isDeleting ? labels.form.removing : labels.form.remove })), jsx("button", { type: "button", onClick: handleSave, disabled: !canSave, "data-testid": `${testId}-save-button`, className: "inline-flex items-center gap-2 rounded-md bg-gradient-to-r from-[#2563EB] to-[#93C5FD] px-4 py-2 text-sm font-medium text-white hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50", children: isSaving ? labels.form.saving : labels.form.save })] })] }));
189836
+ }
189837
+
189838
+ function errorStatus$2(error) {
189839
+ return error === null || error === void 0 ? void 0 : error.status;
189840
+ }
189841
+ function errorDetail$2(error) {
189842
+ const data = error === null || error === void 0 ? void 0 : error.data;
189843
+ if (typeof (data === null || data === void 0 ? void 0 : data.detail) === 'string')
189844
+ return data.detail;
189845
+ if (typeof (data === null || data === void 0 ? void 0 : data.error) === 'string')
189846
+ return data.error;
189847
+ return '';
189848
+ }
189849
+ function useSpendCaps({ tenantKey, mentorId, labels }) {
189850
+ const identityReady = Boolean(tenantKey && mentorId);
189851
+ // 404 is the documented "no cap configured yet" state for the singleton
189852
+ // GETs, not an error — the row simply may not exist (mirrors the grader
189853
+ // tab's config query, which 404s until first saved).
189854
+ const { data: agentCapData, error: agentCapError, isLoading: isAgentCapLoading, refetch: refetchAgentCap, } = useGetAgentSpendCapQuery({ org: tenantKey, mentorId }, { skip: !identityReady });
189855
+ const { data: tenantCapData, error: tenantCapError, isLoading: isTenantCapLoading, refetch: refetchTenantCap, } = useGetTenantSpendCapQuery({ org: tenantKey }, { skip: !tenantKey });
189856
+ // Derive the caps from the error state, not just from `data`: after a
189857
+ // delete, the explicit refetch 404s while RTK Query keeps the entry's
189858
+ // last fulfilled data — without this, the form would keep showing the
189859
+ // just-deleted cap. Keyed on error PRESENCE rather than `status === 404`
189860
+ // because `iblFetchBaseQuery` THROWS its errors and RTK serializes a
189861
+ // thrown plain object down to a bare SerializedError with no `status`.
189862
+ // Any singleton-GET error means there is no trustworthy cap: the
189863
+ // expected 404 is "no cap configured", and the 403 denied state renders
189864
+ // its own panel instead of the form.
189865
+ const agentCap = agentCapError ? undefined : agentCapData;
189866
+ const tenantCap = tenantCapError ? undefined : tenantCapData;
189867
+ const { data: userCaps = [], error: userCapsError, isLoading: isUserCapsLoading, refetch: refetchUserCaps, } = useListAgentUserSpendCapsQuery({ org: tenantKey, mentorId }, { skip: !identityReady });
189868
+ const [upsertAgentCap, { isLoading: isSavingAgentCap }] = useUpsertAgentSpendCapMutation();
189869
+ const [deleteAgentCap, { isLoading: isDeletingAgentCap }] = useDeleteAgentSpendCapMutation();
189870
+ const [upsertTenantCap, { isLoading: isSavingTenantCap }] = useUpsertTenantSpendCapMutation();
189871
+ const [deleteTenantCap, { isLoading: isDeletingTenantCap }] = useDeleteTenantSpendCapMutation();
189872
+ const [upsertUserCap, { isLoading: isSavingUserCap }] = useUpsertUserSpendCapMutation();
189873
+ const [deleteUserCap, { isLoading: isDeletingUserCap }] = useDeleteUserSpendCapMutation();
189874
+ const isAgentForbidden = errorStatus$2(agentCapError) === 403;
189875
+ const isTenantForbidden = errorStatus$2(tenantCapError) === 403;
189876
+ const isUsersForbidden = errorStatus$2(userCapsError) === 403;
189877
+ // Refetch the mutated scope explicitly after every successful save/delete
189878
+ // (same as the grader tab's `refetchConfig()`): the singleton GETs 404
189879
+ // until first created, and a cache entry whose only fetch errored isn't
189880
+ // reachable through the mutation's tag invalidation — the explicit
189881
+ // refetch keeps the form in sync in place regardless.
189882
+ const runSave = async (mutate, refetchScope) => {
189883
+ try {
189884
+ await mutate();
189885
+ refetchScope();
189886
+ toast.success(labels.toasts.saved);
189887
+ return true;
189888
+ }
189889
+ catch (error) {
189890
+ console.error('[AgentSpendCapsTab] save spend cap error', error);
189891
+ toast.error(errorDetail$2(error) || labels.toasts.saveError);
189892
+ return false;
189893
+ }
189894
+ };
189895
+ const runDelete = async (mutate, refetchScope) => {
189896
+ try {
189897
+ await mutate();
189898
+ refetchScope();
189899
+ toast.success(labels.toasts.deleted);
189900
+ return true;
189901
+ }
189902
+ catch (error) {
189903
+ console.error('[AgentSpendCapsTab] delete spend cap error', error);
189904
+ toast.error(errorDetail$2(error) || labels.toasts.deleteError);
189905
+ return false;
189906
+ }
189907
+ };
189908
+ const saveAgentCap = (formData) => runSave(() => upsertAgentCap({ org: tenantKey, mentorId, formData }).unwrap(), refetchAgentCap);
189909
+ const removeAgentCap = () => runDelete(() => deleteAgentCap({ org: tenantKey, mentorId }).unwrap(), refetchAgentCap);
189910
+ const saveTenantCap = (formData) => runSave(() => upsertTenantCap({ org: tenantKey, formData }).unwrap(), refetchTenantCap);
189911
+ const removeTenantCap = () => runDelete(() => deleteTenantCap({ org: tenantKey }).unwrap(), refetchTenantCap);
189912
+ const saveUserCap = (username, formData) => runSave(() => upsertUserCap({ org: tenantKey, mentorId, username, formData }).unwrap(), refetchUserCaps);
189913
+ const removeUserCap = (username) => runDelete(() => deleteUserCap({ org: tenantKey, mentorId, username }).unwrap(), refetchUserCaps);
189914
+ return {
189915
+ agentCap,
189916
+ isAgentCapLoading,
189917
+ isAgentForbidden,
189918
+ saveAgentCap,
189919
+ removeAgentCap,
189920
+ isSavingAgentCap,
189921
+ isDeletingAgentCap,
189922
+ tenantCap,
189923
+ isTenantCapLoading,
189924
+ isTenantForbidden,
189925
+ saveTenantCap,
189926
+ removeTenantCap,
189927
+ isSavingTenantCap,
189928
+ isDeletingTenantCap,
189929
+ userCaps,
189930
+ isUserCapsLoading,
189931
+ isUsersForbidden,
189932
+ saveUserCap,
189933
+ removeUserCap,
189934
+ isSavingUserCap,
189935
+ isDeletingUserCap,
189936
+ };
189937
+ }
189938
+
189939
+ /**
189940
+ * Tenant-settings Billing section for the workspace-wide LLM spend limit.
189941
+ * Renders the same `SpendCapForm` (usage strip included) as the agent
189942
+ * settings Billing tab, so both scopes present the identical editor.
189943
+ * Per-agent and per-user limits are managed in each agent's own Billing tab
189944
+ * (`AgentSpendCapsTab`), since user limits are keyed per agent.
189945
+ * Rendered inside `BillingTab`.
189946
+ */
189947
+ function SpendLimitsSection({ tenantKey, labels: labelsOverride }) {
189948
+ const t = useT();
189949
+ const labels = React__default.useMemo(() => resolveSpendCapsTabLabels(labelsOverride, t), [labelsOverride, t]);
189950
+ const [isDeleteOpen, setIsDeleteOpen] = React__default.useState(false);
189951
+ const { tenantCap, isTenantCapLoading, isTenantForbidden, saveTenantCap, removeTenantCap, isSavingTenantCap, isDeletingTenantCap, } = useSpendCaps({ tenantKey, mentorId: '', labels });
189952
+ const handleConfirmDelete = async () => {
189953
+ const deleted = await removeTenantCap();
189954
+ if (deleted)
189955
+ setIsDeleteOpen(false);
189956
+ };
189957
+ // With no cap configured there are no reconciled counters, so surface the
189958
+ // workspace's actual spend from the financial analytics endpoint instead —
189959
+ // an admin should see what is being spent before deciding on a limit.
189960
+ const skipActualSpend = !tenantKey || isTenantCapLoading || isTenantForbidden || Boolean(tenantCap);
189961
+ const { data: monthlySpend } = useGetFinancialStatsQuery({ metric: 'monthly_costs', date_filter: 'all_time', platform_key: tenantKey }, { skip: skipActualSpend });
189962
+ const { data: totalSpend } = useGetFinancialStatsQuery({ metric: 'total_costs', all_time: 'true', date_filter: 'all_time', platform_key: tenantKey }, { skip: skipActualSpend });
189963
+ 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$1(String(monthlySpend.value)) })] })), 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$1(String(totalSpend.value)) })] }))] }), 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 })] })] }) })] }));
189964
+ }
189965
+
189966
+ function c$3(e,u,c){var i=this,a=useRef(null),o=useRef(0),f=useRef(null),l=useRef([]),v=useRef(),m=useRef(),d=useRef(e),g=useRef(true);d.current=e;var p="undefined"!=typeof window,w=!u&&0!==u&&p;if("function"!=typeof e)throw new TypeError("Expected a function");u=+u||0;var s=!!(c=c||{}).leading,x=!("trailing"in c)||!!c.trailing,h="maxWait"in c,y="debounceOnServer"in c&&!!c.debounceOnServer,F=h?Math.max(+c.maxWait||0,u):null;useEffect(function(){return g.current=true,function(){g.current=false;}},[]);var A=useMemo(function(){var r=function(r){var n=l.current,t=v.current;return l.current=v.current=null,o.current=r,m.current=d.current.apply(t,n)},n=function(r,n){w&&cancelAnimationFrame(f.current),f.current=w?requestAnimationFrame(r):setTimeout(r,n);},t=function(r){if(!g.current)return false;var n=r-a.current;return !a.current||n>=u||n<0||h&&r-o.current>=F},e=function(n){return f.current=null,x&&l.current?r(n):(l.current=v.current=null,m.current)},c=function r(){var c=Date.now();if(t(c))return e(c);if(g.current){var i=u-(c-a.current),f=h?Math.min(i,F-(c-o.current)):i;n(r,f);}},A=function(){if(p||y){var e=Date.now(),d=t(e);if(l.current=[].slice.call(arguments),v.current=i,a.current=e,d){if(!f.current&&g.current)return o.current=a.current,n(c,u),s?r(a.current):m.current;if(h)return n(c,u),r(a.current)}return f.current||n(c,u),m.current}};return A.cancel=function(){f.current&&(w?cancelAnimationFrame(f.current):clearTimeout(f.current)),o.current=0,l.current=a.current=v.current=f.current=null;},A.isPending=function(){return !!f.current},A.flush=function(){return f.current?e(Date.now()):m.current},A},[s,h,u,F,x,w,p,y]);return A}function i$9(r,n){return r===n}function a$3(n,t,a){var o=i$9,f=useRef(n),l=useState({})[1],v=c$3(useCallback(function(r){f.current=r,l({});},[l]),t,a),m=useRef(n);return o(m.current,n)||(v(n),m.current=n),[f.current,v]}
189967
+
189968
+ const Table$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { className: "relative w-full overflow-auto", children: jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props }) })));
189969
+ Table$1.displayName = "Table";
189970
+ const TableHeader$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("thead", { ref: ref, className: cn("[&_tr]:border-b", className), ...props })));
189971
+ TableHeader$1.displayName = "TableHeader";
189972
+ const TableBody = React.forwardRef(({ className, ...props }, ref) => (jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
189973
+ TableBody.displayName = "TableBody";
189974
+ const TableFooter = React.forwardRef(({ className, ...props }, ref) => (jsx("tfoot", { ref: ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })));
189975
+ TableFooter.displayName = "TableFooter";
189976
+ const TableRow$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("tr", { ref: ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props })));
189977
+ TableRow$1.displayName = "TableRow";
189978
+ const TableHead = React.forwardRef(({ className, ...props }, ref) => (jsx("th", { ref: ref, className: cn("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
189979
+ TableHead.displayName = "TableHead";
189980
+ const TableCell$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("td", { ref: ref, className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
189981
+ TableCell$1.displayName = "TableCell";
189982
+ const TableCaption = React.forwardRef(({ className, ...props }, ref) => (jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
189983
+ TableCaption.displayName = "TableCaption";
189984
+
189985
+ /**
189986
+ * Single-select searchable picker: a deb-ounce-agnostic input + absolute
189987
+ * results dropdown (the add-access pattern), collapsing to a removable chip
189988
+ * once something is picked. The caller owns the search term and the fetched
189989
+ * options so any query hook can back it.
189990
+ */
189991
+ function SearchSelect({ labels, selected, onSelect, onClear, searchTerm, onSearchTermChange, options, isLoading, minChars = 2, testId, }) {
189992
+ const [isOpen, setIsOpen] = React__default.useState(false);
189993
+ const blurTimeout = React__default.useRef(null);
189994
+ React__default.useEffect(() => () => {
189995
+ if (blurTimeout.current)
189996
+ clearTimeout(blurTimeout.current);
189997
+ }, []);
189998
+ if (selected) {
189999
+ return (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.label }), jsxs("div", { className: "flex items-center justify-between gap-2 rounded-md border border-gray-200 bg-gray-50 px-3 py-2", "data-testid": `${testId}-selected`, children: [jsxs("div", { className: "min-w-0", children: [jsx("p", { className: "truncate text-sm font-medium text-gray-900", children: selected.label }), selected.sublabel && (jsx("p", { className: "truncate text-xs text-gray-600", children: selected.sublabel }))] }), jsxs(Button$1, { type: "button", size: "icon", variant: "ghost", className: "h-6 w-6 flex-shrink-0 text-gray-500 hover:text-red-600", onClick: onClear, "data-testid": `${testId}-clear`, children: [jsx(X$2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), jsx("span", { className: "sr-only", children: labels.clear })] })] })] }));
190000
+ }
190001
+ return (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", htmlFor: `${testId}-input`, children: labels.label }), jsxs("div", { className: "relative", children: [jsx(Input, { id: `${testId}-input`, className: "focus-visible:border-blue-500 focus-visible:ring-1 focus-visible:ring-blue-500", value: searchTerm, onChange: (e) => onSearchTermChange(e.target.value), onFocus: () => {
190002
+ if (blurTimeout.current)
190003
+ clearTimeout(blurTimeout.current);
190004
+ setIsOpen(true);
190005
+ }, onBlur: () => {
190006
+ blurTimeout.current = setTimeout(() => setIsOpen(false), 100);
190007
+ }, placeholder: labels.placeholder, autoComplete: "off", "aria-autocomplete": "list", "aria-expanded": isOpen, "data-testid": `${testId}-input` }), isOpen && (jsx("div", { className: "absolute top-full right-0 left-0 z-50 mt-1 max-h-64 overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg", "data-testid": `${testId}-results`, children: searchTerm.trim().length < minChars ? (jsx("div", { className: "px-3 py-2 text-sm text-gray-600", children: labels.typeToSearch })) : isLoading ? (jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-gray-600", children: [jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }), labels.searching] })) : options.length > 0 ? (options.map((option) => (jsxs("button", { type: "button", className: "flex w-full flex-col items-start gap-0.5 px-3 py-2 text-left hover:bg-gray-50", onMouseDown: (event) => event.preventDefault(), onClick: () => {
190008
+ onSelect(option);
190009
+ setIsOpen(false);
190010
+ }, "data-testid": `${testId}-option-${option.value}`, children: [jsx("span", { className: "text-sm font-medium text-gray-900", children: option.label }), option.sublabel && (jsx("span", { className: "text-xs text-gray-600", children: option.sublabel }))] }, option.value)))) : (jsx("div", { className: "px-3 py-2 text-sm text-gray-600", "data-testid": `${testId}-no-results`, children: labels.noResults })) }))] })] }));
190011
+ }
190012
+
190013
+ /**
190014
+ * The shared `Profile` viewer in a dialog — the same modal the tenant
190015
+ * Management tab opens when a user row is clicked. Controlled variant for
190016
+ * hosts that trigger it from a whole table row.
190017
+ */
190018
+ function UserProfileDialog({ tenantKey, username, isOpen, onClose }) {
190019
+ const t = useT();
190020
+ if (!isOpen)
190021
+ return null;
190022
+ return (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 })] }) }));
190023
+ }
190024
+ /**
190025
+ * Clickable user identity that opens the shared `Profile` viewer in a dialog
190026
+ * — the same modal the tenant Management tab opens when a user row is
190027
+ * clicked. Used by the per-user limits table and the user-limit modal.
190028
+ */
190029
+ function UserProfileLink({ tenantKey, username, display, className, testId, }) {
190030
+ const t = useT();
190031
+ const [isOpen, setIsOpen] = React__default.useState(false);
190032
+ return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
190033
+ event.stopPropagation();
190034
+ setIsOpen(true);
190035
+ }, 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) })] }));
190036
+ }
190037
+
190038
+ /**
190039
+ * Add/edit dialog for one per-user spend limit, mirroring the grader
190040
+ * criterion modal: create mode starts with a searchable platform-user picker
190041
+ * (no free-text usernames), edit mode goes straight to the form for the
190042
+ * cap's user. The form's Cancel/Save footer acts as the dialog footer.
190043
+ */
190044
+ function UserCapModal({ isOpen, onClose, cap, labels, tenantKey, isSaving, onSave }) {
190045
+ var _a, _b, _c, _d, _e;
190046
+ const isEditing = Boolean(cap);
190047
+ const [pickedUser, setPickedUser] = React__default.useState(null);
190048
+ const [userSearch, setUserSearch] = React__default.useState('');
190049
+ // Reset the picker each time the modal opens for a create.
190050
+ React__default.useEffect(() => {
190051
+ if (!isOpen)
190052
+ return;
190053
+ setPickedUser(null);
190054
+ setUserSearch('');
190055
+ }, [isOpen, cap]);
190056
+ const [debouncedUserSearch] = a$3(userSearch, 300);
190057
+ const shouldSearchUsers = Boolean(isOpen && !isEditing && tenantKey && debouncedUserSearch.trim().length >= 2);
190058
+ const { data: usersData, isFetching: isSearchingUsers } = usePlatformUsersQuery({
190059
+ page: 1,
190060
+ page_size: 20,
190061
+ platform_key: tenantKey,
190062
+ platform_org: tenantKey,
190063
+ query: debouncedUserSearch.trim(),
190064
+ return_policies: 'false',
190065
+ }, { skip: !shouldSearchUsers });
190066
+ // The platform users endpoint returns either `results` or `results.data`
190067
+ // depending on whether policies were requested.
190068
+ const rawResults = usersData === null || usersData === void 0 ? void 0 : usersData.results;
190069
+ const foundUsers = Array.isArray(rawResults)
190070
+ ? rawResults
190071
+ : ((_a = rawResults === null || rawResults === void 0 ? void 0 : rawResults.data) !== null && _a !== void 0 ? _a : []);
190072
+ // Deliberately email-only in the UI (no name/username noise); the cap API
190073
+ // is still keyed by username, which rides along as the option value.
190074
+ const userOptions = foundUsers
190075
+ .filter((user) => Boolean(user.username) && Boolean(user.email))
190076
+ .map((user) => ({
190077
+ value: user.username,
190078
+ label: user.email,
190079
+ }));
190080
+ const username = isEditing ? ((_b = cap === null || cap === void 0 ? void 0 : cap.username) !== null && _b !== void 0 ? _b : '') : ((_c = pickedUser === null || pickedUser === void 0 ? void 0 : pickedUser.value) !== null && _c !== void 0 ? _c : '');
190081
+ const handleSave = async (formData) => {
190082
+ if (!username)
190083
+ return;
190084
+ const saved = await onSave(username, formData);
190085
+ if (saved)
190086
+ onClose();
190087
+ };
190088
+ return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { "aria-describedby": "user-cap-modal-description", "data-testid": "user-cap-modal", className: "flex max-h-[85vh] flex-col sm:max-w-xl", children: [jsx(DialogHeader, { className: "flex-shrink-0", children: jsx(DialogTitle, { className: "ibl-dialog-title", children: isEditing ? labels.users.editTitle : labels.users.addTitle }) }), jsxs("div", { className: cn('min-h-0 flex-1 space-y-4 overflow-y-auto',
190089
+ // Reserve room for the search results dropdown while no user is
190090
+ // picked yet — otherwise the scroll container clips it.
190091
+ !isEditing && !pickedUser && 'min-h-[340px]'), id: "user-cap-modal-description", children: [isEditing ? (jsx("p", { className: "text-sm text-[#646464]", "data-testid": "user-cap-modal-username", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: (_d = cap === null || cap === void 0 ? void 0 : cap.username) !== null && _d !== void 0 ? _d : '', display: (cap === null || cap === void 0 ? void 0 : cap.email) || ((_e = cap === null || cap === void 0 ? void 0 : cap.username) !== null && _e !== void 0 ? _e : ''), className: "inline", testId: "user-cap-modal-profile-link" }) })) : (jsx(SearchSelect, { labels: labels.users.picker, selected: pickedUser, onSelect: setPickedUser, onClear: () => setPickedUser(null), searchTerm: userSearch, onSearchTermChange: setUserSearch, options: userOptions, isLoading: isSearchingUsers, testId: "user-cap-modal-user-picker" })), Boolean(username) && (jsx(SpendCapForm, { labels: labels, cap: cap !== null && cap !== void 0 ? cap : undefined, isSaving: isSaving, isDeleting: false, onSave: handleSave, onCancel: onClose, testId: "user-cap-modal" }))] })] }) }));
190092
+ }
190093
+
190094
+ /**
190095
+ * Per-user limits for one agent: a list of the explicit (user, agent) caps.
190096
+ * Add and edit both go through the `UserCapModal` popup (add picks the user
190097
+ * via a platform-user search — no free-text usernames); delete confirms in a
190098
+ * dialog.
190099
+ */
190100
+ function UserCapsSection({ labels, tenantKey, caps, isLoading, isSaving, isDeleting, onSave, onDelete, }) {
190101
+ var _a;
190102
+ const [isModalOpen, setIsModalOpen] = React__default.useState(false);
190103
+ const [editingCap, setEditingCap] = React__default.useState(null);
190104
+ // The cap queued for deletion — kept whole so the dialog can show the
190105
+ // user's email while the delete call is still keyed by username.
190106
+ const [pendingDelete, setPendingDelete] = React__default.useState(null);
190107
+ // Whole-row click opens the user's profile (Management-tab pattern).
190108
+ const [profileCap, setProfileCap] = React__default.useState(null);
190109
+ const openAdd = () => {
190110
+ setEditingCap(null);
190111
+ setIsModalOpen(true);
190112
+ };
190113
+ const openEdit = (cap) => {
190114
+ setEditingCap(cap);
190115
+ setIsModalOpen(true);
190116
+ };
190117
+ // Status toggle saves immediately (datasets-table pattern) — the PUT is an
190118
+ // upsert, so the cap's current fields ride along with the flipped flag.
190119
+ const toggleEnabled = (cap, checked) => {
190120
+ var _a;
190121
+ void onSave((_a = cap.username) !== null && _a !== void 0 ? _a : '', {
190122
+ interval_type: cap.interval_type,
190123
+ max_cost_usd: cap.max_cost_usd,
190124
+ enforcement: cap.enforcement,
190125
+ alert_thresholds: cap.alert_thresholds,
190126
+ enabled: checked,
190127
+ });
190128
+ };
190129
+ const handleConfirmDelete = async () => {
190130
+ var _a;
190131
+ if (!pendingDelete)
190132
+ return;
190133
+ const deleted = await onDelete((_a = pendingDelete.username) !== null && _a !== void 0 ? _a : '');
190134
+ if (deleted)
190135
+ setPendingDelete(null);
190136
+ };
190137
+ return (jsxs("div", { className: "space-y-3", "data-testid": "user-caps-section", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("div", { children: [jsx("h4", { className: "text-sm font-medium text-gray-900", children: labels.users.sectionTitle }), jsx("p", { className: "text-xs text-gray-600", children: labels.users.sectionDescription })] }), jsxs(Button$1, { className: "ibl-button-primary flex-shrink-0", onClick: openAdd, disabled: isSaving || isDeleting, "data-testid": "user-caps-add-button", children: [jsx(Plus, { className: "mr-1 h-4 w-4" }), labels.users.addButton] })] }), jsx("div", { className: "overflow-hidden rounded-md border", "data-testid": "user-caps-list", children: jsx("div", { className: "overflow-x-auto sm:mx-0", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex w-full items-center justify-center py-10", "data-testid": "user-caps-loading", children: jsx(Spinner, {}) })) : (jsxs(Table$1, { className: "min-w-full", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { className: "bg-muted/50 border-b", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.users.table.userColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.users.table.limitColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.users.table.statusColumn }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: caps.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 4, className: "p-4 text-center text-[#646464]", "data-testid": "user-caps-empty", children: labels.users.emptyState }) })) : (caps.map((cap) => {
190138
+ var _a;
190139
+ const username = (_a = cap.username) !== null && _a !== void 0 ? _a : '';
190140
+ // Email-first everywhere the user is shown or announced;
190141
+ // testids and mutation keys stay on the backend's
190142
+ // username key.
190143
+ const displayName = cap.email || username;
190144
+ return (jsxs(TableRow$1, { onClick: () => setProfileCap(cap), className: "cursor-pointer border-b transition-colors last:border-0 hover:bg-gray-50", "data-testid": `user-cap-row-${username}`, children: [jsx(TableCell$1, { className: "max-w-[240px] p-3 font-medium whitespace-nowrap text-[#646464]", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: username, display: displayName, testId: `user-cap-profile-link-${username}` }) }), jsxs(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: [formatUsd$1(cap.max_cost_usd), " /", ' ', labels.form.intervalOptions[cap.interval_type]] }), jsx(TableCell$1, { className: "p-3", onClick: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex items-center gap-1.5", children: [jsx(CustomSwitch, { checked: cap.enabled, onCheckedChange: (checked) => toggleEnabled(cap, checked), disabled: isSaving || isDeleting, "aria-label": labels.users.statusToggleAria(displayName), "data-testid": `user-cap-status-switch-${username}` }), cap.is_exceeded && (jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-[11px] font-medium text-red-700", children: labels.usage.exceededBadge })), cap.enforcement === 'alert_only' && (jsx("span", { className: "rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium text-amber-800", children: labels.usage.alertOnlyBadge }))] }) }), jsx(TableCell$1, { className: "p-3 text-right", onClick: (e) => e.stopPropagation(), children: jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { type: "button", variant: "ghost", size: "icon", "aria-label": labels.users.actionsAria(displayName), "data-testid": `user-cap-actions-${username}`, children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { onClick: () => openEdit(cap), "data-testid": `user-cap-edit-${username}`, children: [jsx(Pencil, { className: "mr-2 h-4 w-4" }), labels.users.edit] }), jsxs(DropdownMenuItem, { className: "text-red-600 focus:text-red-700", onClick: () => setPendingDelete(cap), "data-testid": `user-cap-delete-${username}`, children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), labels.users.delete] })] })] }) })] }, cap.id));
190145
+ })) })] })) }) }) }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: (_a = profileCap === null || profileCap === void 0 ? void 0 : profileCap.username) !== null && _a !== void 0 ? _a : '', isOpen: profileCap !== null, onClose: () => setProfileCap(null) }), jsx(UserCapModal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), cap: editingCap, labels: labels, tenantKey: tenantKey, isSaving: isSaving, onSave: onSave }), jsx(Dialog, { open: pendingDelete !== null, onOpenChange: () => setPendingDelete(null), children: jsxs(DialogContent, { "aria-describedby": "user-cap-delete-description", "data-testid": "user-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "user-cap-delete-description", children: jsxs("p", { className: "text-sm text-[#646464]", children: [jsx("span", { className: "font-bold italic", children: pendingDelete ? pendingDelete.email || pendingDelete.username : '' }), ' ', "\u2014 ", labels.deleteModal.description] }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setPendingDelete(null), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeleting, "data-testid": "user-cap-delete-confirm", children: isDeleting ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
190146
+ }
190147
+
190148
+ const AgentSettingsContext = React__default.createContext(null);
190149
+ function AgentSettingsProvider({ children, ...value }) {
190150
+ const ctx = React__default.useMemo(() => value,
190151
+ // eslint-disable-next-line react-hooks/exhaustive-deps
190152
+ [
190153
+ value.tenantKey,
190154
+ value.mentorId,
190155
+ value.username,
190156
+ value.enableRBAC,
190157
+ value.rbacPermissions,
190158
+ value.executeGatedAction,
190159
+ value.visibilityOptions,
190160
+ ]);
190161
+ return jsx(AgentSettingsContext.Provider, { value: ctx, children: children });
190162
+ }
190163
+ /**
190164
+ * Read the shared agent identity / config from the nearest
190165
+ * `<AgentSettingsProvider>` if one is present, returning `null` otherwise.
190166
+ *
190167
+ * Use this when a tab can also be driven by direct props (i.e. the
190168
+ * `<AgentSettingsProvider>` wrapper is optional). For tabs that strictly
190169
+ * require the provider, use `useAgentSettings`.
190170
+ */
190171
+ function useAgentSettingsOptional() {
190172
+ return React__default.useContext(AgentSettingsContext);
190173
+ }
190174
+ /**
190175
+ * Read the shared agent identity / config from the nearest
190176
+ * `<AgentSettingsProvider>`. Throws if called outside a provider — this is
190177
+ * intentional: every tab MUST be wrapped.
190178
+ */
190179
+ function useAgentSettings() {
190180
+ const ctx = React__default.useContext(AgentSettingsContext);
190181
+ if (!ctx) {
190182
+ throw new Error('useAgentSettings must be used within an <AgentSettingsProvider>. ' +
190183
+ 'Wrap your tab components with <AgentSettingsProvider tenantKey=... mentorId=... username=... enableRBAC=...>.');
190184
+ }
190185
+ return ctx;
190186
+ }
190187
+
190188
+ const SPEND_CAPS_SUB_TABS = {
190189
+ agent: 'agent',
190190
+ users: 'users',
190191
+ };
190192
+ function DeniedState({ labels }) {
190193
+ return (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-caps-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.description })] }));
190194
+ }
190195
+ /**
190196
+ * "Billing" tab of the agent settings modal, with two sub-tabs matching the
190197
+ * agent-level backend scopes: the agent's own spend limit and per-user limits
190198
+ * on this agent (user picked through a platform-user search). The
190199
+ * workspace-wide limit is managed in the tenant settings Billing tab instead
190200
+ * (`SpendLimitsSection` inside `BillingTab`). Reads/writes the RBAC-gated
190201
+ * admin endpoints; a 403 renders a friendly denied state per scope.
190202
+ */
190203
+ function AgentSpendCapsTab({ labels: labelsOverride, tenantKey: tenantKeyProp, mentorId: mentorIdProp, headerClassName, bodyClassName, }) {
190204
+ var _a, _b;
190205
+ const ctx = useAgentSettingsOptional();
190206
+ const tenantKey = (_a = tenantKeyProp !== null && tenantKeyProp !== void 0 ? tenantKeyProp : ctx === null || ctx === void 0 ? void 0 : ctx.tenantKey) !== null && _a !== void 0 ? _a : '';
190207
+ const mentorId = (_b = mentorIdProp !== null && mentorIdProp !== void 0 ? mentorIdProp : ctx === null || ctx === void 0 ? void 0 : ctx.mentorId) !== null && _b !== void 0 ? _b : '';
190208
+ const t = useT();
190209
+ const labels = React__default.useMemo(() => resolveSpendCapsTabLabels(labelsOverride, t), [labelsOverride, t]);
190210
+ const { agentCap, isAgentCapLoading, isAgentForbidden, saveAgentCap, removeAgentCap, isSavingAgentCap, isDeletingAgentCap, userCaps, isUserCapsLoading, isUsersForbidden, saveUserCap, removeUserCap, isSavingUserCap, isDeletingUserCap, } = useSpendCaps({ tenantKey, mentorId, labels });
190211
+ const [isDeleteOpen, setIsDeleteOpen] = React__default.useState(false);
190212
+ // Height lock: the two sub-tabs have different natural heights, which
190213
+ // would make a content-sized host (the tenant Agent Limits popup) jump on
190214
+ // every switch. Ratchet the tallest rendered height into a min-height so
190215
+ // switching never shrinks the tab — without padding it beyond its own
190216
+ // natural size.
190217
+ const [subTab, setSubTab] = React__default.useState(SPEND_CAPS_SUB_TABS.agent);
190218
+ const tabsRef = React__default.useRef(null);
190219
+ const [minTabsHeight, setMinTabsHeight] = React__default.useState(0);
190220
+ React__default.useLayoutEffect(() => {
190221
+ var _a, _b;
190222
+ const height = (_b = (_a = tabsRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0;
190223
+ setMinTabsHeight((current) => Math.max(current, height));
190224
+ }, [subTab]);
190225
+ const handleConfirmDelete = async () => {
190226
+ const deleted = await removeAgentCap();
190227
+ if (deleted)
190228
+ setIsDeleteOpen(false);
190229
+ };
190230
+ return (jsxs(Fragment$1, { children: [jsx("div", { className: cn('flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block', headerClassName), children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.header.description })] }) }), jsx("div", { className: cn('flex-1 space-y-4 p-3 lg:p-4', bodyClassName), style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "spend-caps-tab-body", children: jsxs(Tabs, { value: subTab, onValueChange: setSubTab, className: "w-full", ref: tabsRef, style: minTabsHeight ? { minHeight: minTabsHeight } : undefined, children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "spend-caps-sub-tabs", children: [jsx(TabsTrigger, { value: SPEND_CAPS_SUB_TABS.agent, "data-testid": "spend-caps-sub-tab-agent", children: labels.subTabs.agent }), jsx(TabsTrigger, { value: SPEND_CAPS_SUB_TABS.users, "data-testid": "spend-caps-sub-tab-users", children: labels.subTabs.users })] }), jsx(TabsContent, { value: SPEND_CAPS_SUB_TABS.agent, className: "space-y-4", children: isAgentCapLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "agent-cap-loading", children: jsx(Spinner, {}) })) : isAgentForbidden ? (jsx(DeniedState, { labels: labels.denied })) : (jsx(SpendCapForm, { labels: labels, cap: agentCap, isSaving: isSavingAgentCap, isDeleting: isDeletingAgentCap, onSave: saveAgentCap, onDelete: () => setIsDeleteOpen(true), testId: "agent-cap" })) }), jsx(TabsContent, { value: SPEND_CAPS_SUB_TABS.users, className: "space-y-4", children: isUsersForbidden ? (jsx(DeniedState, { labels: labels.denied })) : (jsx(UserCapsSection, { labels: labels, tenantKey: tenantKey, caps: userCaps, isLoading: isUserCapsLoading, isSaving: isSavingUserCap, isDeleting: isDeletingUserCap, onSave: saveUserCap, onDelete: removeUserCap })) })] }) }), jsx(Dialog, { open: isDeleteOpen, onOpenChange: () => setIsDeleteOpen(false), children: jsxs(DialogContent, { "aria-describedby": "spend-cap-delete-description", "data-testid": "spend-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "spend-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: isDeletingAgentCap, "data-testid": "spend-cap-delete-confirm", children: isDeletingAgentCap ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
190231
+ }
190232
+
190233
+ function errorStatus$1(error) {
190234
+ return error === null || error === void 0 ? void 0 : error.status;
190235
+ }
190236
+ /**
190237
+ * Tenant-settings Billing "Agent Limits" tab: every configured agent spend
190238
+ * cap in the workspace from the tenant-wide list endpoint (no per-agent
190239
+ * fan-out), with an agent autocomplete filter, a direct status toggle per
190240
+ * row (datasets-table pattern) and a manage action that opens the agent's
190241
+ * full Billing editor (`AgentSpendCapsTab` — agent limit + per-user limits)
190242
+ * in a popup. Rendered inside `BillingTab`.
190243
+ */
190244
+ function AgentLimitsSection({ tenantKey, username, labels: labelsOverride, }) {
190245
+ var _a;
190246
+ const t = useT();
190247
+ const labels = React__default.useMemo(() => resolveSpendCapsTabLabels(labelsOverride, t), [labelsOverride, t]);
190248
+ const [upsertAgentCap, { isLoading: isSavingCap }] = useUpsertAgentSpendCapMutation();
190249
+ // Agent autocomplete filter (also the entry point for setting a limit on
190250
+ // an agent that has none yet).
190251
+ const [agentFilter, setAgentFilter] = React__default.useState(null);
190252
+ const [agentSearch, setAgentSearch] = React__default.useState('');
190253
+ const [debouncedAgentSearch] = a$3(agentSearch, 300);
190254
+ const shouldSearchAgents = Boolean(tenantKey && debouncedAgentSearch.trim().length >= 2);
190255
+ const { data: mentorsResponse, isFetching: isSearchingAgents } = useGetMentorsQuery({ org: tenantKey, username, limit: 20, query: debouncedAgentSearch.trim() }, { skip: !shouldSearchAgents });
190256
+ const agentOptions = ((_a = mentorsResponse === null || mentorsResponse === void 0 ? void 0 : mentorsResponse.results) !== null && _a !== void 0 ? _a : [])
190257
+ .filter((mentor) => Boolean(mentor.unique_id))
190258
+ .map((mentor) => ({
190259
+ value: mentor.unique_id,
190260
+ label: mentor.name || mentor.unique_id,
190261
+ }));
190262
+ // The endpoint supports filtering by mentor server-side — pass it through
190263
+ // instead of fetching everything and filtering client-side.
190264
+ const { data: visibleCaps = [], error: capsError, isLoading, isFetching, } = useListAgentSpendCapsQuery({ org: tenantKey, ...(agentFilter ? { mentor: agentFilter.value } : {}) }, { skip: !tenantKey });
190265
+ const isForbidden = errorStatus$1(capsError) === 403;
190266
+ const [selectedAgent, setSelectedAgent] = React__default.useState(null);
190267
+ // Status toggle saves immediately (datasets-table pattern) — the PUT is an
190268
+ // upsert, so the cap's current fields ride along with the flipped flag.
190269
+ const toggleEnabled = async (cap, checked) => {
190270
+ var _a;
190271
+ try {
190272
+ await upsertAgentCap({
190273
+ org: tenantKey,
190274
+ mentorId: (_a = cap.mentor_unique_id) !== null && _a !== void 0 ? _a : '',
190275
+ formData: {
190276
+ interval_type: cap.interval_type,
190277
+ max_cost_usd: cap.max_cost_usd,
190278
+ enforcement: cap.enforcement,
190279
+ alert_thresholds: cap.alert_thresholds,
190280
+ enabled: checked,
190281
+ },
190282
+ }).unwrap();
190283
+ toast.success(labels.toasts.saved);
190284
+ }
190285
+ catch (error) {
190286
+ console.error('[AgentLimitsSection] toggle spend limit error', error);
190287
+ toast.error(labels.toasts.saveError);
190288
+ }
190289
+ };
190290
+ return (jsxs(Fragment$1, { children: [jsxs("div", { "data-testid": "spend-limits-agents-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.agents.title }), jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: labels.tenantSections.agents.description })] }) }), jsx("div", { className: "space-y-3 border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: isForbidden ? (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": "agent-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(Fragment$1, { children: [jsx("div", { className: "max-w-sm", children: jsx(SearchSelect, { labels: labels.tenantSections.agents.filter, selected: agentFilter, onSelect: setAgentFilter, onClear: () => setAgentFilter(null), searchTerm: agentSearch, onSearchTermChange: setAgentSearch, options: agentOptions, isLoading: isSearchingAgents, testId: "agent-limits-filter" }) }), jsx("div", { className: "overflow-hidden rounded-md border", "data-testid": "agent-limits-list", children: jsx("div", { className: "overflow-x-auto sm:mx-0", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading || isFetching ? (jsx("div", { className: "flex w-full items-center justify-center py-10", "data-testid": "agent-limits-loading", children: jsx(Spinner, {}) })) : (jsxs(Table$1, { className: "min-w-full", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { className: "bg-muted/50 border-b", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.agentColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.limitColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.spentColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.remainingColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.statusColumn }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: visibleCaps.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 6, className: "p-4 text-center text-[#646464]", "data-testid": "agent-limits-empty", children: jsxs("div", { className: "space-y-2", children: [jsx("p", { children: agentFilter
190291
+ ? labels.tenantSections.agents.emptyFiltered
190292
+ : labels.tenantSections.agents.emptyState }), agentFilter && (jsx(Button$1, { className: "ibl-button-primary", onClick: () => setSelectedAgent({
190293
+ uniqueId: agentFilter.value,
190294
+ name: agentFilter.label,
190295
+ }), "data-testid": "agent-limits-set-limit-button", children: labels.tenantSections.agents.setLimitButton }))] }) }) })) : (visibleCaps.map((cap) => {
190296
+ var _a;
190297
+ const uniqueId = (_a = cap.mentor_unique_id) !== null && _a !== void 0 ? _a : '';
190298
+ const name = cap.mentor_name || uniqueId;
190299
+ return (jsxs(TableRow$1, { className: "border-b last:border-0", "data-testid": `agent-limits-row-${uniqueId}`, children: [jsx(TableCell$1, { className: "max-w-[240px] truncate p-3 font-medium whitespace-nowrap text-[#646464]", children: name }), jsxs(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: [formatUsd$1(cap.max_cost_usd), " /", ' ', labels.form.intervalOptions[cap.interval_type]] }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatUsd$1(cap.current_spend_usd) }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatUsd$1(cap.remaining_usd) }), jsx(TableCell$1, { className: "p-3", children: jsxs("div", { className: "flex items-center gap-1.5", children: [jsx(CustomSwitch, { checked: cap.enabled, onCheckedChange: (checked) => toggleEnabled(cap, checked), disabled: isSavingCap, "aria-label": labels.tenantSections.agents.statusToggleAria(name), "data-testid": `agent-limits-status-switch-${uniqueId}` }), cap.is_exceeded && (jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-[11px] font-medium text-red-700", children: labels.usage.exceededBadge })), cap.enforcement === 'alert_only' && (jsx("span", { className: "rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium text-amber-800", children: labels.usage.alertOnlyBadge }))] }) }), jsx(TableCell$1, { className: "p-3 text-right", children: jsx(Button$1, { type: "button", variant: "ghost", size: "icon", "aria-label": labels.tenantSections.agents.manageAria(name), onClick: () => setSelectedAgent({ uniqueId, name }), "data-testid": `agent-limits-manage-${uniqueId}`, children: jsx(SlidersHorizontal, { className: "h-4 w-4" }) }) })] }, cap.id));
190300
+ })) })] })) }) }) })] })) })] }), jsx(Dialog, { open: selectedAgent !== null, onOpenChange: () => setSelectedAgent(null), children: jsxs(DialogContent, { "aria-describedby": undefined, "data-testid": "agent-limits-modal",
190301
+ // Content-driven height (capped at 90vh) — the tab itself locks its
190302
+ // sub-tabs to a shared height so switching doesn't shift the dialog.
190303
+ className: "flex max-h-[90vh] w-[calc(100vw-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl", children: [jsx(DialogHeader, { className: "flex-shrink-0 border-b border-gray-200 px-6 py-4 pr-12", children: jsx(DialogTitle, { className: "ibl-dialog-title", children: selectedAgent === null || selectedAgent === void 0 ? void 0 : selectedAgent.name }) }), jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: selectedAgent && (jsx(AgentSpendCapsTab, { tenantKey: tenantKey, mentorId: selectedAgent.uniqueId, headerClassName: "px-6", bodyClassName: "px-6 pb-6 lg:px-8" })) })] }) })] }));
190304
+ }
190305
+
190306
+ /**
190307
+ * Credit-balance → USD conversion for the Plan & Credits tab. The rate is
190308
+ * never hardcoded — it differs per environment — so everything here derives
190309
+ * from whichever fields the `/api/billing/account/` response actually
190310
+ * carries, and returns null when it carries none (the UI then shows credits
190311
+ * only).
190312
+ */
190313
+ function toFiniteNumber(value) {
190314
+ if (value === null || value === undefined || value === '')
190315
+ return null;
190316
+ const num = typeof value === 'string' ? parseFloat(value) : value;
190317
+ return Number.isFinite(num) ? num : null;
190318
+ }
190319
+ /**
190320
+ * The environment's credits-per-USD rate read off the transaction history:
190321
+ * an `add` transaction records both the credits granted and the dollars paid
190322
+ * for them (`/api/billing/transactions/`), so their ratio IS the rate. Uses
190323
+ * the newest non-failed top-up (rows come most-recent first). Null when no
190324
+ * usable top-up exists yet.
190325
+ */
190326
+ function resolveCreditsPerUsdFromTransactions(transactions) {
190327
+ for (const tx of transactions !== null && transactions !== void 0 ? transactions : []) {
190328
+ if (tx.transaction_type !== 'add' || tx.status === 'failed')
190329
+ continue;
190330
+ const usd = toFiniteNumber(tx.payment_amount_usd);
190331
+ const credits = toFiniteNumber(tx.credits_amount);
190332
+ if (usd !== null && usd > 0 && credits !== null && credits > 0)
190333
+ return credits / usd;
190334
+ }
190335
+ return null;
190336
+ }
190337
+ /**
190338
+ * The environment's credits-per-USD rate, from whichever field the backend
190339
+ * provides: `credits_per_usd` directly, `usd_per_credit` inverted, or — when
190340
+ * only the two balance figures exist — their ratio. Null when none resolve.
190341
+ */
190342
+ function resolveCreditsPerUsd(billing) {
190343
+ const direct = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.credits_per_usd);
190344
+ if (direct !== null && direct > 0)
190345
+ return direct;
190346
+ const usdPerCredit = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.usd_per_credit);
190347
+ if (usdPerCredit !== null && usdPerCredit > 0)
190348
+ return 1 / usdPerCredit;
190349
+ const credits = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits);
190350
+ const usd = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits_usd);
190351
+ if (credits !== null && usd !== null && credits > 0 && usd > 0)
190352
+ return credits / usd;
190353
+ return null;
190354
+ }
190355
+ /**
190356
+ * Dollar value of the available credit balance: the backend's explicit
190357
+ * `available_credits_usd` when present, otherwise the balance divided by the
190358
+ * resolved rate. Null when the response supports neither.
190359
+ */
190360
+ function resolveAvailableCreditsUsd(billing) {
190361
+ const explicit = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits_usd);
190362
+ if (explicit !== null)
190363
+ return explicit;
190364
+ const rate = resolveCreditsPerUsd(billing);
190365
+ const credits = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits);
190366
+ if (rate !== null && credits !== null)
190367
+ return credits / rate;
190368
+ return null;
190369
+ }
190370
+ /**
190371
+ * Format a credits-per-USD rate for the "X credits = $1" hint: whole rates
190372
+ * stay whole ("1,000"), fractional ones keep two decimals.
190373
+ */
190374
+ function formatCreditsPerUsd(rate) {
190375
+ const rounded = Math.round(rate);
190376
+ const isWhole = Math.abs(rate - rounded) < 0.005;
190377
+ return isWhole
190378
+ ? rounded.toLocaleString('en-US')
190379
+ : rate.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
190380
+ }
190381
+
188326
190382
  function formatCredits(credits) {
188327
190383
  if (credits === null || credits === undefined || Number.isNaN(credits))
188328
190384
  return '0';
@@ -188357,17 +190413,17 @@ function formatUsd(value) {
188357
190413
  return '—';
188358
190414
  return `$${num.toFixed(2)}`;
188359
190415
  }
188360
- function Section({ title, description, action, children, testId }) {
188361
- return (jsxs("div", { "data-testid": testId, 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: [jsxs("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: title }), description && (jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: description }))] }), action && jsx("div", { className: "shrink-0", children: action })] }), children && (jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: children }))] }));
190416
+ function Section({ title, description, action, children, testId, className }) {
190417
+ return (jsxs("div", { "data-testid": testId, className: cn('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', className), children: [jsxs("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: title }), description && (jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: description }))] }), action && jsx("div", { className: "shrink-0", children: action })] }), children && (jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: children }))] }));
188362
190418
  }
188363
- function Stat({ label, value, hint }) {
188364
- return (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: label }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", children: value }), hint && (jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: hint }))] }));
190419
+ function Stat({ label, value, hint, info }) {
190420
+ return (jsxs("div", { className: "space-y-0.5", children: [jsxs("div", { className: "flex items-center gap-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: label }), info && jsx(CardInfoIcon, { description: info, className: "-my-1" })] }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", children: value }), hint && (jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: hint }))] }));
188365
190421
  }
188366
190422
  function SectionSkeleton({ withBody = false }) {
188367
190423
  return (jsxs("div", { 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: [jsxs("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: [jsxs("div", { className: "space-y-2", children: [jsx(Skeleton, { className: "h-5 w-32" }), jsx(Skeleton, { className: "h-4 w-56" })] }), jsx(Skeleton, { className: "h-9 w-32" })] }), withBody && (jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [0, 1, 2].map((i) => (jsxs("div", { className: "space-y-2", children: [jsx(Skeleton, { className: "h-3 w-20" }), jsx(Skeleton, { className: "h-6 w-24" })] }, i))) }) }))] }));
188368
190424
  }
188369
190425
  function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, currentUserEmail, redirectUrl, }) {
188370
- var _a;
190426
+ var _a, _b, _c;
188371
190427
  const t = useT();
188372
190428
  const [isAutoRechargeModalOpen, setIsAutoRechargeModalOpen] = useState(false);
188373
190429
  const [isAddCreditsModalOpen, setIsAddCreditsModalOpen] = useState(false);
@@ -188379,6 +190435,10 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188379
190435
  currentUserEmail,
188380
190436
  });
188381
190437
  const { data: billingInfo, isLoading: isBillingInfoLoading } = useGetAccountBillingInfoQuery({ platform_key: tenant }, { refetchOnMountOrArgChange: true });
190438
+ // Top-up rows carry both the credits granted and the dollars paid — the
190439
+ // pair that reveals this environment's credits-per-USD rate for the
190440
+ // dollar-equivalent balance display.
190441
+ const { data: addTransactions } = useGetCreditTransactionsQuery({ platform_key: tenant, transaction_type: 'add' }, { skip: !tenant });
188382
190442
  const [createStripePortal] = useCreateStripeCustomerPortalMutation();
188383
190443
  const handleBilling = async ({ extraRequestBody = {}, }) => {
188384
190444
  return await createStripePortal({
@@ -188414,7 +190474,7 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188414
190474
  }
188415
190475
  };
188416
190476
  if (isBillingInfoLoading) {
188417
- return (jsx("div", { className: "container max-w-3xl !mt-0", children: jsxs("div", { className: "space-y-4", children: [jsx(SectionSkeleton, {}), jsx(SectionSkeleton, { withBody: true }), jsx(SectionSkeleton, { withBody: true })] }) }));
190477
+ return (jsx("div", { className: "container mx-auto max-w-7xl !mt-0", children: jsxs("div", { className: "space-y-4", children: [jsx(SectionSkeleton, {}), jsx(SectionSkeleton, { withBody: true }), jsx(SectionSkeleton, { withBody: true })] }) }));
188418
190478
  }
188419
190479
  const billing = billingInfo;
188420
190480
  const isFreePlan = formatPlanLabel(billing === null || billing === void 0 ? void 0 : billing.current_plan) === 'Free' ||
@@ -188427,6 +190487,27 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188427
190487
  : 'Premium';
188428
190488
  const availableCredits = (_a = Number(billing === null || billing === void 0 ? void 0 : billing.available_credits)) !== null && _a !== void 0 ? _a : 0;
188429
190489
  const consumedCredits = (billing === null || billing === void 0 ? void 0 : billing.consumed_credits) != null ? parseInt(billing.consumed_credits, 10) : null;
190490
+ // Recharging is priced in dollars, so the balance shows its dollar value
190491
+ // too — but only when the API provides the data for it (the rate differs
190492
+ // per environment and is never hardcoded). Preference order: explicit
190493
+ // account fields, then the credits/dollars pair on the newest top-up
190494
+ // transaction.
190495
+ const creditsPerUsd = (_b = resolveCreditsPerUsd(billing)) !== null && _b !== void 0 ? _b : resolveCreditsPerUsdFromTransactions(addTransactions);
190496
+ const availableCreditsUsd = (_c = resolveAvailableCreditsUsd(billing)) !== null && _c !== void 0 ? _c : (creditsPerUsd != null && Number.isFinite(availableCredits)
190497
+ ? availableCredits / creditsPerUsd
190498
+ : null);
190499
+ const availableCreditsHint = availableCreditsUsd != null
190500
+ ? [
190501
+ t('profileBilling.creditsUsdEquivalent', { amount: formatUsd(availableCreditsUsd) }),
190502
+ creditsPerUsd != null
190503
+ ? t('profileBilling.creditsConversionHint', {
190504
+ credits: formatCreditsPerUsd(creditsPerUsd),
190505
+ })
190506
+ : null,
190507
+ ]
190508
+ .filter(Boolean)
190509
+ .join(' · ')
190510
+ : undefined;
188430
190511
  const hasPaymentMethod = Boolean(billing === null || billing === void 0 ? void 0 : billing.has_payment_method);
188431
190512
  const isAutoRechargeEnabled = Boolean(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_enabled);
188432
190513
  const spendingLimitRaw = billing === null || billing === void 0 ? void 0 : billing.auto_recharge_spending_limit_usd;
@@ -188450,21 +190531,21 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188450
190531
  }
188451
190532
  return t('profileBilling.manageSubscriptionAndBilling');
188452
190533
  })();
188453
- return (jsxs("div", { className: "container mx-auto max-w-3xl !mt-0", children: [jsxs("div", { className: "space-y-4", children: [jsx(Section, { title: t('profileBilling.planSectionTitle'), testId: "billing-plan-section", description: planDescription, action: isFreePlan || isTrialPlan ? (jsxs(Button$1, { onClick: () => handleUpgrade('premium'), disabled: isUpgrading, className: "h-9 px-4 text-[14px] font-medium text-white bg-gradient-to-r from-[rgb(56,161,229)] to-[rgb(114,132,255)] hover:opacity-90 disabled:opacity-70", children: [isUpgrading ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Sparkles, { className: "h-4 w-4" })), isUpgrading ? t('profileBilling.redirecting') : t('profileBilling.upgrade')] })) : undefined, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { "data-testid": "billing-plan-label", className: "text-[18px] font-semibold leading-7 text-[#101828] dark:text-gray-100", children: planLabel }), jsx("span", { className: "rounded-full px-2.5 py-0.5 text-[12px] font-medium", style: {
188454
- backgroundColor: '#f5f8ff',
188455
- color: '#155dfc',
188456
- border: '1px solid #d0e0ff',
188457
- }, children: t('profileBilling.currentBadge') })] }) }), jsx(Section, { title: t('profileBilling.creditsSectionTitle'), testId: "billing-credits-section", description: t('profileBilling.creditsDescription'), action: isFreePlan || isTrialPlan ? undefined : hasPaymentMethod ? (jsx(Button$1, { variant: "outline", onClick: () => setIsAddCreditsModalOpen(true), className: "h-9 px-4 text-[14px] font-medium", children: t('profileBilling.addCredits') })) : (jsxs(Button$1, { onClick: handleSetupPaymentMethod, disabled: isRedirectingToPaymentSettings, className: "h-9 px-4 text-[14px] font-medium bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: [isRedirectingToPaymentSettings ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Wallet, { className: "h-4 w-4" })), isRedirectingToPaymentSettings
188458
- ? t('profileBilling.redirecting')
188459
- : t('profileBilling.manageBilling')] })), children: jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [jsx(Stat, { label: t('profileBilling.availableLabel'), value: t('profileBilling.creditsUnit', { amount: formatCredits(availableCredits) }), hint: t('profileBilling.creditsRemaining') }), consumedCredits != null && (jsx(Stat, { label: t('profileBilling.consumedLabel'), value: t('profileBilling.creditsUnit', { amount: formatCredits(consumedCredits) }), hint: t('profileBilling.usedThisPeriod') })), (billing === null || billing === void 0 ? void 0 : billing.credits_reset_date) && (jsx(Stat, { label: t('profileBilling.resetsOnLabel'), value: formatRenewalDate(billing.credits_reset_date), hint: t('profileBilling.nextResetDate') }))] }) }), !isFreePlan && !isTrialPlan && hasPaymentMethod && (jsx(Section, { title: t('profileBilling.autoRechargeSectionTitle'), testId: "billing-auto-recharge-section", description: t('profileBilling.autoRechargeDescription'), action: jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { "data-testid": "billing-auto-recharge-status", className: "rounded-full px-2.5 py-0.5 text-[12px] font-medium", style: {
188460
- backgroundColor: isAutoRechargeEnabled ? '#f5f8ff' : '#f3f4f6',
188461
- color: isAutoRechargeEnabled ? '#155dfc' : '#6a7282',
188462
- border: `1px solid ${isAutoRechargeEnabled ? '#d0e0ff' : '#e5e7eb'}`,
188463
- }, children: isAutoRechargeEnabled
188464
- ? t('profileBilling.enabled')
188465
- : t('profileBilling.disabled') }), jsx(Button$1, { variant: "outline", onClick: () => setIsAutoRechargeModalOpen(true), className: "h-9 px-4 text-[14px] font-medium", children: t('profileBilling.manageUsage') })] }), children: isAutoRechargeEnabled ? (jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [jsx(Stat, { label: t('profileBilling.thresholdLabel'), value: formatUsd(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_threshold_usd) }), jsx(Stat, { label: t('profileBilling.rechargeAmountLabel'), value: formatUsd(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_amount_usd) }), jsx(Stat, { label: t('profileBilling.spendingLimitLabel'), value: isUnlimitedSpendingLimit
188466
- ? t('profileBilling.unlimited')
188467
- : formatUsd(spendingLimitRaw) })] })) : (jsx("p", { className: "text-[14px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: t('profileBilling.autoRechargeDisabledMessage') })) }))] }), jsx(AutoRechargeModal, { tenant: tenant, isOpen: isAutoRechargeModalOpen, onClose: () => setIsAutoRechargeModalOpen(false) }), jsx(AddCreditsModal, { tenant: tenant, isOpen: isAddCreditsModalOpen, onClose: () => setIsAddCreditsModalOpen(false) })] }));
190534
+ return (jsxs("div", { className: "container mx-auto max-w-7xl !mt-0", children: [jsxs(Tabs, { defaultValue: "plan", "aria-label": t('profileBilling.billingTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileBilling.billingTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [jsx(TabsTrigger, { value: "plan", children: t('profileBilling.tabPlanAndCredits') }), jsx(TabsTrigger, { value: "spend-limits", children: t('profileBilling.tabSpendLimits') }), jsx(TabsTrigger, { value: "agent-limits", children: t('profileBilling.tabAgentLimits') })] }), jsx(TabsContent, { className: "mt-4", value: "plan", children: jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "grid grid-cols-1 items-start gap-4 lg:grid-cols-3", children: [jsx(Section, { title: t('profileBilling.planSectionTitle'), testId: "billing-plan-section", description: planDescription, className: "lg:col-span-1 lg:h-full", action: isFreePlan || isTrialPlan ? (jsxs(Button$1, { onClick: () => handleUpgrade('premium'), disabled: isUpgrading, className: "h-9 px-4 text-[14px] font-medium text-white bg-gradient-to-r from-[rgb(56,161,229)] to-[rgb(114,132,255)] hover:opacity-90 disabled:opacity-70", children: [isUpgrading ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Sparkles, { className: "h-4 w-4" })), isUpgrading ? t('profileBilling.redirecting') : t('profileBilling.upgrade')] })) : undefined, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { "data-testid": "billing-plan-label", className: "text-[18px] font-semibold leading-7 text-[#101828] dark:text-gray-100", children: planLabel }), jsx("span", { className: "rounded-full px-2.5 py-0.5 text-[12px] font-medium", style: {
190535
+ backgroundColor: '#f5f8ff',
190536
+ color: '#155dfc',
190537
+ border: '1px solid #d0e0ff',
190538
+ }, children: t('profileBilling.currentBadge') })] }) }), jsx(Section, { title: t('profileBilling.creditsSectionTitle'), testId: "billing-credits-section", description: t('profileBilling.creditsDescription'), className: "lg:col-span-2 lg:h-full", action: isFreePlan || isTrialPlan ? undefined : hasPaymentMethod ? (jsx(Button$1, { variant: "outline", onClick: () => setIsAddCreditsModalOpen(true), className: "h-9 px-4 text-[14px] font-medium", children: t('profileBilling.addCredits') })) : (jsxs(Button$1, { onClick: handleSetupPaymentMethod, disabled: isRedirectingToPaymentSettings, className: "h-9 px-4 text-[14px] font-medium bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: [isRedirectingToPaymentSettings ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Wallet, { className: "h-4 w-4" })), isRedirectingToPaymentSettings
190539
+ ? t('profileBilling.redirecting')
190540
+ : t('profileBilling.manageBilling')] })), children: jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [jsx(Stat, { label: t('profileBilling.availableLabel'), value: t('profileBilling.creditsUnit', { amount: formatCredits(availableCredits) }), hint: availableCreditsHint }), consumedCredits != null && (jsx(Stat, { label: t('profileBilling.consumedLabel'), value: t('profileBilling.creditsUnit', { amount: formatCredits(consumedCredits) }), info: t('profileBilling.usedThisPeriod') })), (billing === null || billing === void 0 ? void 0 : billing.credits_reset_date) && (jsx(Stat, { label: t('profileBilling.resetsOnLabel'), value: formatRenewalDate(billing.credits_reset_date), info: t('profileBilling.nextResetDate') }))] }) })] }), !isFreePlan && !isTrialPlan && hasPaymentMethod && (jsx(Section, { title: t('profileBilling.autoRechargeSectionTitle'), testId: "billing-auto-recharge-section", description: t('profileBilling.autoRechargeDescription'), action: jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { "data-testid": "billing-auto-recharge-status", className: "rounded-full px-2.5 py-0.5 text-[12px] font-medium", style: {
190541
+ backgroundColor: isAutoRechargeEnabled ? '#f5f8ff' : '#f3f4f6',
190542
+ color: isAutoRechargeEnabled ? '#155dfc' : '#6a7282',
190543
+ border: `1px solid ${isAutoRechargeEnabled ? '#d0e0ff' : '#e5e7eb'}`,
190544
+ }, children: isAutoRechargeEnabled
190545
+ ? t('profileBilling.enabled')
190546
+ : t('profileBilling.disabled') }), jsx(Button$1, { variant: "outline", onClick: () => setIsAutoRechargeModalOpen(true), className: "h-9 px-4 text-[14px] font-medium", children: t('profileBilling.manageUsage') })] }), children: isAutoRechargeEnabled ? (jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [jsx(Stat, { label: t('profileBilling.thresholdLabel'), value: formatUsd(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_threshold_usd), info: t('profileBilling.thresholdHint') }), jsx(Stat, { label: t('profileBilling.rechargeAmountLabel'), value: formatUsd(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_amount_usd), info: t('profileBilling.rechargeAmountHint') }), jsx(Stat, { label: t('profileBilling.spendingLimitLabel'), value: isUnlimitedSpendingLimit
190547
+ ? t('profileBilling.unlimited')
190548
+ : formatUsd(spendingLimitRaw), info: t('profileBilling.spendingLimitHint') })] })) : (jsx("p", { className: "text-[14px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: t('profileBilling.autoRechargeDisabledMessage') })) }))] }) }), jsx(TabsContent, { className: "mt-4", value: "spend-limits", children: jsx(SpendLimitsSection, { tenantKey: tenant }) }), jsx(TabsContent, { className: "mt-4", value: "agent-limits", children: jsx(AgentLimitsSection, { tenantKey: tenant, username: username }) })] }), jsx(AutoRechargeModal, { tenant: tenant, isOpen: isAutoRechargeModalOpen, onClose: () => setIsAutoRechargeModalOpen(false) }), jsx(AddCreditsModal, { tenant: tenant, isOpen: isAddCreditsModalOpen, onClose: () => setIsAddCreditsModalOpen(false) })] }));
188468
190549
  }
188469
190550
 
188470
190551
  const SmtpContent = ({ platformKey }) => {
@@ -188719,23 +190800,6 @@ const PlatformMembershipContent = ({ platformKey, authURL, }) => {
188719
190800
  return (jsxs("div", { className: "flex items-center justify-between rounded-lg border px-6 py-6", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: t('advancedPlatformMembership.publicRegistrationLabel') }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('advancedPlatformMembership.infoAriaLabel'), className: "hidden sm:block", children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium whitespace-nowrap text-white shadow-sm transition-opacity duration-300 z-50", children: jsx("p", { children: t('advancedPlatformMembership.infoTooltip') }) })] }) }), (membershipData === null || membershipData === void 0 ? void 0 : membershipData.allow_self_linking) && (jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { onClick: handleCopyToClipboard, "aria-label": t('advancedPlatformMembership.copyAriaLabel'), className: "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 p-1 rounded transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1", children: jsx(Copy, { className: "h-4 w-4 text-gray-400 hover:text-blue-500" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium whitespace-nowrap text-white shadow-sm transition-opacity duration-300 z-50", children: jsx("p", { children: t('advancedPlatformMembership.copyTooltip') }) })] }) }))] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Switch, { checked: membershipData === null || membershipData === void 0 ? void 0 : membershipData.allow_self_linking, onCheckedChange: handleToggleMembership, disabled: isUpdating, "aria-label": `${t('advancedPlatformMembership.switchAriaLabel')} ${(membershipData === null || membershipData === void 0 ? void 0 : membershipData.allow_self_linking) ? t('advancedPlatformMembership.switchEnabled') : t('advancedPlatformMembership.switchDisabled')}`, className: "cursor-pointer data-[state=checked]:bg-blue-500" }), isUpdating && (jsx("div", { className: "w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" }))] })] }));
188720
190801
  };
188721
190802
 
188722
- const Table$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { className: "relative w-full overflow-auto", children: jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props }) })));
188723
- Table$1.displayName = "Table";
188724
- const TableHeader$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("thead", { ref: ref, className: cn("[&_tr]:border-b", className), ...props })));
188725
- TableHeader$1.displayName = "TableHeader";
188726
- const TableBody = React.forwardRef(({ className, ...props }, ref) => (jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
188727
- TableBody.displayName = "TableBody";
188728
- const TableFooter = React.forwardRef(({ className, ...props }, ref) => (jsx("tfoot", { ref: ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })));
188729
- TableFooter.displayName = "TableFooter";
188730
- const TableRow$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("tr", { ref: ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props })));
188731
- TableRow$1.displayName = "TableRow";
188732
- const TableHead = React.forwardRef(({ className, ...props }, ref) => (jsx("th", { ref: ref, className: cn("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
188733
- TableHead.displayName = "TableHead";
188734
- const TableCell$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("td", { ref: ref, className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
188735
- TableCell$1.displayName = "TableCell";
188736
- const TableCaption = React.forwardRef(({ className, ...props }, ref) => (jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
188737
- TableCaption.displayName = "TableCaption";
188738
-
188739
190803
  const normalizeCustomDomains = (response) => {
188740
190804
  if (!response) {
188741
190805
  return [];
@@ -204671,15 +206735,15 @@ var mac$2 = typeof navigator != "undefined" && /Mac/.test(navigator.platform);
204671
206735
  var ie$1 = typeof navigator != "undefined" && /MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
204672
206736
 
204673
206737
  // Fill in the digit keys
204674
- for (var i$9 = 0; i$9 < 10; i$9++) base[48 + i$9] = base[96 + i$9] = String(i$9);
206738
+ for (var i$8 = 0; i$8 < 10; i$8++) base[48 + i$8] = base[96 + i$8] = String(i$8);
204675
206739
 
204676
206740
  // The function keys
204677
- for (var i$9 = 1; i$9 <= 24; i$9++) base[i$9 + 111] = "F" + i$9;
206741
+ for (var i$8 = 1; i$8 <= 24; i$8++) base[i$8 + 111] = "F" + i$8;
204678
206742
 
204679
206743
  // And the alphabetic keys
204680
- for (var i$9 = 65; i$9 <= 90; i$9++) {
204681
- base[i$9] = String.fromCharCode(i$9 + 32);
204682
- shift[i$9] = String.fromCharCode(i$9);
206744
+ for (var i$8 = 65; i$8 <= 90; i$8++) {
206745
+ base[i$8] = String.fromCharCode(i$8 + 32);
206746
+ shift[i$8] = String.fromCharCode(i$8);
204683
206747
  }
204684
206748
 
204685
206749
  // For each code that doesn't have a shift-equivalent, copy the base name
@@ -218729,8 +220793,6 @@ function useAgentSearch({ tenantKey, query, limit = 6, offset, starred, featured
218729
220793
  };
218730
220794
  }
218731
220795
 
218732
- function c$3(e,u,c){var i=this,a=useRef(null),o=useRef(0),f=useRef(null),l=useRef([]),v=useRef(),m=useRef(),d=useRef(e),g=useRef(true);d.current=e;var p="undefined"!=typeof window,w=!u&&0!==u&&p;if("function"!=typeof e)throw new TypeError("Expected a function");u=+u||0;var s=!!(c=c||{}).leading,x=!("trailing"in c)||!!c.trailing,h="maxWait"in c,y="debounceOnServer"in c&&!!c.debounceOnServer,F=h?Math.max(+c.maxWait||0,u):null;useEffect(function(){return g.current=true,function(){g.current=false;}},[]);var A=useMemo(function(){var r=function(r){var n=l.current,t=v.current;return l.current=v.current=null,o.current=r,m.current=d.current.apply(t,n)},n=function(r,n){w&&cancelAnimationFrame(f.current),f.current=w?requestAnimationFrame(r):setTimeout(r,n);},t=function(r){if(!g.current)return false;var n=r-a.current;return !a.current||n>=u||n<0||h&&r-o.current>=F},e=function(n){return f.current=null,x&&l.current?r(n):(l.current=v.current=null,m.current)},c=function r(){var c=Date.now();if(t(c))return e(c);if(g.current){var i=u-(c-a.current),f=h?Math.min(i,F-(c-o.current)):i;n(r,f);}},A=function(){if(p||y){var e=Date.now(),d=t(e);if(l.current=[].slice.call(arguments),v.current=i,a.current=e,d){if(!f.current&&g.current)return o.current=a.current,n(c,u),s?r(a.current):m.current;if(h)return n(c,u),r(a.current)}return f.current||n(c,u),m.current}};return A.cancel=function(){f.current&&(w?cancelAnimationFrame(f.current):clearTimeout(f.current)),o.current=0,l.current=a.current=v.current=f.current=null;},A.isPending=function(){return !!f.current},A.flush=function(){return f.current?e(Date.now()):m.current},A},[s,h,u,F,x,w,p,y]);return A}function i$8(r,n){return r===n}function a$3(n,t,a){var o=i$8,f=useRef(n),l=useState({})[1],v=c$3(useCallback(function(r){f.current=r,l({});},[l]),t,a),m=useRef(n);return o(m.current,n)||(v(n),m.current=n),[f.current,v]}
218733
-
218734
220796
  /**
218735
220797
  * Higher-level agent search hook that manages search state, debounce, and
218736
220798
  * pagination controls on top of `useAgentSearch`.
@@ -254673,37 +256735,46 @@ const EMPTY_SKILL_FORM = {
254673
256735
  };
254674
256736
  /** Page size for both skill lists. */
254675
256737
  const SKILLS_PAGE_SIZE = 10;
254676
- // Resolving the agent's own set (assignment metadata + its private skills)
254677
- // needs a catalog lookup that the API cannot scope server-side yet (there is
254678
- // no `mentor=` filter), so one bounded background fetch covers the join.
254679
- const FULL_LIST_LIMIT = 1000;
254680
256738
  function AgentSkills({ platformKey, mentorUniqueId }) {
254681
- var _a, _b, _c, _d;
256739
+ var _a, _b, _c, _d, _e, _f;
254682
256740
  const t = useT();
254683
256741
  // Skills are managed independently of the sandbox connection — org-level
254684
256742
  // skills and the mentor's assignments both resolve without a wired Claw
254685
256743
  // instance, so we always fetch and let admins add/assign skills freely.
254686
- const { data: joinCatalog, isLoading: isLoadingSkills } = useGetAgentSkillsQuery({
256744
+ // The "Agent Skills" tab pages the assignment list server-side.
256745
+ const [agentPage, setAgentPage] = useState(1);
256746
+ const { data: assignmentsPage, isLoading: isLoadingAssignments } = useGetMentorSkillAssignmentsQuery({
254687
256747
  org: platformKey,
254688
- limit: FULL_LIST_LIMIT,
254689
- offset: 0,
256748
+ mentorUniqueId,
256749
+ limit: SKILLS_PAGE_SIZE,
256750
+ offset: (agentPage - 1) * SKILLS_PAGE_SIZE,
254690
256751
  });
254691
- const { data: assignments, isLoading: isLoadingAssignments } = useGetMentorSkillAssignmentsQuery({
256752
+ const agentAssignments = (_a = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.results) !== null && _a !== void 0 ? _a : [];
256753
+ const agentTotalCount = (_b = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.count) !== null && _b !== void 0 ? _b : 0;
256754
+ const agentTotalPages = Math.max(1, Math.ceil(agentTotalCount / SKILLS_PAGE_SIZE));
256755
+ useEffect(() => {
256756
+ if (agentPage > agentTotalPages)
256757
+ setAgentPage(agentTotalPages);
256758
+ }, [agentPage, agentTotalPages]);
256759
+ // Unpaged lookup of the same (per-agent, naturally small) assignment list —
256760
+ // powers the "Added" chips on the catalog tab, since the API has no
256761
+ // per-skill "is assigned" check to ask instead.
256762
+ const { data: assignmentCoverage } = useGetMentorSkillAssignmentsQuery({
254692
256763
  org: platformKey,
254693
256764
  mentorUniqueId,
254694
- limit: FULL_LIST_LIMIT,
254695
- offset: 0,
254696
256765
  });
254697
- // The "Available skills" tab pages the catalog server-side.
256766
+ const assignedSkillIds = new Set(((_c = assignmentCoverage === null || assignmentCoverage === void 0 ? void 0 : assignmentCoverage.results) !== null && _c !== void 0 ? _c : []).map((a) => a.skill));
256767
+ // The "Available skills" tab pages the catalog server-side. Deliberately
256768
+ // unfiltered: disabled skills stay listed (switch off) so admins can
256769
+ // re-enable, edit or delete them.
254698
256770
  const [catalogPage, setCatalogPage] = useState(1);
254699
256771
  const { data: catalogSkillsPage, isLoading: isLoadingCatalog } = useGetAgentSkillsQuery({
254700
256772
  org: platformKey,
254701
- enabled: true,
254702
256773
  limit: SKILLS_PAGE_SIZE,
254703
256774
  offset: (catalogPage - 1) * SKILLS_PAGE_SIZE,
254704
256775
  });
254705
- const catalogSkills = (_a = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.results) !== null && _a !== void 0 ? _a : [];
254706
- const catalogTotalCount = (_b = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.count) !== null && _b !== void 0 ? _b : 0;
256776
+ const catalogSkills = (_d = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.results) !== null && _d !== void 0 ? _d : [];
256777
+ const catalogTotalCount = (_e = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.count) !== null && _e !== void 0 ? _e : 0;
254707
256778
  const catalogTotalPages = Math.max(1, Math.ceil(catalogTotalCount / SKILLS_PAGE_SIZE));
254708
256779
  useEffect(() => {
254709
256780
  if (catalogPage > catalogTotalPages)
@@ -254721,25 +256792,11 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254721
256792
  const [editingSkill, setEditingSkill] = useState(null);
254722
256793
  const [deletingSkill, setDeletingSkill] = useState(null);
254723
256794
  const [form, setForm] = useState(EMPTY_SKILL_FORM);
254724
- const isLoading = isLoadingSkills || isLoadingAssignments;
254725
- const isMutatingAgentSkill = isCreatingAssignment || isUpdatingAssignment || isDeletingAssignment || isUpdatingSkill;
254726
- const allSkills = (_c = joinCatalog === null || joinCatalog === void 0 ? void 0 : joinCatalog.results) !== null && _c !== void 0 ? _c : [];
254727
- const effectiveSkills = resolveEffectiveAgentSkills(allSkills, assignments !== null && assignments !== void 0 ? assignments : [], mentorUniqueId);
254728
- const effectiveUniqueIds = new Set(effectiveSkills.map((s) => s.unique_id));
254729
- const effectiveSlugs = new Set(effectiveSkills.map((s) => s.slug));
254730
- // The "Agent skills" tab pages the (small, per-agent) effective set locally.
254731
- const [agentPage, setAgentPage] = useState(1);
254732
- const agentTotalPages = Math.max(1, Math.ceil(effectiveSkills.length / SKILLS_PAGE_SIZE));
254733
- useEffect(() => {
254734
- if (agentPage > agentTotalPages)
254735
- setAgentPage(agentTotalPages);
254736
- }, [agentPage, agentTotalPages]);
254737
- const pagedEffectiveSkills = effectiveSkills.slice((agentPage - 1) * SKILLS_PAGE_SIZE, agentPage * SKILLS_PAGE_SIZE);
254738
- // Catalog rows already covered by the agent's set (attached, or shadowed by
254739
- // a same-slug private skill) show an "Added" chip instead of an Add button
254740
- // — hiding them would make server pages uneven. Another agent's private
254741
- // skills are not attachable at all.
254742
- const isCoveredByAgent = (skill) => effectiveUniqueIds.has(skill.unique_id) || effectiveSlugs.has(skill.slug);
256795
+ const isMutatingAgentSkill = isCreatingAssignment || isUpdatingAssignment || isDeletingAssignment;
256796
+ // Catalog rows already assigned to the agent show an "Added" chip instead
256797
+ // of an Add button hiding them would make server pages uneven. Another
256798
+ // agent's private skills get a disabled Add with an explanatory tooltip.
256799
+ const isCoveredByAgent = (skill) => assignedSkillIds.has(skill.unique_id);
254743
256800
  const isOtherAgentsPrivate = (skill) => !!skill.mentor && skill.mentor !== mentorUniqueId;
254744
256801
  // Featured skills from the `main` platform are read-only for tenants — the
254745
256802
  // API 404s writes against them, so hide the edit/delete affordances.
@@ -254749,29 +256806,20 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254749
256806
  return skill.platform_key ? skill.platform_key === platformKey : !skill.is_featured;
254750
256807
  };
254751
256808
  const resetForm = () => setForm(EMPTY_SKILL_FORM);
254752
- const handleToggleEffective = async (effective, shouldEnable) => {
256809
+ const handleToggleAssignment = async (assignment, shouldEnable) => {
254753
256810
  try {
254754
- if (effective.origin === 'private' && effective.skill) {
254755
- await updateSkill({
254756
- org: platformKey,
254757
- id: effective.skill.id,
254758
- enabled: shouldEnable,
254759
- }).unwrap();
254760
- }
254761
- else if (effective.assignmentId !== undefined) {
254762
- await updateAssignment({
254763
- org: platformKey,
254764
- mentorUniqueId,
254765
- assignmentPk: effective.assignmentId,
254766
- enabled: shouldEnable,
254767
- }).unwrap();
254768
- }
256811
+ await updateAssignment({
256812
+ org: platformKey,
256813
+ mentorUniqueId,
256814
+ assignmentPk: assignment.id,
256815
+ enabled: shouldEnable,
256816
+ }).unwrap();
254769
256817
  toast.success(t(shouldEnable
254770
256818
  ? 'clawSandboxAgentSkills.skillEnabled'
254771
- : 'clawSandboxAgentSkills.skillDisabled', { name: effective.name }));
256819
+ : 'clawSandboxAgentSkills.skillDisabled', { name: assignment.skill_name }));
254772
256820
  }
254773
256821
  catch (_a) {
254774
- toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: effective.name }));
256822
+ toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: assignment.skill_name }));
254775
256823
  }
254776
256824
  };
254777
256825
  const handleAttach = async (skill) => {
@@ -254788,19 +256836,30 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254788
256836
  toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: skill.name }));
254789
256837
  }
254790
256838
  };
254791
- const handleDetach = async (effective) => {
254792
- if (effective.assignmentId === undefined)
254793
- return;
256839
+ // Platform-level enable/disable of the skill itself (catalog rows) the
256840
+ // agent-tab switches PATCH the assignment instead.
256841
+ const handleToggleSkill = async (skill, shouldEnable) => {
256842
+ try {
256843
+ await updateSkill({ org: platformKey, id: skill.id, enabled: shouldEnable }).unwrap();
256844
+ toast.success(t(shouldEnable
256845
+ ? 'clawSandboxAgentSkills.skillEnabled'
256846
+ : 'clawSandboxAgentSkills.skillDisabled', { name: skill.name }));
256847
+ }
256848
+ catch (_a) {
256849
+ toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: skill.name }));
256850
+ }
256851
+ };
256852
+ const handleDetach = async (assignment) => {
254794
256853
  try {
254795
256854
  await deleteAssignment({
254796
256855
  org: platformKey,
254797
256856
  mentorUniqueId,
254798
- assignmentPk: effective.assignmentId,
256857
+ assignmentPk: assignment.id,
254799
256858
  }).unwrap();
254800
- toast.success(t('clawSandboxAgentSkills.skillRemoved', { name: effective.name }));
256859
+ toast.success(t('clawSandboxAgentSkills.skillRemoved', { name: assignment.skill_name }));
254801
256860
  }
254802
256861
  catch (_a) {
254803
- toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: effective.name }));
256862
+ toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: assignment.skill_name }));
254804
256863
  }
254805
256864
  };
254806
256865
  const openNewSkillDialog = () => {
@@ -254831,7 +256890,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254831
256890
  return;
254832
256891
  }
254833
256892
  try {
254834
- await createSkill({
256893
+ const created = await createSkill({
254835
256894
  org: platformKey,
254836
256895
  name: form.name,
254837
256896
  slug: form.slug,
@@ -254842,6 +256901,16 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254842
256901
  instruction: form.instruction,
254843
256902
  enabled: true,
254844
256903
  }).unwrap();
256904
+ // A private skill only takes effect once assigned, and the Agent Skills
256905
+ // tab lists assignments — so attach it to this agent right away.
256906
+ if (form.privateToAgent && (created === null || created === void 0 ? void 0 : created.unique_id)) {
256907
+ await createAssignment({
256908
+ org: platformKey,
256909
+ mentorUniqueId,
256910
+ skill: created.unique_id,
256911
+ enabled: true,
256912
+ }).unwrap();
256913
+ }
254845
256914
  toast.success(t('clawSandboxAgentSkills.skillCreated'));
254846
256915
  closeAllDialogs();
254847
256916
  }
@@ -254856,6 +256925,10 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254856
256925
  toast.error(t('clawSandboxAgentSkills.nameAndSlugRequired'));
254857
256926
  return;
254858
256927
  }
256928
+ // A skill private to a different agent keeps its owner: the "Only This
256929
+ // Agent" toggle is locked in the form, and the PATCH omits `mentor` so a
256930
+ // save here can never silently un-private (or steal) it.
256931
+ const privateLocked = isOtherAgentsPrivate(editingSkill);
254859
256932
  try {
254860
256933
  await updateSkill({
254861
256934
  org: platformKey,
@@ -254866,8 +256939,18 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254866
256939
  version: form.version,
254867
256940
  category: form.category,
254868
256941
  instruction: form.instruction,
254869
- mentor: form.privateToAgent ? mentorUniqueId : null,
256942
+ ...(!privateLocked && { mentor: form.privateToAgent ? mentorUniqueId : null }),
254870
256943
  }).unwrap();
256944
+ // Making a skill private to this agent implies it should be usable by
256945
+ // it — attach it if it isn't assigned yet.
256946
+ if (!privateLocked && form.privateToAgent && !assignedSkillIds.has(editingSkill.unique_id)) {
256947
+ await createAssignment({
256948
+ org: platformKey,
256949
+ mentorUniqueId,
256950
+ skill: editingSkill.unique_id,
256951
+ enabled: true,
256952
+ }).unwrap();
256953
+ }
254871
256954
  toast.success(t('clawSandboxAgentSkills.skillUpdated'));
254872
256955
  closeAllDialogs();
254873
256956
  }
@@ -254887,46 +256970,40 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
254887
256970
  toast.error(t('clawSandboxAgentSkills.failedToDeleteSkill'));
254888
256971
  }
254889
256972
  };
254890
- const renderBadges = (skill, effective) => {
254891
- var _a, _b, _c;
254892
- const category = (_a = effective === null || effective === void 0 ? void 0 : effective.category) !== null && _a !== void 0 ? _a : skill === null || skill === void 0 ? void 0 : skill.category;
254893
- const skillType = (_b = effective === null || effective === void 0 ? void 0 : effective.skill_type) !== null && _b !== void 0 ? _b : skill === null || skill === void 0 ? void 0 : skill.skill_type;
254894
- const isFeatured = (_c = effective === null || effective === void 0 ? void 0 : effective.is_featured) !== null && _c !== void 0 ? _c : skill === null || skill === void 0 ? void 0 : skill.is_featured;
254895
- return (jsxs("span", { className: "flex flex-wrap items-center gap-1", children: [category && (jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-[0.625rem] text-gray-500", children: category })), skillType === 'native' && (jsx("span", { className: "rounded-full bg-purple-50 px-2 py-0.5 text-[0.625rem] text-purple-600", children: t('clawSandboxAgentSkills.nativeBadge') })), isFeatured && (jsx("span", { className: "rounded-full bg-amber-50 px-2 py-0.5 text-[0.625rem] text-amber-600", children: t('clawSandboxAgentSkills.featuredBadge') })), (effective === null || effective === void 0 ? void 0 : effective.origin) === 'private' && (jsx("span", { className: "rounded-full bg-blue-50 px-2 py-0.5 text-[0.625rem] text-blue-600", children: t('clawSandboxAgentSkills.privateBadge') }))] }));
254896
- };
254897
- const renderSkillMenu = (skill, effective) => {
254898
- var _a, _b;
254899
- const editable = isSkillEditable(skill);
254900
- const showEdit = editable;
254901
- const showDelete = editable;
254902
- const detachable = (effective === null || effective === void 0 ? void 0 : effective.origin) === 'assignment';
254903
- if (!showEdit && !showDelete && !detachable)
256973
+ const renderBadges = (skill) => (jsxs("span", { className: "flex flex-wrap items-center gap-1", children: [skill.category && (jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-[0.625rem] text-gray-500", children: skill.category })), skill.skill_type === 'native' && (jsx("span", { className: "rounded-full bg-purple-50 px-2 py-0.5 text-[0.625rem] text-purple-600", children: t('clawSandboxAgentSkills.nativeBadge') })), skill.is_featured && (jsx("span", { className: "rounded-full bg-amber-50 px-2 py-0.5 text-[0.625rem] text-amber-600", children: t('clawSandboxAgentSkills.featuredBadge') })), skill.mentor === mentorUniqueId && (jsx("span", { className: "rounded-full bg-blue-50 px-2 py-0.5 text-[0.625rem] text-blue-600", children: t('clawSandboxAgentSkills.privateBadge') }))] }));
256974
+ // Agent-tab rows are assignments: their only row action is detaching.
256975
+ const renderAssignmentMenu = (assignment) => (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name: assignment.skill_name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsx(DropdownMenuContent, { align: "end", children: jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => handleDetach(assignment), children: [jsx(CircleMinus$1, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.removeFromAgent')] }) })] }));
256976
+ // Catalog rows carry the full skill object: edit/delete where allowed.
256977
+ const renderSkillMenu = (skill) => {
256978
+ if (!isSkillEditable(skill))
254904
256979
  return null;
254905
- const name = (_b = (_a = effective === null || effective === void 0 ? void 0 : effective.name) !== null && _a !== void 0 ? _a : skill === null || skill === void 0 ? void 0 : skill.name) !== null && _b !== void 0 ? _b : '';
254906
- return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [detachable && (jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => effective && handleDetach(effective), children: [jsx(CircleMinus$1, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.removeFromAgent')] })), showEdit && skill && (jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openEditSkillDialog(skill), children: [jsx(Pen, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.edit')] })), showDelete && skill && (jsxs(DropdownMenuItem, { variant: "destructive", className: "cursor-pointer", onClick: () => setDeletingSkill(skill), children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.delete')] }))] })] }));
254907
- };
254908
- if (isLoading) {
254909
- return (jsxs("div", { className: "flex items-center gap-2 p-4", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] }));
254910
- }
254911
- return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] })] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') })] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: effectiveSkills.length === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [pagedEffectiveSkills.map((effective) => {
254912
- var _a;
254913
- return (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: effective.name }), ((_a = effective.skill) === null || _a === void 0 ? void 0 : _a.version) && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", effective.skill.version] })), renderBadges(effective.skill, effective)] }), effective.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: effective.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: effective.enabled, onCheckedChange: (checked) => handleToggleEffective(effective, checked), disabled: isMutatingAgentSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(effective.enabled
254914
- ? 'clawSandboxAgentSkills.skillEnabledLabel'
254915
- : 'clawSandboxAgentSkills.skillDisabledLabel', { name: effective.name }) }), renderSkillMenu(effective.skill, effective)] })] }, effective.unique_id));
254916
- }), jsx(IblPagination, { currentPage: agentPage, totalPages: agentTotalPages, onPageChange: setAgentPage, disabled: isMutatingAgentSkill })] })) }), jsx(TabsContent, { value: "available", className: "space-y-3", children: isLoadingCatalog ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : catalogTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noSkillsAvailable') }) })) : (jsxs(Fragment$1, { children: [catalogSkills.map((skill) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "available-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: skill.name }), jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", skill.version] }), renderBadges(skill)] }), skill.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: skill.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isCoveredByAgent(skill) ? (jsx("span", { className: "rounded-full bg-green-50 px-2 py-0.5 text-xs text-green-700", children: t('clawSandboxAgentSkills.addedBadge') })) : (!isOtherAgentsPrivate(skill) && (jsxs(Button$1, { variant: "outline", size: "sm", className: "cursor-pointer", disabled: isMutatingAgentSkill, onClick: () => handleAttach(skill), "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
256980
+ return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name: skill.name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openEditSkillDialog(skill), children: [jsx(Pen, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.edit')] }), jsxs(DropdownMenuItem, { variant: "destructive", className: "cursor-pointer", onClick: () => setDeletingSkill(skill), children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.delete')] })] })] }));
256981
+ };
256982
+ return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] })] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') })] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: isLoadingAssignments ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : agentTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [agentAssignments.map((assignment) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsx("div", { className: "min-w-0 space-y-1", children: jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: assignment.skill_name }), assignment.skill_slug && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["/", assignment.skill_slug] }))] }) }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: assignment.enabled, onCheckedChange: (checked) => handleToggleAssignment(assignment, checked), disabled: isMutatingAgentSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(assignment.enabled
256983
+ ? 'clawSandboxAgentSkills.skillEnabledLabel'
256984
+ : 'clawSandboxAgentSkills.skillDisabledLabel', { name: assignment.skill_name }) }), renderAssignmentMenu(assignment)] })] }, assignment.id))), jsx(IblPagination, { currentPage: agentPage, totalPages: agentTotalPages, onPageChange: setAgentPage, disabled: isMutatingAgentSkill })] })) }), jsx(TabsContent, { value: "available", className: "space-y-3", children: isLoadingCatalog ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : catalogTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noSkillsAvailable') }) })) : (jsxs(Fragment$1, { children: [catalogSkills.map((skill) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "available-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: skill.name }), jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", skill.version] }), renderBadges(skill)] }), skill.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: skill.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isCoveredByAgent(skill) ? (jsx("span", { className: "rounded-full bg-green-50 px-2 py-0.5 text-xs text-green-700", children: t('clawSandboxAgentSkills.addedBadge') })) : isOtherAgentsPrivate(skill) ? (
256985
+ // Another agent's private skill: Add stays visible but
256986
+ // disabled, with the reason on hover/focus.
256987
+ jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { tabIndex: 0, children: jsxs(Button$1, { variant: "outline", size: "sm", disabled: true, "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
256988
+ name: skill.name,
256989
+ }), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] }) }) }), jsx(TooltipContent, { className: "ibl-tooltip-content max-w-64", children: jsx("p", { children: t('clawSandboxAgentSkills.privateToOtherAgentTooltip') }) })] }) })) : (jsxs(Button$1, { variant: "outline", size: "sm", className: "cursor-pointer", disabled: isMutatingAgentSkill, onClick: () => handleAttach(skill), "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
254917
256990
  name: skill.name,
254918
- }), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] }))), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) })] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
256991
+ }), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] })), isSkillEditable(skill) && (jsx(Switch, { checked: skill.enabled, onCheckedChange: (checked) => handleToggleSkill(skill, checked), disabled: isUpdatingSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(skill.enabled
256992
+ ? 'clawSandboxAgentSkills.skillEnabledLabel'
256993
+ : 'clawSandboxAgentSkills.skillDisabledLabel', { name: skill.name }) })), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) })] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
254919
256994
  ? t('clawSandboxAgentSkills.creating')
254920
- : t('clawSandboxAgentSkills.create') })] })] }) }), jsx(OverlayModal, { open: !!editingSkill, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.editSkillDialogTitle'), className: "max-w-2xl", children: jsxs(Tabs, { defaultValue: "details", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.editSkillDialogTitle'), "data-testid": "skill-dialog-tabs", children: [jsx(TabsTrigger, { value: "details", "data-testid": "skill-dialog-tab-details", children: t('clawSandboxAgentSkills.detailsTab') }), jsx(TabsTrigger, { value: "files", "data-testid": "skill-dialog-tab-files", children: t('clawSandboxAgentSkills.filesTab') })] }), jsx(TabsContent, { value: "details", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: jsx(SkillForm, { form: form, onChange: setForm }) }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleUpdateSkill, disabled: isUpdatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isUpdatingSkill
256995
+ : t('clawSandboxAgentSkills.create') })] })] }) }), jsx(OverlayModal, { open: !!editingSkill, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.editSkillDialogTitle'), className: "max-w-2xl", children: jsxs(Tabs, { defaultValue: "details", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.editSkillDialogTitle'), "data-testid": "skill-dialog-tabs", children: [jsx(TabsTrigger, { value: "details", "data-testid": "skill-dialog-tab-details", children: t('clawSandboxAgentSkills.detailsTab') }), jsx(TabsTrigger, { value: "files", "data-testid": "skill-dialog-tab-files", children: t('clawSandboxAgentSkills.filesTab') })] }), jsx(TabsContent, { value: "details", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: jsx(SkillForm, { form: form, onChange: setForm, privateLocked: !!editingSkill && isOtherAgentsPrivate(editingSkill) }) }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleUpdateSkill, disabled: isUpdatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isUpdatingSkill
254921
256996
  ? t('clawSandboxAgentSkills.saving')
254922
- : t('clawSandboxAgentSkills.save') })] })] }) }), jsx(TabsContent, { value: "files", children: jsx("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: editingSkill && (jsx(SkillResourcesSection, { platformKey: platformKey, skillId: editingSkill.id })) }) }) })] }) }), jsxs(OverlayModal, { open: !!deletingSkill, onClose: () => setDeletingSkill(null), title: t('clawSandboxAgentSkills.deleteSkillDialogTitle'), className: "max-w-md", children: [jsx("p", { className: "mt-2 text-sm text-gray-600", children: t('clawSandboxAgentSkills.deleteConfirmation', { name: (_d = deletingSkill === null || deletingSkill === void 0 ? void 0 : deletingSkill.name) !== null && _d !== void 0 ? _d : '' }) }), jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [jsx(Button$1, { variant: "outline", onClick: () => setDeletingSkill(null), children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleDeleteSkill, disabled: isDeletingSkill, className: "bg-red-600 text-white hover:bg-red-700", children: isDeletingSkill
256997
+ : t('clawSandboxAgentSkills.save') })] })] }) }), jsx(TabsContent, { value: "files", children: jsx("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: editingSkill && (jsx(SkillResourcesSection, { platformKey: platformKey, skillId: editingSkill.id })) }) }) })] }) }), jsxs(OverlayModal, { open: !!deletingSkill, onClose: () => setDeletingSkill(null), title: t('clawSandboxAgentSkills.deleteSkillDialogTitle'), className: "max-w-md", children: [jsx("p", { className: "mt-2 text-sm text-gray-600", children: t('clawSandboxAgentSkills.deleteConfirmation', { name: (_f = deletingSkill === null || deletingSkill === void 0 ? void 0 : deletingSkill.name) !== null && _f !== void 0 ? _f : '' }) }), jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [jsx(Button$1, { variant: "outline", onClick: () => setDeletingSkill(null), children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleDeleteSkill, disabled: isDeletingSkill, className: "bg-red-600 text-white hover:bg-red-700", children: isDeletingSkill
254923
256998
  ? t('clawSandboxAgentSkills.deleting')
254924
256999
  : t('clawSandboxAgentSkills.delete') })] })] })] }));
254925
257000
  }
254926
257001
  // ── Shared Skill Form ────────────────────────────────────────────────────────
254927
- function SkillForm({ form, onChange, }) {
257002
+ function SkillForm({ form, onChange, privateLocked = false, }) {
254928
257003
  const t = useT();
254929
- return (jsxs("div", { className: "mt-4 grid gap-4", children: [jsxs("div", { className: "flex items-center justify-between rounded-lg border p-4", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$1, { htmlFor: "skill-private", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.privateToAgent') }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.privateToAgentHint') })] }), jsx(Switch, { id: "skill-private", checked: form.privateToAgent, onCheckedChange: (checked) => onChange({ ...form, privateToAgent: checked }), className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-name", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldName') }), jsx(Input, { id: "skill-name", name: "skill-name", value: form.name, onChange: (e) => onChange({ ...form, name: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldNamePlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-slug", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldSlug') }), jsx(Input, { id: "skill-slug", name: "skill-slug", value: form.slug, onChange: (e) => onChange({ ...form, slug: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldSlugPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-version", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldVersion') }), jsx(Input, { id: "skill-version", name: "skill-version", value: form.version, onChange: (e) => onChange({ ...form, version: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldVersionPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-category", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldCategory') }), jsx(Input, { id: "skill-category", name: "skill-category", value: form.category, onChange: (e) => onChange({ ...form, category: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldCategoryPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-description", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldDescription') }), jsx(Textarea, { id: "skill-description", name: "skill-description", value: form.description, onChange: (e) => onChange({ ...form, description: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldDescriptionPlaceholder'), rows: 3 })] }), jsxs("div", { className: "space-y-2 overflow-hidden", children: [jsx(Label$1, { htmlFor: "skill-instruction", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldInstruction') }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.instructionHint') }), jsx(RichTextEditor, { value: form.instruction, onChange: (value) => onChange({ ...form, instruction: value }) })] })] }));
257004
+ return (jsxs("div", { className: "mt-4 grid gap-4", children: [jsxs("div", { className: "flex items-center justify-between rounded-lg border p-4", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$1, { htmlFor: "skill-private", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.privateToAgent') }), jsx("p", { className: "text-xs text-gray-500", children: t(privateLocked
257005
+ ? 'clawSandboxAgentSkills.privateToOtherAgentHint'
257006
+ : 'clawSandboxAgentSkills.privateToAgentHint') })] }), jsx(Switch, { id: "skill-private", checked: form.privateToAgent, onCheckedChange: (checked) => onChange({ ...form, privateToAgent: checked }), disabled: privateLocked, className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-name", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldName') }), jsx(Input, { id: "skill-name", name: "skill-name", value: form.name, onChange: (e) => onChange({ ...form, name: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldNamePlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-slug", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldSlug') }), jsx(Input, { id: "skill-slug", name: "skill-slug", value: form.slug, onChange: (e) => onChange({ ...form, slug: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldSlugPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-version", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldVersion') }), jsx(Input, { id: "skill-version", name: "skill-version", value: form.version, onChange: (e) => onChange({ ...form, version: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldVersionPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-category", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldCategory') }), jsx(Input, { id: "skill-category", name: "skill-category", value: form.category, onChange: (e) => onChange({ ...form, category: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldCategoryPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-description", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldDescription') }), jsx(Textarea, { id: "skill-description", name: "skill-description", value: form.description, onChange: (e) => onChange({ ...form, description: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldDescriptionPlaceholder'), rows: 3 })] }), jsxs("div", { className: "space-y-2 overflow-hidden", children: [jsx(Label$1, { htmlFor: "skill-instruction", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldInstruction') }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.instructionHint') }), jsx(RichTextEditor, { value: form.instruction, onChange: (value) => onChange({ ...form, instruction: value }) })] })] }));
254930
257007
  }
254931
257008
 
254932
257009
  /** Distance (px) from the list bottom at which the next page is requested. */
@@ -255197,46 +257274,6 @@ function ProjectMentorsList({ projectMentors, onMentorRemoved, onAddMentorClick,
255197
257274
  }) }), showMinimumMentorAlert && (jsx(Suspense, { fallback: null, children: jsx(MinimumMentorAlert$1, { open: showMinimumMentorAlert, onOpenChange: setShowMinimumMentorAlert }) }))] }));
255198
257275
  }
255199
257276
 
255200
- const AgentSettingsContext = React__default.createContext(null);
255201
- function AgentSettingsProvider({ children, ...value }) {
255202
- const ctx = React__default.useMemo(() => value,
255203
- // eslint-disable-next-line react-hooks/exhaustive-deps
255204
- [
255205
- value.tenantKey,
255206
- value.mentorId,
255207
- value.username,
255208
- value.enableRBAC,
255209
- value.rbacPermissions,
255210
- value.executeGatedAction,
255211
- value.visibilityOptions,
255212
- ]);
255213
- return jsx(AgentSettingsContext.Provider, { value: ctx, children: children });
255214
- }
255215
- /**
255216
- * Read the shared agent identity / config from the nearest
255217
- * `<AgentSettingsProvider>` if one is present, returning `null` otherwise.
255218
- *
255219
- * Use this when a tab can also be driven by direct props (i.e. the
255220
- * `<AgentSettingsProvider>` wrapper is optional). For tabs that strictly
255221
- * require the provider, use `useAgentSettings`.
255222
- */
255223
- function useAgentSettingsOptional() {
255224
- return React__default.useContext(AgentSettingsContext);
255225
- }
255226
- /**
255227
- * Read the shared agent identity / config from the nearest
255228
- * `<AgentSettingsProvider>`. Throws if called outside a provider — this is
255229
- * intentional: every tab MUST be wrapped.
255230
- */
255231
- function useAgentSettings() {
255232
- const ctx = React__default.useContext(AgentSettingsContext);
255233
- if (!ctx) {
255234
- throw new Error('useAgentSettings must be used within an <AgentSettingsProvider>. ' +
255235
- 'Wrap your tab components with <AgentSettingsProvider tenantKey=... mentorId=... username=... enableRBAC=...>.');
255236
- }
255237
- return ctx;
255238
- }
255239
-
255240
257277
  function useDatasetsWithPagination(itemsPerPage = 5, opts = {}) {
255241
257278
  const { page: controlledPage, search: controlledSearch, onPageChange, onSearchChange } = opts;
255242
257279
  // Controlled/uncontrolled is decided per axis, independently.
@@ -257775,9 +259812,6 @@ var addMentorToProjectModal = /*#__PURE__*/Object.freeze({
257775
259812
  AddMentorToProjectModal: AddMentorToProjectModal
257776
259813
  });
257777
259814
 
257778
- const CustomSwitch = React.forwardRef(({ className, checked, onCheckedChange, ...props }, ref) => (jsx(Root$c, { checked: checked, onCheckedChange: onCheckedChange, className: cn('peer focus-visible:ring-ring focus-visible:ring-offset-background inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-blue-500 data-[state=unchecked]:bg-gray-200', className), ...props, ref: ref, children: jsx(Thumb, { className: cn('pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0') }) })));
257779
- CustomSwitch.displayName = Root$c.displayName;
257780
-
257781
259815
  const DeleteDatasetModal$1 = dynamic(() => Promise.resolve().then(function () { return deleteDatasetModal; }).then((mod) => ({
257782
259816
  default: mod.DeleteDatasetModal,
257783
259817
  })));
@@ -257902,36 +259936,6 @@ function DatasetItemList({ datasets, onSelect, selectedDatasetId, labels }) {
257902
259936
  return (jsx(TableBody, { children: (datasets === null || datasets === void 0 ? void 0 : datasets.length) > 0 ? (datasets === null || datasets === void 0 ? void 0 : datasets.map((dataset) => (jsx(DatasetItem, { dataset: dataset, onSelect: onSelect, isSelected: selectedDatasetId === dataset.id, labels: labels }, dataset.id)))) : (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 6, className: "p-4 text-center text-[#646464]", children: (_b = (_a = labels === null || labels === void 0 ? void 0 : labels.table) === null || _a === void 0 ? void 0 : _a.emptyState) !== null && _b !== void 0 ? _b : 'No items found' }) })) }));
257903
259937
  }
257904
259938
 
257905
- /**
257906
- * Shared utilities for tab label systems.
257907
- *
257908
- * Every tab exports a `resolve*Labels(override?)` function that deep-merges
257909
- * a partial override into the default AGENT labels. This file provides the
257910
- * DeepPartial type and deepMerge implementation they all share.
257911
- */
257912
- function deepMerge(base, override) {
257913
- const result = { ...base };
257914
- const overrideBag = override;
257915
- for (const key of Object.keys(overrideBag)) {
257916
- const overrideValue = overrideBag[key];
257917
- if (overrideValue === undefined)
257918
- continue;
257919
- const baseValue = base[key];
257920
- if (baseValue &&
257921
- typeof baseValue === 'object' &&
257922
- !Array.isArray(baseValue) &&
257923
- overrideValue &&
257924
- typeof overrideValue === 'object' &&
257925
- !Array.isArray(overrideValue)) {
257926
- result[key] = deepMerge(baseValue, overrideValue);
257927
- }
257928
- else {
257929
- result[key] = overrideValue;
257930
- }
257931
- }
257932
- return result;
257933
- }
257934
-
257935
259939
  const AGENT_DATASETS_TAB_LABELS = {
257936
259940
  header: {
257937
259941
  title: 'Datasets',
@@ -258224,10 +260228,21 @@ const resourceTypes = [
258224
260228
  },
258225
260229
  ];
258226
260230
 
260231
+ /**
260232
+ * Spend-cap blocks (HTTP 429) carry the machine code in `error`/`error_code`
260233
+ * and a friendly, learner-safe sentence in `message`. Detection keys on
260234
+ * `error_code` — never on the HTTP status, which can also mean a provider
260235
+ * rate limit.
260236
+ */
260237
+ const SPEND_CAP_EXCEEDED_ERROR_CODE = 'spend_cap_exceeded';
258227
260238
  const extractErrorMessage$1 = (error, fallback) => {
258228
- var _a, _b;
260239
+ var _a, _b, _c;
258229
260240
  const apiError = error;
258230
- return ((_a = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _a === void 0 ? void 0 : _a.error) || ((_b = apiError === null || apiError === void 0 ? void 0 : apiError.error) === null || _b === void 0 ? void 0 : _b.error) || fallback;
260241
+ if (((_a = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _a === void 0 ? void 0 : _a.error_code) === SPEND_CAP_EXCEEDED_ERROR_CODE) {
260242
+ // Surface the friendly message, never the raw `spend_cap_exceeded` code.
260243
+ return apiError.data.message || fallback;
260244
+ }
260245
+ return ((_b = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _b === void 0 ? void 0 : _b.error) || ((_c = apiError === null || apiError === void 0 ? void 0 : apiError.error) === null || _c === void 0 ? void 0 : _c.error) || fallback;
258231
260246
  };
258232
260247
 
258233
260248
  function useGithubRepoDetails(url, tenantKey) {
@@ -260008,10 +262023,10 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
260008
262023
  onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('billing');
260009
262024
  }, className: `flex items-center gap-2 px-3 py-2 rounded-lg whitespace-nowrap text-sm transition-all ${activeTab === 'billing'
260010
262025
  ? 'bg-blue-50 text-blue-600 font-medium'
260011
- : 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(CreditCard, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('profileAccount.tabBilling') }), jsx("span", { className: "sm:hidden", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsxs("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_b = (_a = TABS.find((t) => t.id === activeTab)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'integrations' && t('profileAccount.descIntegrations'), activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
262026
+ : 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(CreditCard, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('profileAccount.tabBilling') }), jsx("span", { className: "sm:hidden", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsxs("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_b = (_a = TABS.find((t) => t.id === activeTab)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab) }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'integrations' && t('profileAccount.descIntegrations'), activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
260012
262027
  overflowY: 'auto',
260013
262028
  overflowX: 'hidden',
260014
- }, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
262029
+ }, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab) }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
260015
262030
  }
260016
262031
 
260017
262032
  function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showBenchmarks = false, localLLMProps, enableSupportPhone, }) {
@@ -268569,20 +270584,6 @@ function AgentSafetyTab({ renderPromptContent, FlaggedPromptsModal, showFlaggedP
268569
270584
  selectedPrompt: selectedPrompt, isEditing: isEditMentorLoading })), isFlaggedPromptsModalOpen && FlaggedPromptsModal && (jsx(FlaggedPromptsModal, { isOpen: isFlaggedPromptsModalOpen, onClose: () => setIsFlaggedPromptsModalOpen(false), mentorId: mentorId, tenantKey: tenantKey, username: username !== null && username !== void 0 ? username : '' }))] }) })] }));
268570
270585
  }
268571
270586
 
268572
- /**
268573
- * Reusable header used at the top of every capability-gated agent-settings
268574
- * tab. It surfaces the capability's on/off toggle (previously buried in the
268575
- * Settings → Capabilities list) right where the feature is configured, with a
268576
- * naive-friendly explanation.
268577
- *
268578
- * The configuration UI below is always rendered so admins can preview what
268579
- * they'll get, but it's grayed out and made inert (`pointer-events-none`,
268580
- * `aria-hidden`) until the capability is switched on.
268581
- */
268582
- function CapabilityGate({ title, description, enabled, onToggle, disabled = false, ariaLabel, offHint, toggleTestId, children, }) {
268583
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/60 p-4", "data-testid": "capability-gate-header", children: [jsx("div", { className: "min-w-0", children: jsxs("p", { className: "text-xs leading-relaxed text-gray-600", children: [description, !enabled && offHint && (jsxs("span", { className: "font-medium text-gray-500", "data-testid": "capability-gate-off-hint", children: [' ', offHint] }))] }) }), jsx(CustomSwitch, { checked: enabled, onCheckedChange: onToggle, disabled: disabled, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : title, "aria-checked": enabled, "data-testid": toggleTestId, className: "mt-0.5 flex-shrink-0" })] }), jsx("div", { className: cn('transition-opacity', !enabled && 'pointer-events-none select-none opacity-50'), "aria-hidden": !enabled, inert: !enabled, "data-testid": "capability-gate-content", "data-enabled": enabled, children: children })] }));
268584
- }
268585
-
268586
270587
  const AGENT_PRIVACY_TAB_LABELS = {
268587
270588
  header: {
268588
270589
  title: 'Privacy',
@@ -270825,6 +272826,46 @@ function AgentGraderTab({ labels: labelsOverride, tenantKey: tenantKeyProp, ment
270825
272826
  return (jsxs(Fragment$1, { children: [jsx("div", { className: "flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.header.description })] }) }), jsx("div", { className: "flex-1 space-y-4 p-3 lg:p-4", style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "grader-tab-body", children: isLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "grader-tab-loading", children: jsx(Spinner, {}) })) : isForbidden || nothingViewable ? (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": "grader-tab-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(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: enabled, onToggle: toggleGrading, disabled: isToggling, ariaLabel: labels.capability.title, toggleTestId: "grader-capability-toggle", children: [warningMessage && (jsxs("div", { className: "mb-4 flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3", "data-testid": "grader-misconfigured-warning", children: [jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-amber-600" }), jsx("p", { className: "text-xs leading-relaxed text-amber-800", children: warningMessage })] })), jsxs(Tabs, { defaultValue: defaultSubTab, className: "w-full", children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "grader-sub-tabs", children: [canReadConfig && (jsx(TabsTrigger, { value: GRADER_SUB_TABS.setup, "data-testid": "grader-sub-tab-setup", children: labels.subTabs.setup })), canViewCriteria && (jsx(TabsTrigger, { value: GRADER_SUB_TABS.rubric, "data-testid": "grader-sub-tab-rubric", children: labels.subTabs.rubric })), canViewResults && (jsx(TabsTrigger, { value: GRADER_SUB_TABS.results, "data-testid": "grader-sub-tab-results", children: labels.subTabs.results }))] }), jsx(TabsContent, { value: GRADER_SUB_TABS.setup, className: "space-y-4", children: jsx(SetupSection, { labels: labels.config, config: config, isSaving: isSavingConfig, canSave: canSaveConfig, onSave: saveConfig }) }), jsx(TabsContent, { value: GRADER_SUB_TABS.rubric, className: "space-y-4", children: jsx(RubricSection, { labels: labels.criteria, criteria: criteria, hasConfig: hasConfig, isLoading: isCriteriaLoading, isMutating: isMutatingCriteria, totalPoints: totalPoints, canAdd: canAddCriterion, canEdit: canEditCriterion, canDelete: canDeleteCriterion, onAdd: addCriterion, onEdit: editCriterion, onDelete: removeCriterion }) }), jsx(TabsContent, { value: GRADER_SUB_TABS.results, className: "space-y-4", children: jsx(ResultsSection, { labels: labels.results, tenantKey: tenantKey, mentorId: mentorId, username: username, canOverride: canOverrideResults }) })] })] })) })] }));
270826
272827
  }
270827
272828
 
272829
+ /**
272830
+ * Learner-safe spend-cap usage indicator. The status endpoint intentionally
272831
+ * returns only zones and fill percentages — never dollar amounts — so this
272832
+ * renders progress toward each cap, a warning banner near the limit, and a
272833
+ * blocked banner once a hard-block cap is exceeded. Admin dollar figures live
272834
+ * in the `AgentSpendCapsTab` settings tab instead.
272835
+ */
272836
+ function SpendCapUsage({ tenantKey, userId, mentorId, showWhenOk = false, className, }) {
272837
+ const t = useT();
272838
+ const { data, isLoading, error } = useGetSpendCapStatusQuery({ org: tenantKey, userId, mentor: mentorId }, { skip: !tenantKey || !userId });
272839
+ if (isLoading || error || !data)
272840
+ return null;
272841
+ const summary = data;
272842
+ if (summary.caps.length === 0)
272843
+ return null;
272844
+ if (summary.status === 'ok' && !showWhenOk)
272845
+ return null;
272846
+ const scopeLabels = {
272847
+ tenant: t('spendCapUsage.scopeTenant'),
272848
+ agent: t('spendCapUsage.scopeAgent'),
272849
+ user_agent: t('spendCapUsage.scopeUserAgent'),
272850
+ };
272851
+ const intervalLabels = {
272852
+ day: t('spendCapUsage.intervalDay'),
272853
+ week: t('spendCapUsage.intervalWeek'),
272854
+ month: t('spendCapUsage.intervalMonth'),
272855
+ year: t('spendCapUsage.intervalYear'),
272856
+ };
272857
+ return (jsxs("div", { className: cn('space-y-3 rounded-lg border border-gray-200 bg-white p-3', className), "data-testid": "spend-cap-usage", children: [summary.blocked ? (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 p-3", "data-testid": "spend-cap-usage-blocked", children: [jsx(OctagonX, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-red-600" }), jsx("p", { className: "text-xs leading-relaxed text-red-800", children: t('spendCapUsage.blockedBanner') })] })) : summary.status !== 'ok' ? (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3", "data-testid": "spend-cap-usage-warning", children: [jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-amber-600" }), jsx("p", { className: "text-xs leading-relaxed text-amber-800", children: t('spendCapUsage.warningBanner') })] })) : null, jsx("ul", { className: "space-y-2", children: summary.caps.map((cap, index) => {
272858
+ var _a, _b;
272859
+ const percent = Math.min(100, Math.max(0, Math.round(cap.percent_used)));
272860
+ const barColor = cap.status === 'exceeded'
272861
+ ? 'bg-red-500'
272862
+ : cap.status === 'warning'
272863
+ ? 'bg-amber-500'
272864
+ : 'bg-blue-500';
272865
+ return (jsxs("li", { className: "space-y-1", "data-testid": `spend-cap-usage-item-${cap.scope}`, children: [jsxs("div", { className: "flex items-center justify-between text-xs text-gray-600", children: [jsxs("span", { children: [scopeLabels[cap.scope], " \u00B7 ", intervalLabels[cap.interval_type]] }), jsx("span", { "data-testid": `spend-cap-usage-percent-${cap.scope}`, children: t('spendCapUsage.percentUsed', { percent }) })] }), jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-gray-200", role: "progressbar", "aria-valuenow": percent, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": `${scopeLabels[cap.scope]} · ${intervalLabels[cap.interval_type]}`, children: jsx("div", { className: cn('h-full rounded-full transition-all', barColor), style: { width: `${percent}%` } }) })] }, `${cap.scope}-${(_a = cap.mentor_unique_id) !== null && _a !== void 0 ? _a : ''}-${(_b = cap.username) !== null && _b !== void 0 ? _b : ''}-${index}`));
272866
+ }) })] }));
272867
+ }
272868
+
270828
272869
  function ApiKeyModal({ isOpen, onClose, apiKey, labels }) {
270829
272870
  const { copy, status } = useCopyToClipboard(800);
270830
272871
  const Icon = status === 'success' ? Check : Copy;
@@ -280455,6 +282496,27 @@ function Chat({ mode = 'default', isPreviewMode = false, hasBorder = true, isInC
280455
282496
  closeButton: true,
280456
282497
  });
280457
282498
  }, []);
282499
+ // Spend-cap block (429, error_code "spend_cap_exceeded") — deliberately
282500
+ // separate from the 402 credits flow. Learner-safe: names only the interval,
282501
+ // never dollar amounts.
282502
+ const handleSpendCapError = useCallback((data) => {
282503
+ var _a;
282504
+ const interval = (_a = data === null || data === void 0 ? void 0 : data.details) === null || _a === void 0 ? void 0 : _a.interval_type;
282505
+ const message = interval === 'day'
282506
+ ? t('chatIndex.spendCapLimitDay')
282507
+ : interval === 'week'
282508
+ ? t('chatIndex.spendCapLimitWeek')
282509
+ : interval === 'month'
282510
+ ? t('chatIndex.spendCapLimitMonth')
282511
+ : interval === 'year'
282512
+ ? t('chatIndex.spendCapLimitYear')
282513
+ : t('chatIndex.spendCapLimitGeneric');
282514
+ toast.error(message, {
282515
+ description: t('chatIndex.spendCapContactAdmin'),
282516
+ closeButton: true,
282517
+ duration: TOAST_DURATION,
282518
+ });
282519
+ }, [t, TOAST_DURATION]);
280458
282520
  const { showDisclaimerModal, isAgreeing, userAgreement, hasUserAgreement, handleDisclaimerAgree, checkAgreementAndExecute, executePendingSubmit, } = useUserAgreement({ mentorId, tenantKey });
280459
282521
  const { changeTab, activeTab, currentStreamingMessage, enabledGuidedPrompts, isStreaming, mentorName, messages, profileImage, sendMessage, setMessage, stopGenerating, uniqueMentorId, sessionId, startNewChat, enableSafetyDisclaimer, isPending, isLoadingChats, isConnected, refetchChats, } = useAdvancedChat({
280460
282522
  mentorId,
@@ -280495,6 +282557,7 @@ function Chat({ mode = 'default', isPreviewMode = false, hasBorder = true, isInC
280495
282557
  !(searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('token'))),
280496
282558
  mentorShareableToken: searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('token'),
280497
282559
  on402Error: handle402Error,
282560
+ onSpendCapError: handleSpendCapError,
280498
282561
  cachedSessionId,
280499
282562
  onStartNewChat: (sessionId) => {
280500
282563
  dispatch(chatActions.updateSessionIds(sessionId));
@@ -332819,5 +334882,5 @@ var index_es = /*#__PURE__*/Object.freeze({
332819
334882
  vectorsRatio: vectorsRatio
332820
334883
  });
332821
334884
 
332822
- export { AGENT_ACCESS_TAB_LABELS, AGENT_ANALYTICS_TAB_LABELS, AGENT_API_TAB_LABELS, AGENT_DATASETS_TAB_LABELS, AGENT_DISCLAIMERS_TAB_LABELS, AGENT_EMBED_TAB_LABELS, AGENT_EVALUATION_TAB_LABELS, AGENT_GRADER_TAB_LABELS, AGENT_HISTORY_TAB_LABELS, AGENT_HUMAN_SUPPORT_TAB_LABELS, AGENT_LLM_TAB_LABELS, AGENT_LTI_TAB_LABELS, AGENT_MEMORY_TAB_LABELS, AGENT_PICKER_LABELS, AGENT_PRIVACY_TAB_LABELS, AGENT_PROMPTS_TAB_LABELS, AGENT_SAFETY_TAB_LABELS, AGENT_SCREENSHARE_TAB_LABELS, AGENT_SEARCH_LABELS, AGENT_SETTINGS_TAB_LABELS, AGENT_SKILLS_TAB_LABELS, AGENT_TASKS_TAB_LABELS, AGENT_TOOLS_TAB_LABELS, AGENT_VOICE_TAB_LABELS, Account, AgentAccessTab, AgentAnalyticsTab, AgentApiTab, AgentCard, AgentDatasetsTab, AgentDisclaimersTab, AgentEmbedTab, AgentEmptyState, AgentEvaluationTab, AgentGraderTab, AgentHistoryTab, AgentHumanSupportTab, AgentLLMTab, AgentLtiTab, AgentMemoryTab, AgentPrivacyTab, AgentPromptsTab, AgentSafetyTab, AgentScreenShareTab, AgentSearch, AgentSearchFilters, AgentSearchInput, AgentSettingsProvider, AgentSettingsTab, AgentSkillsTab, AgentTasksTab, AgentToolsTab, AgentVoiceTab, AppSidebar, CATALOGS, Chat, ClientErrorPage, ConversationStarters$1 as ConversationStarters, CopyButton, CopyMentorModal, CourseBox, CourseContentLayout, CourseContentTabPage, CreatePathwayModal, CredentialDetailModal, CredentialMiniBox, DEFAULT_ANALYTICS_HUB_TABS, DEFAULT_LOCALE, DeleteMentorModal, EMPTY_PROGRAM_SETTINGS_FORM, EdxIframe, ErrorPage, GRADING_TOOL_NAME, GreetingMethod, McpTab, MediaBox, MentorSelectionGrid, NavBar, OrganizationTab, PLATFORM_SIDEBAR_FLYOUT_ITEM_COLOR, PLATFORM_SIDEBAR_FLYOUT_TITLE_COLOR, PLATFORM_SIDEBAR_NAV_ACTIVE_BG_OPEN, PLATFORM_SIDEBAR_NAV_MUTED, PathwayDetailModal, PlatformAccountSheet, PlatformSidebar, PlatformSidebarCollapsedLabelFlyout, PlatformSidebarCollapsedNavFlyout, PlatformSidebarFooter, PlatformSidebarNavDivider, PlatformSidebarNavSection, PlatformSidebarSubNavItem, ProfileInfoCards, ProfileTabs, ProgramDetailModal, ProjectsSidebarDropdown, SUPPORTED_LOCALES, SidebarInset, SidebarProvider, SidebarTrigger, SsoLogin, StarButton, UserProfileDropdown, UserProfileModal, WebContainersI18nProvider, formatDateString, getCatalog, getLLMProviderDetails, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
334885
+ export { AGENT_ACCESS_TAB_LABELS, AGENT_ANALYTICS_TAB_LABELS, AGENT_API_TAB_LABELS, AGENT_DATASETS_TAB_LABELS, AGENT_DISCLAIMERS_TAB_LABELS, AGENT_EMBED_TAB_LABELS, AGENT_EVALUATION_TAB_LABELS, AGENT_GRADER_TAB_LABELS, AGENT_HISTORY_TAB_LABELS, AGENT_HUMAN_SUPPORT_TAB_LABELS, AGENT_LLM_TAB_LABELS, AGENT_LTI_TAB_LABELS, AGENT_MEMORY_TAB_LABELS, AGENT_PICKER_LABELS, AGENT_PRIVACY_TAB_LABELS, AGENT_PROMPTS_TAB_LABELS, AGENT_SAFETY_TAB_LABELS, AGENT_SCREENSHARE_TAB_LABELS, AGENT_SEARCH_LABELS, AGENT_SETTINGS_TAB_LABELS, AGENT_SKILLS_TAB_LABELS, AGENT_SPEND_CAPS_TAB_LABELS, AGENT_TASKS_TAB_LABELS, AGENT_TOOLS_TAB_LABELS, AGENT_VOICE_TAB_LABELS, Account, AgentAccessTab, AgentAnalyticsTab, AgentApiTab, AgentCard, AgentDatasetsTab, AgentDisclaimersTab, AgentEmbedTab, AgentEmptyState, AgentEvaluationTab, AgentGraderTab, AgentHistoryTab, AgentHumanSupportTab, AgentLLMTab, AgentLtiTab, AgentMemoryTab, AgentPrivacyTab, AgentPromptsTab, AgentSafetyTab, AgentScreenShareTab, AgentSearch, AgentSearchFilters, AgentSearchInput, AgentSettingsProvider, AgentSettingsTab, AgentSkillsTab, AgentSpendCapsTab, AgentTasksTab, AgentToolsTab, AgentVoiceTab, AppSidebar, CATALOGS, Chat, ClientErrorPage, ConversationStarters$1 as ConversationStarters, CopyButton, CopyMentorModal, CourseBox, CourseContentLayout, CourseContentTabPage, CreatePathwayModal, CredentialDetailModal, CredentialMiniBox, DEFAULT_ANALYTICS_HUB_TABS, DEFAULT_LOCALE, DeleteMentorModal, EMPTY_PROGRAM_SETTINGS_FORM, EdxIframe, ErrorPage, GRADING_TOOL_NAME, GreetingMethod, McpTab, MediaBox, MentorSelectionGrid, NavBar, OrganizationTab, PLATFORM_SIDEBAR_FLYOUT_ITEM_COLOR, PLATFORM_SIDEBAR_FLYOUT_TITLE_COLOR, PLATFORM_SIDEBAR_NAV_ACTIVE_BG_OPEN, PLATFORM_SIDEBAR_NAV_MUTED, PathwayDetailModal, PlatformAccountSheet, PlatformSidebar, PlatformSidebarCollapsedLabelFlyout, PlatformSidebarCollapsedNavFlyout, PlatformSidebarFooter, PlatformSidebarNavDivider, PlatformSidebarNavSection, PlatformSidebarSubNavItem, ProfileInfoCards, ProfileTabs, ProgramDetailModal, ProjectsSidebarDropdown, SPEND_CAPS_SUB_TABS, SUPPORTED_LOCALES, SidebarInset, SidebarProvider, SidebarTrigger, SpendCapUsage, SsoLogin, StarButton, UserProfileDropdown, UserProfileModal, WebContainersI18nProvider, formatDateString, getCatalog, getLLMProviderDetails, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
332823
334886
  //# sourceMappingURL=index.esm.js.map