@iblai/iblai-js 2.4.4 → 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).",
@@ -100353,7 +100361,8 @@ var profileBilling$3 = {
100353
100361
  addCredits: "Add Credits",
100354
100362
  manageBilling: "Manage Billing",
100355
100363
  availableLabel: "Available",
100356
- creditsRemaining: "Credits remaining",
100364
+ creditsUsdEquivalent: " {amount}",
100365
+ creditsConversionHint: "{credits} credits = $1",
100357
100366
  creditsUnit: "{amount} Credits",
100358
100367
  consumedLabel: "Consumed",
100359
100368
  usedThisPeriod: "Used this period",
@@ -100365,10 +100374,17 @@ var profileBilling$3 = {
100365
100374
  disabled: "Disabled",
100366
100375
  manageUsage: "Manage Usage",
100367
100376
  thresholdLabel: "Threshold",
100377
+ thresholdHint: "When your available credits fall below this amount, a top-up starts automatically",
100368
100378
  rechargeAmountLabel: "Recharge amount",
100379
+ rechargeAmountHint: "Charged to your payment method and added to your credits with each top-up",
100369
100380
  spendingLimitLabel: "Spending limit",
100381
+ spendingLimitHint: "The most auto recharge can charge per billing period",
100370
100382
  unlimited: "Unlimited",
100371
- 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"
100372
100388
  };
100373
100389
  var profileCatalogInviteModal$3 = {
100374
100390
  title: "Catalog Invite",
@@ -101312,6 +101328,156 @@ var skillsProfileSkillsBox$3 = {
101312
101328
  desired: "Desired",
101313
101329
  noDesiredSkills: "You don't have any desired skills yet."
101314
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
+ };
101315
101481
  var tabsAlertstab$3 = {
101316
101482
  searchAlertsPlaceholder: "Search alerts",
101317
101483
  searchAlertsAriaLabel: "Search alerts",
@@ -103339,6 +103505,8 @@ var en = {
103339
103505
  skillsProfileResumeBox: skillsProfileResumeBox$3,
103340
103506
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$3,
103341
103507
  skillsProfileSkillsBox: skillsProfileSkillsBox$3,
103508
+ spendCapUsage: spendCapUsage$3,
103509
+ spendCapsTabLabels: spendCapsTabLabels$3,
103342
103510
  tabsAlertstab: tabsAlertstab$3,
103343
103511
  tabsDisclaimersTab: tabsDisclaimersTab$3,
103344
103512
  tabsEmbedTab: tabsEmbedTab$3,
@@ -105069,7 +105237,13 @@ var chatIndex$2 = {
105069
105237
  confirmScreenShareTitle: "Confirmer le partage d'écran",
105070
105238
  confirmScreenShareDescription: "Souhaitez-vous démarrer un partage d'écran avec votre agent ?",
105071
105239
  cancel: "Annuler",
105072
- 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."
105073
105247
  };
105074
105248
  var chatInputForm$2 = {
105075
105249
  uploadingOneFile: "Téléversement de 1 fichier...",
@@ -105272,6 +105446,8 @@ var clawSandboxAgentSkills$2 = {
105272
105446
  fieldCategoryPlaceholder: "p. ex. web, code, données",
105273
105447
  privateToAgent: "Cet agent uniquement",
105274
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.",
105275
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.",
105276
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.",
105277
105453
  filesAfterCreateHint: "Vous pouvez joindre des ressources à cette compétence après sa création (rouvrez-la avec Modifier).",
@@ -107089,7 +107265,8 @@ var profileBilling$2 = {
107089
107265
  addCredits: "Ajouter des crédits",
107090
107266
  manageBilling: "Gérer la facturation",
107091
107267
  availableLabel: "Disponibles",
107092
- creditsRemaining: "Crédits restants",
107268
+ creditsUsdEquivalent: " {amount}",
107269
+ creditsConversionHint: "{credits} crédits = 1 $",
107093
107270
  creditsUnit: "{amount} Crédits",
107094
107271
  consumedLabel: "Consommés",
107095
107272
  usedThisPeriod: "Utilisés cette période",
@@ -107101,10 +107278,17 @@ var profileBilling$2 = {
107101
107278
  disabled: "Désactivé",
107102
107279
  manageUsage: "Gérer l'utilisation",
107103
107280
  thresholdLabel: "Seuil",
107281
+ thresholdHint: "Quand vos crédits disponibles passent sous ce montant, une recharge démarre automatiquement",
107104
107282
  rechargeAmountLabel: "Montant de rechargement",
107283
+ rechargeAmountHint: "Débité de votre moyen de paiement et ajouté à vos crédits à chaque recharge",
107105
107284
  spendingLimitLabel: "Limite de dépenses",
107285
+ spendingLimitHint: "Le maximum que la recharge automatique peut débiter par période de facturation",
107106
107286
  unlimited: "Illimité",
107107
- 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"
107108
107292
  };
107109
107293
  var profileCatalogInviteModal$2 = {
107110
107294
  title: "Invitation au catalogue",
@@ -108048,6 +108232,156 @@ var skillsProfileSkillsBox$2 = {
108048
108232
  desired: "Souhaitées",
108049
108233
  noDesiredSkills: "Vous n'avez pas encore de compétences souhaitées."
108050
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
+ };
108051
108385
  var tabsAlertstab$2 = {
108052
108386
  searchAlertsPlaceholder: "Rechercher des alertes",
108053
108387
  searchAlertsAriaLabel: "Rechercher des alertes",
@@ -109910,6 +110244,8 @@ var fr = {
109910
110244
  skillsProfileResumeBox: skillsProfileResumeBox$2,
109911
110245
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$2,
109912
110246
  skillsProfileSkillsBox: skillsProfileSkillsBox$2,
110247
+ spendCapUsage: spendCapUsage$2,
110248
+ spendCapsTabLabels: spendCapsTabLabels$2,
109913
110249
  tabsAlertstab: tabsAlertstab$2,
109914
110250
  tabsDisclaimersTab: tabsDisclaimersTab$2,
109915
110251
  tabsEmbedTab: tabsEmbedTab$2,
@@ -111639,7 +111975,13 @@ var chatIndex$1 = {
111639
111975
  confirmScreenShareTitle: "Confirmar uso compartido de pantalla",
111640
111976
  confirmScreenShareDescription: "¿Deseas iniciar un uso compartido de pantalla con tu agente?",
111641
111977
  cancel: "Cancelar",
111642
- 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."
111643
111985
  };
111644
111986
  var chatInputForm$1 = {
111645
111987
  uploadingOneFile: "Subiendo 1 archivo...",
@@ -111842,6 +112184,8 @@ var clawSandboxAgentSkills$1 = {
111842
112184
  fieldCategoryPlaceholder: "p. ej. web, código, datos",
111843
112185
  privateToAgent: "Solo este agente",
111844
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í.",
111845
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.",
111846
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.",
111847
112191
  filesAfterCreateHint: "Puedes adjuntar recursos a esta habilidad después de crearla (vuelve a abrirla con Editar).",
@@ -113659,7 +114003,8 @@ var profileBilling$1 = {
113659
114003
  addCredits: "Agregar créditos",
113660
114004
  manageBilling: "Gestionar facturación",
113661
114005
  availableLabel: "Disponibles",
113662
- creditsRemaining: "Créditos restantes",
114006
+ creditsUsdEquivalent: " {amount}",
114007
+ creditsConversionHint: "{credits} créditos = $1",
113663
114008
  creditsUnit: "{amount} Créditos",
113664
114009
  consumedLabel: "Consumidos",
113665
114010
  usedThisPeriod: "Usados este período",
@@ -113671,10 +114016,17 @@ var profileBilling$1 = {
113671
114016
  disabled: "Deshabilitado",
113672
114017
  manageUsage: "Gestionar uso",
113673
114018
  thresholdLabel: "Umbral",
114019
+ thresholdHint: "Cuando tus créditos disponibles bajan de este importe, se inicia una recarga automática",
113674
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",
113675
114022
  spendingLimitLabel: "Límite de gasto",
114023
+ spendingLimitHint: "Lo máximo que la recarga automática puede cobrar por periodo de facturación",
113676
114024
  unlimited: "Ilimitado",
113677
- 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"
113678
114030
  };
113679
114031
  var profileCatalogInviteModal$1 = {
113680
114032
  title: "Invitación al catálogo",
@@ -114618,6 +114970,156 @@ var skillsProfileSkillsBox$1 = {
114618
114970
  desired: "Deseadas",
114619
114971
  noDesiredSkills: "Todavía no tienes habilidades deseadas."
114620
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
+ };
114621
115123
  var tabsAlertstab$1 = {
114622
115124
  searchAlertsPlaceholder: "Buscar alertas",
114623
115125
  searchAlertsAriaLabel: "Buscar alertas",
@@ -116480,6 +116982,8 @@ var es = {
116480
116982
  skillsProfileResumeBox: skillsProfileResumeBox$1,
116481
116983
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$1,
116482
116984
  skillsProfileSkillsBox: skillsProfileSkillsBox$1,
116985
+ spendCapUsage: spendCapUsage$1,
116986
+ spendCapsTabLabels: spendCapsTabLabels$1,
116483
116987
  tabsAlertstab: tabsAlertstab$1,
116484
116988
  tabsDisclaimersTab: tabsDisclaimersTab$1,
116485
116989
  tabsEmbedTab: tabsEmbedTab$1,
@@ -118209,7 +118713,13 @@ var chatIndex = {
118209
118713
  confirmScreenShareTitle: "确认屏幕共享",
118210
118714
  confirmScreenShareDescription: "您要与您的助手开始屏幕共享吗?",
118211
118715
  cancel: "取消",
118212
- confirm: "确认"
118716
+ confirm: "确认",
118717
+ spendCapLimitDay: "您已达到此助手今天的使用限额。",
118718
+ spendCapLimitWeek: "您已达到此助手本周的使用限额。",
118719
+ spendCapLimitMonth: "您已达到此助手本月的使用限额。",
118720
+ spendCapLimitYear: "您已达到此助手今年的使用限额。",
118721
+ spendCapLimitGeneric: "您已达到此助手的使用限额。",
118722
+ spendCapContactAdmin: "请在限额重置后重试,或联系您的管理员。"
118213
118723
  };
118214
118724
  var chatInputForm = {
118215
118725
  uploadingOneFile: "正在上传 1 个文件...",
@@ -118412,6 +118922,8 @@ var clawSandboxAgentSkills = {
118412
118922
  fieldCategoryPlaceholder: "例如 web、代码、数据",
118413
118923
  privateToAgent: "仅此智能体",
118414
118924
  privateToAgentHint: "私有技能仅对此智能体可用,并优先于具有相同 slug 的平台技能。",
118925
+ privateToOtherAgentHint: "该技能为其他智能体私有,因此无法在此更改该设置。",
118926
+ privateToOtherAgentTooltip: "该技能为其他智能体私有,无法在此添加。",
118415
118927
  instructionHint: "使用该技能时智能体遵循的操作指南。修改会立即生效,包括正在进行的对话。",
118416
118928
  filesHint: "智能体在使用此技能时可参考的可选资源。参考资料和脚本是文本文件;资产是上传的文件。",
118417
118929
  filesAfterCreateHint: "创建技能后即可附加资源(通过“编辑”重新打开)。",
@@ -120229,7 +120741,8 @@ var profileBilling = {
120229
120741
  addCredits: "添加积分",
120230
120742
  manageBilling: "管理账单",
120231
120743
  availableLabel: "可用",
120232
- creditsRemaining: "剩余积分",
120744
+ creditsUsdEquivalent: "约合 {amount}",
120745
+ creditsConversionHint: "{credits} 积分 = $1",
120233
120746
  creditsUnit: "{amount} 积分",
120234
120747
  consumedLabel: "已消耗",
120235
120748
  usedThisPeriod: "本期已使用",
@@ -120241,10 +120754,17 @@ var profileBilling = {
120241
120754
  disabled: "已禁用",
120242
120755
  manageUsage: "管理用量",
120243
120756
  thresholdLabel: "阈值",
120757
+ thresholdHint: "当可用积分低于此金额时,将自动开始充值",
120244
120758
  rechargeAmountLabel: "充值金额",
120759
+ rechargeAmountHint: "每次充值时从您的付款方式扣款,并添加到您的积分中",
120245
120760
  spendingLimitLabel: "消费限额",
120761
+ spendingLimitHint: "每个账单周期内自动充值可扣款的上限",
120246
120762
  unlimited: "无限制",
120247
- autoRechargeDisabledMessage: "自动充值当前已禁用。启用后可在不中断服务的情况下保持余额充足。"
120763
+ autoRechargeDisabledMessage: "自动充值当前已禁用。启用后可在不中断服务的情况下保持余额充足。",
120764
+ billingTabsAriaLabel: "账单板块",
120765
+ tabPlanAndCredits: "套餐与积分",
120766
+ tabSpendLimits: "支出限额",
120767
+ tabAgentLimits: "智能体限额"
120248
120768
  };
120249
120769
  var profileCatalogInviteModal = {
120250
120770
  title: "目录邀请",
@@ -121188,6 +121708,156 @@ var skillsProfileSkillsBox = {
121188
121708
  desired: "期望",
121189
121709
  noDesiredSkills: "您还没有任何期望的技能。"
121190
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
+ };
121191
121861
  var tabsAlertstab = {
121192
121862
  searchAlertsPlaceholder: "搜索提醒",
121193
121863
  searchAlertsAriaLabel: "搜索提醒",
@@ -123050,6 +123720,8 @@ var zh = {
123050
123720
  skillsProfileResumeBox: skillsProfileResumeBox,
123051
123721
  skillsProfileSkillDetailModal: skillsProfileSkillDetailModal,
123052
123722
  skillsProfileSkillsBox: skillsProfileSkillsBox,
123723
+ spendCapUsage: spendCapUsage,
123724
+ spendCapsTabLabels: spendCapsTabLabels,
123053
123725
  tabsAlertstab: tabsAlertstab,
123054
123726
  tabsDisclaimersTab: tabsDisclaimersTab,
123055
123727
  tabsEmbedTab: tabsEmbedTab,
@@ -188711,6 +189383,1002 @@ function AddCreditsModal({ isOpen, onClose, tenant }) {
188711
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')) })] })] })] }) }));
188712
189384
  }
188713
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
+
188714
190382
  function formatCredits(credits) {
188715
190383
  if (credits === null || credits === undefined || Number.isNaN(credits))
188716
190384
  return '0';
@@ -188745,17 +190413,17 @@ function formatUsd(value) {
188745
190413
  return '—';
188746
190414
  return `$${num.toFixed(2)}`;
188747
190415
  }
188748
- function Section({ title, description, action, children, testId }) {
188749
- 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 }))] }));
188750
190418
  }
188751
- function Stat({ label, value, hint }) {
188752
- 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 }))] }));
188753
190421
  }
188754
190422
  function SectionSkeleton({ withBody = false }) {
188755
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))) }) }))] }));
188756
190424
  }
