Skip to content

feat(integrations): add Grok Build integration + Experimental ACP - #2772

Open
antonvishal wants to merge 2 commits into
browserbase:mainfrom
antonvishal:acp+grok-build
Open

feat(integrations): add Grok Build integration + Experimental ACP#2772
antonvishal wants to merge 2 commits into
browserbase:mainfrom
antonvishal:acp+grok-build

Conversation

@antonvishal

@antonvishal antonvishal commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

What

Adds a Grok Build example that can use Stagehand through the existing facade.

Grok Build exposes ACP rather than a TypeScript SDK for driving the agent, so this adds a small ACP runner under integrations/core. The Grok-specific package only handles its CLI setup, authentication, and tool mapping.

The ACP process runs with the user's existing Grok login or XAI_API_KEY. The Stagehand MCP process receives only the environment variables it needs.

Question for maintainers

I put the ACP runner in integrations/core because it is not specific to Grok another ACP-compatible agent could use it by providing its own profile.

Does that feel like the right place for it?

If ACP is something we want to support as an integration path, I can also add a general ACP docs page. For this PR, I kept the documentation focused on Grok Build.

Testing

  • pnpm check
  • core integration tests
  • Grok Build tests
  • tested locally with an authenticated Grok Build install and a local browser

Summary by cubic

Adds a Grok Build integration that runs the Stagehand facade over ACP and introduces a reusable ACP runner in @browserbasehq/stagehand-integrations. Grok Build now gets one persistent browser with run, snapshot, and screenshot, with strict env isolation and a hardened lifecycle.

  • Adds a generic ACP client in packages/integrations/core/src/acp (exported as @browserbasehq/stagehand-integrations/acp) that initializes/authenticates, mounts the facade as the only MCP server, approves only facade tool calls, cancels on abort, and terminates the full agent process tree if unresponsive (including on Windows).
  • Adds a launcher that starts the real facade process with an allowlisted environment: only non-empty STAGEHAND_* and BROWSERBASE_* plus minimal OS runtime vars reach the facade; host secrets like XAI_API_KEY never do.
  • Implements a Grok-specific example in packages/integrations/grok-build that resolves the packaged @xai-official/grok binary, creates a disposable home/workspace and isolated GROK_HOME, uses XAI_API_KEY if set or copies only cached auth.json when available, deny-lists shell/file/subagent/plan/memory/web-search, and allowlists search_tool/use_tool.
  • Tightens permission flow: the ACP client selects allow-once only for stagehand__run|snapshot|screenshot; all other tool requests are rejected or cancelled, and non-active-session permission prompts are cancelled.
  • Hardens shutdown: the facade server now uses process.on for repeated signal forwarding; the ACP bridge sends cancel, then SIGTERM/SIGKILL with a grace period, and cleans up descendants.
  • Adds docs and nav for Grok Build, including a new integration page and icon; updates the integrations overview to describe the ACP path.
  • No migrations. New deps: @agentclientprotocol/sdk in core, @xai-official/grok in the example; workspace and Turbo tasks updated. To try locally: build @browserbasehq/stagehand-integrations, set XAI_API_KEY (or run grok login), then run the packages/integrations/grok-build example.

Written for commit 304c403. Summary will update on new commits.

Review in cubic

@antonvishal
antonvishal requested a review from a team as a code owner August 18, 2026 16:38
@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 304c403

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run.
Approving the latest commit mirrors it into an internal PR owned by the approver.
If new commits are pushed later, the internal PR stays open but is marked stale until someone approves the latest external commit and refreshes it.

@github-actions github-actions Bot added external-contributor Tracks PRs mirrored from external contributor forks. external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. labels Aug 18, 2026
@socket-security

socket-security Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​xai-official/​grok@​1.0.5911008796100
Addednpm/​@​agentclientprotocol/​sdk@​1.3.01001009498100

View full report

@antonvishal antonvishal changed the title Add Grok Build integration feat(integrations): add Grok Build integration Aug 18, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 25 files

