diff --git a/packages/types/src/deployment-configuration.ts b/packages/types/src/deployment-configuration.ts index 4423d557827..86bc150a212 100644 --- a/packages/types/src/deployment-configuration.ts +++ b/packages/types/src/deployment-configuration.ts @@ -11,7 +11,6 @@ export type ClientDeploymentConfiguration = { MEASUREMENT_ID?: string; BACKEND_API_ENDPOINT?: string; ENVIRONMENT_NAME?: string; - ENABLE_NEW_URL_SCHEME?: boolean; ENABLE_SHARING_2?: boolean; GOOGLE_OAUTH_TOKEN_ENDPOINT?: string; GOOGLE_OAUTH_AUTH_ENDPOINT?: string; @@ -35,7 +34,7 @@ export type ClientDeploymentConfiguration = { * value is "false" */ ALLOW_3P_MODULES?: boolean; - ENABLE_EMAIL_OPT_IN?: boolean; + FAKE_MODE?: boolean; SHARE_SURFACE_URL_TEMPLATES: Record; domains?: Record; diff --git a/packages/types/src/flags.ts b/packages/types/src/flags.ts index 5796be43136..32bbfe3de44 100644 --- a/packages/types/src/flags.ts +++ b/packages/types/src/flags.ts @@ -45,27 +45,7 @@ export type RuntimeFlags = { * Enables output templates for consistent output. */ outputTemplates: boolean; - /** - * Requres users consent to use of get_webpage tool - */ - requireConsentForGetWebpage: boolean; - /** - * Requres users consent to use of open_webpage tool - */ - requireConsentForOpenWebpage: boolean; - /** - * Enables SSE streaming for planner calls. - */ - streamPlanner: boolean; - /** - * Enables SSE streaming for GenerateWebpage (output node HTML generation). - */ - streamGenWebpage: boolean; - /** - * Enables the "Add from Drive" option in lite mode (it is always enabled in - * non-lite mode). - */ - enableDrivePickerInLiteMode: boolean; + /** * Enables "export to Drive" capability for the agent */ @@ -159,26 +139,7 @@ export const RUNTIME_FLAG_META: Record = { title: "Output Templates", description: "Enable output templates for consistent output", }, - requireConsentForGetWebpage: { - title: "Consent for Get Webpage", - description: "Require user consent to use the get_webpage tool", - }, - requireConsentForOpenWebpage: { - title: "Consent for Open Webpage", - description: "Require user consent to use the open_webpage tool", - }, - streamPlanner: { - title: "Stream Planner", - description: "Enable SSE streaming for planner calls", - }, - streamGenWebpage: { - title: "Stream Generate Webpage", - description: "Enable SSE streaming for HTML generation", - }, - enableDrivePickerInLiteMode: { - title: "Drive Picker (Lite Mode)", - description: "Enable 'Add from Drive' in lite mode", - }, + enableGoogleDriveTools: { title: "Google Drive Tools", description: "Enable 'export to Drive' capability", diff --git a/packages/unified-server/src/config.ts b/packages/unified-server/src/config.ts index c594f0e3ff3..61b02e11b5a 100644 --- a/packages/unified-server/src/config.ts +++ b/packages/unified-server/src/config.ts @@ -58,9 +58,9 @@ export async function createClientConfig(opts: { SHELL_GUEST_ORIGIN: flags.SHELL_GUEST_ORIGIN, SHELL_HOST_ORIGINS: flags.SHELL_HOST_ORIGINS, SHELL_PREFIX: flags.SHELL_PREFIX, - ENABLE_EMAIL_OPT_IN: flags.ENABLE_EMAIL_OPT_IN, + SHARE_SURFACE_URL_TEMPLATES: flags.SHARE_SURFACE_URL_TEMPLATES, - ENABLE_NEW_URL_SCHEME: flags.ENABLE_NEW_URL_SCHEME, + ENABLE_SHARING_2: flags.ENABLE_SHARING_2, FAKE_MODE: flags.FAKE_MODE, domains: flags.DOMAIN_CONFIG, @@ -72,12 +72,7 @@ export async function createClientConfig(opts: { opalAdk: flags.ENABLE_OPAL_ADK, outputTemplates: flags.ENABLE_OUTPUT_TEMPLATES, googleOne: flags.ENABLE_GOOGLE_ONE, - requireConsentForGetWebpage: flags.ENABLE_REQUIRE_CONSENT_FOR_GET_WEBPAGE, - requireConsentForOpenWebpage: - flags.ENABLE_REQUIRE_CONSENT_FOR_OPEN_WEBPAGE, - streamPlanner: flags.STREAM_PLANNER, - streamGenWebpage: flags.ENABLE_STREAM_GEN_WEBPAGE, - enableDrivePickerInLiteMode: flags.ENABLE_DRIVE_PICKER_IN_LITE_MODE, + enableGoogleDriveTools: flags.ENABLE_GOOGLE_DRIVE_TOOLS, enableNotebookLm: flags.ENABLE_NOTEBOOK_LM, enableResumeAgentRun: flags.ENABLE_RESUME_AGENT_RUN, diff --git a/packages/unified-server/src/flags.ts b/packages/unified-server/src/flags.ts index 3ca65063318..7e11869b4b6 100644 --- a/packages/unified-server/src/flags.ts +++ b/packages/unified-server/src/flags.ts @@ -38,14 +38,8 @@ export const ENABLE_FORCE_2D_GRAPH: boolean = getBoolean( export const ENABLE_SHARING_2: boolean = getBoolean("ENABLE_SHARING_2"); -export const ENABLE_STREAM_GEN_WEBPAGE: boolean = getBoolean( - "ENABLE_STREAM_GEN_WEBPAGE" -); - export const ENABLE_AGENT_MODE: boolean = getBoolean("ENABLE_AGENT_MODE"); -export const STREAM_PLANNER: boolean = getBoolean("ENABLE_STREAM_PLANNER"); - export const OBSERVE_SYSTEM_THEME = getBoolean("OBSERVE_SYSTEM_THEME"); export const ENABLE_CONSISTENT_UI: boolean = getBoolean("ENABLE_CONSISTENT_UI"); @@ -124,30 +118,15 @@ export const SHELL_HOST_ORIGINS = getStringList("SHELL_HOST_ORIGINS"); export const SHELL_PREFIX = getString("SHELL_PREFIX"); -export const ENABLE_EMAIL_OPT_IN = getBoolean("ENABLE_EMAIL_OPT_IN"); - export const ENABLE_OPAL_ADK = getBoolean("ENABLE_OPAL_ADK"); export const ENABLE_OUTPUT_TEMPLATES = getBoolean("ENABLE_OUTPUT_TEMPLATES"); export const ENABLE_GOOGLE_ONE = getBoolean("ENABLE_GOOGLE_ONE"); -export const ENABLE_REQUIRE_CONSENT_FOR_GET_WEBPAGE = getBoolean( - "ENABLE_REQUIRE_CONSENT_FOR_GET_WEBPAGE" -); - -export const ENABLE_REQUIRE_CONSENT_FOR_OPEN_WEBPAGE = getBoolean( - "ENABLE_REQUIRE_CONSENT_FOR_OPEN_WEBPAGE" -); - -export const ENABLE_NEW_URL_SCHEME = getBoolean("ENABLE_NEW_URL_SCHEME"); export const SHARE_SURFACE_URL_TEMPLATES = (getJson("SHARE_SURFACE_URL_TEMPLATES") as Record) ?? {}; -export const ENABLE_DRIVE_PICKER_IN_LITE_MODE = getBoolean( - "ENABLE_DRIVE_PICKER_IN_LITE_MODE" -); - export const ENABLE_GOOGLE_DRIVE_TOOLS = getBoolean( "ENABLE_GOOGLE_DRIVE_TOOLS" ); diff --git a/packages/visual-editor/src/a2/a2/html-generator.ts b/packages/visual-editor/src/a2/a2/html-generator.ts index c128b9cf7ec..48c9822f189 100644 --- a/packages/visual-editor/src/a2/a2/html-generator.ts +++ b/packages/visual-editor/src/a2/a2/html-generator.ts @@ -3,134 +3,21 @@ */ import { LLMContent, Outcome } from "@breadboard-ai/types"; -import type { - ContentMap, - ExecuteStepRequest, - ExecuteStepArgs, -} from "./step-executor.js"; -import { executeStep } from "./step-executor.js"; import { executeWebpageStream } from "./generate-webpage-stream.js"; -import { encodeBase64, err, mergeContent, ok } from "./utils.js"; import { A2ModuleArgs } from "../runnable-module-factory.js"; -import { createReporter } from "../agent/progress-work-item.js"; import { isInlineData } from "../../data/common.js"; export { callGenWebpage }; -const OUTPUT_KEY = "rendered_outputs"; - -/** - * Legacy (non-streaming) implementation of GenerateWebpage. - */ -async function callGenWebpageLegacy( - moduleArgs: A2ModuleArgs, - instruction: string, - content: LLMContent[], - renderMode: string, - modelName: string -): Promise> { - const executionInputs: ContentMap = {}; - const inputParameters: string[] = []; - let i = 0; - for (const val of content) { - if (!val.parts) continue; - for (const part of val.parts) { - i++; - if ("text" in part) { - const key = `text_${i}`; - inputParameters.push(key); - executionInputs[key] = { - chunks: [ - { - mimetype: "text/plain", - data: encodeBase64(part.text), - }, - ], - }; - } else if ("inlineData" in part) { - const key = `media_${i}`; - inputParameters.push(key); - executionInputs[key] = { - chunks: [ - { - mimetype: part.inlineData.mimeType, - data: part.inlineData.data, - }, - ], - }; - } else if ("storedData" in part) { - const key = `media_${i}`; - inputParameters.push(key); - let handle = part.storedData.handle; - if (handle.startsWith("drive:/")) { - const driveId = handle.replace(/^drive:\/+/, ""); - handle = `https://drive.google.com/file/d/${driveId}/preview`; - } - executionInputs[key] = { - chunks: [ - { - mimetype: "url/" + part.storedData.mimeType, - data: encodeBase64(handle), - }, - ], - }; - } else { - console.error("Skipping unexpected content part"); - } - } - } - const body = { - planStep: { - stepName: "GenerateWebpage", - modelApi: "generate_webpage", - inputParameters: inputParameters, - systemPrompt: "", - stepIntent: "", - output: OUTPUT_KEY, - options: { - disablePromptRewrite: true, - renderMode: renderMode, - modelName: modelName, - systemInstruction: instruction, - }, - }, - execution_inputs: executionInputs, - } satisfies ExecuteStepRequest; - // Add the contents - // TODO(askerryryan): Remove once functional. - console.log("request body"); - console.log(body); - const reporter = createReporter(moduleArgs, { - title: `Calling generate_webpage`, - icon: "spark", - }); - const args: ExecuteStepArgs = { ...moduleArgs, reporter }; - const response = await executeStep(args, body, { - expectedDurationInSec: 70, - }); - if (!ok(response)) { - let errorMessage; - if (response.$error.includes("The service is currently unavailable")) { - errorMessage = - "Request timed out. The model may be experiencing heavy load"; - } else { - errorMessage = response.$error; - } - return err("Webpage generation failed: " + errorMessage); - } - - return mergeContent(response.chunks, "model"); -} - /** * Main entry point for generating webpage HTML. - * Uses streaming API when streamGenWebpage flag is enabled. + * Always uses the streaming API. */ async function callGenWebpage( moduleArgs: A2ModuleArgs, instruction: string, content: LLMContent[], - renderMode: string, + _renderMode: string, modelName: string ): Promise> { // If the content already contains HTML inlineData, pass it through @@ -143,22 +30,5 @@ async function callGenWebpage( } } - const flags = await moduleArgs.context.flags?.flags(); - const useStreaming = flags?.streamGenWebpage ?? false; - - if (useStreaming) { - console.log("[html-generator] Using streaming API for GenerateWebpage"); - return executeWebpageStream(moduleArgs, instruction, content, modelName); - } else { - console.log( - "[html-generator] Using legacy executeStep for GenerateWebpage" - ); - return callGenWebpageLegacy( - moduleArgs, - instruction, - content, - renderMode, - modelName - ); - } + return executeWebpageStream(moduleArgs, instruction, content, modelName); } diff --git a/packages/visual-editor/src/main-base.ts b/packages/visual-editor/src/main-base.ts index fb172f6f9c2..425148ad6c0 100644 --- a/packages/visual-editor/src/main-base.ts +++ b/packages/visual-editor/src/main-base.ts @@ -156,16 +156,14 @@ abstract class MainBase extends SignalWatcher(LitElement) { } }); - if (this.sca.services.globalConfig.ENABLE_EMAIL_OPT_IN) { - this.sca.services.emailPrefsManager.refreshPrefs().then(() => { - if ( - this.sca.services.emailPrefsManager.prefsValid && - !this.sca.services.emailPrefsManager.hasStoredPreferences - ) { - this.sca.controller.global.main.show.add("WarmWelcome"); - } - }); - } + this.sca.services.emailPrefsManager.refreshPrefs().then(() => { + if ( + this.sca.services.emailPrefsManager.prefsValid && + !this.sca.services.emailPrefsManager.hasStoredPreferences + ) { + this.sca.controller.global.main.show.add("WarmWelcome"); + } + }); // Admin — side-effect: exposes `window.o` when URL has #owner-tools. new Admin( diff --git a/packages/visual-editor/src/sca/actions/board/board-actions.ts b/packages/visual-editor/src/sca/actions/board/board-actions.ts index 43b681f14e4..70c4f0f3643 100644 --- a/packages/visual-editor/src/sca/actions/board/board-actions.ts +++ b/packages/visual-editor/src/sca/actions/board/board-actions.ts @@ -910,7 +910,7 @@ async function runBoard(): Promise { // b/452677430 - Check for consent before running shared Opals that // use the get_webpage tool, as this could be a data exfiltration vector. - if ((await controller.global.flags.flags()).requireConsentForGetWebpage) { + { const url = gc.url; const boardServer = services.googleDriveBoardServer; const isGalleryApp = url && boardServer.galleryGraphs.has(url); diff --git a/packages/visual-editor/src/sca/controller/subcontrollers/global/flag-controller.ts b/packages/visual-editor/src/sca/controller/subcontrollers/global/flag-controller.ts index 3fcd49d7246..5cc22f5f80a 100644 --- a/packages/visual-editor/src/sca/controller/subcontrollers/global/flag-controller.ts +++ b/packages/visual-editor/src/sca/controller/subcontrollers/global/flag-controller.ts @@ -18,9 +18,6 @@ export class FlagController @field({ persist: "idb" }) private accessor _consistentUI: boolean | null = null; - @field({ persist: "idb" }) - private accessor _enableDrivePickerInLiteMode: boolean | null = null; - @field({ persist: "idb" }) private accessor _enableGoogleDriveTools: boolean | null = null; @@ -42,18 +39,6 @@ export class FlagController @field({ persist: "idb" }) private accessor _outputTemplates: boolean | null = null; - @field({ persist: "idb" }) - private accessor _requireConsentForGetWebpage: boolean | null = null; - - @field({ persist: "idb" }) - private accessor _requireConsentForOpenWebpage: boolean | null = null; - - @field({ persist: "idb" }) - private accessor _streamGenWebpage: boolean | null = null; - - @field({ persist: "idb" }) - private accessor _streamPlanner: boolean | null = null; - @field({ persist: "idb" }) private accessor _enableNotebookLm: boolean | null = null; @@ -72,12 +57,6 @@ export class FlagController return this._consistentUI; } - get enableDrivePickerInLiteMode() { - if (this._enableDrivePickerInLiteMode === null) - throw new Error("enableDrivePickerInLiteMode was not set by environment"); - return this._enableDrivePickerInLiteMode; - } - get enableGoogleDriveTools() { if (this._enableGoogleDriveTools === null) { throw new Error("enableGoogleDriveTools was not set by environment"); @@ -121,32 +100,6 @@ export class FlagController return this._outputTemplates; } - get requireConsentForGetWebpage() { - if (this._requireConsentForGetWebpage === null) - throw new Error("requireConsentForGetWebpage was not set by environment"); - return this._requireConsentForGetWebpage; - } - - get requireConsentForOpenWebpage() { - if (this._requireConsentForOpenWebpage === null) - throw new Error( - "requireConsentForOpenWebpage was not set by environment" - ); - return this._requireConsentForOpenWebpage; - } - - get streamGenWebpage() { - if (this._streamGenWebpage === null) - throw new Error("streamGenWebpage was not set by environment"); - return this._streamGenWebpage; - } - - get streamPlanner() { - if (this._streamPlanner === null) - throw new Error("streamPlanner was not set by environment"); - return this._streamPlanner; - } - get enableNotebookLm() { if (this._enableNotebookLm === null) throw new Error("enableNotebookLm was not set by environment"); @@ -198,12 +151,6 @@ export class FlagController return; } - case "enableDrivePickerInLiteMode": { - if (onlyIfNull && this._enableDrivePickerInLiteMode !== null) return; - this._enableDrivePickerInLiteMode = value; - return; - } - case "force2DGraph": { if (onlyIfNull && this._force2DGraph !== null) return; this._force2DGraph = value; @@ -234,30 +181,6 @@ export class FlagController return; } - case "requireConsentForGetWebpage": { - if (onlyIfNull && this._requireConsentForGetWebpage !== null) return; - this._requireConsentForGetWebpage = value; - return; - } - - case "requireConsentForOpenWebpage": { - if (onlyIfNull && this._requireConsentForOpenWebpage !== null) return; - this._requireConsentForOpenWebpage = value; - return; - } - - case "streamGenWebpage": { - if (onlyIfNull && this._streamGenWebpage !== null) return; - this._streamGenWebpage = value; - return; - } - - case "streamPlanner": { - if (onlyIfNull && this._streamPlanner !== null) return; - this._streamPlanner = value; - return; - } - case "enableGoogleDriveTools": { if (onlyIfNull && this._enableGoogleDriveTools !== null) return; this._enableGoogleDriveTools = value; @@ -304,28 +227,11 @@ export class FlagController env.enableGoogleDriveTools, onlyIfNull ); - this.#set( - "enableDrivePickerInLiteMode", - env.enableDrivePickerInLiteMode, - onlyIfNull - ); this.#set("force2DGraph", env.force2DGraph, onlyIfNull); this.#set("googleOne", env.googleOne, onlyIfNull); this.#set("mcp", env.mcp, onlyIfNull); this.#set("opalAdk", env.opalAdk, onlyIfNull); this.#set("outputTemplates", env.outputTemplates, onlyIfNull); - this.#set( - "requireConsentForGetWebpage", - env.requireConsentForGetWebpage, - onlyIfNull - ); - this.#set( - "requireConsentForOpenWebpage", - env.requireConsentForOpenWebpage, - onlyIfNull - ); - this.#set("streamGenWebpage", env.streamGenWebpage, onlyIfNull); - this.#set("streamPlanner", env.streamPlanner, onlyIfNull); this.#set("enableResumeAgentRun", env.enableResumeAgentRun, onlyIfNull); this.#set("enableNotebookLm", env.enableNotebookLm, onlyIfNull); this.#set( diff --git a/packages/visual-editor/src/ui/config/client-deployment-configuration.ts b/packages/visual-editor/src/ui/config/client-deployment-configuration.ts index 6efe809be08..ac6b4bb8390 100644 --- a/packages/visual-editor/src/ui/config/client-deployment-configuration.ts +++ b/packages/visual-editor/src/ui/config/client-deployment-configuration.ts @@ -22,11 +22,6 @@ const DEFAULT_FLAG_VALUES: RuntimeFlags = { opalAdk: false, outputTemplates: false, googleOne: false, - requireConsentForGetWebpage: false, - requireConsentForOpenWebpage: false, - streamPlanner: false, - streamGenWebpage: false, - enableDrivePickerInLiteMode: false, enableGoogleDriveTools: false, enableResumeAgentRun: false, enableNotebookLm: false, diff --git a/packages/visual-editor/src/ui/elements/input/floating-input/floating-input.ts b/packages/visual-editor/src/ui/elements/input/floating-input/floating-input.ts index f279fb73610..7f2cc1c9afe 100644 --- a/packages/visual-editor/src/ui/elements/input/floating-input/floating-input.ts +++ b/packages/visual-editor/src/ui/elements/input/floating-input/floating-input.ts @@ -42,7 +42,6 @@ import { isStoredData, isTextCapabilityPart, } from "../../../../data/common.js"; -import { parseUrl } from "../../../utils/urls.js"; import { createRef, ref } from "lit/directives/ref.js"; import { SignalWatcher } from "@lit-labs/signals"; import { scaContext } from "../../../../sca/context/context.js"; @@ -64,8 +63,6 @@ interface SupportedActions { }; } -const parsedUrl = parseUrl(window.location.href); - @customElement("bb-floating-input") export class FloatingInput extends SignalWatcher(LitElement) { @consume({ context: scaContext }) @@ -532,9 +529,7 @@ export class FloatingInput extends SignalWatcher(LitElement) { render() { let inputContents: HTMLTemplateResult | symbol = nothing; - const showGDrive = - !parsedUrl.lite || - !!this.sca.controller.global.flags?.enableDrivePickerInLiteMode; + const showGDrive = true; const showNotebookLm = !!this.sca.controller.global.flags?.enableNotebookLm; if (this.schema) { const props = Object.entries(this.schema.properties ?? {}); diff --git a/packages/visual-editor/src/ui/elements/shell/global-settings.ts b/packages/visual-editor/src/ui/elements/shell/global-settings.ts index 45ff8712f12..6ec11a035ce 100644 --- a/packages/visual-editor/src/ui/elements/shell/global-settings.ts +++ b/packages/visual-editor/src/ui/elements/shell/global-settings.ts @@ -18,7 +18,6 @@ import * as BreadboardUI from "../../../ui/index.js"; import type { UI } from "../../../sca/types.js"; import { EmailPrefsManager } from "../../utils/email-prefs-manager.js"; import { SignalWatcher } from "@lit-labs/signals"; -import { CLIENT_DEPLOYMENT_CONFIG } from "../../../ui/config/client-deployment-configuration.js"; import { consume } from "@lit/context"; import { scaContext } from "../../../sca/context/context.js"; import { type SCA } from "../../../sca/sca.js"; @@ -33,7 +32,7 @@ enum TabId { function getTabEnabledMap(sca: SCA | undefined): Record { return { - [TabId.GENERAL]: Boolean(CLIENT_DEPLOYMENT_CONFIG.ENABLE_EMAIL_OPT_IN), + [TabId.GENERAL]: true, [TabId.INTEGRATIONS]: Boolean(sca?.controller?.global?.flags?.mcp), [TabId.EXPERIMENTAL]: Boolean( sca?.controller?.global.main.experimentalComponents @@ -78,9 +77,7 @@ export class VEGlobalSettingsModal extends SignalWatcher(LitElement) { ) { this.activeTabId = this.initialTab as TabId; } - if (CLIENT_DEPLOYMENT_CONFIG.ENABLE_EMAIL_OPT_IN) { - this.emailPrefsManager?.refreshPrefs(); - } + this.emailPrefsManager?.refreshPrefs(); } static styles = [ @@ -178,42 +175,40 @@ export class VEGlobalSettingsModal extends SignalWatcher(LitElement) { [TabId.GENERAL]: { name: Strings.from("LABEL_SETTINGS_GENERAL"), template: () => - html` ${CLIENT_DEPLOYMENT_CONFIG.ENABLE_EMAIL_OPT_IN - ? html` - ` - : nothing}`, + html` ${html` + `}`, }, [TabId.INTEGRATIONS]: { name: Strings.from("LABEL_SETTINGS_INTEGRATIONS"), diff --git a/packages/visual-editor/src/ui/flow-gen/flow-generator.ts b/packages/visual-editor/src/ui/flow-gen/flow-generator.ts index 290305c4f2b..1b800194f80 100644 --- a/packages/visual-editor/src/ui/flow-gen/flow-generator.ts +++ b/packages/visual-editor/src/ui/flow-gen/flow-generator.ts @@ -134,7 +134,7 @@ export class FlowGenerator { const responseMessages: string[] = []; const suggestions: string[] = []; - if (flags.streamPlanner && !constraint) { + if (!constraint) { await this.#streamOneShot( intent, context, diff --git a/packages/visual-editor/src/ui/utils/urls.ts b/packages/visual-editor/src/ui/utils/urls.ts index 427ff35d0b7..6393a5cc218 100644 --- a/packages/visual-editor/src/ui/utils/urls.ts +++ b/packages/visual-editor/src/ui/utils/urls.ts @@ -13,7 +13,6 @@ import { MakeUrlInit, OpenUrlInit, } from "../../sca/types.js"; -import { CLIENT_DEPLOYMENT_CONFIG } from "../config/client-deployment-configuration.js"; export function devUrlParams(): Required["dev"] { // TODO(aomarks) Add a flag so that we only allow these in dev. @@ -41,8 +40,7 @@ const DEV_PREFIX = "dev-"; */ export function makeUrl( init: MakeUrlInit, - base: string | URL = window.location.href, - enableNewUrlScheme = CLIENT_DEPLOYMENT_CONFIG.ENABLE_NEW_URL_SCHEME + base: string | URL = window.location.href ): string { const baseOrigin = typeof base === "string" ? new URL(base).origin : base.origin; @@ -71,9 +69,6 @@ export function makeUrl( url.searchParams.set(NEW, init.new === true ? "true" : "false"); } } else if (page === "graph") { - if (!enableNewUrlScheme) { - url.searchParams.set(FLOW, init.flow); - } if (init.resourceKey) { url.searchParams.set(RESOURCE_KEY, init.resourceKey); } @@ -97,21 +92,17 @@ export function makeUrl( : COLOR_SCHEME_DARK ); } - if (!enableNewUrlScheme) { - url.searchParams.set(MODE, init.mode); + const driveId = extractGoogleDriveFileId(init.flow); + if (!driveId) { + throw new Error("unsupported graph id " + init.flow); + } + if (init.mode === "app") { + url.pathname = "app/" + encodeURIComponent(driveId); + } else if (init.mode === "canvas") { + url.pathname = "edit/" + encodeURIComponent(driveId); } else { - const driveId = extractGoogleDriveFileId(init.flow); - if (!driveId) { - throw new Error("unsupported graph id " + init.flow); - } - if (init.mode === "app") { - url.pathname = "app/" + encodeURIComponent(driveId); - } else if (init.mode === "canvas") { - url.pathname = "edit/" + encodeURIComponent(driveId); - } else { - init.mode satisfies never; - throw new Error("unsupported mode " + init.mode); - } + init.mode satisfies never; + throw new Error("unsupported mode " + init.mode); } } else if (page === "landing") { url.pathname = "landing/"; @@ -136,10 +127,12 @@ export function makeUrl( ); } if (init.redirect.page === "graph") { - // To encode the redirect URL, we just copy all the search params directly - // onto the landing page URL, and then pick them off later. Note this - // could be a little brittle if we add more pages, since we aren't - // explicitly representing which page we're redirecting to. + // To encode the redirect URL, we store the graph parameters as search + // params on the landing URL, and then pick them off later during parsing. + // We explicitly set flow and mode since makeUrl encodes them in the + // pathname rather than as search params. + url.searchParams.set(FLOW, init.redirect.flow); + url.searchParams.set(MODE, init.redirect.mode); const redirectUrl = new URL(makeUrl(init.redirect, base)); for (const [redirectParam, redirectValue] of redirectUrl.searchParams) { url.searchParams.set(redirectParam, redirectValue); diff --git a/packages/visual-editor/tests/sca/actions/board/board-route-actions.test.ts b/packages/visual-editor/tests/sca/actions/board/board-route-actions.test.ts index bc6f8445229..904ce223c1f 100644 --- a/packages/visual-editor/tests/sca/actions/board/board-route-actions.test.ts +++ b/packages/visual-editor/tests/sca/actions/board/board-route-actions.test.ts @@ -81,7 +81,7 @@ function makeMockRouteController( queryConsent: async () => true, }, flags: { - flags: async () => ({ requireConsentForGetWebpage: false }), + flags: async () => ({}), }, toasts: { toast: () => "toast-id", @@ -291,17 +291,6 @@ suite("Board Route Actions", () => { editor: mockEditor, }); - // Override flags to require consent - ( - controller.global as unknown as { - flags: { - flags: () => Promise<{ requireConsentForGetWebpage: boolean }>; - }; - } - ).flags = { - flags: async () => ({ requireConsentForGetWebpage: true }), - }; - // Override consent to deny ( controller.global as unknown as { @@ -342,17 +331,6 @@ suite("Board Route Actions", () => { editor: mockEditor, }); - // Override flags to require consent - ( - controller.global as unknown as { - flags: { - flags: () => Promise<{ requireConsentForGetWebpage: boolean }>; - }; - } - ).flags = { - flags: async () => ({ requireConsentForGetWebpage: true }), - }; - const boardServer = makeMockBoardServer({}); (boardServer as unknown as { galleryGraphs: Set }).galleryGraphs = new Set(["drive:/gallery-board"]); diff --git a/packages/visual-editor/tests/sca/controller/data/default-flags.ts b/packages/visual-editor/tests/sca/controller/data/default-flags.ts index 1e5c0ab66e4..851f3e63f4d 100644 --- a/packages/visual-editor/tests/sca/controller/data/default-flags.ts +++ b/packages/visual-editor/tests/sca/controller/data/default-flags.ts @@ -9,16 +9,11 @@ import { RuntimeFlags } from "@breadboard-ai/types"; export const defaultRuntimeFlags: RuntimeFlags = { agentMode: false, consistentUI: false, - enableDrivePickerInLiteMode: false, force2DGraph: false, googleOne: false, mcp: false, opalAdk: false, outputTemplates: false, - requireConsentForGetWebpage: true, - requireConsentForOpenWebpage: true, - streamGenWebpage: false, - streamPlanner: false, enableGoogleDriveTools: false, enableNotebookLm: false, enableResumeAgentRun: false, diff --git a/packages/visual-editor/tests/sca/controller/subcontrollers/global/flag-controller.test.ts b/packages/visual-editor/tests/sca/controller/subcontrollers/global/flag-controller.test.ts index 68cfeeb35ad..451b5a93182 100644 --- a/packages/visual-editor/tests/sca/controller/subcontrollers/global/flag-controller.test.ts +++ b/packages/visual-editor/tests/sca/controller/subcontrollers/global/flag-controller.test.ts @@ -91,11 +91,6 @@ suite("FlagController", () => { new Error("consistentUI was not set by environment") ); - assert.throws( - () => String(store.enableDrivePickerInLiteMode), - new Error("enableDrivePickerInLiteMode was not set by environment") - ); - assert.throws( () => String(store.force2DGraph), new Error("force2DGraph was not set by environment") @@ -121,26 +116,6 @@ suite("FlagController", () => { new Error("outputTemplates was not set by environment") ); - assert.throws( - () => String(store.requireConsentForGetWebpage), - new Error("requireConsentForGetWebpage was not set by environment") - ); - - assert.throws( - () => String(store.requireConsentForOpenWebpage), - new Error("requireConsentForOpenWebpage was not set by environment") - ); - - assert.throws( - () => String(store.streamGenWebpage), - new Error("streamGenWebpage was not set by environment") - ); - - assert.throws( - () => String(store.streamPlanner), - new Error("streamPlanner was not set by environment") - ); - assert.throws( () => String(store.enableGoogleDriveTools), new Error("enableGoogleDriveTools was not set by environment") diff --git a/packages/visual-editor/tests/sca/controller/subcontrollers/router/router-controller.test.ts b/packages/visual-editor/tests/sca/controller/subcontrollers/router/router-controller.test.ts index fafa00939b2..352361b87de 100644 --- a/packages/visual-editor/tests/sca/controller/subcontrollers/router/router-controller.test.ts +++ b/packages/visual-editor/tests/sca/controller/subcontrollers/router/router-controller.test.ts @@ -7,7 +7,6 @@ import assert from "node:assert"; import { afterEach, beforeEach, suite, test } from "node:test"; import { RouterController } from "../../../../../src/sca/controller/subcontrollers/router/router-controller.js"; -import { CLIENT_DEPLOYMENT_CONFIG } from "../../../../../src/ui/config/client-deployment-configuration.js"; import { setDOM, unsetDOM } from "../../../../fake-dom.js"; import { SignalWatcher } from "../../../../signal-watcher.js"; import { Signal } from "signal-polyfill"; @@ -75,8 +74,8 @@ suite("RouterController", () => { assert.strictEqual(controller.parsedUrl.flow, "drive:/abcdef"); assert.strictEqual(controller.parsedUrl.mode, "canvas"); } - // URL should be updated - assert.ok(window.location.href.includes("flow=drive:/abcdef")); + // URL should be updated (new URL scheme uses pathname) + assert.ok(window.location.href.includes("/edit/abcdef")); }); test("go() is a no-op if URL matches current location", async () => { @@ -135,8 +134,7 @@ suite("RouterController", () => { const url = new URL(window.location.href); assert.strictEqual(url.searchParams.has("flow"), false); assert.strictEqual(url.searchParams.has("tab0"), false); - // mode should still be there (doesn't start with 'flow' or 'tab') - assert.strictEqual(url.searchParams.has("mode"), true); + assert.strictEqual(url.searchParams.has("mode"), false); }); test("updateFromCurrentUrl syncs parsedUrl with browser URL", async () => { @@ -201,11 +199,12 @@ suite("RouterController", () => { const controller = new RouterController(); await controller.isHydrated; - // The URL should be canonicalized to use drive:/ instead of drive%3A%2F + // The URL should be canonicalized — old search-param-based URLs get + // rewritten to the new pathname-based scheme (/edit/{driveId}). assert.notStrictEqual(window.location.href, originalHref); assert.ok( - window.location.href.includes("drive:/test"), - "URL should be canonicalized" + window.location.href.includes("/edit/test"), + "URL should be canonicalized to new pathname scheme" ); }); @@ -327,40 +326,26 @@ suite("RouterController", () => { }); test("falls back to home and sets urlError for invalid flow ID", async () => { - // Enable new URL scheme so makeUrl validates Drive IDs - const original = structuredClone( - CLIENT_DEPLOYMENT_CONFIG.ENABLE_NEW_URL_SCHEME + // Simulate a URL with an unsupported flow ID (not a Drive URL) + window.history.pushState(null, "", "http://localhost/?flow=foo&mode=app"); + const controller = new RouterController(); + await controller.isHydrated; + + // Should NOT throw — falls back to home + assert.strictEqual(controller.parsedUrl.page, "home"); + + // urlError should capture the original error message + assert.ok(controller.urlError, "urlError should be set"); + assert.ok( + controller.urlError!.includes("foo"), + "urlError should mention the invalid flow ID" + ); + + // Browser URL should be reset to the origin + assert.strictEqual( + window.location.href, + "http://localhost/", + "URL should be reset to origin" ); - ( - CLIENT_DEPLOYMENT_CONFIG as Record - ).ENABLE_NEW_URL_SCHEME = true; - - try { - // Simulate a URL with an unsupported flow ID (not a Drive URL) - window.history.pushState(null, "", "http://localhost/?flow=foo&mode=app"); - const controller = new RouterController(); - await controller.isHydrated; - - // Should NOT throw — falls back to home - assert.strictEqual(controller.parsedUrl.page, "home"); - - // urlError should capture the original error message - assert.ok(controller.urlError, "urlError should be set"); - assert.ok( - controller.urlError!.includes("foo"), - "urlError should mention the invalid flow ID" - ); - - // Browser URL should be reset to the origin - assert.strictEqual( - window.location.href, - "http://localhost/", - "URL should be reset to origin" - ); - } finally { - ( - CLIENT_DEPLOYMENT_CONFIG as Record - ).ENABLE_NEW_URL_SCHEME = original; - } }); }); diff --git a/packages/visual-editor/tests/sca/helpers/mock-services.ts b/packages/visual-editor/tests/sca/helpers/mock-services.ts index 76b1f4c2dc2..e83871dad50 100644 --- a/packages/visual-editor/tests/sca/helpers/mock-services.ts +++ b/packages/visual-editor/tests/sca/helpers/mock-services.ts @@ -134,6 +134,8 @@ export function makeMockBoardServer(options: { // For event bridge support addEventListener: () => {}, removeEventListener: () => {}, + // Consent check support (always-on after flag removal) + galleryGraphs: new Set(), // Test helpers get lastSavedGraph() { return lastSavedGraph; diff --git a/packages/visual-editor/tests/urls.test.ts b/packages/visual-editor/tests/urls.test.ts index aee01baad60..4b6690e9b79 100644 --- a/packages/visual-editor/tests/urls.test.ts +++ b/packages/visual-editor/tests/urls.test.ts @@ -17,8 +17,9 @@ function testSymmetrical( init: MakeUrlInit ): void { test(oldUrl, () => { - assert.deepEqual(makeUrl(init, BASE_URL, true), newUrl); - assert.deepEqual(makeUrl(init, BASE_URL, false), oldUrl); + // makeUrl always generates the new URL scheme now + assert.deepEqual(makeUrl(init, BASE_URL), newUrl); + // parseUrl still supports both old and new URL formats assert.deepEqual(parseUrl(newUrl), init); assert.deepEqual(parseUrl(oldUrl), init); }); @@ -280,7 +281,7 @@ suite("landing", () => { ); testSymmetrical( - `${BASE_URL}/landing/?flow=drive:/abc123&resourcekey=ghi789&results=def456&mode=app`, + `${BASE_URL}/landing/?flow=drive:/abc123&mode=app&resourcekey=ghi789&results=def456`, `${BASE_URL}/landing/?flow=drive:/abc123&resourcekey=ghi789&results=def456&mode=app`, { page: "landing",