188757
190425
  function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, currentUserEmail, redirectUrl, }) {
188758
- var _a;
190426
+ var _a, _b, _c;
188759
190427
  const t = useT();
188760
190428
  const [isAutoRechargeModalOpen, setIsAutoRechargeModalOpen] = useState(false);
188761
190429
  const [isAddCreditsModalOpen, setIsAddCreditsModalOpen] = useState(false);
@@ -188767,6 +190435,10 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188767
190435
  currentUserEmail,
188768
190436
  });
188769
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 });
188770
190442
  const [createStripePortal] = useCreateStripeCustomerPortalMutation();
188771
190443
  const handleBilling = async ({ extraRequestBody = {}, }) => {
188772
190444
  return await createStripePortal({
@@ -188802,7 +190474,7 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188802
190474
  }
188803
190475
  };
188804
190476
  if (isBillingInfoLoading) {
188805
- 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 })] }) }));
188806
190478
  }
188807
190479
  const billing = billingInfo;
188808
190480
  const isFreePlan = formatPlanLabel(billing === null || billing === void 0 ? void 0 : billing.current_plan) === 'Free' ||
@@ -188815,6 +190487,27 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188815
190487
  : 'Premium';
188816
190488
  const availableCredits = (_a = Number(billing === null || billing === void 0 ? void 0 : billing.available_credits)) !== null && _a !== void 0 ? _a : 0;
