Ship: global apps (storage/slug/public web root/skills/standalone route) + desktop browser fixes#59
Merged
Merged
Conversation
Apps are now global on the local host (filesystem source-of-truth at <dataDir>/apps/<applicationId>/, manifest.id == applicationId, no install DB). Adds global /api/v1/apps/* routes, BB_APPS_ROOT/BB_APP_* runtime roots, app-session message target context, atomic create/tombstone-delete, bb app current CLI, daemon app-data tracking + apps-root watcher, and a top-level sidebar Apps section (drops per-manager app nesting). Removes the old thread-scoped /threads/:id/apps routes and the built-in manager status app.
Global apps now open on their own thread-independent route via a shared AppViewer (factored out of AppTabContent). Sidebar Apps rows navigate to the standalone route and are no longer disabled without a selected thread; adds loading / app_missing / invalid_manifest states and deep-linking.
Aggregates skills from every global app's apps/<applicationId>/skills/ and the data-dir-level <dataDir>/skills/ into one validated catalog (agentskills.io contract; fail-closed name collisions) and injects them into every new/resumed runtime via the existing AgentRuntimeSkillRoot plumbing (codex skills/extraRoots/set, claude-code generated local plugin). Adds an injectedSkillSources field to the host-daemon runtime context (protocol 30), server-side catalog resolver, daemon staging/packaging, and host-watcher coverage of both skill roots. Also: per-host thread storage no longer inherits ambient BB_THREAD_STORAGE; daemon heartbeat lease extension is monotonic.
The built-in status app removal deleted src/services/threads/default-template, but the @bb/server build still tried to --copy-dir it, breaking the prod bundle build (ENOENT). Remove the stale copy step.
The app id is now a validated slug (^[a-z0-9][a-z0-9-]*$, max 64) that is the folder name and manifest.id, replacing the opaque app_<hex> scheme. bb app new derives a slug from --name or takes an explicit --id; duplicate ids are rejected (409 app_exists). manifest.name stays optional and is used for display when set, falling back to the slug otherwise.
Apps now serve their public/ folder as the web root: /api/v1/apps/<slug>/ maps to public/index.html and /<path> to public/<path>, with the public/ folder name absent from the URL. Renames the old assets/ concept to public/ (resolveApplicationPublicPath / PUBLIC_DIRECTORY_NAME) and removes the special /assets/* route. manifest.json and data/ stay private siblings outside public/ (traversal-validated; data/ behind authenticated routes). Standard relative builds resolve without <base> injection or URL rewriting. bb app new scaffolds public/index.html; guidance: build into public/ with a relative base.
Global apps are filesystem-backed with no DB, so the app list had no realtime invalidation. Adds an 'apps-changed' system change kind: create/delete routes emit it, and the host-watcher's app folder/manifest changes broadcast it (the server diffs the visible app-list signature before broadcasting). The frontend realtime cache registry invalidates the app list/detail queries, so the sidebar Apps section updates without a bb restart.
Re-adds per-thread memory of whether the secondary panel is open: adds a threadId-keyed, localStorage-persisted atom (bb.thread.secondaryPanel.open) mirroring the existing per-thread conversation-collapse atom, wired through the panel toggle + ThreadDetailView so each thread restores its own open/closed state on switch (default closed). Regressed when conversation-collapse moved to a per-thread atom while panel-open stayed transient.
The in-app browser's native WebContentsView was detached/destroyed when a thread's panel deck unmounted, so switching threads flashed (and recreated + reloaded the view on return). Browser views are now retained per tab in a shared registry with thread/environment ownership: unmount only releases visibility (hide), not the view; views are destroyed only on tab close or thread/environment teardown. Switching threads hides-before-show + bounds- before-show, eliminating the flicker.
The browser WebContentsView synced bounds only on ResizeObserver + window resize, so a left-sidebar width drag shifted the panel's left/top without a size change and the native view overflowed over the sidebar. Now a sidebar width/open-state change emits a bounds-sync event (position-only resyncs), and measured bounds are clamped to the viewport on both the renderer and before the native setBounds, so the view stays anchored inside its container. Preserves the rAF resize coalescing and keep-alive behavior.
Updates server-contract + host-daemon-contract invariant test fixtures for changes that landed in this batch but whose contract tests weren't in the per-package validation filters (CI caught them): - appManifestSchema.name is now optional (defaults to slug) — allowlist entry. - app-data broadcast/resync + app-data-change-request use applicationId (not appId/threadId). - SYSTEM_CHANGE_KINDS includes apps-changed. - thread runtime context requires injectedSkillSources; session-open response requires trackedApplicationDataTargets.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ships the sawyer-next batch (10 commits, rebased on current main, all validated green incl. real bundle builds).
Global apps
<dataDir>/apps/<applicationId>/is the source of truth (no install DB). Removed the old/threads/:id/apps/:slugroutes and the built-in status app.applicationIdis a validated slug (^[a-z0-9][a-z0-9-]*$) = folder =manifest.id; optionalnamefor display.bb app newderives a slug; duplicate ids 409.public/folder as the app root (/api/v1/apps/<slug>/<path>, folder name not in URL);manifest.json+data/private. Standard relative builds work with no<base>/rewriting./apps/:applicationIdroute (shared AppViewer) so apps open thread-independently.apps/<slug>/skills/and~/.bb/skills/inject into every agent runtime (codexskills/extraRoots/set, claude-code local plugin).Desktop browser surface
WebContentsViewalive across thread switches (no flicker/reload).Other
default-templatecopy from the server build.🤖 Generated with Claude Code