Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/actions/detect-codemode-live-credentials/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Detect code-mode live credentials
description: Decide whether Browserbase, Vercel, and optional OpenAI live proofs can run.

inputs:
require-openai:
description: Require OPENAI_API_KEY in addition to Browserbase and Vercel credentials.
default: "false"

outputs:
available:
description: Whether every credential required by the live proof is available.
value: ${{ steps.detect.outputs.available }}

runs:
using: composite
steps:
- id: detect
shell: bash
env:
REQUIRE_OPENAI: ${{ inputs.require-openai }}
run: |
browserbase_ready=false
vercel_ready=false
openai_ready=false

if [[ -n "$BROWSERBASE_API_KEY" && -n "$BROWSERBASE_PROJECT_ID" ]]; then
browserbase_ready=true
fi
if [[ -n "$VERCEL_TOKEN" ]]; then
if [[ -n "$VERCEL_TEAM_ID" && -n "$VERCEL_PROJECT_ID" ]]; then
vercel_ready=true
fi
elif [[ -n "$VERCEL_OIDC_TOKEN" ]]; then
vercel_ready=true
fi
if [[ "$REQUIRE_OPENAI" != "true" || -n "$OPENAI_API_KEY" ]]; then
openai_ready=true
fi

if [[ "$browserbase_ready" == "true" && "$vercel_ready" == "true" && "$openai_ready" == "true" ]]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
82 changes: 66 additions & 16 deletions .github/workflows/codemode-framework-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ jobs:
- uses: ./.github/actions/setup-chrome-verified
id: setup-chrome

- run: pnpm exec turbo run build --filter @browserbasehq/stagehand-codemode
- run: pnpm --filter ${{ matrix.package }} typecheck
- run: pnpm --filter ${{ matrix.package }} test:contract
- run: pnpm --filter ${{ matrix.package }} pack:artifacts
Expand All @@ -70,20 +69,14 @@ jobs:
STAGEHAND_BROWSER: local
- name: Detect live test credentials
id: live-credentials
uses: ./.github/actions/detect-codemode-live-credentials
env:
BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }}
BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }}
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
if [[ -n "$BROWSERBASE_API_KEY" && -n "$BROWSERBASE_PROJECT_ID" ]] && \
[[ -n "$VERCEL_OIDC_TOKEN" || ( -n "$VERCEL_TEAM_ID" && -n "$VERCEL_PROJECT_ID" && -n "$VERCEL_TOKEN" ) ]]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Run live package-installed sandbox proof
if: steps.live-credentials.outputs.available == 'true'
run: pnpm --filter ${{ matrix.package }} e2e
Expand Down Expand Up @@ -111,12 +104,14 @@ jobs:
with:
use-prebuilt-artifacts: "false"

- run: pnpm exec turbo run build --filter @browserbasehq/stagehand-codemode
- run: pnpm --filter @browserbasehq/stagehand-integrations-example-mastra typecheck
- run: pnpm --filter @browserbasehq/stagehand-integrations-example-mastra test:contract
- run: pnpm --filter @browserbasehq/stagehand-integrations-example-vercel-sandbox pack:artifacts
- name: Detect Mastra live test credentials
id: mastra-live-credentials
uses: ./.github/actions/detect-codemode-live-credentials
with:
require-openai: "true"
env:
BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }}
BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }}
Expand All @@ -125,13 +120,6 @@ jobs:
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
if [[ -n "$BROWSERBASE_API_KEY" && -n "$BROWSERBASE_PROJECT_ID" && -n "$OPENAI_API_KEY" ]] && \
[[ -n "$VERCEL_OIDC_TOKEN" || ( -n "$VERCEL_TEAM_ID" && -n "$VERCEL_PROJECT_ID" && -n "$VERCEL_TOKEN" ) ]]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Run Mastra live sandbox proof
if: steps.mastra-live-credentials.outputs.available == 'true'
run: pnpm --filter @browserbasehq/stagehand-integrations-example-mastra e2e
Expand All @@ -144,3 +132,65 @@ jobs:
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

crewai:
name: CrewAI
if: >-
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository ||
contains(github.event.pull_request.labels.*.name, 'safe-to-test')
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- uses: ./.github/actions/setup-node-pnpm
with:
use-prebuilt-artifacts: "false"

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
cache: pip
cache-dependency-path: packages/integrations/examples/crewai/requirements.txt