Confidence score: 2/5

  • packages/integrations/grok-build/src/agent.ts may print ACP or runtime failures containing API keys, CDP URLs, project IDs, or other secrets, creating a direct information-exposure risk; sanitize or redact messages before handleFailure reports them.
  • packages/integrations/core/src/acp/facade-launcher.ts can expose an uncaught exception and stack trace when the server path is missing; replace it with a typed Stagehand error and controlled user-facing handling.
  • packages/integrations/core/src/acp/env.ts duplicates the STAGEHAND_/BROWSERBASE_ filtering loop, so future allowlist changes could diverge between integrations; centralize the filtering in one shared helper.
  • Lower-risk follow-up remains in packages/docs/v4/integrations/grok-build.mdx, packages/integrations/grok-build/README.md, and packages/integrations/grok-build/tests/agent.test.ts: align the prose with the active-voice guide, use pnpm install --frozen-lockfile, and make the path assertion Windows-safe.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/integrations/grok-build/README.md">

<violation number="1" location="packages/integrations/grok-build/README.md:10">
P2: The quickstart uses a non-reproducible install command. Replace `pnpm install` with `pnpm install --frozen-lockfile` so users get deterministic dependency resolution that matches the committed lockfile.

(Based on your team's feedback about frozen lockfile pnpm integration installs.)</violation>
</file>

<file name="packages/integrations/grok-build/tests/agent.test.ts">

<violation number="1" location="packages/integrations/grok-build/tests/agent.test.ts:115">
P3: On Windows, require.resolve() returns a backslash-separated path (e.g. C:\repo\node_modules\@xai-official\grok\bin\grok.cjs), so the forward-slash substring "@xai-official/grok/bin/grok" will never match and this test fails despite a valid executable. Normalize separators before asserting, for example by asserting against executable.replaceAll("\\", "/") or by splitting on the platform separator and joining with "/".</violation>
</file>

<file name="packages/docs/v4/integrations/grok-build.mdx">

<violation number="1" location="packages/docs/v4/integrations/grok-build.mdx:72">
P2: Custom agent: **Stagehand docs prose guide**

These added sentences use agentless passive voice for capability restrictions, contrary to the guide’s active-voice requirement. Name the responsible component: the profile denies capabilities, the client rejects permissions, and the launcher disables imports and does not load user or project resources.</violation>
</file>

<file name="packages/integrations/grok-build/src/agent.ts">

<violation number="1" location="packages/integrations/grok-build/src/agent.ts:239">
P1: Custom agent: **Exception and error message sanitization**

The Grok CLI prints ACP and runtime failure messages without sanitization. If an error includes an API key, CDP URL, project ID, or other secret, `handleFailure` exposes it verbatim; route the message through the shared sanitizer before writing it to stderr.</violation>
</file>

<file name="packages/integrations/core/src/acp/facade-launcher.ts">

<violation number="1" location="packages/integrations/core/src/acp/facade-launcher.ts:6">
P2: Custom agent: **Exception and error message sanitization**

When the launcher has no server path, this uncaught `new Error()` exposes a generic exception and stack trace to the user. Throw a typed Stagehand error class instead, as required by the error sanitization rule.</violation>
</file>

<file name="packages/integrations/core/src/acp/env.ts">

<violation number="1" location="packages/integrations/core/src/acp/env.ts:1">
P3: This adds a third copy of the same `STAGEHAND_`/`BROWSERBASE_` allowlist loop, which increases drift risk when the allowlist rules change. Reuse one shared helper so all integrations enforce the same filtering behavior.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant CLI as Grok CLI (user CLI)
    participant ACP as ACP Client (Stagehand)
    participant AGENT as Grok Agent Process
    participant FACE as Facade MCP Server (Stagehand)
    participant STAGE as Stagehand Core
    participant BROWSER as Browser

    Note over CLI,ACP: User runs: pnpm --dir packages/integrations/grok-build start -- "task"
    CLI->>ACP: runGrokBuild(instruction)
    ACP->>ACP: Create disposable runtime (home, cwd, GROK_HOME, agent profile)
    ACP->>AGENT: Spawn grok CLI with ACP stdio + restricted tools
    ACP->>AGENT: ACP initialize
    AGENT-->>ACP: Initialize response with auth methods
    alt XAI_API_KEY exists
        ACP->>AGENT: Authenticate via xai.api_key
    else cached auth.json only
        ACP->>AGENT: Authenticate via cached_token
    end
    ACP->>AGENT: session.new with Stagehand facade as only MCP server
    AGENT->>FACE: Launch facade stdio server (via launcher binary)
    FACE->>STAGE: Initialize Stagehand (lazily start browser)
    STAGE->>BROWSER: Start persistent browser session
    ACP->>AGENT: Prompt with task + Stagehand instructions
    AGENT->>AGENT: Discover MCP tools (run, snapshot, screenshot)
    AGENT->>ACP: Permission request for Stagehand tool
    alt Tool is stagehand__run/snapshot/screenshot
        ACP->>AGENT: Allow once
    else Other tool (shell, file, etc.)
        ACP->>AGENT: Reject or cancel
    end
    AGENT->>FACE: MCP tool call (e.g., stagehand__run)
    FACE->>STAGE: Execute browser automation
    STAGE->>BROWSER: Navigate, act, snapshot, screenshot
    BROWSER-->>STAGE: Results
    STAGE-->>FACE: Tool result
    FACE-->>AGENT: Tool response
    AGENT-->>ACP: Stream text messages (agent updates)
    ACP-->>CLI: Final assistant response

    Note over ACP,AGENT: On abort/signal
    ACP->>AGENT: ACP session.cancel
    ACP->>AGENT: SIGTERM to process tree
    opt No graceful exit
        ACP->>AGENT: SIGKILL to process tree
    end
    ACP->>ACP: Clean up disposable runtime directories
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/integrations/core/src/acp/agent-process.ts
Comment thread packages/integrations/core/src/acp/facade-launcher.ts Outdated

function handleFailure(error: unknown): void {
// oxlint-disable-next-line no-console -- CLI example reports failures to stderr.
console.error(error instanceof Error ? error.message : error);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Exception and error message sanitization

The Grok CLI prints ACP and runtime failure messages without sanitization. If an error includes an API key, CDP URL, project ID, or other secret, handleFailure exposes it verbatim; route the message through the shared sanitizer before writing it to stderr.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/grok-build/src/agent.ts, line 239:

<comment>The Grok CLI prints ACP and runtime failure messages without sanitization. If an error includes an API key, CDP URL, project ID, or other secret, `handleFailure` exposes it verbatim; route the message through the shared sanitizer before writing it to stderr.</comment>

<file context>
@@ -0,0 +1,241 @@
+
+function handleFailure(error: unknown): void {
+  // oxlint-disable-next-line no-console -- CLI example reports failures to stderr.
+  console.error(error instanceof Error ? error.message : error);
+  process.exitCode = 1;
+}
</file context>

From the repository root:

```bash
pnpm install

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The quickstart uses a non-reproducible install command. Replace pnpm install with pnpm install --frozen-lockfile so users get deterministic dependency resolution that matches the committed lockfile.

(Based on your team's feedback about frozen lockfile pnpm integration installs.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/grok-build/README.md, line 10:

<comment>The quickstart uses a non-reproducible install command. Replace `pnpm install` with `pnpm install --frozen-lockfile` so users get deterministic dependency resolution that matches the committed lockfile.

(Based on your team's feedback about frozen lockfile pnpm integration installs.) </comment>

<file context>
@@ -0,0 +1,25 @@
+From the repository root:
+
+```bash
+pnpm install
+pnpm exec turbo run build --filter @browserbasehq/stagehand-integrations
+export XAI_API_KEY=xai-...
</file context>
Suggested change
pnpm install
pnpm install --frozen-lockfile

Comment thread packages/docs/v4/integrations/grok-build.mdx Outdated
@@ -0,0 +1,96 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Stagehand docs prose guide

These added sentences use agentless passive voice for capability restrictions, contrary to the guide’s active-voice requirement. Name the responsible component: the profile denies capabilities, the client rejects permissions, and the launcher disables imports and does not load user or project resources.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/docs/v4/integrations/grok-build.mdx, line 72:

<comment>These added sentences use agentless passive voice for capability restrictions, contrary to the guide’s active-voice requirement. Name the responsible component: the profile denies capabilities, the client rejects permissions, and the launcher disables imports and does not load user or project resources.</comment>

<file context>
@@ -0,0 +1,96 @@
+
+The example opens one ACP session with Grok and mounts exactly one MCP server: the Stagehand facade. That server owns the browser for the full prompt turn, so `run`, `snapshot`, and `screenshot` share page state. Preserve that lifetime if you adapt the integration; a new process per tool call starts a new browser.
+
+Grok is restricted to a browser-only tool surface, matching the Claude and Codex examples. An agent profile allowlists only `search_tool` and `use_tool` (required to discover and invoke lazy MCP tools). Shell, file, plan, subagent, memory, and web-search capabilities are denied. The client approves one-time permission only for `stagehand__run`, `stagehand__snapshot`, and `stagehand__screenshot`; everything else is rejected or cancelled.
+
+Each run uses a disposable workspace, home directory, and `GROK_HOME`. Compatibility MCP imports from Claude/Cursor are disabled, and user or project MCP servers, plugins, hooks, skills, and rules are not loaded. A minimal launcher creates the actual facade runtime with non-empty `STAGEHAND_*` and `BROWSERBASE_*` variables plus basic OS values needed to launch Node and local Chrome; `XAI_API_KEY` and unrelated host secrets are unavailable to the facade. The ACP bridge terminates the full agent process tree on cancellation or failure.
</file context>

import { buildAcpFacadeRuntimeEnv } from "./env.js";

const [facadeServerPath, ...facadeArgs] = process.argv.slice(2);
if (!facadeServerPath) throw new Error("Stagehand ACP facade launcher requires a server path.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Exception and error message sanitization

When the launcher has no server path, this uncaught new Error() exposes a generic exception and stack trace to the user. Throw a typed Stagehand error class instead, as required by the error sanitization rule.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/core/src/acp/facade-launcher.ts, line 6:

<comment>When the launcher has no server path, this uncaught `new Error()` exposes a generic exception and stack trace to the user. Throw a typed Stagehand error class instead, as required by the error sanitization rule.</comment>

<file context>
@@ -0,0 +1,31 @@
+import { buildAcpFacadeRuntimeEnv } from "./env.js";
+
+const [facadeServerPath, ...facadeArgs] = process.argv.slice(2);
+if (!facadeServerPath) throw new Error("Stagehand ACP facade launcher requires a server path.");
+
+// ACP agents may merge an MCP server's declared env with their own environment.
</file context>

@@ -0,0 +1,36 @@
export function buildAcpFacadeEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This adds a third copy of the same STAGEHAND_/BROWSERBASE_ allowlist loop, which increases drift risk when the allowlist rules change. Reuse one shared helper so all integrations enforce the same filtering behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/core/src/acp/env.ts, line 1:

<comment>This adds a third copy of the same `STAGEHAND_`/`BROWSERBASE_` allowlist loop, which increases drift risk when the allowlist rules change. Reuse one shared helper so all integrations enforce the same filtering behavior.</comment>

<file context>
@@ -0,0 +1,36 @@
+export function buildAcpFacadeEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> {
+  const env: Record<string, string> = {};
+  for (const [key, value] of Object.entries(source)) {
</file context>

Comment thread packages/docs/v4/integrations/grok-build.mdx Outdated
it("resolves the packaged Grok executable", async () => {
const executable = resolveGrokExecutable();
await expect(access(executable)).resolves.toBeUndefined();
expect(executable).toContain("@xai-official/grok/bin/grok");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: On Windows, require.resolve() returns a backslash-separated path (e.g. C:\repo\node_modules@xai-official\grok\bin\grok.cjs), so the forward-slash substring "@xai-official/grok/bin/grok" will never match and this test fails despite a valid executable. Normalize separators before asserting, for example by asserting against executable.replaceAll("\", "/") or by splitting on the platform separator and joining with "/".

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/grok-build/tests/agent.test.ts, line 115:

<comment>On Windows, require.resolve() returns a backslash-separated path (e.g. C:\repo\node_modules\@xai-official\grok\bin\grok.cjs), so the forward-slash substring "@xai-official/grok/bin/grok" will never match and this test fails despite a valid executable. Normalize separators before asserting, for example by asserting against executable.replaceAll("\\", "/") or by splitting on the platform separator and joining with "/".</comment>

<file context>
@@ -0,0 +1,212 @@
+  it("resolves the packaged Grok executable", async () => {
+    const executable = resolveGrokExecutable();
+    await expect(access(executable)).resolves.toBeUndefined();
+    expect(executable).toContain("@xai-official/grok/bin/grok");
+  });
+});
</file context>
Suggested change
expect(executable).toContain("@xai-official/grok/bin/grok");
expect(executable.replaceAll("\\", "/")).toContain("@xai-official/grok/bin/grok");

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 8 files (changes from recent commits).

Confidence score: 3/5

  • In packages/integrations/core/src/acp/agent-process.ts, raw Node spawn errors can be propagated through runAcpFacadeAgent, exposing unsanitized details to callers; sanitize or replace startup error messages before rethrowing.
  • In packages/integrations/grok-build/src/agent.ts, Windows Git Bash environments may resolve .grok under the wrong home directory and miss cached login state; prefer USERPROFILE on Windows and use HOME only as a fallback.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/integrations/core/src/acp/agent-process.ts">

<violation number="1" location="packages/integrations/core/src/acp/agent-process.ts:27">
P1: Custom agent: **Exception and error message sanitization**

When ACP startup fails, `child.on("error", reject)` forwards Node's raw spawn error into `runAcpFacadeAgent`, which rethrows its message through generic `new Error(...)`. Wrap spawn failures in a dedicated typed error with a sanitized message before exposing the rejection.</violation>
</file>

<file name="packages/integrations/grok-build/src/agent.ts">

<violation number="1" location="packages/integrations/grok-build/src/agent.ts:61">
P2: On Windows, `HOME` can be a Git Bash path while `USERPROFILE` is the actual Windows home, so this selects the wrong `.grok` directory and misses cached login. Prefer `USERPROFILE` on Windows, with `HOME` as the fallback.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

});
const started = new Promise<void>((resolve, reject) => {
child.once("spawn", resolve);
child.on("error", reject);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Exception and error message sanitization

When ACP startup fails, child.on("error", reject) forwards Node's raw spawn error into runAcpFacadeAgent, which rethrows its message through generic new Error(...). Wrap spawn failures in a dedicated typed error with a sanitized message before exposing the rejection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/core/src/acp/agent-process.ts, line 27:

<comment>When ACP startup fails, `child.on("error", reject)` forwards Node's raw spawn error into `runAcpFacadeAgent`, which rethrows its message through generic `new Error(...)`. Wrap spawn failures in a dedicated typed error with a sanitized message before exposing the rejection.</comment>

<file context>
@@ -21,6 +22,10 @@ export function spawnAcpAgentProcess(options: {
   });
+  const started = new Promise<void>((resolve, reject) => {
+    child.once("spawn", resolve);
+    child.on("error", reject);
+  });
   child.stderr.pipe(options.stderr, { end: false });
</file context>

export function resolveGrokAuthHome(env: NodeJS.ProcessEnv): string | undefined {
const configured = env.GROK_HOME?.trim();
if (configured) return configured;
const userHome = env.HOME?.trim() || env.USERPROFILE?.trim();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: On Windows, HOME can be a Git Bash path while USERPROFILE is the actual Windows home, so this selects the wrong .grok directory and misses cached login. Prefer USERPROFILE on Windows, with HOME as the fallback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/integrations/grok-build/src/agent.ts, line 61:

<comment>On Windows, `HOME` can be a Git Bash path while `USERPROFILE` is the actual Windows home, so this selects the wrong `.grok` directory and misses cached login. Prefer `USERPROFILE` on Windows, with `HOME` as the fallback.</comment>

<file context>
@@ -55,6 +55,13 @@ export function resolveGrokExecutable(): string {
+export function resolveGrokAuthHome(env: NodeJS.ProcessEnv): string | undefined {
+  const configured = env.GROK_HOME?.trim();
+  if (configured) return configured;
+  const userHome = env.HOME?.trim() || env.USERPROFILE?.trim();
+  return userHome ? join(userHome, ".grok") : undefined;
+}
</file context>

@antonvishal antonvishal changed the title feat(integrations): add Grok Build integration feat(integrations): add Grok Build integration + Experimental ACP Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. external-contributor Tracks PRs mirrored from external contributor forks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant