Repository navigation
feat(bestie): Bestie tab and Remembers you for a hosted Bestie - #2478
rileycrane wants to merge 9 commits into
Conversation
fd3dbd7 to
3226cb8
Compare
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>
5ab9a24 to
b33ca2d
Compare
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 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
left a comment
There was a problem hiding this comment.
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
| return ( | ||
| (agent.place === "here" || | ||
| (agent.place === "hosted" && | ||
| !!agents.hostedInstructions?.(agent.pubkey))) && |
There was a problem hiding this comment.
[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
There was a problem hiding this comment.
🤖 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.
| const order = ++hostedReads; | ||
| const record = (read?: string) => { | ||
| const last = hostedRead.get(pubkey); | ||
| if (last && last.order > order) return; |
There was a problem hiding this comment.
[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
There was a problem hiding this comment.
🤖 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.
There was a problem hiding this comment.
🤖 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.
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>
…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>
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:
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'swhendecides:The host still shows no added tabs for an agent running on another of your devices.
Hosted instructions, provider-neutral
RemoteAgentTypegets an optionalinstructionscapability withgetandset. It is reached throughagents.hostedInstructions(key).Agents.save, which refuses hosted agents. After a save, the tab reads them back.settakes 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.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
whennow also shows for hosted agents. Every bundled tab now states its own rule.docs/plugin-architecture.mddescribes the new rule.Testing
when;agents.save;vitest run: all pass exceptbrowser-host/media-preparation.integration.test.mjs(tiled HEIC, local ffmpeg). It fails without this change too.typecheck,lint,format:checkanddesign:checkpass.🤖 Generated with Claude Code