diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index 2da52942477..b90e41040e2 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -108,8 +108,11 @@ const EnvironmentSchema = z DASHBOARD_AGENT_SECRET_KEY: z.string().optional(), // Global default for the `hasDashboardAgentAccess` flag. "0" (off) ships the // agent dark; flip to "1" to enable it for everyone at GA. Per-org overrides - // (org featureFlags) and admins/impersonators win regardless. + // (org featureFlags) win regardless. DASHBOARD_AGENT_ENABLED: z.string().default("0"), + // "1" gives admins/impersonators an everywhere-preview (default off), + // separate from the per-org rollout flag above. + DASHBOARD_AGENT_ADMIN_PREVIEW: z.string().default("0"), // Anthropic key for the dashboard agent's Head Start route only (the warm // first-turn step-1 LLM call runs in this process). The agent run itself // uses its own key on the Trigger side. When unset, Head Start is disabled diff --git a/apps/webapp/app/v3/canAccessDashboardAgent.server.ts b/apps/webapp/app/v3/canAccessDashboardAgent.server.ts index b453a694e71..dd3f4b0769a 100644 --- a/apps/webapp/app/v3/canAccessDashboardAgent.server.ts +++ b/apps/webapp/app/v3/canAccessDashboardAgent.server.ts @@ -5,10 +5,10 @@ import { makeFlag } from "~/v3/featureFlags.server"; /** * Whether the in-dashboard AI agent is available to this user in this org. - * Mirrors `canAccessAi`: admins/impersonators always pass, then the global / - * per-org feature flag with `DASHBOARD_AGENT_ENABLED` as the global default, so - * a per-org override (incl. disabling it) wins. Enforced server-side so a - * non-flagged user can't start sessions by hitting the resource route directly. + * Gated by the global / per-org `hasDashboardAgentAccess` flag, with + * `DASHBOARD_AGENT_ENABLED` as the global default (a per-org override wins). + * Admins/impersonators bypass it only when `DASHBOARD_AGENT_ADMIN_PREVIEW` is on + * (default off). Enforced server-side so a non-flagged user can't start sessions. */ export async function canAccessDashboardAgent(options: { userId: string; @@ -22,7 +22,7 @@ export async function canAccessDashboardAgent(options: { }): Promise { const { userId, isAdmin, isImpersonating, organizationSlug, orgFeatureFlags } = options; - if (isAdmin || isImpersonating) { + if ((isAdmin || isImpersonating) && env.DASHBOARD_AGENT_ADMIN_PREVIEW === "1") { return true; } diff --git a/apps/webapp/app/v3/featureFlags.ts b/apps/webapp/app/v3/featureFlags.ts index bcbdd09b258..46434bebf30 100644 --- a/apps/webapp/app/v3/featureFlags.ts +++ b/apps/webapp/app/v3/featureFlags.ts @@ -26,7 +26,7 @@ export const FeatureFlagCatalog = { [FEATURE_FLAG.hasLogsPageAccess]: z.coerce.boolean(), [FEATURE_FLAG.hasAiAccess]: z.coerce.boolean(), // Gates the in-dashboard AI agent panel. Controllable globally and per-org - // (org wins); admins/impersonators always see it. Defaults off via DASHBOARD_AGENT_ENABLED. + // (org wins). Defaults off via DASHBOARD_AGENT_ENABLED. [FEATURE_FLAG.hasDashboardAgentAccess]: z.coerce.boolean(), [FEATURE_FLAG.hasComputeAccess]: z.coerce.boolean(), [FEATURE_FLAG.hasPrivateConnections]: z.coerce.boolean(),