188817
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;
188818
190511
  const hasPaymentMethod = Boolean(billing === null || billing === void 0 ? void 0 : billing.has_payment_method);
188819
190512
  const isAutoRechargeEnabled = Boolean(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_enabled);
188820
190513
  const spendingLimitRaw = billing === null || billing === void 0 ? void 0 : billing.auto_recharge_spending_limit_usd;
@@ -188838,21 +190531,21 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
188838
190531
  }
188839
190532
  return t('profileBilling.manageSubscriptionAndBilling');
188840
190533
  })();
188841
- 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: {
188842
- backgroundColor: '#f5f8ff',
188843
- color: '#155dfc',
188844
- border: '1px solid #d0e0ff',
188845
- }, 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
188846
- ? t('profileBilling.redirecting')
188847
- : 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: {
188848
- backgroundColor: isAutoRechargeEnabled ? '#f5f8ff' : '#f3f4f6',
188849
- color: isAutoRechargeEnabled ? '#155dfc' : '#6a7282',
188850
- border: `1px solid ${isAutoRechargeEnabled ? '#d0e0ff' : '#e5e7eb'}`,
188851
- }, children: isAutoRechargeEnabled
188852
- ? t('profileBilling.enabled')
188853
- : 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
188854
- ? t('profileBilling.unlimited')
188855
- : 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) })] }));
188856
190549
  }
