# Build a Beacon lens

A Beacon lens is one self-contained HTML file that renders a focused view of a
recorded coding-agent session. It runs in a sandboxed iframe beside Beacon's
built-in session views.

## Output and constraints

- Produce one HTML file, no build step, no package.json, at most 16 MiB.
- Include a `<head>` element. Put all CSS and JavaScript inline.
- Do not use `<link>`, `<iframe>`, `<frame>`, `<object>`, `<embed>`, or `<base>`,
  or shadow DOM. Beacon removes nested frames and disables WebRTC and
  `attachShadow` inside lenses.
- Do not load external URLs or use `@import`, `fetch`, XMLHttpRequest,
  WebSockets, EventSource, WebRTC, form submission, or scripted navigation.
- Inline SVG, canvas, and `data:` images or fonts are allowed.
- Treat every value from the trace as untrusted. Insert it with `textContent`,
  never `innerHTML`.
- Render loading, readable error, empty, and truncated states.
- Handle missing optional fields and unknown roles, part types, and system
  event subtypes without throwing.
- Avoid fixed-height or internally scrolling page shells. Beacon grows the
  iframe to fit its content.

## SDK

Beacon provides the Traces surface-compatible API before your code runs:

```js
window.traces.version // "surface-sdk.v1"
const session = await window.traces.getTrace()
```

`getTrace()` resolves once with:

```ts
type SurfaceTrace = {
  trace: {
    id: string
    title?: string
    agentId: string
    namespace: { id: string; slug: string }
    author?: { id: string; displayName?: string; avatarUrl?: string }
    model?: string
    createdAt: number
    startedAt?: number
    messageCount?: number
    tokenUsage?: TokenUsage
    project?: { name?: string; path?: string; gitRemoteUrl?: string; gitBranch?: string }
  }
  messages: Array<{
    id: string
    role: string
    text?: string
    model?: string
    order?: number
    timestamp?: number
    parts: Array<{
      type: "text" | "thinking" | "tool_call" | "tool_result" | "error" | "system_event"
      order: number
      content: Record<string, unknown>
      tokenUsage?: TokenUsage
    }>
    tokenUsage?: TokenUsage
  }>
  truncated: boolean
}
```

Text and thinking content is `{text}`. Tool calls are
`{callId, toolName, args}`. Tool results are
`{callId, toolName, output, status}`. Pair calls and results by `callId`.

## Publish with Beacon MCP

Publishing needs write access. When your MCP client signs in, tick "Also let
it create and publish lenses" on the Beacon approval page, or use an access
token created with "Read + publish lenses". Read-only connections do not list
the tools below.

1. `beacon_lenses_create` creates private or public marketplace metadata.
2. `beacon_lenses_prepare_upload` returns a short-lived upload URL for a new
   semantic version.
3. POST the raw file directly to that URL with `Content-Type: text/html`.
4. Pass the returned `artifactId` to `beacon_lenses_complete_upload`.
5. Test the lens, then use `beacon_lenses_release_version` to make that
   immutable version current.

Never paste the HTML into an MCP tool call. Uploading and completing a version
does not release it.
