diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 01c31940e549..82da55d5c55e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -263,3 +263,7 @@ development/generate-lavamoat-policies.js @MetaMask/extension-platfo # Background Process app/scripts/lib/createMetaRPCHandler.js @MetaMask/extension-platform app/scripts/lib/metaRPCClientFactory.ts @MetaMask/extension-platform +app/scripts/lib/setup-initial-state-hooks.* @MetaMask/extension-platform + +# UI Bootstrap +/ui/index.js @MetaMask/extension-platform diff --git a/.metamaskrc.dist b/.metamaskrc.dist index 3b14bdc1daa9..9c611de193f6 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -61,6 +61,11 @@ BLOCKAID_PUBLIC_KEY= ; Enables the Settings Page - Developer Options ; ENABLE_SETTINGS_PAGE_DEV_OPTIONS=true +; Enables agentic/CDP automation hooks on `globalThis.stateHooks` in debug builds. +; Requires `METAMASK_DEBUG=true`. Only set this when running the CDP automation +; harness — not needed for manual QA or general local development. +; METAMASK_AGENTIC_HOOKS=true + ; SENTRY CONFIGURATION ; SENTRY_DSN - For production builds only (do NOT use in development) ; SENTRY_DSN_DEV - For local dev and one-off QA testing (sends to 'test-metamask' project) diff --git a/builds.yml b/builds.yml index b27b92771dd7..d3c0d331ba99 100644 --- a/builds.yml +++ b/builds.yml @@ -329,6 +329,10 @@ env: # Modified in /development/build/scripts.js:@setEnvironmentVariables # Also see DEBUG and NODE_DEBUG - METAMASK_DEBUG: false + # Enables agentic/CDP automation hooks on `globalThis.stateHooks` + # (see `ui/index.js`). Requires `METAMASK_DEBUG=true` to take effect. + # Off by default so manual QA debug builds do not expose the automation surface. + - METAMASK_AGENTIC_HOOKS: false # Modified in /development/build/scripts.js:@setEnvironmentVariables - IN_TEST # Modified in /development/build/scripts.js:@setEnvironmentVariables diff --git a/types/global.d.ts b/types/global.d.ts index 6ea6ee640efe..c0d5ff40f6b9 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -324,15 +324,21 @@ type StateHooks = { */ resetWebVitalsMetrics?: () => void; - // Agentic dev hooks (METAMASK_DEBUG only) — expose internals for CDP automation. - // Typed as `unknown` because these are untyped debug-only entry points consumed - // by CDP automation scripts that perform their own runtime checks. - store?: unknown; + // Agentic dev hooks — curated surface for CDP automation. + // Present only when both METAMASK_DEBUG and METAMASK_AGENTIC_HOOKS are true + // at build time. Typed loosely because these are debug-only entry points + // consumed by CDP automation scripts that perform their own runtime checks. + store?: { + getState: () => unknown; + subscribe: (listener: () => void) => () => void; + }; submitRequestToBackground?: ( method: string, args?: unknown[], ) => Promise; - getPerpsStreamManager?: () => unknown; + services?: { + perps: unknown; + }; }; export declare global { diff --git a/ui/index.js b/ui/index.js index ccd7a48b77d3..5c20d794c979 100644 --- a/ui/index.js +++ b/ui/index.js @@ -463,11 +463,38 @@ function setupStateHooks(store) { exposeLongTaskMetricsForTesting(); } - // Agentic dev hooks — expose internals for CDP automation - if (process.env.METAMASK_DEBUG) { - globalThis.stateHooks.store = store; - globalThis.stateHooks.submitRequestToBackground = submitRequestToBackground; - globalThis.stateHooks.getPerpsStreamManager = getPerpsStreamManager; + // Agentic dev hooks — curated surface for CDP automation. + // Requires METAMASK_DEBUG AND METAMASK_AGENTIC_HOOKS. Both are compile-time + // constants; DefinePlugin dead-code-eliminates this block in release builds. + if (process.env.METAMASK_DEBUG && process.env.METAMASK_AGENTIC_HOOKS) { + // Background RPC methods allowlisted for agentic automation. + // Additions require PR review — each entry should document its use case. + const AGENTIC_BACKGROUND_METHODS = new Set([ + // seed empty — harness team opens a follow-up PR to populate + ]); + + // Read-only store facade: getState + subscribe, no dispatch. + globalThis.stateHooks.store = { + getState: () => store.getState(), + subscribe: (listener) => store.subscribe(listener), + }; + + // Allowlisted background RPC — same signature as the real submitRequestToBackground. + globalThis.stateHooks.submitRequestToBackground = (method, args) => { + if (!AGENTIC_BACKGROUND_METHODS.has(method)) { + throw new Error( + `stateHooks.submitRequestToBackground: "${method}" is not in the ` + + `agentic allowlist. Add it to AGENTIC_BACKGROUND_METHODS in ` + + `ui/index.js via PR review.`, + ); + } + return submitRequestToBackground(method, args); + }; + + // UI-side service singletons for agentic automation. + globalThis.stateHooks.services = { + perps: getPerpsStreamManager(), + }; } }