188857
190550
 
188858
190551
  const SmtpContent = ({ platformKey }) => {
@@ -189107,23 +190800,6 @@ const PlatformMembershipContent = ({ platformKey, authURL, }) => {
189107
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" }))] })] }));
189108
190801
  };
189109
190802
 
189110
- 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 }) })));
189111
- Table$1.displayName = "Table";
189112
- const TableHeader$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("thead", { ref: ref, className: cn("[&_tr]:border-b", className), ...props })));
189113
- TableHeader$1.displayName = "TableHeader";
189114
- const TableBody = React.forwardRef(({ className, ...props }, ref) => (jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
189115
- TableBody.displayName = "TableBody";
189116
- 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 })));
189117
- TableFooter.displayName = "TableFooter";
189118
- 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 })));
189119
- TableRow$1.displayName = "TableRow";
189120
- 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 })));
189121
- TableHead.displayName = "TableHead";
189122
- 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 })));
189123
- TableCell$1.displayName = "TableCell";
189124
- const TableCaption = React.forwardRef(({ className, ...props }, ref) => (jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
189125
- TableCaption.displayName = "TableCaption";
189126
-
189127
190803
  const normalizeCustomDomains = (response) => {
189128
190804
  if (!response) {
189129
190805
  return [];
@@ -205059,15 +206735,15 @@ var mac$2 = typeof navigator != "undefined" && /Mac/.test(navigator.platform);
205059
206735
  var ie$1 = typeof navigator != "undefined" && /MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
205060
206736
 
205061
206737
  // Fill in the digit keys
205062
- 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);
205063
206739
 
205064
206740
  // The function keys
205065
- 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;
205066
206742
 
205067
206743
  // And the alphabetic keys
205068
- for (var i$9 = 65; i$9 <= 90; i$9++) {
205069
- base[i$9] = String.fromCharCode(i$9 + 32);
205070
- 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);
205071
206747
  }
205072
206748
 
205073
206749
  // For each code that doesn't have a shift-equivalent, copy the base name
@@ -219117,8 +220793,6 @@ function useAgentSearch({ tenantKey, query, limit = 6, offset, starred, featured
219117
220793
  };
219118
220794
  }
219119
220795
 
219120
- 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]}
219121
-
219122
220796
  /**
219123
220797
  * Higher-level agent search hook that manages search state, debounce, and
219124
220798
  * pagination controls on top of `useAgentSearch`.
@@ -255061,37 +256735,46 @@ const EMPTY_SKILL_FORM = {
255061
256735
  };
255062
256736
  /** Page size for both skill lists. */