- run: python -m pip install -r packages/integrations/examples/crewai/requirements.txt
- run: >-
python -m py_compile
packages/integrations/examples/shared/vercel_sandbox_lease.py
packages/integrations/examples/shared/test_vercel_sandbox_lease.py
packages/integrations/examples/crewai/agent.py
packages/integrations/examples/crewai/sandbox.py
packages/integrations/examples/crewai/test_agent.py
packages/integrations/examples/crewai/test_e2e.py
packages/integrations/examples/crewai/e2e.py
- run: python packages/integrations/examples/shared/test_vercel_sandbox_lease.py
- run: python packages/integrations/examples/crewai/test_agent.py
- run: python packages/integrations/examples/crewai/test_e2e.py
- run: pnpm --filter @browserbasehq/stagehand-integrations-example-vercel-sandbox pack:artifacts
- name: Detect CrewAI live test credentials
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
id: crewai-live-credentials
uses: ./.github/actions/detect-codemode-live-credentials
with:
require-openai: "true"
env:
BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }}
BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }}
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Run CrewAI live sandbox proof
if: steps.crewai-live-credentials.outputs.available == 'true'
run: python packages/integrations/examples/crewai/e2e.py
env:
STAGEHAND_SANDBOX_ARTIFACTS: ${{ github.workspace }}/packages/integrations/examples/vercel-sandbox/.artifacts
BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }}
BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }}
VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }}
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
2 changes: 2 additions & 0 deletions packages/integrations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,5 @@ implementation modules and an in-process arbitrary-code executor are not public
- [Vercel Sandbox](./examples/vercel-sandbox) installs the exact packed artifact inside a
Firecracker microVM and returns a framework-neutral, bearer-authenticated MCP connection.
- [Mastra](./examples/mastra) consumes that connection with one persistent remote MCP client.
- [CrewAI](./examples/crewai) keeps its context-managed MCP adapter open across every tool call in
one crew execution.
79 changes: 79 additions & 0 deletions packages/integrations/examples/crewai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# CrewAI with Stagehand code mode

This example gives a CrewAI agent one browser tool, `code_execute`, without running generated
JavaScript in the Python agent process. A small trusted Python lease owner starts the
package-installed [Vercel Sandbox example](../vercel-sandbox) and receives its `{ url, token }`
connection. CrewAI then connects directly through authenticated Streamable HTTP:

```text
CrewAI MCPServerAdapter -> authenticated HTTPS -> Vercel Sandbox -> Stagehand code-mode MCP
`-> generated JavaScript
```

The adapter discovers the canonical tool description and uses it as the agent's backstory. It does
not copy the executor, schema, or code-mode skill.

## Setup

Create a Python 3.12 environment and install the pinned CrewAI example dependencies:

```bash
python3.12 -m venv .venv
. .venv/bin/activate
python -m pip install -r packages/integrations/examples/crewai/requirements.txt
```

Build and pack the exact Stagehand packages under review:

```bash
pnpm install
pnpm --filter @browserbasehq/stagehand-integrations-example-vercel-sandbox pack:artifacts
```

## Run the end-to-end proof

```bash
STAGEHAND_SANDBOX_ARTIFACTS="$PWD/packages/integrations/examples/vercel-sandbox/.artifacts" \
BROWSERBASE_API_KEY=<api-key> \
BROWSERBASE_PROJECT_ID=<project-id> \
VERCEL_OIDC_TOKEN=<oidc-token> \
OPENAI_API_KEY=<openai-key> \
python packages/integrations/examples/crewai/e2e.py
```

For external CI, replace `VERCEL_OIDC_TOKEN` with `VERCEL_TEAM_ID`, `VERCEL_PROJECT_ID`, and
`VERCEL_TOKEN`. Set `CREWAI_MODEL` in your own wrapper if you want to pass a model other than the
example's `openai/gpt-5-mini` default.

The proof uses one live package-installed sandbox and one context-managed CrewAI MCP adapter. It:

1. invokes `code_execute` twice directly and requires the same page ID and DOM marker;
2. records CrewAI's tool-usage event from a real model-selected `code_execute` call;
3. invokes the tool again to independently verify the model's browser-side change;
4. proves the model key and a host-only marker are absent inside generated code; and
5. closes the CrewAI MCP adapter before ending the sandbox lease, emitting `PASS` only afterward.

## Use the agent

Run the snippet with the example directory as the working directory so its local `agent.py` and
`sandbox.py` modules resolve:

```bash
cd packages/integrations/examples/crewai
```

```python
from agent import run_stagehand_agent
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
from sandbox import StagehandSandboxLease

with StagehandSandboxLease() as connection:
result = run_stagehand_agent(
connection,
"Open https://example.com and return its title and URL.",
)
print(result)
```

The lease subprocess receives only Browserbase, Vercel, artifact, and runtime variables from its
allowlist. Outer model-provider credentials are intentionally excluded, and the sandbox foundation
brokers the Browserbase credential at its egress boundary.
Loading
Loading