diff --git a/.github/workflows/orphaned-features-check.yml b/.github/workflows/orphaned-features-check.yml
index b3254e06ce9e..ee239cebf867 100644
--- a/.github/workflows/orphaned-features-check.yml
+++ b/.github/workflows/orphaned-features-check.yml
@@ -60,10 +60,7 @@ jobs:
exit 0
fi
- # Why only 5?
- # Because, we're not in a hurry and anything larger than that would
- # make the PR too intimidatingly big to review.
- npm run find-orphaned-features -- delete --max 5 --verbose /tmp/orphaned-features.json
+ npm run find-orphaned-features -- delete --verbose /tmp/orphaned-features.json
git status
diff --git a/assets/images/social-cards/actions.png b/assets/images/social-cards/actions.png
new file mode 100644
index 000000000000..e9e60b7cc489
Binary files /dev/null and b/assets/images/social-cards/actions.png differ
diff --git a/assets/images/social-cards/copilot.png b/assets/images/social-cards/copilot.png
new file mode 100644
index 000000000000..9278a93657f6
Binary files /dev/null and b/assets/images/social-cards/copilot.png differ
diff --git a/assets/images/social-cards/default.png b/assets/images/social-cards/default.png
new file mode 100644
index 000000000000..715cbb8e3efa
Binary files /dev/null and b/assets/images/social-cards/default.png differ
diff --git a/assets/images/social-cards/issues.png b/assets/images/social-cards/issues.png
new file mode 100644
index 000000000000..afedca548a4d
Binary files /dev/null and b/assets/images/social-cards/issues.png differ
diff --git a/assets/images/social-cards/security.png b/assets/images/social-cards/security.png
new file mode 100644
index 000000000000..25c557b0e574
Binary files /dev/null and b/assets/images/social-cards/security.png differ
diff --git a/data/reusables/copilot/differences-cfi-cfb-table.md b/data/reusables/copilot/differences-cfi-cfb-table.md
index a42c3ac890cf..14ec865dd40a 100644
--- a/data/reusables/copilot/differences-cfi-cfb-table.md
+++ b/data/reusables/copilot/differences-cfi-cfb-table.md
@@ -3,7 +3,7 @@
| | {% data variables.product.prodname_copilot_free_short %} | {% data variables.product.prodname_copilot_pro_short %} | {% data variables.product.prodname_copilot_pro_plus_short %} | {% data variables.product.prodname_copilot_business_short %} | {% data variables.product.prodname_copilot_enterprise_short %} |
| --- | --- | --- | --- | --- | --- |
| Pricing | Not applicable | {% data variables.copilot.cfi_price_per_month %} per month, or
{% data variables.copilot.cfi_price_per_year %} per year
(free for some users) | {% data variables.copilot.cpp_price_per_month %} per month, or
{% data variables.copilot.cpp_price_per_year %} per year
| {% data variables.copilot.cfb_price_per_month %} per granted seat per month | {% data variables.copilot.ce_price_per_month %} per granted seat per month |
-| Premium requests | 50 per month | 300 per month | 1500 per month | 300 per month | 1000 per month |
+| Premium requests | 50 per month | 300 per month | 1500 per month | 300 per user per month | 1000 per user per month |
| Purchase additional premium requests at $0.04/request| {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
{% endrowheaders %}
@@ -89,8 +89,8 @@
| --- | --- | --- | --- | --- | --- |
| {% data variables.product.prodname_copilot_for_prs %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| Audit logs | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} |{% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
-| {% data variables.product.prodname_copilot_short %} knowledge bases |{% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
-| Fine tuning a custom large language model (limited {% data variables.release-phases.public_preview %})[^6] | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
+| {% data variables.product.prodname_copilot_short %} knowledge bases |{% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} |
+| Fine tuning a custom large language model (limited {% data variables.release-phases.public_preview %})[^6] | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.product.prodname_copilot_cli_short %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
{% endrowheaders %}
diff --git a/data/reusables/copilot/premium-requests-for-enterprises.md b/data/reusables/copilot/premium-requests-for-enterprises.md
index 1f616968082d..84c3462bab5c 100644
--- a/data/reusables/copilot/premium-requests-for-enterprises.md
+++ b/data/reusables/copilot/premium-requests-for-enterprises.md
@@ -1,6 +1,6 @@
Each {% data variables.product.prodname_copilot_short %} plan includes a per-user allowance for premium requests:
-* 300 requests per month for {% data variables.product.prodname_copilot_business_short %}
-* 1000 requests per month for {% data variables.product.prodname_copilot_enterprise_short %}
+* 300 requests per user per month for {% data variables.product.prodname_copilot_business_short %}
+* 1000 requests per user per month for {% data variables.product.prodname_copilot_enterprise_short %}
{% data variables.product.prodname_copilot_chat_short %}, {% data variables.product.prodname_copilot_agent_short %} mode, {% data variables.product.prodname_copilot_short %} code review, and {% data variables.product.prodname_copilot_extensions_short %} use premium requests, with usage varying by model.
diff --git a/src/frame/components/DefaultLayout.tsx b/src/frame/components/DefaultLayout.tsx
index 57ca23c59120..65656ee71a9a 100644
--- a/src/frame/components/DefaultLayout.tsx
+++ b/src/frame/components/DefaultLayout.tsx
@@ -59,6 +59,20 @@ export const DefaultLayout = (props: Props) => {
const metaDescription = page.introPlainText ? page.introPlainText : t('default_description')
+ const SOCIAL_CATEGORIES = new Set(['security', 'actions', 'issues', 'copilot'])
+ const SOCIAL_CARD_IMG_BASE_URL = '/assets/cb-345/images/social-cards'
+
+ function getCategoryImageUrl(category: string): string {
+ return `${SOCIAL_CARD_IMG_BASE_URL}/${category}.png`
+ }
+
+ function getSocialCardImage(): string {
+ if (currentProduct && SOCIAL_CATEGORIES.has(currentProduct.id)) {
+ return getCategoryImageUrl(currentProduct.id)
+ }
+ return getCategoryImageUrl('default')
+ }
+
return (
@@ -111,10 +125,7 @@ export const DefaultLayout = (props: Props) => {
-
+
>
)}
diff --git a/src/frame/components/ui/MarkdownContent/UnrenderedMarkdownContent.tsx b/src/frame/components/ui/MarkdownContent/UnrenderedMarkdownContent.tsx
index 7fec3217e515..ac54ebe8c0fc 100644
--- a/src/frame/components/ui/MarkdownContent/UnrenderedMarkdownContent.tsx
+++ b/src/frame/components/ui/MarkdownContent/UnrenderedMarkdownContent.tsx
@@ -60,6 +60,15 @@ export const UnrenderedMarkdownContent = ({
{...props}
href={href}
target={openLinksInNewTab ? '_blank' : undefined}
+ rel={openLinksInNewTab ? 'noopener noreferrer' : undefined}
+ onClick={(e) => {
+ // For some reason we need to override the default onClick to get these links to open in a new tab
+ if (openLinksInNewTab) {
+ e.stopPropagation()
+ e.preventDefault()
+ window.open(href, '_blank')
+ }
+ }}
data-group-key={eventGroupKey}
data-group-id={eventGroupId}
>
diff --git a/src/shielding/middleware/rate-limit.ts b/src/shielding/middleware/rate-limit.ts
index e3e1712216a7..e093a17f5995 100644
--- a/src/shielding/middleware/rate-limit.ts
+++ b/src/shielding/middleware/rate-limit.ts
@@ -14,7 +14,7 @@ if (isNaN(MAX)) {
}
// We apply this rate limiter to _all_ routes in src/shielding/index.ts except for `/api/*` routes
-export function createRateLimiter(max = MAX, isAPILimiter = false) {
+export function createRateLimiter(max = MAX) {
return rateLimit({
// 1 minute
windowMs: EXPIRES_IN_AS_SECONDS * 1000,
@@ -47,14 +47,9 @@ export function createRateLimiter(max = MAX, isAPILimiter = false) {
return true
}
- // We handle /api/* routes with a separate rate limiter
- // When it is a separate rate limiter, isAPILimiter will be passed as true
- if (req.path.startsWith('/api/') || isAPILimiter) {
- return false
- }
-
- // If the request is not suspicious, don't rate limit it
- if (!isSuspiciousRequest(req)) {
+ // If the query string looks totally regular and is not a
+ // search endpoint, then skip
+ if (!isSuspiciousSearchRequest(req)) {
return true
}
@@ -137,7 +132,14 @@ const MISC_KEYS = [
* @param {Request} req
* @returns boolean
*/
-function isSuspiciousRequest(req: Request) {
+function isSuspiciousSearchRequest(req: Request) {
+ if (
+ req.originalUrl.includes('/api/search') ||
+ req.originalUrl.includes('/api/ai-search') ||
+ req.originalUrl.includes('/api/combined-search')
+ )
+ return false
+
const keys = Object.keys(req.query)
// Since this function can only speculate by query strings (at the