255063
256737
  const SKILLS_PAGE_SIZE = 10;
255064
- // Resolving the agent's own set (assignment metadata + its private skills)
255065
- // needs a catalog lookup that the API cannot scope server-side yet (there is
255066
- // no `mentor=` filter), so one bounded background fetch covers the join.
255067
- const FULL_LIST_LIMIT = 1000;
255068
256738
  function AgentSkills({ platformKey, mentorUniqueId }) {
255069
- var _a, _b, _c, _d;
256739
+ var _a, _b, _c, _d, _e, _f;
255070
256740
  const t = useT();
255071
256741
  // Skills are managed independently of the sandbox connection — org-level
255072
256742
  // skills and the mentor's assignments both resolve without a wired Claw
255073
256743
  // instance, so we always fetch and let admins add/assign skills freely.
255074
- 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({
255075
256747
  org: platformKey,
255076
- limit: FULL_LIST_LIMIT,
255077
- offset: 0,
256748
+ mentorUniqueId,
256749
+ limit: SKILLS_PAGE_SIZE,
256750
+ offset: (agentPage - 1) * SKILLS_PAGE_SIZE,
255078
256751
  });
255079
- 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({
255080
256763
  org: platformKey,
255081
256764
  mentorUniqueId,
255082
- limit: FULL_LIST_LIMIT,
255083
- offset: 0,
255084
256765
  });
255085
- // 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.
255086
256770
  const [catalogPage, setCatalogPage] = useState(1);
255087
256771
  const { data: catalogSkillsPage, isLoading: isLoadingCatalog } = useGetAgentSkillsQuery({
255088
256772
  org: platformKey,
255089
- enabled: true,
255090
256773
  limit: SKILLS_PAGE_SIZE,
255091
256774
  offset: (catalogPage - 1) * SKILLS_PAGE_SIZE,
255092
256775
  });
255093
- const catalogSkills = (_a = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.results) !== null && _a !== void 0 ? _a : [];
255094
- 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;
255095
256778
  const catalogTotalPages = Math.max(1, Math.ceil(catalogTotalCount / SKILLS_PAGE_SIZE));
255096
256779
  useEffect(() => {
255097
256780
  if (catalogPage > catalogTotalPages)
@@ -255109,25 +256792,11 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255109
256792
  const [editingSkill, setEditingSkill] = useState(null);
255110
256793
  const [deletingSkill, setDeletingSkill] = useState(null);
255111
256794
  const [form, setForm] = useState(EMPTY_SKILL_FORM);
255112
- const isLoading = isLoadingSkills || isLoadingAssignments;
255113
- const isMutatingAgentSkill = isCreatingAssignment || isUpdatingAssignment || isDeletingAssignment || isUpdatingSkill;
255114
- const allSkills = (_c = joinCatalog === null || joinCatalog === void 0 ? void 0 : joinCatalog.results) !== null && _c !== void 0 ? _c : [];
255115
- const effectiveSkills = resolveEffectiveAgentSkills(allSkills, assignments !== null && assignments !== void 0 ? assignments : [], mentorUniqueId);
255116
- const effectiveUniqueIds = new Set(effectiveSkills.map((s) => s.unique_id));
255117
- const effectiveSlugs = new Set(effectiveSkills.map((s) => s.slug));
255118
- // The "Agent skills" tab pages the (small, per-agent) effective set locally.
255119
- const [agentPage, setAgentPage] = useState(1);
255120
- const agentTotalPages = Math.max(1, Math.ceil(effectiveSkills.length / SKILLS_PAGE_SIZE));
255121
- useEffect(() => {
255122
- if (agentPage > agentTotalPages)
255123
- setAgentPage(agentTotalPages);
255124
- }, [agentPage, agentTotalPages]);
255125
- const pagedEffectiveSkills = effectiveSkills.slice((agentPage - 1) * SKILLS_PAGE_SIZE, agentPage * SKILLS_PAGE_SIZE);
255126
- // Catalog rows already covered by the agent's set (attached, or shadowed by
255127
- // a same-slug private skill) show an "Added" chip instead of an Add button
255128
- // — hiding them would make server pages uneven. Another agent's private
255129
- // skills are not attachable at all.
255130
- 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);
255131
256800
  const isOtherAgentsPrivate = (skill) => !!skill.mentor && skill.mentor !== mentorUniqueId;
255132
256801
  // Featured skills from the `main` platform are read-only for tenants — the
255133
256802
  // API 404s writes against them, so hide the edit/delete affordances.
@@ -255137,29 +256806,20 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255137
256806
  return skill.platform_key ? skill.platform_key === platformKey : !skill.is_featured;
255138
256807
  };
255139
256808
  const resetForm = () => setForm(EMPTY_SKILL_FORM);
255140
- const handleToggleEffective = async (effective, shouldEnable) => {
256809
+ const handleToggleAssignment = async (assignment, shouldEnable) => {
255141
256810
  try {
255142
- if (effective.origin === 'private' && effective.skill) {
255143
- await updateSkill({
255144
- org: platformKey,
255145
- id: effective.skill.id,
255146
- enabled: shouldEnable,
255147
- }).unwrap();
255148
- }
255149
- else if (effective.assignmentId !== undefined) {
255150
- await updateAssignment({
255151
- org: platformKey,
255152
- mentorUniqueId,
255153
- assignmentPk: effective.assignmentId,
255154
- enabled: shouldEnable,
255155
- }).unwrap();
255156
- }
256811
+ await updateAssignment({
256812
+ org: platformKey,
256813
+ mentorUniqueId,
256814
+ assignmentPk: assignment.id,
256815
+ enabled: shouldEnable,
256816
+ }).unwrap();
255157
256817
  toast.success(t(shouldEnable
255158
256818
  ? 'clawSandboxAgentSkills.skillEnabled'
255159
- : 'clawSandboxAgentSkills.skillDisabled', { name: effective.name }));
256819
+ : 'clawSandboxAgentSkills.skillDisabled', { name: assignment.skill_name }));
255160
256820
  }
255161
256821
  catch (_a) {
255162
- toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: effective.name }));
256822
+ toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: assignment.skill_name }));
255163
256823
  }
