Quickstart (5 minutes)

From zero to a verified artefact: install, point the runtime at a provider, run one goal, read the evidence.

1 · Install
npm install @mlola/browser-runtime
npx @mlola/browser-cli browser doctor      # every line should be green
2 · Providers — pick one
mlola browser config init                  # writes mlola.config.json

# { "providers": {
#     "decision":  { "provider": "typesafe", "model": "jev-latest" },
#     "reasoning": { "provider": "openrouter", "model": "openai/gpt-5.6-luna" } } }

# or stay with environment variables:
export TYPESAFE_API_KEY=...                # TypeSafe is the default fast path
export OPENROUTER_API_KEY=...              # one key also runs everything: --fast openrouter
3 · Run a goal
npx @mlola/browser-cli browser run \
  --goal "Download my latest invoice" \
  --url https://billing.example.com/invoices
4 · Read the evidence
# the run prints status, verification and the artefact
npx @mlola/browser-cli browser trace show <runId>   # every decision, action and recovery
mlola browser config show                            # which routes resolved, credentials redacted

Nothing here needs a third-party site until you point `--url` at one. The repository also ships a local fixture and two runnable demos (`npm run demo`); see demos/README.md for the use cases each one proves.

Requirements

Node 22.18 or newer (Node 24 LTS recommended) and a Chromium-based browser. The CLI drives the Chrome you already have by default, and can fall back to a Playwright-managed Chromium.

Check your environment
node --version        # v22.18+ or v24.x
npx @mlola/browser-cli doctor

`doctor` checks Node, the fast-path key, the browser, the runtime home and the daemon. Every line should be green except the optional items.

Install

SDK + CLI
npm install @mlola/browser-runtime
npx @mlola/browser-cli browser doctor        # no global install needed

# or install the CLI globally:
npm install -g @mlola/browser-cli
mlola browser doctor

The SDK is a normal ESM package; the CLI and the daemon are optional. Daemon: `npm install -g @mlola/browser-daemon` then `mlola-browserd`.

Fast path: Jev API key

Routine decisions are made by the fast path — TypeSafe Jev — which needs an API key. Without a key you can still run everything with the offline baseline (`--provider keyword`), but that is a deterministic demo baseline, not the decision model.

  1. 1Create a key at https://typesafe.ai (TypeSafe account → API keys).
  2. 2Store it in the environment — pick one of the options below.
  3. 3Verify with doctor until the "Jev fast path" line is green.
Option 1 — export in your shell (persists per session)
export TYPESAFE_API_KEY="tsk_..."
mlola browser doctor
Option 2 — make it permanent (zsh)
echo 'export TYPESAFE_API_KEY="tsk_..."' >> ~/.zshrc
source ~/.zshrc
Option 3 — inline for a single run (CI friendly)
TYPESAFE_API_KEY="tsk_..." npx @mlola/browser-cli browser run \
  --goal "Download my latest invoice" \
  --url https://billing.example.com/invoices
Option 4 — .env file (your runner must load it)
# .env
echo 'TYPESAFE_API_KEY=tsk_...' > .env
node --env-file=.env ./node_modules/.bin/mlola browser run --goal "..."
Option 5 — config file (recommended, nothing to export)
mlola browser config init          # writes mlola.config.json
mlola browser config show          # what the file says + what actually resolved

# and if you only have an OpenRouter key, the fast path runs on it too:
export OPENROUTER_API_KEY=...
mlola browser run --fast openrouter --goal "Download my latest invoice" --url https://...

The CLI reads the shell first, then `.env.local`, then `.env` in the working directory — a local file is enough. In GitHub Actions, store the key as a secret and export it in the step.

Overriding the endpoint or model

VariableDefaultPurpose
TYPESAFE_API_KEYJev credential (required for the fast path)
MLOLA_JEV_BASE_URLhttps://api.typesafe.aiAPI base URL
MLOLA_JEV_MODELjev-latestModel id sent in the request
MLOLA_JEV_TIMEOUT_MS20000Per-request timeout

Choosing the decision provider

Provider selection
mlola browser run --provider typesafe …   # fast path (needs a key)
mlola browser run --provider keyword  …   # offline baseline, no key required

Your first run

Download an invoice
mlola browser doctor

mlola browser run \
  --goal "Download my latest invoice" \
  --url https://billing.example.com/invoices

The CLI prints every decision and action, then a summary: status, independent verification, artifacts, provider usage and the fast-path share.

Typical output (trimmed)
step click → 1 (358ms, conf 0.91)
  ⤓ downloaded invoice-2026-08.pdf (invoice)
  → CLICK [1] (324ms)
step done (344ms, conf 0.99)
  ✔ verified: artifact evidence: a1 invoice-2026-08.pdf

run completed
  steps            2 in 2.1s
  verification     verified — artifact evidence: a1 invoice-2026-08.pdf
  artifacts        a1 invoice-2026-08.pdf  invoice, verified
  usage            decision 2 · planner 0 · text 0 · $0.0000
  trace            ~/.mlola/browser-runtime/runs/r_xxx/events.jsonl

Unattended runs (CI, servers) should pass `--no-human --yes`; without a person the runtime stops safely instead of guessing.

Stronger model (optional)

The fast path covers routine steps. A stronger model is used only when it is genuinely needed — and Jev decides that: when a provider is configured the action space gains an ESCALATE operation, and choosing it makes the runtime ask that provider for a decision from the very same candidate list, validated the same way. The same provider also plans long-horizon goals and writes field values that cannot be reused from the goal.

Providers

ProviderBase URLKeyExample models
openrouterhttps://openrouter.ai/api/v1OPENROUTER_API_KEYdeepseek/deepseek-v4.1-flash, openai/gpt-5.6-luna
openaihttps://api.openai.com/v1OPENAI_API_KEYgpt-5.6-luna (or any model on your account)
customMLOLA_LLM_BASE_URLMLOLA_LLM_API_KEYanything OpenAI-compatible
OpenRouter: one key, many models
export OPENROUTER_API_KEY="sk-or-..."
export MLOLA_LLM_MODEL="deepseek/deepseek-v4.1-flash"   # or openai/gpt-5.6-luna

mlola browser doctor --check-llm          # verify the key, the model and the round trip
mlola browser run --goal "…" --url https://…
OpenAI, or any compatible endpoint
export OPENAI_API_KEY="sk-..."
export MLOLA_LLM_PROVIDER=openai
export MLOLA_LLM_MODEL="gpt-5.6-luna"

# endpoint internal
export MLOLA_LLM_PROVIDER=custom
export MLOLA_LLM_BASE_URL="https://llm.internal.example/v1"
export MLOLA_LLM_API_KEY="…"

Economics you can see

Escalation is charged to the planner role and recorded as fallback events, so the run summary separates cheap decisions from expensive ones: planner calls, fast-path share, and the provider-reported cost. The recovery ladder also caps strong-model consultations (two per run) so a model that cannot fix a problem cannot be consulted forever.

Measure the fast path alone
mlola browser run --llm none --goal "…" --url …    # no escalation, no planning, no text model
mlola browser trace summary <runId>               # planner calls, fast-path share, cost

The stronger model is optional in the truest sense: with no provider configured the runtime plans less, never escalates, and asks a person for a value it cannot reuse — nothing breaks.

Your own Chrome (extension)

The extension backend drives the Chrome you already use, so your authenticated sessions work without copying a profile. It opens its own Agent Window for the tabs it creates, and a user tab is only moved in after you approve the borrow — then it is returned to its original window and index.

Setup
npm run build && npm run build:extension
mlola browser daemon start

# chrome://extensions → Developer mode → Load unpacked → extensions/chrome
# mlola browser extension pair   → token copied to your clipboard + the two fields
# extension options → paste ws://127.0.0.1:7717/extension and the token

mlola browser extension status          # is a browser attached, and what can it do?
mlola browser run --browser-extension --goal "Download my latest invoice" --url https://…
CapabilityValue
Agent Window + tab groupsyes
Explicit tab borrowingapproval first, returned afterwards
Trusted inputno — synthetic DOM input; use Playwright when fidelity matters
Native dialogsalerts suppressed; confirm/prompt need a person
Console / networknot available
Uploadsfile inputs and drop zones (no file chooser)

The loadable extension ships inside `@mlola/backend-chrome` (`node_modules/@mlola/backend-chrome/extensions/chrome`), so a registry install does not need a repository clone. After editing it, press reload in `chrome://extensions` and confirm the version reported by `mlola browser extension status` — Chrome keeps the old service worker alive otherwise.

The fixture site picks a random port; `npm run fixtures -- --port 4311` pins it so every command in this page works as written.

Capabilities are advertised, never faked: the runtime tells you what the attached browser can do (`mlola browser extension status`) and escalates instead of pretending an action worked.

Configuration

Pin both provider roles in `mlola.config.json` (working directory, then the runtime home) instead of exporting anything.

mlola.config.json
{
  "providers": {
    "decision":  { "provider": "typesafe", "model": "jev-latest" },
    "reasoning": { "provider": "openai",   "model": "gpt-5.6-luna" }
  }
}
RoleProvidersWhat it decides
decisiontypesafe | openrouter | keywordthe fast path: which operation and target to act on
reasoningopenrouter | openai | custom | noneESCALATE, planning and fallback; `planner` is an accepted alias

Each role also accepts `model`, `apiKey`, `apiKeyEnv` and `baseUrl` — that is how Azure-style endpoints and gateway keys are pinned.

Inspect it
mlola browser config init      # write the starter file (never overwrites)
mlola browser config show     # the file, plus what actually resolved
mlola browser config show --json

Precedence for every field is flag > shell environment > `.env.local`/`.env` > `mlola.config.json`, so a file is a default and never surprises someone who exported a variable. Unknown roles and providers a role cannot use are rejected at startup, with the offending path named.

Use cases and demo

The runtime is for work where looking like it worked is not enough: documents that must be verified, forms that must not leak a secret, and browsers where you are already signed in.

Use caseWhat proves it
Document downloadan oracle-verified artifact with type, name and digest
Decoy-heavy listscompletion criteria pin the exact document (2026-08, not 2026-07)
Forms with secretssecret fields never enter a snapshot, a decision or a trace
Your own loginsAgent Window, explicit tab borrowing, capabilities reported honestly
Irreversible stepsapproval or human takeover; DONE is not offered mid-dialog
Debugging and auditone JSONL trace per run: decisions, actions, verification, recovery
LLM optionalJev decides when to escalate; the strong model only picks runtime-owned options
Deterministic CIscripted decisions against the same fixture: red means the runtime changed
Run the demo
npm install && npm run build
npm run demo                # headless Playwright + your own Chrome
npm run demo -- invoice     # headless only
npm run demo -- extension   # your own Chrome only (daemon + loaded extension)
mlola browser config llm    # which provider, model and key it will use

The demo runs against the local fixture site with the real decision path and exits non-zero when a case that should pass did not. Credentials come from the same places the CLI uses: the shell, .env.local, .env.

CLI reference

CommandWhat it does
mlola browser runExecute a goal (flags below) or a task JSON file
mlola browser doctorCheck Node, key, browser, runtime home and daemon
mlola browser sessions [list|show <id>]List recent runs / inspect one run
mlola browser trace show <id> [--type T]Print the event log (filterable)
mlola browser trace summary <id>Metrics as JSON
mlola browser eval --suite core|matrix|security|regression|stress|allRun the deterministic eval corpus
mlola browser daemon start|stop|statusManage the local daemon
mlola browser extension statusInspect the browser-extension bridge
mlola browser config init|showWrite or inspect mlola.config.json; shows resolved routes and where each credential came from
mlola browser profilesList managed browser profiles

run flags

