npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opentelemetry-instrumentation-browserbase

v0.1.0

Published

OpenTelemetry instrumentation for Browserbase and Stagehand.

Downloads

13

Readme

opentelemetry-instrumentation-browserbase

OpenTelemetry API-only instrumentation helpers for Browserbase and Stagehand browser agents.

This package wraps existing SDK objects with JavaScript proxies. It does not install an OpenTelemetry SDK, exporter, or provider; the host app owns that setup.

Five-Minute Quickstart

Install the library, Stagehand, and the OpenTelemetry packages your app uses:

npm install opentelemetry-instrumentation-browserbase @browserbasehq/stagehand @opentelemetry/api @opentelemetry/api-logs @opentelemetry/sdk-node

Set your collector endpoint with the standard OpenTelemetry environment variables used by your app, then wrap your Stagehand instance:

import { NodeSDK } from "@opentelemetry/sdk-node";
import { Stagehand } from "@browserbasehq/stagehand";
import {
  instrumentStagehand,
  startWorkflow,
  withAgentScopeLogger
} from "opentelemetry-instrumentation-browserbase";

const sdk = new NodeSDK();
sdk.start();

const stagehand = instrumentStagehand(
  new Stagehand(
    withAgentScopeLogger({
      env: "LOCAL",
      modelName: "google/gemini-2.5-flash"
    })
  )
);

await startWorkflow("example.browser_task", async () => {
  await stagehand.init();
  await stagehand.page.goto("https://example.com");
  await stagehand.observe("find the main heading");
  await stagehand.extract("extract the page title");
  await stagehand.close();
});

await sdk.shutdown();

Two-line wrapper shape:

const stagehand = instrumentStagehand(new Stagehand(withAgentScopeLogger(options)));
await startWorkflow("checkout", () => stagehand.init());

For direct Browserbase SDK calls:

npm install opentelemetry-instrumentation-browserbase @opentelemetry/api @opentelemetry/api-logs
const bb = instrumentBrowserbase(new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY }));
await bb.search.web({ query: "site example", numResults: 5 });

For plain Playwright/Puppeteer on Browserbase, attach CAPTCHA telemetry directly to a page:

attachCaptchaListener(page);

Public API

  • instrumentStagehand(stagehand, options?)
  • instrumentBrowserbase(client, options?)
  • startWorkflow(name, attrs, fn)
  • getWorkflow(ctx?)
  • attachCaptchaListener(page, options?)
  • agentScopeLogger() and withAgentScopeLogger(options)
  • configureRedaction(options)
  • configureCost(options)
  • ATTR, SPAN, and METRIC
  • Advanced helpers for custom instrumentation: withSpan, emitLog, and runRiskDetectors

Config Reference

| Option | Where | Default | Description | | --- | --- | --- | --- | | instrumentStagehand(..., { attributes }) | Stagehand wrapper | {} | Static caller attributes added to Stagehand spans. | | instrumentStagehand(..., { captureInstructions }) | Stagehand wrapper | true | Set false to suppress natural-language instruction/action text attributes. | | instrumentStagehand(..., { attributeHook }) | Stagehand wrapper | none | Last-chance hook to add, change, or remove span attributes before they are set. | | instrumentStagehand(..., { riskDetectors }) | Stagehand wrapper | built-ins | Replaces the built-in pure risk detector list. | | instrumentBrowserbase(..., { fetchFallbackContentLength }) | Browserbase wrapper | 512 | Minimum content length before Fetch is treated as suspiciously short. | | instrumentBrowserbase(..., { captureSearchQuery }) | Browserbase wrapper | true | Set false to suppress browserbase.search.query. | | instrumentBrowserbase(..., { attributeHook }) | Browserbase wrapper | none | Last-chance hook to add, change, or remove span attributes before they are set. | | startWorkflow(name, attrs, fn) | Workflow root | generated run id | Creates root span and mutable workflow state. | | configureCost({ pricing }) | Cost module | built-in table | Overrides per-model input/output/cached-input pricing. | | configureRedaction({ extraPatterns, disableDefaults }) | Redaction module | defaults enabled | Redacts sensitive attributes before span/log emission. | | withAgentScopeLogger(options) | Stagehand constructor options | disablePino: true | Injects the OTel log bridge into Stagehand options. |

connectUrl and seleniumRemoteUrl are dropped unconditionally by redaction. Dynamic variable values are never recorded; only browser_agent.variables_used=true is emitted.

Stagehand support is for v3 instance-level act, observe, and extract. Stagehand v2 page-level methods are not wrapped automatically; use direct page instrumentation where needed.

