Skip to content

Add the thread-briefs plugin - #1

Merged
trotterdylan merged 2 commits into
mainfrom
thread-briefs-plugin
Sep 26, 2026
Merged

trotterdylan merged 2 commits into
mainfrom
thread-briefs-plugin

Conversation

@trotterdylan

Copy link
Copy Markdown
Collaborator

Phase 1 of thread briefs: brief generation plus display on the two additive surfaces bb supports.

Every thread gets a short, durable brief — goal, currentState, nextStep, blockedOn, constraints — generated outside the working chat so the thread's own context stays clean.

Generation

  • Trigger: bb.events.on("thread.idle") starts a per-thread quiet-period debounce; thread.active cancels it so a thread mid-burst is not summarized every turn. A */10 * * * * sweep backstops activity whose event never arrived (restart, reload, a turn that ended in error).
  • Input: threads.conversationOutline() keeping the head and tail with the middle elided (the opening states the goal, the tail states the position), threads.output() for the last message in full, and the previous brief.
  • Cost control: maxSeq is the activity cursor, so a thread that has not actually moved never costs a request.
  • Model: any OpenAI-compatible /chat/completions endpoint. bb.experimental_aiServices is supply-side only — its sole member is register, and its tasks are fixed to titles/commits/voice — so a plugin cannot add a task slot and has to make its own call.

Stage and status

stage is a model judgement, overridable by hand; the override is anchored to the activity cursor and retires itself on the next real turn, so it needs no timer.

status is derived mechanically, not asked of the model, so it stays correct between summaries: empty nextStep → done, non-empty blockedOn → waiting-on-other, a trailing question or a live pending interaction → waiting-on-me, else working. Ordering means a finished thread reads as done even if its last turn ended in a question, and the model is told never to invent a next step.

Display

There is no additive per-thread-row slot. app.slots.experimental_threadList is exclusive — a plugin replaces bb's whole sidebar list or touches none of it — and the per-row hooks are for a replacement list to consume. Inline row expansion would mean forking plugins/thread-list (~28k lines) and re-merging it forever, so this uses:

  • a row glyph from a content script's experimental_setThreadRowStatus, fed by an experimental_appOverlay that owns the rpc + realtime subscription (a content script gets no React hooks, and only a content script can set a row status);
  • the five fields in an experimental_threadHeaderAction popover, with the stage control and Re-summarize.

Only the three statuses that are news draw a glyph: bb paints a plugin row status in place of its unsent-draft pencil, so decorating every row would cost the draft indicator everywhere to say something the row already implies. The pending-interaction half of that decision resolves client-side from experimental_useSidebarThreads(), so no row needs a server round trip to stay current.

Verification

59 tests and a clean typecheck; both bundles build. The bb server runs in a different pod from the machine pod, so a local-path install is not possible here — verification is the SDK's own harness: createFakePluginHost driving refresh → summarize → kv → rpc against a stubbed endpoint, and mountPluginContentScripts + renderSlot for the glyphs and popover.

Not yet exercised against a real LLM endpoint — it needs bb plugin config thread-briefs set apiKey <key> first.

@get-bb/plugin-sdk is pinned to the published 0.5.24 while this bb ships 0.5.26; 0.5.26 is unpublished and cannot be packed without the full monorepo build. Every API used is in the 0.5.24 declarations, and the build only warns, but bb plugin types --check will report drift until it publishes.

Deferred to later phases, as scoped: stage ring, dashboard, auto-archive, status grouping, GitHub integration.

🤖 Generated with Claude Code

trotterdylan and others added 2 commits September 26, 2026 16:47
Gives every thread a short, durable brief — goal, current state, next step,
blocked on, constraints — summarized outside the working chat so the thread's
own context stays clean.

Generation runs in the server entry: `thread.idle` starts a per-thread
quiet-period debounce (`thread.active` cancels it), and a 10-minute cron sweep
backstops activity whose event never arrived. The summarizer input is
`conversationOutline()` with the middle elided plus the previous brief, and
`maxSeq` is the activity cursor, so a thread that has not moved costs no
request. Summaries go through any OpenAI-compatible endpoint.

Status is derived mechanically rather than asked of the model, so it stays
correct between summaries: no next step means done, a non-empty blockedOn means
waiting on someone else, a question or pending interaction means waiting on me.

Display uses the two additive surfaces bb supports. There is no additive
per-thread-row slot — `experimental_threadList` is exclusive, so inline row
expansion would mean forking bb's 28k-line thread-list — so the sidebar gets a
row glyph from a content script and the five fields live in a thread-header
popover. Only the three statuses that are news draw a glyph, because bb paints
a plugin row status in place of its own unsent-draft pencil.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The matrix names plugins explicitly, so a new plugin directory is not covered
until it is listed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@trotterdylan
trotterdylan merged commit 40574a4 into main Sep 26, 2026
4 of 6 checks passed
@trotterdylan
trotterdylan deleted the thread-briefs-plugin branch September 26, 2026 17:04
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