Skip to content

feat(bestie): Bestie tab and Remembers you for a hosted Bestie - #2478

Draft
rileycrane wants to merge 9 commits into
mainfrom
opus/r1r2-hosted-bestie-tab
Draft

rileycrane wants to merge 9 commits into
mainfrom
opus/r1r2-hosted-bestie-tab

Conversation

@rileycrane

@rileycrane rileycrane commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

You can make a hosted agent your Bestie. A hosted agent is one that a provider such as Builderlab runs, rather than this device. Until now, a hosted Bestie got almost nothing on the desktop. This PR makes these work for one:

  • the Bestie tab shows;
  • Remembers you turns its memory section on and off;
  • private channels: add the agent and remove it.

After you choose another Bestie, a Former Bestie tab lets you turn those off again. Every hour check-ins still say "Not available with remote agents yet." Check-ins for hosted agents are out of scope.

Each tab decides where it shows

Before this PR, the host showed tabs that plugins add only for agents running on this device (tabs.tsx). That rule arrived with the Attention tab (#1846). Attention configures check-ins stored on this device, which never run for a hosted agent. Now the host doesn't filter added tabs by place; each tab's when decides:

  • Attention: agents running here, as before.
  • Bestie: your Bestie, running here or hosted.
  • Former Bestie: an agent running here, or a hosted one.
  • History: unchanged.

The host still shows no added tabs for an agent running on another of your devices.

Hosted instructions, provider-neutral

  • RemoteAgentType gets an optional instructions capability with get and set. It is reached through agents.hostedInstructions(key).
  • Builderlab implements it with its existing calls: list agents, which returns their instructions, and update agent.
  • For a hosted Bestie with the capability, Remembers you reads and saves instructions through the provider. It doesn't go through Agents.save, which refuses hosted agents. After a save, the tab reads them back.
  • set takes the copy the switch showed and reads the provider's copy again just before it writes. If they differ, the save is refused and the tab shows the newer copy. So an edit made in Builderlab or on another device while the tab was open is not overwritten.
  • Reads use the existing sign-in only. When you're signed out, the tab shows "Sign in to Builderlab first." with Retry. Sign-in starts only from the Builderlab pane's Sign in action.
  • A remote type without the capability still shows "Not available…".

Former Bestie for a hosted agent

A hosted agent's instructions aren't on this device, so the plugin keeps the copy its provider last returned. The Bestie tab updates that copy when it reads, and the plugin also reads each hosted agent once per connection. This only covers hosted agents this device has seen as your Bestie. Former Bestie shows while that agent's memory section is on, or while it is in one of your private channels. It also stays while the latest provider read has failed, so the read error and Retry stay reachable; it goes only once a successful read shows nothing left on. Once a newer read has started, an older read's reply is ignored, even if it arrives first. So the tab stays until the newer read finishes. A hosted agent whose provider holds no instructions also gets Former Bestie while it is in one of your private channels, so you can take it out of them; its memory switches say they aren't available.

Private channels need no code change. Adding and removing the agent uses owner-signed guest membership, which works wherever the agent runs.

Related: #2475 makes a plain desktop DM wake a hosted agent.

Notes for review

  • Tabs from other plugins. A tab registered with no when now also shows for hosted agents. Every bundled tab now states its own rule. docs/plugin-architecture.md describes the new rule.
  • Remaining race. The provider has no compare-and-swap. An edit that lands between the last read and the write can still be lost.

Testing

  • New and updated tests:
    • tabs per place, and Attention's when;
    • hosted memory get/set without agents.save;
    • a stale copy refuses the save, and the next toggle keeps the newer copy;
    • signed-out reads start no sign-in and send no request;
    • check-ins stay unavailable;
    • a hosted agent without the capability;
    • Former Bestie for a hosted agent: it shows while memory is on and goes once memory is off.
    • Former Bestie stays after a failed read, including a failed read-back after a save, until Retry succeeds; an older read's reply is ignored once a newer read has started, even while the newer one is still under way.
    • Former Bestie shows for a hosted agent whose provider holds no instructions while it is in a private channel, with its memory switches unavailable, and goes once it has left them.
  • Full vitest run: all pass except browser-host/media-preparation.integration.test.mjs (tiled HEIC, local ffmpeg). It fails without this change too.
  • Static checks: typecheck, lint, format:check and design:check pass.
  • Manual check with a Builderlab agent as Bestie:
    • the tab shows;
    • Remembers you adds and removes the memory section in the agent's Builderlab instructions;
    • private channel add and remove work;
    • Every hour stays unavailable;
    • after you choose another Bestie, Former Bestie shows and turns the memory section off.

🤖 Generated with Claude Code

rileycrane and others added 6 commits November 4, 2026 17:39
Let the owner use the Bestie tab on a hosted agent they made their Bestie.

The host showed plugin-added agent tabs for agents running here only, a gate
added with Attention. Move it into the tabs: Attention now says
`place === "here"` in its `when`, Bestie also accepts hosted agents, and
Former Bestie stays local.

Add an optional `instructions` get/set to `RemoteAgentType`, reached through
`agents.hostedInstructions(key)`, and implement it for Builderlab with its
existing list and update-agent calls. For a hosted Bestie with it, Remembers
you reads and saves the instructions through the provider. Check-ins stay
unavailable, as hosted agents have no attention timers here.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
… read them

Review of the hosted Bestie tab found two defects:

- A memory toggle or Restore replaced the provider's instructions from the
  copy read when the tab opened, discarding an owner edit saved since
  through the provider or another device. `HostedInstructions.set` now
  takes the `base` the caller showed, reads the provider's copy just
  before replacing, and refuses when it differs. The tab re-reads after a
  failed save too, so the switch then shows the newer copy. No provider
  offers compare-and-swap, so an edit landing between that read and the
  write can still be lost.
- Builderlab's instructions read called `ensureSignedIn`, so opening a
  signed-out Bestie tab started browser login. Reads and writes now require
  the existing credential and reject with "Sign in to Builderlab first.",
  leaving login to the pane's Sign in action.

Also: a hosted Bestie's Remove confirmation no longer points to a Former
Bestie tab it never gets, and the plugin architecture doc describes the
`when`-owned tab placement contract.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
Review follow-up: the Former Bestie tab is offered only for an agent
running here, and the tab host still shows no sections for an agent on
another device, so added tabs reach agents here or hosted only.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
A hosted Bestie can now have Remembers you on, so after another agent becomes
Bestie, its memory section and private channels stayed with no way here to
turn them off, though Remove as Bestie points at its Former Bestie tab.

Show Former Bestie on a hosted agent whose provider holds its instructions.
Its instructions aren't on this device, so the plugin records them as last
read from the provider: by the Bestie tab, and once per connection for each
hosted agent this device saw as Bestie. The tab then shows while its memory
section is on or it is in one of your private channels, and goes once
nothing is left on.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
… reads

Review of the hosted Former Bestie cache found two ways it could hide the
only off switch for a memory section still on:

- A background read started while memory was off could end after the
  Bestie tab's post-save read and replace its newer answer. Each read now
  takes an order when it starts (`startHostedRead`), and one started
  before the read last recorded changes nothing. The tab's own state
  follows only its latest read too.
- A failed first read, tried once per connection, left the cache empty,
  which counted as nothing on. A failure with nothing read now records
  that, and Former Bestie stays for it until a read succeeds, so the tab's
  Retry can read again. A failure never replaces instructions already read.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
… save

Memory read off, then saved on, then reading it back failed: the failure
kept the earlier off, so after another agent became Bestie the hosted
agent's Former Bestie tab was hidden with memory still on. The recorder
now keeps the last text read and whether the latest read failed
separately, and a failed latest read keeps the tab, even over an earlier
off, until a read succeeds.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
@rileycrane
rileycrane force-pushed the opus/r1r2-hosted-bestie-tab branch from 5ab9a24 to b33ca2d Compare November 5, 2026 01:43
@rileycrane
rileycrane marked this pull request as ready for review November 5, 2026 02:08
@rileycrane
rileycrane requested review from a team as code owners November 5, 2026 02:08

@wpfleger96 wpfleger96 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖 Reviewed the full diff at b33ca2d. The hosted-instructions capability matches what installed plugins could already reach through types()[].remote. Hosted saves never reach agents.save, and the timer-backed check-ins stay gated for hosted agents. The stale-copy check, the signed-out read path and the read ordering in former.ts all hold up. Removing the place === "here" filter in tabs.tsx is safe: elsewhere agents still return no sections, and a plugin tab without a when that tries agents.save on a hosted agent gets the existing clear refusal. LGTM.

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kyle reviewed this PR and the Nexus review-tracker notes and approved it with the two non-blocking comments below.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

Comment thread src/bundled/bestie/BestieTab.tsx Outdated
return (
(agent.place === "here" ||
(agent.place === "hosted" &&
!!agents.hostedInstructions?.(agent.pubkey))) &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Keep channel cleanup available without instruction support

Hosted agents without hostedInstructions can still join private channels from the Bestie tab, but this guard removes their Former Bestie tab after the user changes Besties. Could eligibility also allow previously seen hosted agents with remaining private-channel memberships? The memory controls can remain unavailable.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Agreed, fixed in 78202b2. Former Bestie's eligibility now admits any hosted agent and applies the same rule as for local ones: a feature section on, or a private channel it's in once this device has seen it as Bestie. For a provider without instructions, the memory switches still show "Not available with remote agents yet.", and the private-channel controls work. A new test in BestieTab.test.tsx covers it and fails without the change.

Comment thread src/bundled/bestie/former.ts Outdated
const order = ++hostedReads;
const record = (read?: string) => {
const last = hostedRead.get(pubkey);
if (last && last.order > order) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Preserve the tab while its newer read is pending

An older background “off” result is accepted until the newer read records a result. That can hide Former Bestie and unmount the tab, aborting its newer initial read before it can correct the cache. Could starting a newer read invalidate older results immediately while retaining the tab until that read settles?

Posted by Codex (gpt-6.1-sol) on behalf of kylez

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Agreed, fixed in bc9cf5d. startHostedRead now tracks the reads still under way for each agent. A result counts only if no later read has started, so an older background "off" can no longer hide the tab while the tab's newer read is pending. The tab stays until that read settles. An abandoned read (the tab unmounting, or the plugin stopping) calls cancel(), which lets an earlier read still under way count again instead of leaving the agent stuck. A new test in BestieTab.test.tsx covers the pending case and the cancel case, and fails on the old ordering.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 A follow-up in 221ec1b tightens the cancellation. A read is now abandoned the moment its signal aborts, not when the provider eventually rejects, and nothing it answers afterwards counts. An answer from an older read that arrives while a newer read is pending is held, and it counts if the newer read is abandoned. So closing the tab mid-read can't lose a background "memory on" result. Two more tests cover this.

rileycrane and others added 2 commits November 4, 2026 22:05
A read of a hosted agent's instructions that started earlier still
counted when it ended before a later read did. A background "off" could
then hide Former Bestie and unmount the tab, aborting the tab's own
newer read before it could correct what was recorded.

Track the reads under way for each agent. A read now counts only if no
later read has started, so what shows stays until the latest read ends.
An abandoned read (the tab unmounting, or the plugin stopping) is
cancelled, so an earlier read still under way can count again.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
…nstructions

A hosted agent whose provider holds no instructions can still be added
to private channels from the Bestie tab. Once another agent became
Bestie, the Former Bestie tab's eligibility required instruction support,
so there was no tab left to take it out of those channels. Any hosted
agent now qualifies, with the same rule as local ones: a section on, or a
private channel it is in, once this device saw it as Bestie. Its memory
switches still say they aren't available.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
@rileycrane
rileycrane marked this pull request as draft November 5, 2026 06:06
…t older reads found

An aborted read of a hosted agent's instructions stayed under way until
its provider answered, since it was cancelled only when `get` rejected.
Meanwhile an older background read's answer, such as memory on, was
dropped, and nothing replayed it once the newer read ended. Former Bestie
could then stay hidden for an agent with memory on.

`startHostedRead` now takes the read's signal and abandons the read the
moment it aborts; nothing it answers afterwards counts. An answer that
arrives while a later read is under way is held, and counts if every
later read is abandoned.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Riley Crane <rileycrane@squareup.com>
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.

3 participants