255164
256824
  };
255165
256825
  const handleAttach = async (skill) => {
@@ -255176,19 +256836,30 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255176
256836
  toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: skill.name }));
255177
256837
  }
255178
256838
  };
255179
- const handleDetach = async (effective) => {
255180
- if (effective.assignmentId === undefined)
255181
- 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) => {
255182
256853
  try {
255183
256854
  await deleteAssignment({
255184
256855
  org: platformKey,
255185
256856
  mentorUniqueId,
255186
- assignmentPk: effective.assignmentId,
256857
+ assignmentPk: assignment.id,
255187
256858
  }).unwrap();
255188
- toast.success(t('clawSandboxAgentSkills.skillRemoved', { name: effective.name }));
256859
+ toast.success(t('clawSandboxAgentSkills.skillRemoved', { name: assignment.skill_name }));
255189
256860
  }
255190
256861
  catch (_a) {
255191
- toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: effective.name }));
256862
+ toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: assignment.skill_name }));
255192
256863
  }
255193
256864
  };
255194
256865
  const openNewSkillDialog = () => {
@@ -255219,7 +256890,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255219
256890
  return;
255220
256891
  }
255221
256892
  try {
255222
- await createSkill({
256893
+ const created = await createSkill({
255223
256894
  org: platformKey,
255224
256895
  name: form.name,
255225
256896
  slug: form.slug,
@@ -255230,6 +256901,16 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255230
256901
  instruction: form.instruction,
255231
256902
  enabled: true,
255232
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
+ }
255233
256914
  toast.success(t('clawSandboxAgentSkills.skillCreated'));
255234
256915
  closeAllDialogs();
255235
256916
  }
@@ -255244,6 +256925,10 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255244
256925
  toast.error(t('clawSandboxAgentSkills.nameAndSlugRequired'));
255245
256926
  return;
255246
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);
255247
256932
  try {
255248
256933
  await updateSkill({
255249
256934
  org: platformKey,
@@ -255254,8 +256939,18 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255254
256939
  version: form.version,
255255
256940
  category: form.category,
255256
256941
  instruction: form.instruction,
255257
- mentor: form.privateToAgent ? mentorUniqueId : null,
256942
+ ...(!privateLocked && { mentor: form.privateToAgent ? mentorUniqueId : null }),
255258
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
+ }
255259
256954
  toast.success(t('clawSandboxAgentSkills.skillUpdated'));
255260
256955
  closeAllDialogs();
255261
256956
  }
@@ -255275,46 +256970,40 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
255275
256970
  toast.error(t('clawSandboxAgentSkills.failedToDeleteSkill'));
255276
256971
  }
255277
256972
  };
255278
- const renderBadges = (skill, effective) => {
255279
- var _a, _b, _c;
255280
- 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;
255281
- 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;
255282
- 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;
255283
- 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') }))] }));
255284
- };
255285
- const renderSkillMenu = (skill, effective) => {
255286
- var _a, _b;
255287
- const editable = isSkillEditable(skill);
255288
- const showEdit = editable;
255289
- const showDelete = editable;
255290
- const detachable = (effective === null || effective === void 0 ? void 0 : effective.origin) === 'assignment';
255291
- 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))
255292
256979
  return null;