Attributes

| Attribute | Type | Description | | --- | --- | --- | | gen_ai.request.model | string | Model requested for an AI-backed browser operation. | | gen_ai.usage.input_tokens | int | Input tokens used by an AI-backed browser operation. | | gen_ai.usage.output_tokens | int | Output tokens used by an AI-backed browser operation. | | gen_ai.usage.cached_input_tokens | int | Cached input tokens when the runtime reports them. | | gen_ai.operation.name | string | AI operation name such as act, observe, extract, or agent.execute. | | url.full | string | Full URL involved in the operation. | | url.domain | string | Domain component of the URL. | | http.response.status_code | int | HTTP response status code. | | server.address | string | Server address contacted by the operation. | | error.type | string | Error class or derived type name. | | browser_agent.failure.origin | string | page, model, or infra; unknown errors default to infra. | | browser_agent.workflow.name | string | Caller-provided workflow name. | | browser_agent.run_id | string | Workflow correlation id. | | browser_agent.status | string | Workflow status: ok or error. | | browser_agent.action.method | string | Browser action method. | | browser_agent.action.selector | string | Selector or element reference used for an action. | | browser_agent.action.success | boolean | Whether a browser action succeeded. | | browser_agent.action.self_healed | boolean | Whether an action recovered by using an alternate target; emitted only when the Stagehand result exposes it. | | browser_agent.action.description | string | Natural-language action instruction or description. | | browser_agent.cache.status | string | Conditional cache state for AI-planned work; local Stagehand runs usually do not expose it. | | browser_agent.extract.schema | string | Schema name or description used for extraction. | | browser_agent.extract.result_count | int | Count of extraction results. | | browser_agent.extract.empty | boolean | Whether extraction returned no usable content. | | browser_agent.extract.screenshot_used | boolean | Whether screenshot input was requested or used. | | browser_agent.extract.scoped | boolean | Whether extraction was scoped to a selector or sub-tree. | | browser_agent.observe.instruction | string | Natural-language observation instruction. | | browser_agent.observe.action_count | int | Number of candidate actions returned. | | browser_agent.auth.captcha.encountered | boolean | Whether a CAPTCHA challenge was observed. | | browser_agent.auth.captcha.solved | boolean | Whether the CAPTCHA challenge was solved. | | browser_agent.auth.captcha.duration_ms | int | CAPTCHA solve duration in milliseconds. | | browser_agent.auth.context.reused | boolean | Whether persisted browser state was reused. | | browser_agent.auth.login_avoided | boolean | Whether state reuse avoided an interactive login. | | browser_agent.auth.state | string | Auth state such as valid, expired, or missing. | | browser_agent.path | string | Browser path selected for the operation: fetch or session. | | browser_agent.path.fallback_reason | string | Reason for changing path. | | browser_agent.risk.categories | string[] | Risk categories detected during the workflow. | | browser_agent.risk.injection | boolean | Instruction-injection risk flag. | | browser_agent.risk.credentials | boolean | Credential-solicitation risk flag. | | browser_agent.risk.secrets | boolean | Secret-material risk flag. | | browser_agent.risk.loop | boolean | Repeated-action loop risk flag. | | browser_agent.risk.domain | boolean | Unexpected-domain risk flag. | | browser_agent.risk.max_severity | string | Maximum risk severity. | | browser_agent.risk.finding_count | int | Number of findings produced by a risk scan. | | browser_agent.variables_used | boolean | Whether dynamic variables were supplied; values are never recorded. | | browserbase.session_id | string | Browserbase session identifier. | | browserbase.session_url | string | Browserbase session URL. | | browserbase.debug_url | string | Browserbase live debugging URL. | | browserbase.replay_url | string | Browserbase replay URL. | | browserbase.region | string | Browserbase region. | | browserbase.request_id | string | Browserbase API request id. | | browserbase.proxy_bytes | int | Bytes transferred through Browserbase proxy infrastructure. | | browserbase.context_id | string | Browserbase context id. | | browserbase.search.query | string | Redacted Browserbase Search query. | | browserbase.search.requested_count | int | Requested Search result count. | | browserbase.search.returned_count | int | Returned Search result count. | | browserbase.search.result_domains | string[] | Distinct domains returned by Search. | | browserbase.fetch.content_type | string | Fetch response content type. | | browserbase.fetch.encoding | string | Fetch response encoding. | | browserbase.fetch.size | int | Fetch response size. | | browserbase.session.status | string | Browserbase session status. | | browserbase.session.duration_ms | int | Session or session-operation duration in milliseconds. | | browserbase.replay.page_count | int | Number of replay pages returned. | | browserbase.logs.event_count | int | Conditional count emitted only when session-log ingestion runs. | | browserbase.logs.network_error_count | int | Conditional count emitted only when session-log ingestion runs. | | browserbase.logs.console_error_count | int | Conditional count emitted only when session-log ingestion runs. | | browserbase.usage.browser_minutes | double | Browser minutes from usage APIs when available; current usage responses can be zero and do not drive duration panels. | | llm.cost.usd | double | Estimated LLM cost in USD. | | llm.cost.unknown_model | boolean | Whether cost could not be computed for the model. | | llm.gateway | string | Gateway or provider prefix used on LLM cost metrics. |

