diff --git a/enterprise/coderd/license/license.go b/enterprise/coderd/license/license.go index b58ee4e79a..13fc7afb1b 100644 --- a/enterprise/coderd/license/license.go +++ b/enterprise/coderd/license/license.go @@ -347,12 +347,6 @@ func LicensesEntitlements( keys map[string]ed25519.PublicKey, featureArguments FeatureArguments, ) (codersdk.Entitlements, error) { - // TODO: Remove this tracking once AI Bridge is enforced as an add-on license. - // Track if AI Bridge was explicitly granted via license Features (add-on) - // vs inherited from FeatureSet (Premium). Only explicit grants should - // suppress the soft warning for AI Bridge GA. - hasExplicitAIBridgeEntitlement := false - // Each valid license's FeatureUserLimit claim forms a candidate pairing of // seat limit and counting mode: licenses carrying the AI Governance // addon count workspace-capable users, others count all active users. @@ -514,15 +508,6 @@ func LicensesEntitlements( }) } - // TODO: Remove this tracking once AI Bridge is enforced as an add-on license. - // Track explicit AI Bridge entitlement (add-on license). This is checked - // at the license level since AI Bridge may come from the FeatureSet - // (Premium) rather than being explicitly listed in claims.Features. - // Only having the AI Governance addon should suppress the soft warning. - if slices.Contains(claims.Addons, codersdk.AddonAIGovernance) { - hasExplicitAIBridgeEntitlement = true - } - // Add all features from the feature set. for _, featureName := range claims.FeatureSet.Features() { if featureName.UsesLimit() || featureName.UsesUsagePeriod() { @@ -924,17 +909,6 @@ func LicensesEntitlements( default: } } - - // TODO: Remove this soft warning block once AI Bridge is enforced as an add-on license. - // AI Bridge soft warning: Show warning when AI Bridge is enabled and - // entitled via Premium FeatureSet but not via explicit add-on license. - // This is a transitional warning as AI Bridge moves to GA and will - // require a separate add-on license in future versions. - aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] - if aiBridgeFeature.Enabled && aiBridgeFeature.Entitlement.Entitled() && !hasExplicitAIBridgeEntitlement { - entitlements.Warnings = append(entitlements.Warnings, - "The AI Governance add-on is required to use AI Gateway. Please reach out to your account team or sales@coder.com to learn more.") - } } // Wrap up by disabling all features that are not entitled. diff --git a/enterprise/coderd/license/license_test.go b/enterprise/coderd/license/license_test.go index e52eaab094..7c28710a41 100644 --- a/enterprise/coderd/license/license_test.go +++ b/enterprise/coderd/license/license_test.go @@ -57,9 +57,6 @@ func premiumRuntimeHoursFixture(t *testing.T) (*dbmock.MockStore, *coderdenttest // warning cannot pollute the callers' warning assertions. GraceAt: dbtime.Now().Add(time.Hour * 24 * 60).Truncate(time.Second), ExpiresAt: dbtime.Now().Add(time.Hour * 24 * 90).Truncate(time.Second), - // The addon marks AI Bridge as explicitly entitled, suppressing - // the unrelated "AI Governance add-on is required to use AI - // Gateway" warning that Premium would otherwise produce. }).UserLimit(100).AIGovernanceAddon(100).AgentRuntimeHours(100, ptr.Ref[int64](80), ptr.Ref[int64](120)) lic := database.License{ @@ -2301,163 +2298,6 @@ func TestLicenseEntitlements(t *testing.T) { } } -func TestAIBridgeSoftWarning(t *testing.T) { - t.Parallel() - - aiBridgeEnabledEnablements := map[codersdk.FeatureName]bool{ - codersdk.FeatureAIBridge: true, - } - - aiBridgeDisabledEnablements := map[codersdk.FeatureName]bool{ - codersdk.FeatureAIBridge: false, - } - - aiBridgeWarningMessage := "The AI Governance add-on is required to use AI Gateway. Please reach out to your account team or sales@coder.com to learn more." - - // A Premium license grants a managed agent limit and a grandfathered - // agent runtime allocation by default: a nil AgentRuntimeMsFn is a hard - // developer error and a nil ManagedAgentCountFn degrades into an - // entitlements error, so these subtests wire zero-usage closures. - zeroUsageArgs := license.FeatureArguments{ - ManagedAgentCountFn: func(_ context.Context, _, _ time.Time) (int64, error) { - return 0, nil - }, - AgentRuntimeMsFn: func(_ context.Context, _, _ time.Time) (int64, error) { - return 0, nil - }, - } - - t.Run("NoAddon_AIBridgeOff", func(t *testing.T) { - t.Parallel() - // License without addon and AI Bridge disabled should NOT show warning. - lo := (&coderdenttest.LicenseOptions{ - AccountType: "salesforce", - AccountID: "test", - FeatureSet: codersdk.FeatureSetPremium, - }).Valid(time.Now()) - - generatedLicenses := []database.License{ - { - ID: 1, - UploadedAt: time.Now().Add(time.Hour * -1), - JWT: lo.Generate(t), - Exp: lo.GraceAt, - UUID: uuid.New(), - }, - } - - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeDisabledEnablements, coderdenttest.Keys, zeroUsageArgs) - require.NoError(t, err) - - aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] - assert.False(t, aiBridgeFeature.Enabled) - require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage) - }) - - t.Run("NoAddon_AIBridgeOn", func(t *testing.T) { - t.Parallel() - // License without addon and AI Bridge enabled SHOULD show warning. - lo := (&coderdenttest.LicenseOptions{ - AccountType: "salesforce", - AccountID: "test", - FeatureSet: codersdk.FeatureSetPremium, - }).Valid(time.Now()) - - generatedLicenses := []database.License{ - { - ID: 1, - UploadedAt: time.Now().Add(time.Hour * -1), - JWT: lo.Generate(t), - Exp: lo.GraceAt, - UUID: uuid.New(), - }, - } - - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeEnabledEnablements, coderdenttest.Keys, zeroUsageArgs) - require.NoError(t, err) - - aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] - assert.True(t, aiBridgeFeature.Enabled) - assert.Equal(t, codersdk.EntitlementEntitled, aiBridgeFeature.Entitlement) - require.Contains(t, entitlements.Warnings, aiBridgeWarningMessage) - }) - - t.Run("Addon_AIBridgeOff", func(t *testing.T) { - t.Parallel() - // License with addon and AI Bridge disabled should NOT show warning. - lo := (&coderdenttest.LicenseOptions{ - AccountType: "salesforce", - AccountID: "test", - FeatureSet: codersdk.FeatureSetPremium, - Addons: []codersdk.Addon{codersdk.AddonAIGovernance}, - Features: license.Features{ - codersdk.FeatureAIGovernanceUserLimit: 100, - }, - }).Valid(time.Now()) - - generatedLicenses := []database.License{ - { - ID: 1, - UploadedAt: time.Now().Add(time.Hour * -1), - JWT: lo.Generate(t), - Exp: lo.GraceAt, - UUID: uuid.New(), - }, - } - - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeDisabledEnablements, coderdenttest.Keys, zeroUsageArgs) - require.NoError(t, err) - - aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] - assert.False(t, aiBridgeFeature.Enabled) - require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage) - }) - - t.Run("Addon_AIBridgeOn", func(t *testing.T) { - t.Parallel() - // License with addon and AI Bridge enabled should NOT show warning. - lo := (&coderdenttest.LicenseOptions{ - AccountType: "salesforce", - AccountID: "test", - FeatureSet: codersdk.FeatureSetPremium, - Addons: []codersdk.Addon{codersdk.AddonAIGovernance}, - Features: license.Features{ - codersdk.FeatureAIGovernanceUserLimit: 100, - }, - }).Valid(time.Now()) - - generatedLicenses := []database.License{ - { - ID: 1, - UploadedAt: time.Now().Add(time.Hour * -1), - JWT: lo.Generate(t), - Exp: lo.GraceAt, - UUID: uuid.New(), - }, - } - - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), generatedLicenses, aiBridgeEnabledEnablements, coderdenttest.Keys, zeroUsageArgs) - require.NoError(t, err) - - aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] - assert.True(t, aiBridgeFeature.Enabled) - assert.Equal(t, codersdk.EntitlementEntitled, aiBridgeFeature.Entitlement) - require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage) - }) - - t.Run("NoLicense_AIBridgeOn", func(t *testing.T) { - t.Parallel() - // No license with AI Bridge enabled should NOT show the soft warning - // (it will show the generic "not entitled" warning instead). - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), []database.License{}, aiBridgeEnabledEnablements, coderdenttest.Keys, zeroUsageArgs) - require.NoError(t, err) - - aiBridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] - assert.Equal(t, codersdk.EntitlementNotEntitled, aiBridgeFeature.Entitlement) - require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage) - }) -} - func TestUsageLimitFeatures(t *testing.T) { t.Parallel() @@ -3781,8 +3621,6 @@ func TestAIGovernanceAddon(t *testing.T) { // AI Bridge should be enabled without warning when addon is present. aibridgeFeature := entitlements.Features[codersdk.FeatureAIBridge] require.True(t, aibridgeFeature.Enabled, "AI Bridge should be enabled when addon is present and enablements are set") - aiBridgeWarningMessage := "The AI Governance add-on is required to use AI Gateway. Please reach out to your account team or sales@coder.com to learn more." - require.NotContains(t, entitlements.Warnings, aiBridgeWarningMessage, "AI Bridge warning should not appear when AI Governance addon is present") // require.Equal(t, codersdk.EntitlementEntitled, aibridgeFeature.Entitlement, "AI Bridge should be entitled when addon is present")