255293
- 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 : '';
255294
- 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')] }))] })] }));
255295
- };
255296
- if (isLoading) {
255297
- 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') })] }));
255298
- }
255299
- 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) => {
255300
- var _a;
255301
- 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
255302
- ? 'clawSandboxAgentSkills.skillEnabledLabel'
255303
- : 'clawSandboxAgentSkills.skillDisabledLabel', { name: effective.name }) }), renderSkillMenu(effective.skill, effective)] })] }, effective.unique_id));
255304
- }), 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', {
255305
256990
  name: skill.name,
255306
- }), 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
255307
256994
  ? t('clawSandboxAgentSkills.creating')
255308
- : 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
255309
256996
  ? t('clawSandboxAgentSkills.saving')
255310
- : 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
255311
256998
  ? t('clawSandboxAgentSkills.deleting')
255312
256999
  : t('clawSandboxAgentSkills.delete') })] })] })] }));
255313
257000
  }
255314
257001
  // ── Shared Skill Form ────────────────────────────────────────────────────────
255315
- function SkillForm({ form, onChange, }) {
257002
+ function SkillForm({ form, onChange, privateLocked = false, }) {
255316
257003
  const t = useT();
255317
- 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 }) })] })] }));
255318
257007
  }
255319
257008
 
255320
257009
  /** Distance (px) from the list bottom at which the next page is requested. */
@@ -255585,46 +257274,6 @@ function ProjectMentorsList({ projectMentors, onMentorRemoved, onAddMentorClick,
255585
257274
  }) }), showMinimumMentorAlert && (jsx(Suspense, { fallback: null, children: jsx(MinimumMentorAlert$1, { open: showMinimumMentorAlert, onOpenChange: setShowMinimumMentorAlert }) }))] }));
255586
257275
  }
255587
257276
 
