> ## Documentation Index
> Fetch the complete documentation index at: https://tracevell.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Kernel observability

> Trace computer-using agents that drive Kernel browsers, with the browser session recording synced to the agent's steps.

## Overview

[Kernel](https://onkernel.com/) is a platform that provides infrastructure for
running browser and computer using agents. You can run your browser agents on
Kernel to get top tier performance and developer experience. Kernel's SDKs
are tightly integrated with Tracevell, allowing you to trace your agents down
to the LLM calls and browser/computer actions.

> [!NOTE]
>
> A version of this guide is available in [Kernel's documentation](https://www.onkernel.com/docs/integrations/tracevell).

## Advantages of using Kernel

* Reliable infrastructure and quick computer startup times.
  * It's [open source](https://github.com/onkernel/kernel-images)!
* Reusable sessions, cookies, browser history and more.
* Stealth mode for browser automation avoids blockers and CAPTCHAs.
* Framework agnostic automations – you can use any browser framework you want.

## Prerequisites

1. A [Kernel](https://dashboard.onkernel.com/sign-up) account.
2. A [Tracevell](https://tracevell.com) account or a [self-hosted instance](https://github.com/lmnr-ai/lmnr).
3. API keys for both Kernel and Tracevell set as `KERNEL_API_KEY` and `LMNR_PROJECT_API_KEY` environment variables.

## Getting Started

### Installation

> [tab] JavaScript/TypeScript
>
> ```bash theme={null}
> npm install @lmnr-ai/lmnr@latest @onkernel/sdk@latest
> ```

> [tab] Python
>
> ```bash theme={null}
> pip install -U 'lmnr[all]' kernel
> ```

### Setting up API keys

1. Signup on the [Kernel dashboard](https://dashboard.onkernel.com/sign-up).
2. Create an API key during the onboarding process or in the [API keys page](https://dashboard.onkernel.com/api-keys).
3. Store the API key in the `KERNEL_API_KEY` environment variable.
4. Create a Tracevell project.
5. Navigate to the project settings and create a new project API key.

```sh theme={null}
export KERNEL_API_KEY=<your-kernel-api-key>
export LMNR_PROJECT_API_KEY=<your-tracevell-project-api-key>
```

## Running agents on Kernel

### Browser Use

Tracevell provides observability for Browser Use and Kernel, so if you host your Browser Use agents on Kernel,
you will get full observability of both the browser agent and the computer interactions.

[Full guide on browser-use integration](https://tracevell.com/docs/integrations/browser-use.md)

```python {7-8, 11-16, 22, 49-50} theme={null}
import os
import asyncio
from lmnr import Tracevell, observe
from browser_use import Agent, Browser, ChatGoogle
from kernel import Kernel

# Initialize Tracevell
Laminar.initialize(project_api_key=os.environ["LMNR_PROJECT_API_KEY"])

@observe()
async def main():
    # Initialize Kernel and create a browser
    client = Kernel()
    kernel_browser = client.browsers.create(
        stealth=True,
        viewport={'width': 1920, 'height': 1080},
    )

    print(f"Live view url: {kernel_browser.browser_live_view_url}")

    # Configure Browser Use with Kernel's CDP URL
    browser = Browser(
        cdp_url=kernel_browser.cdp_ws_url,
        headless=False,
        window_size={'width': 1920, 'height': 1080},
        viewport={'width': 1920, 'height': 1080},
        device_scale_factor=1.0
    )

    # Initialize the model. Don't forget to set GOOGLE_API_KEY env variable.
    llm = ChatGoogle(
        model="gemini-2.5-flash",
    )

    # Create and run the agent with job extraction task
    agent = Agent(
        task="""
        Go to duckduckgo.com and search for "browser agent observability".
        """,
        llm=llm,
        browser_session=browser
    )

    result = await agent.run()
    print(f"Job URLs found:\n{result.final_result()}")


    # Delete the browser for those who left open the live view url
    client.browsers.delete_by_id(kernel_browser.session_id)

asyncio.run(main())
# Flush traces to Tracevell
Laminar.flush()
```

### Playwright

You can use Tracevell to trace your Playwright browser sessions hosted on Kernel.

> [tab] JavaScript/TypeScript
>
> ```javascript {7-12, 15-16, 19-22, 26, 53} theme={null}
> import { Tracevell } from '@lmnr-ai/lmnr';
> import Kernel from '@onkernel/sdk';
> import { chromium } from 'playwright';
>
> // Initialize Tracevell with Playwright and Kernel instrumentations
> Laminar.initialize({
>   instrumentModules: {
>     playwright: {
>       chromium
>     },
>     kernel: Kernel
>   }
> });
>
> // Initialize Kernel and create a cloud browser
> const kernel = new Kernel();
>
> const main = async () => {
>   const kernelBrowser = await kernel.browsers.create({
>     stealth: true,
>   });
>
>   console.log("Live view url:", kernelBrowser.browser_live_view_url);
>
>   // Connect Playwright to Kernel's browser via CDP
>   const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url);
>   // Use existing context and page or create a new one
>   const context = browser.contexts()[0] || (await browser.newContext());
>   const page = context.pages()[0] || (await context.newPage());
>
>   // Your automation code
>   await page.goto('https://www.duckduckgo.com/')
>   await page.waitForTimeout(2000)
>   await page.goto('https://www.github.com/trending');
>
>   // Wait for 2 seconds
>   await page.waitForTimeout(2000);
>
>   // Extract all trending repos
>   const trendingRepos = await page.locator("h2 a.Link").evaluateAll((repos) => 
>     repos.map((repo) => repo.textContent.trim().replace(/[\n\s]/g, ''))
>   );
>
>   console.log('Trending repos:', trendingRepos);
>   await page.waitForTimeout(2000);
>
>   // Clean up the browser and flush traces to Tracevell
>   await browser.close();
>   await Laminar.flush();
>
>   // Delete the browser
>   await kernel.browsers.deleteByID(kernelBrowser.session_id);
> }
>
> main().catch(console.error);
> ```

> [tab] Python
>
> ```python {11-13,19, 49-50} theme={null}
> import asyncio
> from playwright.async_api import async_playwright
> from lmnr import Tracevell, observe
> from kernel import Kernel
>
> # Initialize Tracevell
> Laminar.initialize()
>
> @observe()
> async def main():
>     kernel_client = Kernel()
>     kernel_browser = kernel_client.browsers.create(stealth=True)
>     cdp_url = kernel_browser.cdp_ws_url
>
>     print(f"Live view url: {kernel_browser.browser_live_view_url}")
>
>     async with async_playwright() as p:
>         # Connect to Kernel's browser via CDP
>         browser = await p.chromium.connect_over_cdp(cdp_url)
>         # Use existing context and page or create a new one
>         context = (
>             browser.contexts[0]
>             if browser.contexts
>             else await browser.new_context()
>         )
>         page = context.pages[0] if context.pages else await context.new_page()
>
>         # Your automation code
>         await page.goto('https://www.duckduckgo.com/')
>         await page.wait_for_timeout(2000)
>         await page.goto('https://www.github.com/trending')
>         await page.wait_for_timeout(2000)
>
>         # Extract all trending repos
>         trending_repos = await page.locator("h2 a.Link").evaluate_all(r"""
>             (repos) => 
>                 repos.map((repo) => 
>                     repo.textContent.trim().replace(/[\n\s]/g, '')
>                 )
>             """)
>
>         print('Trending repos:', trending_repos)
>
>         await page.wait_for_timeout(2000)
>
>         # Clean up the browser
>         await browser.close()
>
>         # Delete the browser
>         kernel_client.browsers.delete_by_id(kernel_browser.session_id)
>
> asyncio.run(main())
> Laminar.flush()
> ```

## Deploying Kernel apps and using Kernel's Computer and Process APIs

When you use Kernel's [App platform](https://www.onkernel.com/docs/apps/develop)
or use their [Computer controls](https://www.onkernel.com/docs/browsers/computer-controls),
Tracevell will trace the computer and process interactions.

In addition, you don't have to `observe` your kernel `app.actions` and worry about manual trace flushing inside your Kernel apps.

> [!TIP]
>
> Tracevell will take care of trace lifecycle automatically.

As a result of the example below, you will see the following in the Tracevell UI:

![Kernel app trace](https://mintcdn.com/laminarai/9qtR0lxstxLlbiBX/images/traces/kernel.png?fit=max\&auto=format\&n=9qtR0lxstxLlbiBX\&q=85\&s=da1500ef2eeae47b72ce23a3048e3f18)

### Example Kernel app with Tracevell tracing

To deploy this example on Kernel, follow the steps in [Kernel's documentation](https://www.onkernel.com/docs/apps/deploy).

> [tab] JavaScript/TypeScript
>
> ```javascript theme={null}
> import { Tracevell } from '@lmnr-ai/lmnr';
> import { chromium } from 'playwright';
> import { config } from 'dotenv';
> import Kernel, { type KernelContext } from "@onkernel/sdk";
>
> config();
>
>
> Laminar.initialize({
>   instrumentModules: {
>     playwright: {
>       chromium,
>     },
>     kernel: Kernel,
>   }
> });
> const kernel = new Kernel();
> const app = kernel.app('my-app-name');
>
> app.action('my-action', async (ctx: KernelContext, payload) => {
>   const kernelBrowser = await kernel.browsers.create({
>     invocation_id: ctx.invocation_id,
>   });
>   const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url);
>   const context = browser.contexts[0] || (await browser.newContext());
>   const page = context.pages[0] || (await context.newPage());
>
>   // Your automation code
>   await page.goto('https://www.duckduckgo.com/')
>   await page.waitForTimeout(2000)
>   await page.goto('https://www.github.com/trending');
>
>   // Wait for 2 seconds
>   await page.waitForTimeout(2000);
>
>   // Extract all trending repos
>   const trendingRepos = await page.locator("h2 a.Link").evaluateAll((repos) =>
>     repos.map((repo) => repo.textContent.trim().replace(/[\n\s]/g, ''))
>   );
>
>   console.log('Trending repos:', trendingRepos);
>   await page.waitForTimeout(2000);
>
>   // Computer tool call
>   await kernel.browsers.computer.captureScreenshot(
>     kernelBrowser.session_id,
>   );
>   // Process tool call
>   await kernel.browsers.process.exec(
>     kernelBrowser.session_id,
>     {
>       command: 'ls',
>       args: ['-la'],
>     }
>   )
>
>   await browser.close();
>   await kernel.browsers.deleteByID(kernelBrowser.session_id);
> });
>
> ```

> [tab] Python
>
> ```python theme={null}
> from lmnr import Tracevell
> from playwright.async_api import async_playwright
> from kernel import App, Kernel, KernelContext
>
> # Initialize Tracevell
> Laminar.initialize()
> kernel_client = Kernel()
> app = App("my-app-name")
>
> @app.action(name="my-action")
> async def run_automation(ctx: KernelContext):
>     # Connect Playwright to Kernel's browser
>     async with async_playwright() as p:
>         kernel_browser = kernel_client.browsers.create(
>             invocation_id=ctx.invocation_id
>         )
>         browser = await p.chromium.connect_over_cdp(kernel_browser.cdp_ws_url)
>         context = (
>             browser.contexts[0]
>             if browser.contexts
>             else await browser.new_context()
>         )
>         page = context.pages[0] if context.pages else await context.new_page()
>
>         # Your automation code
>         await page.goto('https://www.duckduckgo.com/')
>         await page.wait_for_timeout(2000)
>         await page.goto('https://www.github.com/trending')
>         await page.wait_for_timeout(2000)
>
>         # Extract all trending repos
>         trending_repos = await page.locator("h2 a.Link").evaluate_all(r"""
>             (repos) => 
>                 repos.map((repo) => 
>                     repo.textContent.trim().replace(/[\n\s]/g, '')
>                 )
>             """)
>
>         print('Trending repos:', trending_repos)
>
>         kernel_client.browsers.process.exec(
>             id=kernel_browser.session_id,
>             command="ls",
>             args=["-la"],
>         )
>
>         kernel_client.browsers.computer.move_mouse(
>             id=kernel_browser.session_id,
>             x=100,
>             y=100,
>         )
>
>         # Wait for 3 seconds
>         await page.wait_for_timeout(3000)
>
>         # Clean up the browser
>         await browser.close()
>
>
>     # Delete the browser for those who left open the Kernel live view url
>     kernel_client.browsers.delete_by_id(kernel_browser.session_id)
>
>     print("Done")
> ```

## Next steps

* Understand [session replay for browser agents](https://tracevell.com/docs/tracing/browser-agent-observability.md) in more detail.
* Learn about [Profiles](https://www.onkernel.com/docs/browsers/profiles) in Kernel to persist browser state between runs.
* Learn about calling playwright [directly](https://www.onkernel.com/docs/browsers/playwright-execution) through Kernel's API.

## Related integrations

> [card] [Browser Use](https://tracevell.com/docs/integrations/browser-use.md)
>
> Trace Browser Use agents with synced session recordings.

> [card] [Stagehand](https://tracevell.com/docs/integrations/stagehand.md)
>
> Trace Stagehand runs with synced session recordings.

> [card] [Skyvern](https://tracevell.com/docs/integrations/skyvern.md)
>
> Trace Skyvern workflows with synced session recordings.

> [card] [Playwright](https://tracevell.com/docs/integrations/playwright.md)
>
> Record Playwright browser sessions alongside your traces.

> [card] [OpenHands SDK](https://tracevell.com/docs/integrations/openhands-sdk.md)
>
> Trace OpenHands software agents end to end.

> [card] [All integrations](https://tracevell.com/docs/integrations.md)
>
> Browse every provider, framework, coding agent, and browser integration Tracevell supports.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.