Skip to content

feat: vendored Laminar OpenCode plugin (@browser-use/bcode-laminar)#33

Merged
Alezander9 merged 2 commits intofeat/embed-lmnr-keyfrom
feat/bcode-laminar-plugin
May 2, 2026
Merged

feat: vendored Laminar OpenCode plugin (@browser-use/bcode-laminar)#33
Alezander9 merged 2 commits intofeat/embed-lmnr-keyfrom
feat/bcode-laminar-plugin

Conversation

@Alezander9
Copy link
Copy Markdown
Member

Lands the Laminar OpenCode tracing integration as a vendored, built-in plugin. Depends on #32 (the telemetry-key injection PR) — branch is forked off feat/embed-lmnr-key and either alone is a no-op.

Summary

  • New workspace package packages/bcode-laminar/ (~600 lines, vendored from lmnr-ai/lmnr-opencode-plugin@bb2fceaff + lmnr-ai/lmnr-ts@5ebe07a6, both Apache-2.0). One-time pull; we don't recurring-sync this.
  • Single Yellow-zone edit: register LaminarPlugin in INTERNAL_PLUGINS so tracing ships built-in. No bcode.json configuration needed.
  • First-run notice (one short line, suppressed under DO_NOT_TRACK=1).

What it does

When LMNR_PROJECT_API_KEY is set in the environment (which #32 does at startup, gated by DO_NOT_TRACK and BYO-key precedence):

  1. Plugin loads inside the opencode server.
  2. Registers an OTel NodeSDK with one custom span processor.
  3. On every chat.message event, creates a "turn" span scoped to the opencode session id.
  4. Re-parents AI-SDK telemetry spans (already emitted by experimental_telemetry: true in session/llm.ts:406) onto the turn span.
  5. Stamps lmnr.span.path / lmnr.span.ids_path ancestor attributes that the Laminar UI uses to nest spans (it does NOT nest by OTel parentSpanId).
  6. Tags spans created inside the task tool with lmnr.spawning_subagent.tool_use_id so sub-agent traces link back to the spawning tool call.
  7. Exports OTLP/gRPC to ${LMNR_BASE_URL}:${LMNR_GRPC_PORT} (defaults https://api.lmnr.ai:8443) with authorization: Bearer <key>.

When the key is absent (DO_NOT_TRACK or self-build), the plugin loads, hits the no-key fast-path, and does nothing. Zero if (telemetryEnabled) branches anywhere.

What's vendored vs trimmed

See packages/bcode-laminar/VENDOR.md for the full file map. Key trims vs upstream:

  • Drop LMNR_ROLLOUT_SESSION_ID (rollout-sessions feature we don't use).
  • Drop HTTP/protobuf exporter fallback; gRPC only.
  • Drop parseOtelHeaders / OTEL_HEADERS env path.
  • Drop pino logger; route through client.app.log.
  • Drop loadEnv() (opencode loads .env already).
  • Drop caller-side context injection (we don't ship a TS host).
  • Reduce Laminar.startSpan to the minimal startTurnSpan we need.

First-run notice

Added to packages/bcode-browser/src/telemetry.ts:

BrowserCode sends anonymous usage traces. Set DO_NOT_TRACK=1 to opt out.

Marker at {state-dir}/bcode/telemetry-notice-shown (XDG / Application Support / %LOCALAPPDATA%). Suppressed entirely when DO_NOT_TRACK=1 (no need to print to opted-out users).

Verification on the dev sprite

  • bun run typecheck clean across 6 packages.
  • bun run --cwd packages/opencode build succeeds across all 12 platforms.
  • With LMNR_PROJECT_API_KEY=fake_key LMNR_BASE_URL=http://127.0.0.1:9 bcode models --print-logs --log-level DEBUG: emits service=laminar Laminar tracing initialized → http://127.0.0.1:9.
  • With DO_NOT_TRACK=1: no Laminar log, no notice, no marker file written.
  • With BCODE_DEFAULT_LMNR_KEY=... baked at build time: notice prints once, marker written, plugin initializes on first run.

Files

File Change
packages/bcode-laminar/ New package (8 source files, package.json, tsconfig, LICENSE, VENDOR.md).
packages/opencode/src/plugin/index.ts +2: import LaminarPlugin, append to INTERNAL_PLUGINS.
packages/opencode/package.json +1: workspace dep on @browser-use/bcode-laminar.
packages/bcode-browser/src/telemetry.ts +showFirstRunNoticeOnce() after the existing key gate.
package.json (root) Add bcode-laminar to typecheck filter list.

Maintenance

One-time vendor. packages/bcode-laminar/VENDOR.md records the source commits + per-file mapping for the next manual re-pull. Re-pull only when an upstream behavior fix or new attribute is worth bringing across — Laminar's plugin shape changes rarely.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 18 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/bcode-laminar/src/processor.ts">

<violation number="1" location="packages/bcode-laminar/src/processor.ts:168">
P1: Memory leak: `spawningSpanIdToToolUseId` entries are never cleaned up because the key format used for insertion (UUID via `otelSpanIdToUUID`) differs from the key used for deletion (raw hex span ID). The `delete` will never match an existing entry.</violation>
</file>

<file name="packages/bcode-laminar/src/plugin.ts">

<violation number="1" location="packages/bcode-laminar/src/plugin.ts:25">
P2: `LMNR_GRPC_PORT` is not validated; invalid values produce `NaN` and an invalid OTLP endpoint URL.</violation>

<violation number="2" location="packages/bcode-laminar/src/plugin.ts:68">
P1: Dispose handling is incomplete: it shuts down the processor but does not end/clear active turn spans or shut down the NodeSDK lifecycle.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread packages/bcode-laminar/src/processor.ts Outdated
Comment thread packages/bcode-laminar/src/plugin.ts Outdated
Comment thread packages/bcode-laminar/src/plugin.ts Outdated
bcode added 2 commits May 2, 2026 03:55
…de-laminar

Lands tracing as a built-in internal plugin. ~600 lines vendored from
lmnr-ai/lmnr-opencode-plugin@bb2fceaff and lmnr-ai/lmnr-ts@5ebe07a6
(both Apache-2.0). Trims rollout-session, OTEL_HEADERS, HTTP/protobuf,
and pino paths we don't need.

Wire format: OTLP/gRPC + bearer token. No proprietary surface — all
deps are standard OTel.

Plugin registration is one Yellow-zone line in INTERNAL_PLUGINS. The
no-key fast-path keeps DO_NOT_TRACK and self-builds trace-free with
zero `if (telemetryEnabled)` branches.

First-run notice in bcode-browser/src/telemetry.ts: one line,
suppressed when DO_NOT_TRACK is set. Marker file at
{state-dir}/bcode/telemetry-notice-shown.

Smoke-tested on linux-x64: typecheck clean across 6 packages, build
succeeds across all 12 platforms, plugin initializes when key is set,
no-ops when DO_NOT_TRACK=1, embedded BCODE_DEFAULT_LMNR_KEY flows
through correctly.

Depends on #32 (telemetry-key injection).
- processor.ts: spawningSpanIdToToolUseId is keyed by UUID
  (otelSpanIdToUUID(spanId)) at insert time but onEnd was deleting by
  raw hex span id, so entries were never cleaned up. Use the UUID for
  delete to match.
- plugin.ts: server.instance.disposed now ends any open turn spans,
  clears the subagent map, and shuts down the NodeSDK (which drains
  the BatchSpanProcessor and unregisters the global TracerProvider).
- plugin.ts: LMNR_GRPC_PORT is validated (positive integer in
  1..65535) before use, with fallback to the appropriate default
  (8443 for api.lmnr.ai, 443 elsewhere). Garbage values no longer
  produce NaN endpoint URLs.
@Alezander9 Alezander9 force-pushed the feat/bcode-laminar-plugin branch from fcb965f to f5f70c1 Compare May 2, 2026 03:58
@Alezander9
Copy link
Copy Markdown
Member Author

Tested on windows, looks good on Laminar

@Alezander9 Alezander9 merged commit 01826e8 into feat/embed-lmnr-key May 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant