From d638b1aaed2f657ca6f1f263133a68d7ff29a2c2 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:29:20 +1000 Subject: [PATCH] chore: gate Coder Agents app and port tabs behind experiment (#26395) The workspace-app and port preview tabs in the Coder Agents right panel were previously gated behind a `devel` prerelease build check, which can't be toggled in real deployments. This replaces that check with a proper `agent-app-tabs` deployment experiment, registered in `ExperimentsKnown`, so the feature can be enabled via `CODER_EXPERIMENTS=agent-app-tabs` like any other experiment. The frontend now reads `experiments.includes("agent-app-tabs")` from the dashboard instead of `getPrereleaseFlag(buildInfo) === "devel"`. Depends on #26208 --- coderd/apidoc/docs.go | 10 +++++++--- coderd/apidoc/swagger.json | 10 +++++++--- codersdk/deployment.go | 4 ++++ docs/reference/api/schemas.md | 6 +++--- site/src/api/typesGenerated.ts | 2 ++ site/src/pages/AgentsPage/AgentChatPageView.tsx | 12 +++++++----- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 66e8b9f231..7e1f4afd5a 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -19344,10 +19344,12 @@ const docTemplate = `{ "workspace-build-updates", "nats_pubsub", "minimum-implicit-member", - "ai-gateway-cost-control" + "ai-gateway-cost-control", + "agent-app-tabs" ], "x-enum-comments": { "ExperimentAIGatewayCostControl": "Enables AI Gateway cost control functionality.", + "ExperimentAgentAppTabs": "Enables workspace-app and port preview tabs in the Coder Agents right panel.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentExample": "This isn't used for anything.", "ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.", @@ -19368,7 +19370,8 @@ const docTemplate = `{ "Enables publishing workspace build updates to the all builds pubsub channel.", "Enables embedded NATS pubsub.", "Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts.", - "Enables AI Gateway cost control functionality." + "Enables AI Gateway cost control functionality.", + "Enables workspace-app and port preview tabs in the Coder Agents right panel." ], "x-enum-varnames": [ "ExperimentExample", @@ -19380,7 +19383,8 @@ const docTemplate = `{ "ExperimentWorkspaceBuildUpdates", "ExperimentNATSPubsub", "ExperimentMinimumImplicitMember", - "ExperimentAIGatewayCostControl" + "ExperimentAIGatewayCostControl", + "ExperimentAgentAppTabs" ] }, "codersdk.ExternalAPIKeyScopes": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 8a6ebb0edf..f791b142d5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -17554,10 +17554,12 @@ "workspace-build-updates", "nats_pubsub", "minimum-implicit-member", - "ai-gateway-cost-control" + "ai-gateway-cost-control", + "agent-app-tabs" ], "x-enum-comments": { "ExperimentAIGatewayCostControl": "Enables AI Gateway cost control functionality.", + "ExperimentAgentAppTabs": "Enables workspace-app and port preview tabs in the Coder Agents right panel.", "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", "ExperimentExample": "This isn't used for anything.", "ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.", @@ -17578,7 +17580,8 @@ "Enables publishing workspace build updates to the all builds pubsub channel.", "Enables embedded NATS pubsub.", "Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts.", - "Enables AI Gateway cost control functionality." + "Enables AI Gateway cost control functionality.", + "Enables workspace-app and port preview tabs in the Coder Agents right panel." ], "x-enum-varnames": [ "ExperimentExample", @@ -17590,7 +17593,8 @@ "ExperimentWorkspaceBuildUpdates", "ExperimentNATSPubsub", "ExperimentMinimumImplicitMember", - "ExperimentAIGatewayCostControl" + "ExperimentAIGatewayCostControl", + "ExperimentAgentAppTabs" ] }, "codersdk.ExternalAPIKeyScopes": { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index f1d91d2897..9caba9d1eb 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -5154,6 +5154,7 @@ const ( ExperimentNATSPubsub Experiment = "nats_pubsub" // Enables embedded NATS pubsub. ExperimentMinimumImplicitMember Experiment = "minimum-implicit-member" // Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts. ExperimentAIGatewayCostControl Experiment = "ai-gateway-cost-control" // Enables AI Gateway cost control functionality. + ExperimentAgentAppTabs Experiment = "agent-app-tabs" // Enables workspace-app and port preview tabs in the Coder Agents right panel. ) func (e Experiment) DisplayName() string { @@ -5178,6 +5179,8 @@ func (e Experiment) DisplayName() string { return "Gateway Accounts (minimum implicit member)" case ExperimentAIGatewayCostControl: return "AI Gateway Cost Control" + case ExperimentAgentAppTabs: + return "Coder Agents App and Port Tabs" default: // Split on hyphen and convert to title case // e.g. "mcp-server-http" -> "Mcp Server Http" @@ -5198,6 +5201,7 @@ var ExperimentsKnown = Experiments{ ExperimentWorkspaceBuildUpdates, ExperimentMinimumImplicitMember, ExperimentAIGatewayCostControl, + ExperimentAgentAppTabs, } // ExperimentsSafe should include all experiments that are safe for diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index d691e36f5f..f9abd8b0be 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -6999,9 +6999,9 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o #### Enumerated Values -| Value(s) | -|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `ai-gateway-cost-control`, `auto-fill-parameters`, `example`, `mcp-server-http`, `minimum-implicit-member`, `nats_pubsub`, `notifications`, `oauth2`, `workspace-build-updates`, `workspace-usage` | +| Value(s) | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `agent-app-tabs`, `ai-gateway-cost-control`, `auto-fill-parameters`, `example`, `mcp-server-http`, `minimum-implicit-member`, `nats_pubsub`, `notifications`, `oauth2`, `workspace-build-updates`, `workspace-usage` | ## codersdk.ExternalAPIKeyScopes diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index a5ced43139..2c519f8afe 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -4379,6 +4379,7 @@ export const EntitlementsWarningHeader = "X-Coder-Entitlements-Warning"; // From codersdk/deployment.go export type Experiment = | "ai-gateway-cost-control" + | "agent-app-tabs" | "auto-fill-parameters" | "example" | "mcp-server-http" @@ -4391,6 +4392,7 @@ export type Experiment = export const Experiments: Experiment[] = [ "ai-gateway-cost-control", + "agent-app-tabs", "auto-fill-parameters", "example", "mcp-server-http", diff --git a/site/src/pages/AgentsPage/AgentChatPageView.tsx b/site/src/pages/AgentsPage/AgentChatPageView.tsx index 7dadaf5988..ef85af316c 100644 --- a/site/src/pages/AgentsPage/AgentChatPageView.tsx +++ b/site/src/pages/AgentsPage/AgentChatPageView.tsx @@ -23,7 +23,6 @@ import { isWorkspaceAppEmbeddable } from "#/modules/apps/apps"; import { WorkspaceAppFrame } from "#/modules/apps/WorkspaceAppFrame"; import { findWorkspaceAppWithAgent } from "#/modules/apps/workspaceApps"; import { useDashboard } from "#/modules/dashboard/useDashboard"; -import { getPrereleaseFlag } from "#/utils/buildInfo"; import { cn } from "#/utils/cn"; import { pageTitle } from "#/utils/page"; import { findWorkspaceAgent } from "#/utils/workspace"; @@ -378,10 +377,7 @@ export const AgentChatPageView: FC = ({ const queryClient = useQueryClient(); const { proxy } = useProxy(); const wildcardHostname = proxy.preferredWildcardHostname; - const { buildInfo } = useDashboard(); - // Workspace app and port preview tabs are experimental and limited to - // devel builds for now. Terminal tabs are generally available. - const userAppTabsEnabled = getPrereleaseFlag(buildInfo) === "devel"; + const { experiments } = useDashboard(); const canOpenChatSharing = canShareChat && organizationId !== undefined; @@ -488,6 +484,12 @@ export const AgentChatPageView: FC = ({ const availableDesktopChatId = workspace && workspaceAgent ? desktopChatId : undefined; + // Workspace app and port preview tabs are gated behind the agent-app-tabs + // experiment. Terminal tabs are generally available. Derived after all hook + // calls so the React Compiler keeps the tab list and the tab-open handlers + // below in a single memoization scope. + const userAppTabsEnabled = experiments.includes("agent-app-tabs"); + // When app and port tabs are gated off, persisted tabs of those kinds // are hidden rather than deleted; the save effect persists the raw tab // state, so they reappear if the gate lifts.