Skip to content

Add Playgrounds as a bundled plugin, not installed by default - #5293

Closed
brsbl wants to merge 14 commits into
mainfrom
bb/interactive-answers-as-a-built-in-plugin-thr_x6wkp5kcx3
Closed

brsbl wants to merge 14 commits into
mainfrom
bb/interactive-answers-as-a-built-in-plugin-thr_x6wkp5kcx3

Conversation

@brsbl

@brsbl brsbl commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Human comments

What was wrong

Playgrounds (formerly Interactive Answers) existed only as a community plugin draft (brsbl/bb-plugins#392). A stock bb install had no way for agents to publish answers people can explore in place: calculators, charts, comparisons, or step-by-step guides that update as you change an input, without another agent turn.

What changed

Adds Playgrounds, "answers you can play with: calculators, guides, previews", as a bundled plugin that is not installed by default, on the same model as Browser Automation. It is listed under BB Official in Plugins, and installing it there or with bb plugin install playgrounds turns it on.

Surface Name
Plugin id / directory bb--playgrounds / plugins/playgrounds
Agent tool playground
CLI bb playgrounds guide|example|publish|state|watch|actions|do
Message directive ::playground{id}
Skill playgrounds
HTML bridge window.playground, with --pg-* tokens and .pg-* kit classes
  • Plugin:
    • Native playgrounds cover controls, metrics, charts, tables and diagrams. They use bounded arithmetic and run no code.
    • HTML playgrounds render in a sandboxed frame.
    • State is shared through the server, so every open copy in a thread updates live.
    • The demo, 3D and launch-video assets from Fix desktop launch: export bb-app/dist/bb-app.js #392 are not included.
  • Forks and side chats:
    • Forking a thread copies its playgrounds and their current inputs into the fork, on thread.created or on first access for forks made before install. The copy is independent: changing it doesn't change the original, and deleting the original doesn't affect it.
    • A side chat is a hidden fork owned by its thread (lifecycleOwnerThreadId === sourceThreadId). It is part of the same conversation, so it shows the thread's live playground.
    • Each playground's published content is stored once (answer_documents). Threads hold only a link to it and their own inputs, so a fork copies a few small rows, and content is deleted when no thread links to it. This is the schema from the first migration, so no shipped data ever needs migrating.
  • Registration:
    • OFFICIAL_PLUGINS (with autoInstall: false) and its test.
    • The web marketplace's install-on-request set.
    • @bb/bundled-plugins, bb-official.json (Thread content), forkable-plugins.json and the turbo tasks.
    • The bb guide plugins entry.
  • No contract changes: there is no new SDK surface, no daemon wire change and no protocol bump.

Sandboxed HTML playgrounds

HTML playgrounds are served from the plugin route /frame and render in <iframe sandbox="allow-scripts" loading="lazy">. The route sets nosniff, no-referrer, and a strict Content-Security-Policy: sandbox allow-scripts; default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob: https://upload.wikimedia.org; media-src data: blob:; font-src data: blob:; connect-src 'none'; form-action 'none'; base-uri 'none'.

  • No network: a playground cannot send what you type anywhere. fetch, XHR, sendBeacon, WebSockets, form posts, and remote scripts, fonts, and styles are blocked, and images load only from data:, blob:, and Wikimedia.
  • Font: bb sends the frame the Inter font it already loaded, so the frame makes no font request.
  • Links and composer pills work only right after a click inside the playground, at most once per second. Links open through useBbNavigate().openUrl, http and https only.
  • Rate limits: events and action lists from the frame are rate limited, on top of server-side size limits.
  • Untrusted output: agents are told to treat script output as untrusted, and shared data is wrapped in <playground-data>.
  • Stale saves: state sent to the frame carries its version. A frame save based on an older version is dropped and the frame receives the newer state, so a lazily loaded frame can't overwrite newer state.

How you verified

  • Remote CI on this branch.
  • Branch web dev app (pnpm dev, isolated data dir), driven by BB Browser Automation in headless Chrome for Testing at 1440×900, 2×:
    • Playgrounds was absent until installed, and it appeared under BB Official.
    • Real Codex threads published a native playground and an HTML playground with bb playgrounds publish.
    • Inputs recalculated and persisted. state, actions and do drove the open card.
    • Sandbox isolation and the CSP held: fetch, outside images, sendBeacon and WebSockets were blocked, and Wikimedia images and bb's Inter loaded.
    • Forks: setting the original to step 2 and forking copied step 2 into the fork. Advancing the fork to step 3 left the original at step 2.
    • Deleting the original: after forking A → X → Y and deleting X, Y still rendered its playground with its own state. The content stayed a single stored row, and only X's link was removed.
    • Side chat: changing a side chat's playground changed its thread's.
  • Server test: fork → change fork → original unchanged; side chat shares; fork-of-fork lazy copy; delete original → fork still renders through RPC and the frame; unrelated thread unavailable; the content is stored once and removed after the last thread linking to it is deleted.
  • Slop Cop on shared content at 49521bf66e (security, architecture, code quality, performance; end-to-end covered in the branch dev app): no P0, P1, or new P2.
  • Slop Cop three times: at 9adc58d2d0, at 8ff48b19b6 (every P1 fixed with regression tests), and on the fork-copy and rename scope at 6337f01dd5, which found no P0 or P1.

Screenshots

Before is merge base 1ae755fce6, where the plugin doesn't exist and threads show the raw directive. After is this branch. Both use the same data directory, threads, route, 1440×900 viewport and 2× density.

State Before After
Plugins catalog, searching "Playgrounds" Before: no Playgrounds card After: Playgrounds listed as BB Official, not installed
Native playground (bill splitter) Before: raw directive After: interactive bill splitter
HTML playground, original thread at step 2 Before: raw directive After: stepper at step 2 of 3
Forked thread, its copy advanced to step 3 Before: raw directive in the fork After: fork's own copy at step 3 of 3, original unchanged

Follow-ups (P2, not in this PR)

  • Forking a side chat: this copies only the side chat's own playgrounds up front. Playgrounds it shows from its thread are copied when the fork first opens them, so if the thread is deleted first, they're unavailable.
  • Side-chat rule: side chats are recognized as hidden forks owned by their source thread, not by originPluginId === "side-chat". A third-party plugin making similar hidden branches would share live playgrounds too.
  • Side-chat lookups: every request from a side chat looks up the thread, uncached.
  • Missed deletion events: a thread deleted while the plugin is disabled, or while a lazy copy is running, can leave its link behind, which keeps the shared content stored. That's a storage leak, not a correctness problem. It existed before the shared-content change.
  • Items addressed in Playgrounds: command routing, latest events, and review follow-ups #5314 (stacked on this PR) are not repeated here.

BB-Thread: Interactive Answers as a built-in plugin

AGENT GENERATED

🤖 Generated with Claude Code

brsbl and others added 5 commits October 9, 2026 15:10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd budgets, quoted skill metadata

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brsbl and others added 3 commits October 9, 2026 16:12
…d edge, general 3D guidance

Ports brsbl/bb-plugins 621f3faeb, a9106eaa1 and 70477277f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ports the describe() fix from brsbl/bb-plugins thr_qq2zmm2g8p.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… stale state

The frame response now sets default-src 'none', inline scripts and styles only,
connect-src/form-action/base-uri 'none', and images from data:, blob:, and
upload.wikimedia.org. State relayed to the frame carries its version; a frame
save based on an older version is dropped and the frame gets the newer state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…answer

Answers and their state are now keyed by (id, thread_id). Appended migrations
rebuild both tables, so data written by earlier builds carries over. Forking a
thread copies its answers and current inputs into the fork (on thread.created,
or on first access for forks created before install), so the copy changes
independently and survives deleting the original. A side chat (a hidden fork
whose lifecycle belongs to its source) resolves to its thread's answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brsbl added a commit that referenced this pull request Oct 10, 2026
Forks now get independent copies of answers (from the parent PR). Removes this
PR's cross-thread owner lookup and its shared-state fork test, and keeps the
routing, latest-events, wrapped output, existence-check, index, and rate-limit
fixes. The answer_events thread index is appended after the parent's rebuilds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plugin id bb--playgrounds (plugins/playgrounds), agent tool playground, CLI
bb playgrounds, directive ::playground{id}, skill playgrounds, HTML bridge
window.playground with --pg-/.pg- kit tokens, <playground-data> wrapper, and
publish --playground-stdin. Overview: answers you can play with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brsbl added a commit that referenced this pull request Oct 10, 2026
Applies the rename to this PR's additions: <playground-data> wrapper, playground
wording in CLI output and errors, and test fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brsbl and others added 2 commits October 9, 2026 20:09
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The plugin has never shipped and its new id starts from an empty database, so
the migration list creates answers and answer_state with (id, thread_id) keys
instead of rebuilding an older layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brsbl added a commit that referenced this pull request Oct 10, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brsbl brsbl changed the title Add Interactive Answers as a bundled plugin, not installed by default Add Playgrounds as a bundled plugin, not installed by default Oct 10, 2026
brsbl added a commit that referenced this pull request Oct 10, 2026
Interactive Answers is now Playgrounds (#5293/#5314).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
answer_documents holds the published content; answers links it to each thread.
Forking copies only the link and the fork's own inputs. Deleting a thread
removes its links and any content no other thread still uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brsbl

brsbl commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by brsbl/bb-plugins#405: Playgrounds isn't bundled with bb; it ships as a regular plugin installed from brsbl/bb-plugins, with everything from this PR (security lockdown, copy-on-fork with shared content, review fixes) plus a private save-to-library feature. Branch and history are kept.

AGENT GENERATED

@brsbl brsbl closed this Oct 10, 2026
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