Skip to content

feat(bcode-laminar): wire LMNR_PARENT_SPAN_CONTEXT env (re-land plugin on main)#39

Merged
Alezander9 merged 2 commits intomainfrom
feat/laminar-parent-span-context-env
May 6, 2026
Merged

feat(bcode-laminar): wire LMNR_PARENT_SPAN_CONTEXT env (re-land plugin on main)#39
Alezander9 merged 2 commits intomainfrom
feat/laminar-parent-span-context-env

Conversation

@Alezander9
Copy link
Copy Markdown
Member

Summary

Two commits, in order:

  1. Re-land packages/bcode-laminar/ on main. The original PR (feat: vendored Laminar OpenCode plugin (@browser-use/bcode-laminar) #33) was merged into the feat/embed-lmnr-key feature branch, not main. v0.0.8 / v0.0.9 were tagged from the feature branch. Subsequent harness/upstream syncs landed directly on main, leaving the laminar package absent from the canonical history. This commit reapplies the package contents verbatim from v0.0.9 plus the small companion changes (root package.json typecheck filter, packages/opencode/package.json workspace dep, packages/opencode/src/plugin/index.ts INTERNAL_PLUGINS entry, packages/bcode-browser/src/telemetry.ts first-run notice).

  2. LMNR_PARENT_SPAN_CONTEXT env hook. When the env var is set, every turn span the plugin opens is parented under the given Laminar span context. This is for our internal evaluation harness — it spawns bcode as a subprocess and wants the agent traces to nest under its EVALUATION span so judge + agent appear as siblings on one trace per task instead of producing a fresh root trace per bcode process.

    • Read once at plugin init via closure (one process = one parent for the eval-subprocess use case).
    • Passed through to startTurnSpan({ ..., parentSpanContext }). span.ts already accepts this argument and parses it via parseLaminarSpanContext.
    • Format: JSON serialization of LaminarSpanContext. The lmnr Python SDK exposes Laminar.serialize_span_context() which returns exactly this shape; the parser already accepts both snake_case and camelCase.
    • Default behavior (env unset) is unchanged.

Verification

  • Filtered turbo typecheck passes 6/6 across both commits (@browser-use/{bcode-browser,bcode-laminar,browsercode-core} + @opencode-ai/{core,plugin,sdk}).
  • No new dependencies; the package re-vendor is byte-identical to v0.0.9's tree under packages/bcode-laminar/.
  • bun install regenerated bun.lock with only the new workspace dep additions.

Use from the Python eval side

from lmnr import Laminar

ctx = Laminar.serialize_span_context()  # within an @observe(span_type=EVALUATION)
env = {**os.environ, LMNR_PARENT_SPAN_CONTEXT: ctx, LMNR_PROJECT_API_KEY: ...}
subprocess.run([bcode, run, ...], env=env)

Notes on the re-land

I considered cherry-picking 58cc2fdc1 and f5f70c109 but the conflicts on packages/opencode/src/plugin/index.ts (the WorkspaceAdaptor -> WorkspaceAdapter rename and the ServerAuth.headers() refactor on main) made a verbatim re-checkout from v0.0.9 plus a fresh INTERNAL_PLUGINS insertion cleaner than resolving cherry-pick conflicts. The packages/bcode-laminar/ package itself, the telemetry-notice block, and the workspace deps are all taken verbatim from v0.0.9.

Once this merges, cut v0.0.10 from main. The eval-side PR (benchmark-x-laminar) can then bump to it and inject LMNR_PARENT_SPAN_CONTEXT from Laminar.serialize_span_context() inside the @observe wrapper.

bcode added 2 commits May 6, 2026 16:12
Re-applies PR #33 onto current main. The original PR was merged into the
feat/embed-lmnr-key feature branch (and shipped as v0.0.8/v0.0.9), but the
feature branch was never re-merged into main, so the package was lost when
subsequent harness/upstream syncs landed directly on main.

Identical contents to v0.0.9: 13 files in packages/bcode-laminar/ vendored
from lmnr-ai/lmnr-opencode-plugin@bb2fceaff and lmnr-ai/lmnr-ts@5ebe07a6,
plus the first-run telemetry notice in bcode-browser/src/telemetry.ts and
the one-line LaminarPlugin registration in INTERNAL_PLUGINS.

Filtered turbo typecheck 6/6 passes.
…Span

When LMNR_PARENT_SPAN_CONTEXT is set in the process env, every 'turn' span
the plugin opens is parented under the given Laminar span context (via the
parentSpanContext arg startTurnSpan already accepts).

Used by external evaluation harnesses that spawn bcode as a subprocess and
want the agent's traces to nest under their own evaluation span — one trace
per task, judge + agent siblings — instead of producing a separate root
trace per bcode process.

Format is the JSON serialization of a LaminarSpanContext (the lmnr Python
SDK exposes Laminar.serialize_span_context() which returns exactly this
shape; the JS-side parseLaminarSpanContext in span.ts already accepts
snake_case and camelCase keys).

Default behavior (env unset) is unchanged: bcode opens a fresh root turn
span per chat.message event as before.

Read once at plugin init via closure; one process is one parent for our
intended use case (eval harness spawns bcode per task).
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.

No issues found across 18 files

@Alezander9 Alezander9 merged commit 80da728 into main May 6, 2026
3 checks 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