Metrics

| Instrument | Type | Unit | Dimensions | | --- | --- | --- | --- | | browser_agent.workflow.duration | Histogram | ms | workflow name, run id | | browser_agent.action.count | Counter | {action} | caller attributes | | browser_agent.extract.result_count | Histogram | {result} | caller attributes | | browser_agent.extract.empty.count | Counter | {call} | caller attributes | | browser_agent.auth.captcha.count | Counter | {challenge} | workflow/session attributes | | browser_agent.auth.captcha.duration | Histogram | ms | workflow/session attributes | | browserbase.proxy.bytes | Counter | By | workflow/session attributes | | browserbase.session.duration | Histogram | ms | workflow/session attributes | | llm.cost.usd | Counter | USD | gen_ai.request.model, llm.gateway |

Logs

Risk findings and Stagehand bridge messages emit real OTel log records through @opentelemetry/api-logs. They no-op until the host app registers a logs provider.

Risk logs use a structured body:

{
  "risk.category": "injection",
  "risk.severity": "high",
  "risk.evidence": "[redacted or excerpt]",
  "risk.source": "browser_agent.risk.scan"
}

Logs are emitted inside the active span context.

CAPTCHA Telemetry

Browserbase emits page console messages named browserbase-solving-started and browserbase-solving-finished when its CAPTCHA solver runs. This package converts those messages into browserbase.captcha.solve spans with browser_agent.auth.captcha.* attributes.

Runtime support observed with Stagehand v3.7.1:

  • env: "BROWSERBASE" with default hosted Stagehand API mode does not expose those page console events to the local Node process. Solve-duration telemetry is not available in that mode.
  • env: "BROWSERBASE", disableAPI: true drives the Browserbase session through a local CDP/Playwright connection. In this mode the console events are visible and browserbase.captcha.solve spans include a real duration.
  • Plain Browserbase SDK plus Playwright-over-CDP can also observe the console events when the listener is attached to the Playwright Page.

If your runtime hides page console events, report only challenge encounter signals derived from DOM behavior. Do not invent solve durations.

Known Runtime Behaviors

  • Stagehand v3.7.1 rejects a top-level contextId. The working shape is:
new Stagehand({
  env: "BROWSERBASE",
  browserbaseSessionCreateParams: {
    browserSettings: {
      context: { id: contextId, persist: true }
    }
  }
});
  • Stagehand's Browserbase page wrapper does not expose every Playwright convenience method in every runtime. In particular, fill() was not available in Browserbase mode during field testing; use Stagehand actions, DOM evaluation, or an underlying Playwright page when exact control is required.
  • Browserbase-mode self-heal can attempt to use a local provider key. Disable self-heal or provide a real provider key when using that path.
  • Failed Stagehand actions return ActResult.success === false with a message instead of throwing. Instrumentation treats that as an error span and derives error.type from the returned message.
  • Node auto HTTP instrumentation can capture Browserbase signingKey query parameters from replay/CDP URLs unless query redaction is configured in the host app. Configure the host app's HTTP instrumentation redactedQueryParams to include signingKey, token, api_key, apikey, key, secret, and signature.
  • Duplicate @opentelemetry/api-logs versions can silently swallow log records because the app and library may use different global API singletons. Keep a single resolved version across the workspace.

Roadmap

The following are intentionally not part of the frozen dashboard schema:

  • sessions.downloads.list file artifact summaries.
  • Session log event ingestion as individual OTel log records.
  • Full context CRUD coverage beyond the current reusable-context signals.
  • Certificate and extension wrapping.
  • Browserbase Agents API and Runs API wrapping.
  • CDP-layer instrumentation for direct Playwright/Puppeteer users.
  • Replay timestamp deep-linking.
  • LLM-backed risk detector backends.
  • Usage API panels. Keep the wrapper summary, but do not depend on usage values for session duration.