FlagMeaning
--goal "…"The task, in plain language
--url <url>Start page
--task <file.json>Task specification file (see below)
--provider typesafe|keywordDecision provider
--llm openrouter|openai|custom|noneStronger model for ESCALATE and planning
--llm-model <id>Model id for that provider
--llm-api-key <key>Credential for that provider, instead of exporting it
--fast typesafe|openrouter|keywordWhich fast path to use (default: typesafe)
--fast-model <id> / --fast-api-key <key> / --fast-base-url <url>Override the fast path
--llm-base-url <url>Custom endpoint (Azure, gateways, local servers)
--planner-model/-api-key/-base-urlSame, for the planner role
--model <id>Override the fast-path model id
--browser chrome|chromium|msedgeBrowser channel
--browser-extensionDrive your own Chrome through the loaded extension
--headlessRun without a visible window
--profile <dir>Persistent browser profile
--cdp <url>Attach to an existing Chrome (e.g. http://127.0.0.1:9222)
--policy <file.json>Policy configuration file
--stop-before "…, …"Stop at the requested boundary instead of doing it
--max-steps <n>Step budget for this run
--screenshot off|on-failure|alwaysScreenshot policy (default on-failure)
--yesAuto-approve approval-required actions
--no-humanDisable human help (unattended)
--json / --quietMachine-readable result / summary only

Task file & boundaries

A task JSON file makes a run reproducible: goal, start URL, the success oracle, the stop boundary, budgets, policy and screenshot mode.

invoice-claim.json
{
  "goal": "Download my latest invoice, attach it to a new reimbursement claim titled \"August travel\", then stop before submitting",
  "startUrl": "https://billing.example.com/invoices",
  "successCriteria": { "criteria": { "kind": "artifact", "documentType": "invoice" } },
  "stopBefore": ["submit claim"],
  "budgets": { "maxSteps": 30, "maxCostUsd": 0.5 },
  "browser": { "backend": "playwright", "headless": true },
  "policy": { "defaults": { "upload": "allow" } },
  "screenshot": "on-failure"
}
Run it
mlola browser run invoice-claim.json
mlola browser run --task invoice-claim.json --stop-before "submit claim,payment"

`successCriteria` is the independent oracle: `url_contains`, `url_matches`, `title_contains`, `element_visible`, `text_visible`, `artifact`, `download_completed`, `manual`, plus the `all`, `any` and `not` combinations. Without an oracle, a completion the runtime cannot verify is reported honestly (`passed_with_unverified`) instead of being treated as success.

Policy & permissions

Risk is classified in code from the operation and the target label. Modes: `allow`, `ask` (a person approves), `takeover` (a person performs it), `deny`.

RiskDefaultExamples
readallowclick, type, select, scroll, wait
navigateallowlinks, tabs, history
downloadallowfile downloads
uploadaskfile inputs, choosers, drop zones
send_messageasksend, submit, post, confirm
publishaskpublish, share, make public
purchasetakeoverpay, buy, place order, transfer, subscribe
deletetakeoverdelete, remove, deactivate, unsubscribe
credential_changetakeoverpassword, OTP, card fields
policy.json
{
  "defaults": { "upload": "allow", "send_message": "ask", "purchase": "takeover" },
  "siteRules": [
    { "pattern": "*.internal.example.com", "risk": "upload", "mode": "allow" }
  ],
  "blockedOrigins": ["*.ads.example.com"],
  "restrictNavigation": false,
  "secretsRequireTakeover": true,
  "maxTypedLength": 2000
}

Password, OTP and card fields are a hard takeover floor: they are never typed by the runtime and never enter model state or traces. Site rules cannot lower that floor.

Recipes

Copy-paste patterns for the tasks people actually run. Replace the domains, goals and field names with your own; nothing here is specific to any one customer.

1. Download an invoice, attach it to a claim, stop before submitting

One run handles the whole flow: the runtime downloads the file, registers it as an artifact, opens the portal, signs in, types the literal title, uploads the artifact and stops at the boundary instead of submitting.

billing → portal, stop before submit
mlola browser run   --goal 'Download my latest invoice, attach it to a new reimbursement claim titled "August travel", then stop before submitting'   --url https://billing.example.com/invoices   --stop-before "submit claim"   --yes

The uploaded file is the artifact this run downloaded — no path guessing. Without --stop-before the runtime would finish the form, which is exactly what a "draft only" policy must prevent.

2. Sites that need login, MFA or a CAPTCHA

Nothing special to configure: the runtime never types a credential. It hands the step to the person, waits, then re-observes and continues on its own.

Interactive
mlola browser run   --goal "Sign in and open my claims list"   --url https://portal.example.com

# what you will see:
#   human needed (credential)
#   Take over the browser, finish this step, then hand control back.
#   (press Enter when done — the runtime re-observes and resumes)

Credential fields are not merely gated — they never reach the decision model at all. The backend marks a field as secret from its type, autocomplete, label, placeholder, name or id (including labels such as Kata Sandi or Kode Verifikasi) and the action space leaves it out. Unattended runs (--no-human) stop cleanly instead of guessing; in the SDK the same moment arrives as a takeover-required event.

3. Multi-tab flows

OPEN_TAB is offered only for links that really open a new tab, and SWITCH_TAB appears once more than one tab is open. Tabs the runtime opens are closed again at the end; user tabs are never touched.

Download here, upload there
mlola browser run   --goal "Download my latest invoice, then open the reimbursement portal in a new tab, sign in and attach it"   --url https://billing.example.com/invoices   --stop-before "submit"

4. Literal text vs generated text

Quoted values in the goal are reused verbatim, with no model involved. A text provider is only consulted when a value genuinely has to be written from scratch.

Literal first, generation only when needed
# literal: the quoted value is typed as-is (no model call)
mlola browser run   --goal 'Enter "August travel" as the claim title'   --url https://portal.example.com/claim

# generation: configure a text provider, otherwise the runtime asks a person
export MLOLA_TEXT_BASE_URL="https://api.openai.com/v1"
export MLOLA_TEXT_API_KEY="sk-..."
export MLOLA_TEXT_MODEL="gpt-4o-mini"
mlola browser run   --goal "Write a short description of this month's travel expenses in the notes field"   --url https://portal.example.com/claim

On a terminal the CLI answers that question itself: it asks "what should I type?" and types your answer (the trace records the value as user-supplied). Nothing is asked when the goal already contains the value — and exploratory goals such as "search some startups" always ask, because there is no value to reuse and inventing one is not the runtime's job.

5. Scheduled runs (daemon or cron)

The daemon owns runs that outlive the command, so a scheduler only has to fire an HTTP request. A plain cron line works too if you prefer everything in-process.

Queue a background run
mlola browser daemon start

TOKEN=$(cat ~/.mlola/browser-runtime/daemon/token)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json'   -d '{"goal":"Download my monthly invoice","startUrl":"https://billing.example.com/invoices","providers":{"decision":"typesafe"}}'   localhost:7717/v1/runs
Or straight from cron
# crontab -e — every 1st at 08:00
0 8 1 * * cd /srv/mlola && TYPESAFE_API_KEY=tsk_... /usr/bin/node ./node_modules/.bin/mlola browser run --no-human --yes --goal "Download my monthly invoice" --url https://billing.example.com/invoices >> /var/log/mlola-invoice.log 2>&1

6. Run it from GitHub Actions

Headless, unattended, with the key from a secret. The run summary is your evidence; the downloaded artifacts are uploaded as a build artifact.

.github/workflows/invoice.yml
name: invoice
on:
  schedule: [{ cron: "0 8 1 * *" }]
  workflow_dispatch:

jobs:
  download:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 24 }
      - run: npx playwright install --with-deps chromium
      - name: Run the task
        env:
          TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
        run: |
          npx @mlola/browser-cli browser run \
            --task task.json \
            --browser chromium --headless --no-human --yes --json | tee result.json
      - uses: actions/upload-artifact@v4
        with:
          name: invoice
          path: ~/.mlola/browser-runtime/runs/*/artifacts/**

7. Safety-bounded automation

A policy file turns "please be careful" into something the runtime enforces: two allowed origins, uploads allowed, everything that sends or pays stopped or handed over.

policy.json
{
  "defaults": { "upload": "allow", "send_message": "deny", "purchase": "takeover" },
  "restrictNavigation": true,
  "allowedOrigins": ["https://billing.example.com", "https://portal.example.com"],
  "secretsRequireTakeover": true
}
Run inside those limits
mlola browser run --policy policy.json --task task.json --stop-before "submit,pay"

8. Drive it from the SDK

Same runtime, your own process: subscribe to the events you care about and let the completion oracle decide the outcome.

agent.ts
import { BrowserAgent } from "@mlola/browser-runtime";

const agent = new BrowserAgent({ decision: "typesafe" });

agent.on("artifact-created", (event) => console.log("artifact:", event.payload.artifact.name));
agent.on("takeover-required", (event) => console.log("needs a person:", event.payload.prompt));
agent.on("fallback", (event) => console.log("escalated to", event.payload.to));

const run = await agent.run({
  goal: "Download my latest invoice",
  startUrl: "https://billing.example.com/invoices",
  successCriteria: { criteria: { kind: "artifact", documentType: "invoice" } },
});

console.log(run.status, run.verification?.status, run.artifacts);

Daemon (background runs)

The daemon owns runs that outlive a single CLI command, plus the event stream and the Chrome extension bridge. It binds to 127.0.0.1 only and uses a local token.

Lifecycle
mlola browser daemon start --port 7717
mlola browser daemon status
mlola browser daemon stop

npx mlola-browserd --port 7717        # foreground, when the daemon package is installed
Local API
TOKEN=$(cat ~/.mlola/browser-runtime/daemon/token)
curl -s -H "Authorization: Bearer $TOKEN" localhost:7717/v1/health
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"goal":"Download my latest invoice","startUrl":"https://billing.example.com/invoices"}' \
  localhost:7717/v1/runs
curl -s -H "Authorization: Bearer $TOKEN" "localhost:7717/v1/runs/<id>/events?since=0"
EndpointPurpose
GET /v1/healthLiveness, version, protocol, sessions (no token)
GET /v1/metaProtocol version + runtime capabilities
POST /v1/runsCreate a run (detached)
GET /v1/runs/:idStatus, result, pending help ids
GET /v1/runs/:id/events?since=N&stream=1Event page or SSE stream
POST /v1/runs/:id/cancelCancel the run
POST /v1/runs/:id/help/:helpIdResolve a human help request
POST /v1/runs/:id/approvals/:idResolve an approval

Runs, traces & artifacts

Runtime home
~/.mlola/browser-runtime/
  runs/<runId>/
    meta.json        goal, backend, start time
    events.jsonl     append-only event log (source of truth)
    summary.json     final result
    artifacts/       downloads + registry.json
    screenshots/     only per the screenshot policy
  daemon/            token, state, pid

MLOLA_HOME=/path/other   # override the runtime home
Inspect a run
mlola browser sessions                    # daftar run terakhir
mlola browser sessions show r_abc12345     # ringkasan + metrik
mlola browser trace show r_abc12345 --limit 40
mlola browser trace show r_abc12345 --type RECOVERY
mlola browser trace summary r_abc12345     # JSON metrik

Every step records the snapshot fingerprint, the offered action space, decision probabilities, policy outcome, freshness result, execution result, verification, recovery state, provider usage and timings — enough to replay a run without guessing.

Safety model

  • Typed actions only — no selector, coordinate or JavaScript ever comes from a model.
  • Risk can only be raised by target context; a model cannot lower it.
  • Secrets are a takeover floor, never typed or traced.
  • Instruction-like page content is quarantined, not negotiated.
  • A stale decision never executes: freshness, visibility and occlusion are re-checked before every interaction.
  • Unknown-effect actions (submits, uploads) are never retried blindly.

Evals

Run the suites
mlola browser eval --suite core        # acceptance suite, fast
mlola browser eval --suite security    # injection traps, secrets, destructive actions
mlola browser eval --suite matrix      # seeded DOM variations
mlola browser eval --suite stress      # large pages, re-render, slow downloads
mlola browser eval --suite all

npx @mlola/browser-evals run --suite core --provider typesafe   # the real fast path

Results are written to `packages/evals/results/*.json` with status `passed`, `passed_with_unverified`, `failed` or `blocked`. The suites run in a real Chrome against the local fixture site — never a third-party site.

Troubleshooting

SymptomFix
missing Jev API keySet TYPESAFE_API_KEY (see above). Offline: --provider keyword.
Browser does not launchRun doctor; it reports the channel it tried and falls back to bundled Chromium. Install Chrome or run npx playwright install chromium.
unsupported capabilityThe backend does not advertise that operation (for example tab borrowing without the extension). This is intentional, not a bug to work around.
Run stops at a credential stepExpected: secret fields never reach the model and are never typed; a person completes them (or --no-human stops cleanly). Use --yes only for approval-required actions, never for secrets.
The run asks what to type, or hands a step overThe goal has no reusable value for that field. Answer the prompt, quote the value in the goal, or configure a text provider (MLOLA_TEXT_*). Exploratory goals always ask by design.
Costs are higher than expectedCheck planner calls and fast-path share in the trace summary. Escalation is capped at two per run; --llm none disables the stronger model entirely so you can measure the fast path.
stop-before triggeredThe goal asked to stop at that boundary; the run completes with "stopped at the requested boundary".
Daemon unreachablemlola browser daemon status, then stop/start. Stale state files are cleared on stop.
npm install cannot find the package right after a releaseThe registry serves a new package through a CDN; the packument can 404 for a few minutes. Retry; dist-tags and tarballs are already live.

Packages

PackageUse it for
@mlola/browser-runtimeThe SDK: BrowserAgent, providers, policy, traces
@mlola/browser-cliThe mlola browser command line
@mlola/browser-daemonmlola-browserd background daemon
@mlola/decision-jevFast-path provider, planner/text adapters, offline baselines
@mlola/backend-playwrightThe Playwright browser backend
@mlola/backend-chromeDrive your own Chrome through the extension
@mlola/browser-evalsFixture site and eval suites
@mlola/browser-coreOrchestrator, action space, executor, verification