Laminar is an open-source observability and evaluation platform for autonomous AI agents. You can create a cloud account or self-host Laminar for your infrastructure. By integrating Laminar with Kernel, you can trace and monitor your browser automations with full visibility into LLM calls, browser actions, session recordings, and performance metrics.
Why use Laminar with Kernel?
No local browser management : Run automations in the cloud while maintaining full observability
Scalability : Launch multiple browser sessions with independent traces
Debugging : Use Kernel’s live view during development and Laminar’s session recordings for post-execution analysis
Cost optimization : Track LLM costs across all your browser automations
Performance tuning : Identify slow operations and optimize your agent workflows
Prerequisites
Before integrating Laminar with Kernel, you’ll need:
A Kernel account with a Kernel API Key
A Laminar account and project
Your Laminar project API key from the Project Settings page
Installation
npm install @lmnr-ai/lmnr @onkernel/sdk
Getting your Laminar API key
Log in to your Laminar dashboard
Navigate to Project Settings
Generate a new API key in your project
Copy your Project API Key
Set it as an environment variable:
export LMNR_PROJECT_API_KEY = your_api_key_here
You will also need to generate a KERNEL_API_KEY from your Kernel dashboard to authenticate with Kernel’s browser infrastructure.
Browser Agent Framework Examples
Select your browser automation framework to enable Laminar tracing with Kernel:
Always call Laminar.flush() or ensure your traced functions complete to submit traces to Laminar.
Playwright
Playwright is a popular low-level browser automation framework. Here’s how to use it with Laminar and Kernel:
The Playwright examples include waitForTimeout() calls to help ensure Laminar traces populate properly for these short, fast code snippets.
Typescript/Javascript
Python
import { Laminar , observe } from '@lmnr-ai/lmnr' ;
import Kernel from '@onkernel/sdk' ;
import { chromium } from 'playwright' ;
// Initialize Laminar with Playwright instrumentation
Laminar . initialize ({
projectApiKey: process . env . LMNR_PROJECT_API_KEY ,
instrumentModules: {
playwright: {
chromium
},
kernel: Kernel ,
}
});
// Initialize Kernel and create a cloud browser
const kernel = new Kernel ();
const main = async () => observe ({ name: '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 );
const context = browser . contexts ()[ 0 ] || ( await browser . newContext ());
const page = context . pages ()[ 0 ] || ( await context . newPage ());
// Wait for 3 second
await page . waitForTimeout ( 3000 );
// Your automation code
await page . goto ( 'https://www.onkernel.com/docs' );
// Wait for 2 second
await page . waitForTimeout ( 2000 );
// Navigate to careers page
await page . goto ( 'https://www.onkernel.com/docs/careers/intro' );
// Extract all job URLs from the ul next to #open-roles
const jobLinks = await page . locator ( '#open-roles + ul a' ). evaluateAll (( links ) => {
const baseUrl = 'https://www.onkernel.com' ;
return links
. map ( link => {
const href = link . getAttribute ( 'href' );
if ( ! href ) return null ;
// Convert relative URLs to absolute URLs
return href . startsWith ( 'http' ) ? href : baseUrl + href ;
})
. filter ( href => href !== null );
});
console . log ( 'Job URLs found:' , jobLinks );
console . log ( `Total jobs: ${ jobLinks . length } ` );
// Wait for 3 seconds
await page . waitForTimeout ( 3000 );
// Clean up the browser
await browser . close ();
// Delete the browser for those who left open the live view url
await kernel . browsers . deleteByID ( kernelBrowser . session_id );
});
await main ();
await Laminar . flush ();
Browser Use
Browser Use is an AI browser agent framework. Here’s how to integrate it with Laminar and Kernel:
import os
import asyncio
from lmnr import Laminar, observe
from browser_use import Agent, Browser, ChatOpenAI
from kernel import Kernel
# Initialize Laminar
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
llm = ChatOpenAI(
model = "gpt-4.1" ,
)
# Create and run the agent with job extraction task
agent = Agent(
task = """1. Go to https://www.onkernel.com/docs
2. Navigate to the main Jobs page
3. Extract all the job posting URLs. List each URL you find.""" ,
llm = llm,
browser_session = browser
)
result = await agent.run()
print ( f "Job URLs found: \n { result.final_result() } " )
# Flush traces to Laminar
Laminar.flush()
# Delete the browser for those who left open the live view url
client.browsers.delete_by_id(kernel_browser.session_id)
asyncio.run(main())
Stagehand
Stagehand is an AI browser automation framework. Here’s how to use it with Laminar and Kernel:
This example shows Stagehand v2 instrumentation setup.
import { Laminar , observe } from '@lmnr-ai/lmnr' ;
import { Stagehand } from '@browserbasehq/stagehand' ;
import Kernel from '@onkernel/sdk' ;
import { z } from 'zod' ;
// Initialize Laminar with Stagehand instrumentation
Laminar . initialize ({
projectApiKey: process . env . LMNR_PROJECT_API_KEY ,
instrumentModules: {
stagehand: Stagehand ,
kernel: Kernel ,
},
});
// Initialize Kernel and create a browser
const kernel = new Kernel ();
const main = async () => observe ({ name: 'main' }, async () => {
const kernelBrowser = await kernel . browsers . create ({ stealth: true });
console . log ( "Live view url:" , kernelBrowser . browser_live_view_url );
// Configure Stagehand to use Kernel's browser
const stagehand = new Stagehand ({
env: 'LOCAL' ,
verbose: 1 ,
domSettleTimeoutMs: 30_000 ,
modelName: 'openai/gpt-4.1' ,
modelClientOptions: {
apiKey: process . env . OPENAI_API_KEY
},
localBrowserLaunchOptions: {
cdpUrl: kernelBrowser . cdp_ws_url
}
});
await stagehand . init ();
// Your automation code
const page = stagehand . page ;
await page . goto ( 'https://www.onkernel.com/docs' );
// Navigate to careers page
await page . goto ( 'https://www.onkernel.com/docs/careers/intro' );
// Extract all job URLs
const output = await page . extract ({
instruction: 'Extract all job posting URLs from the Open Roles section.' ,
schema: z . object ({
jobUrls: z . array ( z . string ()). describe ( 'Array of job posting URLs' )
})
});
console . log ( 'Job URLs found:' , output . jobUrls );
console . log ( `Total jobs: ${ output . jobUrls . length } ` );
// Clean up and flush traces to Laminar
await stagehand . close ();
// Delete the browser for those who left open the live view url
await kernel . browsers . deleteByID ( kernelBrowser . session_id );
});
await main ();
await Laminar . flush ();
Tracing Kernel Apps & Computer Controls
When you use Kernel’s App platform or Computer controls , Laminar will automatically trace the computer and process interactions.
In addition, you don’t have to manually observe your kernel app.actions or worry about manual trace flushing inside your Kernel apps.
Laminar will take care of trace lifecycle automatically for Kernel apps.
Example Kernel app with Laminar tracing
To deploy this example on Kernel, follow the steps in Kernel’s app deployment guide .
Typescript/Javascript
Python
import { Laminar } 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 );
return { success: true , result: "Action completed successfully" };
});
Viewing traces in Laminar
View your traces in the Laminar UI’s traces tab to see synchronized browser session recordings and agent execution steps.
After running your automation:
Log in to your Laminar dashboard
Navigate to the Traces tab
Find your recent trace to view:
Full execution timeline
LLM calls and responses
Browser session recordings
Computer and process interactions (for Kernel apps)
Token usage and costs
Latency metrics
Timeline highlights indicate which step your agent is currently executing, making it easy to debug and optimize your automations.
Next steps