Repository navigation
Conversation
rileycrane
left a comment
There was a problem hiding this comment.
🤖 Princess Donut: frontend review findings below. Both are optional UX/product feedback; I found no must-fix implementation defect in my assigned frontend paths. All six Bestie browser cases passed across Chromium and WebKit at this head.
rileycrane
left a comment
There was a problem hiding this comment.
🤖 Service/runner review at 783ac1cd52f1: two reproduced correctness findings below. Reproductions used the production Inbox callback and real relay session owners. All changed service/MCP/Bestie Vitest tests passed; the full run had 17,977 passes, 22 skips, and one failure in untouched HEIC conversion code, with no causal link established.
There was a problem hiding this comment.
🟡 Changes recommended
The Inbox can return incomplete notifications or incorrect unread state because the release gate permits incomplete channel discovery and read-marker synchronization failures are neither retried nor blocked.
2 open issues
What changed in this PR
Adds bounded, read-only Inbox access for the current local Bestie through the shared agent service and MCP tools, while separating Inbox permission from channel membership in settings.
Changes:
- Adds saved-permission checks, notification reconciliation checks, and pending-result lifetime guards.
- Groups memory and hourly check-ins under Behavior, with separate Inbox and private-channel access controls.
- Updates regression coverage, the interactive preview, and documentation of privacy limits and remaining acceptance work.
| File | Description |
|---|---|
tests/fixtures/bestie-tab.tsx |
Makes synthetic settings interactive and adds Inbox state. |
tests/browser/bestie-tab.spec.mjs |
Exercises Inbox controls and responsive layout. |
src/features/agents/service.ts |
Implements authorized owner Inbox reads. |
src/features/agents/service.test.ts |
Covers permission, reconciliation, retry, and lifecycle failures. |
src/features/agents/runner.ts |
Guards pending Inbox results against runtime retirement. |
src/features/agents/bestie-features.ts |
Centralizes instruction-section permission handling. |
src/buzz-mcp/tools.ts |
Adds the read-only Inbox tool and result projection. |
src/buzz-mcp/tools.test.ts |
Covers Inbox serialization and unavailable delegation. |
src/buzz-mcp/client.ts |
Defines the optional Inbox client capability. |
src/buzz-mcp/app-client.ts |
Forwards Inbox reads with cancellation checks. |
src/buzz-mcp/app-client.test.ts |
Covers cancellation during an Inbox read. |
src/bundled/bestie/README.md |
Documents settings, access safeguards, and limitations. |
src/bundled/bestie/PrivateChannels.tsx |
Refines channel-access headings and explanatory text. |
src/bundled/bestie/PrivateChannels.test.tsx |
Updates assertions for revised channel controls. |
src/bundled/bestie/index.tsx |
Updates default-feature comments and failure logging. |
src/bundled/bestie/former.ts |
Enables memory and Inbox for locally selected Besties. |
src/bundled/bestie/former.test.ts |
Covers initialization of both default features. |
src/bundled/bestie/features.ts |
Defines Inbox instructions and re-exports shared permission helpers. |
src/bundled/bestie/BestieTab.tsx |
Separates Behavior from read-access settings. |
src/bundled/bestie/BestieTab.test.tsx |
Covers Inbox permission controls and settings grouping. |
docs/buzz-help.md |
Explains user-facing settings and privacy limitations. |
docs/agents.md |
Documents the shared Inbox authorization and lifecycle contract. |
Review effort: Balanced
rileycrane
left a comment
There was a problem hiding this comment.
🤖 Follow-up service/runner review at cf1a21e5d3f21619b170a17e00b152aebf0b4f88: one P2 MUST-FIX remains in the original read-marker thread: local unread intent can mask failed remote observation and allow a successful stale unread count. The channel-discovery finding is addressed. No concrete new blocker found in the added private DM/Solo authorization, trusted runtime context, or runner release checks. Existing feedback overlaps the original readiness findings; the separate private-context concern is addressed by the new enforcement.
Validation at this exact clean head: full Vitest 18,227 passed, 22 skipped, 1 failed. The sole failure is the same tiled HEIC dimension check (512×512 versus 1536×1024) in files unchanged by this PR; no causal link established. A separate deterministic diagnostic reproduced the remaining marker defect. Princess Donut reports the full Bestie browser file passed 6/6 across Chromium/WebKit. Hosted checks are green. Live owner-Inbox and packaged/native acceptance remain untested.
…uzz tools (#2707) ## TL;DR An approved external app (like Nexus) now gets the selected agent's whole Buzz MCP tool catalog and native dispatcher, including tools plugins add later, after one explicit "full access" approval. Before, it got a hand-picked list of six tools with rewritten arguments. Older narrow approvals still connect but grant no tools until the owner approves full access again, so no existing grant widens on its own. Initiative status: https://g2.sqprod.co/apps/kylez-nexus-buzz-plugins-status Fixes NEXUS-4262 ## What changes - **One scope, `agent`.** New links request `scope=agent`, and `agent` can't be combined with other scopes. Rust pairing accepts only `["agent"]` for new approvals (`valid_scopes`). Saved `read`/`post`/`create` approvals still load, renew and resume, but grant nothing. - **No adapter.** `respondExternal` now passes `tools/list` and `tools/call` to `respond` in `rpc.ts`, using the same `sessionTools(added)` catalog as native sessions. Plugin-added tools come from `addedTools.snapshot()` (the source native harnesses use) and are re-read on every message, so enabling or disabling a plugin changes the catalog live. The old policy map, argument rewriting and Bestie injection are removed. - **The agent's own authority.** The connection uses the agent's run handle (`runners.handle(key, undefined, true)`), so it gets device settings, plugin requests, Git access and handoff exactly as a native run does. Plugin installs still go through the owner's confirmation. Memory reads use the agent's `memoryListing`, like the native plugin path. Protected agents are still refused. `plugin_disable` acts at once, as it does natively, and the consent text says so. - **Handoff with true provenance.** `self_handoff` goes through the run's own handoff and `Agents.wakeIn`, but its `from` is `{ external: true }`. `wakeSection` shows it as an `<app-wake>` section that says an external app the owner approved sent it, and that it is not a message, a watch or timer, or another of the agent's conversations. - **Files where the agent reads them.** `send` with `path`/`files` calls a new optional `AgentType.readFile`. Claude, Codex, Pi and goose implement it with the reader their own tools use, in the agent's saved workspace on this device. Relative paths start there; absolute and `~/` paths reach any file the account can read, as they do natively, and the consent text says so. A Claude or Codex agent bound to an execution target refuses, because only a conversation's session opens that target. A type without `readFile` refuses with the same message its tools give natively. Closing the connection cancels a read in progress. - **No implicit conversation.** Calls run with an empty context, so every tool needs explicit channel or message IDs. - **Consent.** The dialog now states full access plainly: future plugin tools, Git access, waking the agent, Nexus workers, settings changes, posting any file the agent can read (including outside its workspace), and immediate plugin disabling. A legacy narrow link shows an update-required notice with Deny only, which sends the normal denial callback. `approve()` refuses non-`agent` requests as a second check. Agent settings flags a saved legacy approval as no longer working. ## Companion-client contract (for the Nexus consumer) | | Behavior | | --- | --- | | Request | `scope=agent` alone. Narrow scopes get the update-required notice and only a denial callback. | | `tools/list` | The full native catalog: 66 built-in tools with settings, plus plugin tools. Each tool is listed as natively, plus `_meta: {"buzz/requiredScopes": ["agent"]}`. Result `_meta`: `buzz/grantedScopes` (`["agent"]`, or `[]` for a legacy approval) and `buzz/supportedScopes: ["agent"]`. Cursors are rejected (single page). | | `tools/call` without the grant | `isError` result, nothing dispatched, `_meta["buzz/error"] = {code: "authorization_required", required_scopes: ["agent"]}`. | | `tools/call` with the grant | Native result and errors, unchanged. | | `self_handoff` | Native arguments. The wake reaches the agent as an `<app-wake>` from the app. | | `send` `path`/`files` | Read by the agent's type in its saved workspace. Agents on an execution target, and types without a reader, return their refusal. | | `channel_create` | Takes native arguments. A private Solo chat with the owner's Bestie must now pass `visibility: "private"`, `solo: true` and `members` explicitly. Buzz no longer fills them in. | An unupdated consumer that accepts only `read`/`post`/`create` will reject `supportedScopes: ["agent"]`. Its upgrade path is to accept `agent` and request `scope=agent`, which takes one owner approval. ## Overlap These open PRs touch nearby files. This PR neither depends on nor replaces any of them: - #2260 and #2685 cover external pairing and identity. - #2647 adds plugin skills to `session-tools`. - #2622 covers Bestie settings. - #1939, #1909, #1854, #2311 and #2070 change `service.ts`/`runner.ts`. - #2610 has no shared files. ## Validation Ran on a Linux Blox box against the current head: - `tsc --noEmit` and `tsc -p tsconfig.design.json`; - Biome on the touched files; - Vitest for `src/buzz-mcp`, the external connection, dialog and settings tests, `AgentsPage.test.tsx`, `service.test.ts`, `plugin-wake.test.ts` and the Claude, Codex, Pi and goose runtime tests. New tests cover the external handoff's trigger and `<app-wake>` wording, file reads through the type's reader (target workspace, cancellation on close, native refusal), the Claude and Codex workspace and execution-target cases, a saved narrow approval that a link asks to resume as `agent`, and memory read from the selected agent. The Rust pairing tests (`host_mcp::pairing`) need the Tauri system libraries, so they are left to PR CI. No browser journeys were added or removed. Independent security reviews traced authorize, approve, resume, renew and call, and found no path that grants tools without the `agent` scope. Not done: - The native desktop consent dialog and a live external connection have not been exercised against this build, including a live `<app-wake>` turn and a file read in a running Claude, Codex, Pi or goose agent. - No human testing yet. - The Nexus side of the contract is a separate change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Kyle Zemek <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
|
🤖 UI walkthrough recorded on Blox at Real production components with synthetic, in-memory host data; no account changes. pr-2622-bestie-inbox-demo.mp4
Annotations explain each action and result. Rendered UI only: this does not establish live-account persistence, relay/Inbox authorization, or packaged/native acceptance. |
Group Behavior and reading access, clarify guest-only channel additions, and expose the owner's bounded Inbox to their selected local Bestie. Keep check-ins hourly; leave contextual asks and navigation separate. Work in progress: Inbox reconciliation, complete-section authorization, and post-await runtime invalidation still need safety follow-up before live use. Private-conversation restrictions are currently prompt policy. Signed-off-by: Clay Delk <[email protected]> Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Share the saved feature policy with Settings, withhold unreconciled notification content, and recheck the captured runner lifetime before releasing a result. Preserve hourly check-ins and current access defaults. Document privacy, posting-approval, and ordinary Interrupt questions for team review; these remain outside this safeguard slice. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Reject pending, failed or partial discovery before reading notifications, retry through the existing channel owner, and recheck before release. Cover held/failed startup and discovery overflow across the tool path. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Withhold observed notifications when remote read-marker sync is loading, failed or stale, and retry failed reads through the existing unread owner. Keep local-only reconnects usable and cover recovery and the final-check race. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Omit absent optional fixture settings, align the query gate's array type, and include the required filter limit without changing fixture behavior. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Use trusted runtime context and fresh relay-authored metadata and rosters before notification reads and before release. Deny group, public, missing or uncertain contexts, and keep outside-run handles without Inbox access. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Keep memory defaults but do not enable Inbox when choosing a Bestie. Preserve saved on, off and customized Inbox choices on reselection. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Distinguish a former Bestie's retained Inbox preference from active access without changing its saved memory or channel memberships. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Keep missing remote observation distinct from local read intent so Inbox retries it before release. Do not let continuous replay erase a marker failure from before its gap; preserve healthy pending and local-only reads. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Signed-off-by: Clay Delk <[email protected]>
ee0d97b to
79fbfac
Compare
Restore the local-only Inbox guard without blocking hosted memory editing. Cover the unavailable row and the single hosted-memory Retry action. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
Preserve current navigation and browser synchronization fixes. Co-authored-by: goose <[email protected]> Signed-off-by: Clay Delk <[email protected]>
|
🤖 Design review, requested by Morgan (design) Design is fine with this once engineering approves it. Nothing here blocks it from design's side. |
## TL;DR Removes the six owner-session read tools that let a Buzz-hosted agent read with its owner's own access: channel, member and DM lists, channel details, messages and canvases. An agent's own reads and the channel archive, unarchive and delete changes are unchanged. The tools are gone from discovery on every harness, and a cached or direct call to one is refused before anything is read with the owner's session. Fixes NEXUS-4348 · Initiative: https://blockcell.sqprod.co/sites/kylez-buzz-mcp-initiative/ ## Scope Kyle's request was to remove "the channel/member/dm list/get" endpoints and leave the archive/delete channel changes for now. Removing `owner_read` and the two Canvas tools as well is the initiative lead's interpretation: they read through the same owner session and handle, and leaving them would keep the owner-read capability this removes. If that is wrong, the #2881 half can be restored on its own. ### Removed From #2745 (owner channels and members): - `owner_channel_list`, `owner_channel_get`, `owner_member_list`, their hint text and discovery on Claude Code, Codex, Pi, the session RPC and the harness kit. - `src/features/agents/owner-access.ts`, the run handle's `ownerAccess` field and the runner's host hook that built it for bundled-revision runs. - The `BUZZ_OWNER_MCP_COMMUNITIES` build setting (`vite.config.ts`). A distributor build that still sets it now gets nothing from it; `docs/configuration.md` lists it with the other retired inputs. - The owner-tool sections in `docs/agents.md`, `docs/configuration.md`, `docs/buzz-help.md` and `src/buzz-mcp/AGENTS.md`. From #2881 (owner messages and canvases): - `owner_read`, `owner_canvas_get`, `owner_canvas_history`, `src/buzz-mcp/owner.ts` and the `Owner*` client types. - The `docs/relay-queries.md` paragraph, and the exports of `tools.ts` helpers that only `owner.ts` used. `edited()` keeps its author-only edit selection, minus the owner parameter. ### Kept - From #2745: the Stop and cancellation fences in the Claude Code, Goose and ACP sessions (`workLifetime`, `current()`), which every Buzz tool call now depends on, and the channel-lifecycle changes (`channel-lifecycle.ts`, including its `records` parameter). - `addTool` still refuses plugin tool names starting with `owner_`, so a stale call to a retired name can never reach plugin code. There is no other owner-prefixed restriction and no new permission layer. - Ordinary agent-scoped reads, Codex per-thread/turn tool validation, plugin and external isolation. ## Alternate routes checked Nothing else reached these tools: no Rust, IPC, sidecar or RPC path, and Mesh and external agents use the same run handle that no longer carries owner access. Codex thread tool keys no longer carry the `:owner` suffix, so threads created with owner tools are replaced instead of reused with a stale tool list. ## Tests The new tests list the six names as a fixed fixture rather than reading them from the catalog: - `session-tools.test.ts`: `sessionTools()` and `sessionHint()`, which every harness lists from, name none of the six. - `service.test.ts`: a real bundled run handle has no `ownerAccess`. With the owner's session stubbed to return an `OWNER_ONLY` message for an owner-only channel, each retired name is refused with `No tool <name>` before any relay query. An ordinary `read` of that channel then runs with the agent's own key, filtered to that channel, and returns `No messages.`. The same client's `read` of the agent's own channel returns its message, and no read through the owner's session ever names the owner-only channel. External agents see none of the names and are refused the same way. - `codex/runtime.test.ts`: `dynamicTools` lists none of the names, each call fails Codex's validation, a planted owner-access spy is never called, and the thread's own `read` works. - `app-client.test.ts`: a handle that still carries an owner capability doesn't pass it to the tool client. - The Claude Code and Goose Stop/timer tests now hold an ordinary `mem_get` read instead of an owner tool. On a Blox box with these commits applied to main: `tsc` passes, and the 12 affected Vitest files pass (claude, codex, goose, pi runtimes, app-client, tools, session-tools, service, kit, ACP client, channel-lifecycle). Full CI covers the rest. `git push --no-verify` skipped the pre-push lanes (types-and-related-unit-tests, design-system, rust-clippy); CI's `pnpm check`, sharded Vitest and clippy jobs repeat all three. Fail-before check: the removal tests in `service.test.ts` and `codex/runtime.test.ts` were run against the pre-removal source (0a5c190). Three of them fail on real assertions: - Codex `dynamicTools` contains `owner_channel_list`. - An external agent's call returns "Owner tools are not available to this agent here; nothing was read." instead of `No tool owner_channel_list`. - The bundled handle has `ownerAccess`. All three pass on this branch. Vitest strips types, so this check doesn't cover `tsc`. The `session-tools.test.ts` discovery test also passes on the old source, because that source only lists the tools when the owner setting is on, so it doesn't count as a fail-before check. ## Not covered - No native app run or live relay check. The removal is exercised through the service, runner and harness tests above. - A Buzz process already running a build with these tools keeps them until it updates. This doesn't revoke anything remotely. ## Related open PRs - Human work in overlapping files keeps its behavior but may need a rebase: #2700, #2974, #3081, #2972 (cancel added tools on native Stop), #2935, #1634, #2622, #3095, #2882. - #3004, #3005, #3006, #3008 and #2980 build on the removed owner access and are not part of this change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) (claude-opus-5-5) on behalf of kylez --------- Signed-off-by: Kyle Zemek <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
rileycrane
left a comment
There was a problem hiding this comment.
🤖 Review from Riley's agent, checked against practical-information-flow-for-buzz-agents.md. @jmecom, could you look at this through the lens of your doc?
Read at 9a64d52d. Not tested live.
The read side matches the doc
- The
inboxtool runs only when the session's conversation is a private DM or Solo chat whose members are exactly the owner and the Bestie. The app takes that conversation from the harness (agent-harness/kit.ts:418), and a model-suppliedchannelis ignored (covered intools.test.ts). - It reads fresh relay-signed 39000/39002 events before loading notifications and again before returning them. It also re-checks that this agent is still the account's Bestie with Inbox enabled, and anything partial or stale fails closed.
- In the doc's terms, this is an owner-only domain reading owner-only data, so A(D) ⊆ R(x) holds. Event admission, session selection and brokered reads (enforcement points 1–3) are all covered.
The output and memory sides don't match yet
- Publishing isn't checked. After a turn calls
inbox, the same session can stillsend/reply_in_subthreadto any channel the Bestie belongs to. Nothing enforces A(d) ⊆ R(z) for that session. The PR description already says non-disclosure is a prompt rule. - Managed memory has no labels.
mem_set/notes_set/canvas_settake Inbox-derived text unlabeled. Bestie core memory and recent notes are injected into the Bestie's sessions in shared channels too, which is the doc's "unlabeled memory reused across public and private sessions" path. "Don't copy Inbox content to memory" is also only a prompt rule. - Untrusted text enters a privileged session. Inbox previews are written by anyone who can mention or DM the owner, and they land in a session that has every tool. The doc puts integrity (FIDES) out of scope, but this feature widens that surface a lot.
- No session rotation on a membership change. Membership is checked when the tool is called. If someone is later added to the Solo chat, the session keeps the Inbox content in its history. I didn't check whether the runtime rotates sessions elsewhere.
- Shell,
gh, network and other MCP tools (for example Slack or email) stay unmediated. The doc scopes these out too, so this item is a note, not a blocker.
Suggestion
A cheap version of the doc's coarse rule R(z) = A(D): once a session has successfully called inbox, mark it. In app-client, then limit that session's Buzz writes (send, reply_in_subthread, mem_set, notes_set, canvas_set) to the conversation that triggered it, and return an error that says why. Every Buzz write already goes through app-client, so this is small, and it covers items 1 and 2 on the brokered paths. Item 4 could clear that mark, or end the session, when the roster changes.
That seems a reasonable bar before Inbox is on for anyone other than its developers. Shipping it off by default and local-only, as it is now, seems fine meanwhile.
…ge plugins (#3073) ## Problem Both **Import an agent** dialogs (Agents → New agent → Import an agent, and a shared agent's Import) said only "Enable an agent type plugin to import agents." In a browser no agent type can ever register, because Agents is unavailable outside the desktop app, so that advice could not help. On desktop nothing linked to where plugins are turned on. Bestie's **Remembers you** row told people to "Clear memory in the Memory tab", but the Memory tab only reads memories; deletion there never shipped. ## Change `NoImportTypes` in `AgentBrowse.tsx` now owns the empty state for both Import dialogs and branches on the Agents snapshot status: - **unavailable** (browser): "Agents runs in the desktop app. Open Buzz there to import one.", worded as the Agents page's own empty state. - **error**: "Agents could not load." with the snapshot's error, or the page's "Try again." fallback. - **loading**: a delayed "Loading agent types…" status through `LoadingRegion`, since types may not have registered yet. - **ready with no runnable type**: "No harness that runs agents on this computer is available. Check Manage plugins." with **Manage plugins** as a `SettingsSectionLink` to Settings → Plugins. Only this state has a link. It names what is missing, not why, because the component cannot tell every plugin off from only a hosted type such as Builderlab on, or from a plugin that is on but failed to activate; Manage plugins shows a plugin enabled in the last two. The Agents page passes its existing `openSettings` to `ImportAgents` and the Marketplace's shared agent panel. An agent card in a conversation renders the same shared dialog, so it opens Settings through the navigator it already uses. Each dialog closes before Settings opens. Bestie's description now ends "To clear a memory, ask Bestie to forget it." Forgetting comes from the shared Buzz base prompt and `mem_rm`, so it works whether Remembers you is on or off; verified for Claude Code, Codex and Goose. The Bestie README and `docs/buzz-help.md` say the same, and the help notes that Import works only in the desktop app and, when no agent type plugin that runs agents on this computer is on, that the dialog says no harness that runs agents on this computer is available and links Manage plugins. ## Before and after Left is `main`, right is this fix. Three segments: the browser Import dialog, the Bestie Remembers you row and the Memory tab, then Import with the only agent type plugin turned off, after turning it off in Settings → Manage plugins. Recorded in the browser build against the fixture relay. https://github.com/user-attachments/assets/5276ed5e-4ef1-4095-9bd0-578fa4821d35    ## Verification | Check | Result | | --- | --- | | vitest on `AgentBrowse`, `AgentsPage`, `BestieTab`, `SettingsSectionLink`, `AgentCardAttachment` | 5 files, 208 tests pass | | The same five files with `main`'s `AgentBrowse.tsx` swapped back | 9 fail, 199 pass | | `tsc --noEmit` | clean | | biome on changed files | clean | | `git diff --check` | clean | Two agent review rounds: the first flagged plugin advice leaking into the error and loading states, a wording mismatch with the Agents page empty state, unclear help wording and an unverified Bestie forget claim, all fixed in this commit; the second found nothing to change. ## Overlap #2622 edits `src/bundled/bestie/features.ts`, the Bestie README and the help, and keeps the "Clear memory in the Memory tab" sentence. Whichever lands second takes a trivial conflict on that sentence. ## Not checked - The native desktop app was not run; the plugin-off segment uses the browser fixture's fake native layer. - Dark theme, narrow width and enlarged text were not inspected. - A hosted Builderlab Bestie's prompt and tools belong to its provider and cannot be verified from this repo, so the sentence says what to do rather than promising a result. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: mtoohey <[email protected]> Signed-off-by: Matt Toohey <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>

Why
Your Bestie needs a bounded way to help triage your Inbox without joining every conversation behind a notification. These previews can include private channels and DMs, so permission and result handling must be explicit.
What
How
Inbox reads are all-or-nothing. Channel discovery must be ready and complete before notification reads begin, and it is checked again before release. The tool returns a retryable error while discovery is pending, failed or partial; unread evidence is stale; remote read-marker sync is unhealthy; or notification edits and deletions remain unresolved. A later call retries failed reads through the existing owners. Valid local-only read state and pending publication remain usable. Local unread actions cannot clear missing remote observation. Continuous replay repairs its own gap, not an earlier marker failure. The tool never presents a partial result as complete or a read error as an empty Inbox. This remains bounded recent evidence, not full notification history or an atomic snapshot of the relay.
Inbox starts off until the owner enables it. Choosing or reselecting a Bestie preserves its saved choice. Settings and the tool use the same saved-permission check. Missing or damaged instruction sections deny access; customized wording inside a complete section stays enabled.
Only a run can use Inbox. Trusted runtime context must identify an explicitly private DM or Solo chat containing exactly the owner and Bestie. Fresh relay-authored metadata and membership are checked before notification reads and before release. Each phase then strongly reads the account’s Bestie choice and rechecks permission and the owner session. Missing, public, group, ambiguous, or unavailable evidence denies access. Newer observed membership or privacy changes also deny access. Model-supplied channel arguments grant no authority. These checks are bounded evidence, not an atomic relay transaction.
The runner checks its captured lifetime again before releasing a result. Stopping or retyping the runtime invalidates pending reads. Already returned content cannot be retracted. Ordinary Interrupt does not provide the same pending-response fence in every harness; Claude and Goose need additional lifecycle work if that becomes required.
Private-channel additions use Viewing only access. Existing members keep their roles. A channel owner or admin can change access to Participating. These controls do not add Bestie to DMs. Inbox itself grants no membership, conversation history, or posting rights.
Risk
The owner-data release boundary changes in the shared Agents service and runner. Browser and desktop callers share this policy, but fixtures do not establish packaged/native acceptance. The branch includes main through
3b98f2bdfd8. The hosted-Inbox guard is restored; hosted memory editing and its single Retry action remain available. Inbox remains local-only. Hosted CI and packaged/native acceptance remain separate gates.The private read-context boundary is enforced. Non-disclosure elsewhere and not copying Inbox content to memory remain prompt rules, not output confinement. No posting-approval workflow is added. The remaining team decisions and acceptance testing stay open before release.
Testing
The settings layout was inspected in the synthetic browser preview, including light/default and dark/narrow enlarged text. Keyboard and pointer toggles preserve private-channel membership. The preview changes no account settings and proves no live Inbox permissions.
Fail-before/pass-after checks reproduced data release from wrong or missing runtime contexts, automatic Inbox enablement, overwritten off choices, and misleading former-Bestie copy. Removing the hosted-capability guard also reproduced an unsupported Inbox switch after the merge. A further fail-before/pass-after check reproduced marker failure hidden by an unrelated local unread action, including a failure after an earlier successful observation and during final release. A reconnect check also reproduced earlier marker debt incorrectly cleared by continuous replay. These checks used synthetic identities and no live owner data. No browser cases were added or removed for this review-fix pass.
No live owner Inbox or packaged/native acceptance test has run. Human acceptance of these safeguards remains open. Do not treat this description as shipping approval.
Team decisions before release
Owner-private reads and explicit Inbox opt-in are implemented. Former-Bestie settings distinguish the retained preference from current access.
Configurable check-in cadence stays a separate feature. This branch contains no contextual-ask/navigation work.
Generated with Goose