255588
- const AgentSettingsContext = React__default.createContext(null);
255589
- function AgentSettingsProvider({ children, ...value }) {
255590
- const ctx = React__default.useMemo(() => value,
255591
- // eslint-disable-next-line react-hooks/exhaustive-deps
255592
- [
255593
- value.tenantKey,
255594
- value.mentorId,
255595
- value.username,
255596
- value.enableRBAC,
255597
- value.rbacPermissions,
255598
- value.executeGatedAction,
255599
- value.visibilityOptions,
255600
- ]);
255601
- return jsx(AgentSettingsContext.Provider, { value: ctx, children: children });
255602
- }
255603
- /**
255604
- * Read the shared agent identity / config from the nearest
255605
- * `<AgentSettingsProvider>` if one is present, returning `null` otherwise.
255606
- *
255607
- * Use this when a tab can also be driven by direct props (i.e. the
255608
- * `<AgentSettingsProvider>` wrapper is optional). For tabs that strictly
255609
- * require the provider, use `useAgentSettings`.
255610
- */
255611
- function useAgentSettingsOptional() {
255612
- return React__default.useContext(AgentSettingsContext);
255613
- }
255614
- /**
255615
- * Read the shared agent identity / config from the nearest
255616
- * `<AgentSettingsProvider>`. Throws if called outside a provider — this is
255617
- * intentional: every tab MUST be wrapped.
255618
- */
255619
- function useAgentSettings() {
255620
- const ctx = React__default.useContext(AgentSettingsContext);
255621
- if (!ctx) {
255622
- throw new Error('useAgentSettings must be used within an <AgentSettingsProvider>. ' +
255623
- 'Wrap your tab components with <AgentSettingsProvider tenantKey=... mentorId=... username=... enableRBAC=...>.');
255624
- }
255625
- return ctx;
255626
- }
255627
-
255628
257277
  function useDatasetsWithPagination(itemsPerPage = 5, opts = {}) {
255629
257278
  const { page: controlledPage, search: controlledSearch, onPageChange, onSearchChange } = opts;
255630
257279
  // Controlled/uncontrolled is decided per axis, independently.
@@ -258163,9 +259812,6 @@ var addMentorToProjectModal = /*#__PURE__*/Object.freeze({
258163
259812
  AddMentorToProjectModal: AddMentorToProjectModal
258164
259813
  });
258165
259814
 
258166
- 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') }) })));
258167
- CustomSwitch.displayName = Root$c.displayName;
258168
-
258169
259815
  const DeleteDatasetModal$1 = dynamic(() => Promise.resolve().then(function () { return deleteDatasetModal; }).then((mod) => ({
258170
259816
  default: mod.DeleteDatasetModal,
258171
259817
  })));
@@ -258290,36 +259936,6 @@ function DatasetItemList({ datasets, onSelect, selectedDatasetId, labels }) {
258290
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' }) })) }));
258291
259937
  }
258292
259938
 
258293
- /**
258294
- * Shared utilities for tab label systems.
258295
- *
258296
- * Every tab exports a `resolve*Labels(override?)` function that deep-merges
258297
- * a partial override into the default AGENT labels. This file provides the
258298
- * DeepPartial type and deepMerge implementation they all share.
258299
- */
258300
- function deepMerge(base, override) {
258301
- const result = { ...base };
258302
- const overrideBag = override;
258303
- for (const key of Object.keys(overrideBag)) {
258304
- const overrideValue = overrideBag[key];
258305
- if (overrideValue === undefined)
258306
- continue;
258307
- const baseValue = base[key];
258308
- if (baseValue &&
258309
- typeof baseValue === 'object' &&
258310
- !Array.isArray(baseValue) &&
258311
- overrideValue &&
258312
- typeof overrideValue === 'object' &&
258313
- !Array.isArray(overrideValue)) {
258314
- result[key] = deepMerge(baseValue, overrideValue);
258315
- }
258316
- else {
258317
- result[key] = overrideValue;
258318
- }
258319
- }
258320
- return result;
258321
- }
258322
-
258323
259939
  const AGENT_DATASETS_TAB_LABELS = {
258324
259940
  header: {
258325
259941
  title: 'Datasets',
@@ -258612,10 +260228,21 @@ const resourceTypes = [
258612
260228
  },
258613
260229
  ];
258614
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';
258615
260238
  const extractErrorMessage$1 = (error, fallback) => {
258616
- var _a, _b;
260239
+ var _a, _b, _c;
258617
260240
  const apiError = error;
258618
- 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;
258619
260246
  };
258620
260247
 
258621
260248
  function useGithubRepoDetails(url, tenantKey) {
@@ -260396,10 +262023,10 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
260396
262023
  onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('billing');
260397
262024
  }, className: `flex items-center gap-2 px-3 py-2 rounded-lg whitespace-nowrap text-sm transition-all ${activeTab === 'billing'
260398
262025
  ? 'bg-blue-50 text-blue-600 font-medium'
260399
- : '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: {
260400
262027
  overflowY: 'auto',
260401
262028
  overflowX: 'hidden',
260402
- }, 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, {})] }));
260403
262030
  }
260404
262031
 
260405
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, }) {
@@ -268957,20 +270584,6 @@ function AgentSafetyTab({ renderPromptContent, FlaggedPromptsModal, showFlaggedP
268957
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 : '' }))] }) })] }));
268958
270585
  }
268959
270586
 
268960
- /**
268961
- * Reusable header used at the top of every capability-gated agent-settings
268962
- * tab. It surfaces the capability's on/off toggle (previously buried in the
268963
- * Settings → Capabilities list) right where the feature is configured, with a
268964
- * naive-friendly explanation.
268965
- *
268966
- * The configuration UI below is always rendered so admins can preview what
268967
- * they'll get, but it's grayed out and made inert (`pointer-events-none`,
268968
- * `aria-hidden`) until the capability is switched on.
268969
- */
268970
- function CapabilityGate({ title, description, enabled, onToggle, disabled = false, ariaLabel, offHint, toggleTestId, children, }) {
268971
- 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 })] }));
268972
- }
268973
-
268974
270587
  const AGENT_PRIVACY_TAB_LABELS = {
268975
270588
  header: {
268976
270589
  title: 'Privacy',
@@ -271213,6 +272826,46 @@ function AgentGraderTab({ labels: labelsOverride, tenantKey: tenantKeyProp, ment
271213
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 }) })] })] })) })] }));
271214
272827
  }
271215
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
+
271216
272869
  function ApiKeyModal({ isOpen, onClose, apiKey, labels }) {
271217
272870
  const { copy, status } = useCopyToClipboard(800);
271218
272871
  const Icon = status === 'success' ? Check : Copy;
@@ -280843,6 +282496,27 @@ function Chat({ mode = 'default', isPreviewMode = false, hasBorder = true, isInC
280843
282496
  closeButton: true,
280844
282497
  });
280845
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]);
280846
282520
  const { showDisclaimerModal, isAgreeing, userAgreement, hasUserAgreement, handleDisclaimerAgree, checkAgreementAndExecute, executePendingSubmit, } = useUserAgreement({ mentorId, tenantKey });
280847
282521
  const { changeTab, activeTab, currentStreamingMessage, enabledGuidedPrompts, isStreaming, mentorName, messages, profileImage, sendMessage, setMessage, stopGenerating, uniqueMentorId, sessionId, startNewChat, enableSafetyDisclaimer, isPending, isLoadingChats, isConnected, refetchChats, } = useAdvancedChat({
280848
282522
  mentorId,
@@ -280883,6 +282557,7 @@ function Chat({ mode = 'default', isPreviewMode = false, hasBorder = true, isInC
280883
282557
  !(searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('token'))),
280884
282558
  mentorShareableToken: searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('token'),
280885
282559
  on402Error: handle402Error,
282560
+ onSpendCapError: handleSpendCapError,
280886
282561
  cachedSessionId,
280887
282562
  onStartNewChat: (sessionId) => {
280888
282563
  dispatch(chatActions.updateSessionIds(sessionId));
@@ -333207,5 +334882,5 @@ var index_es = /*#__PURE__*/Object.freeze({
333207
334882
  vectorsRatio: vectorsRatio
333208
334883
  });
333209
334884
 
333210
- 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 };
333211
334886
  //# sourceMappingURL=index.esm.js.map