diff --git a/.github/actions/setup-bun-rust/action.yml b/.github/actions/setup-bun-rust/action.yml
index f941f482..fc1e0f19 100644
--- a/.github/actions/setup-bun-rust/action.yml
+++ b/.github/actions/setup-bun-rust/action.yml
@@ -13,7 +13,7 @@ runs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
- bun-version: latest
+ bun-version: 1.3.9
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9b0acb4c..8c8ecc11 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,7 +19,7 @@ default:
image: node:22-bookworm
interruptible: true
before_script:
- - curl -fsSL https://bun.sh/install | bash
+ - curl -fsSL https://bun.sh/install | bash -s "bun-v1.3.9"
- export PATH="$HOME/.bun/bin:$PATH"
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
- export PATH="$CARGO_HOME/bin:$PATH"
diff --git a/.size-limit.json b/.size-limit.json
index 9d050c78..20024115 100644
--- a/.size-limit.json
+++ b/.size-limit.json
@@ -1,8 +1,8 @@
[
{
- "name": "shell renderer JS bundle (gz) — per docs/shell/13-frontend-stack.md performance budgets; bumped 270→290 KB on 2026-05-28 for 12.1 (FormatJS ICU runtime, ~12.6 KB); 290→292 KB on 2026-06-01 for the new-vault onboarding pass (welcome redesign + Join/Devices first-run entry + theme-provider rework, 282.6→290.5 KB gz); 292→350 KB on 2026-06-01 for 8.8 (@react-fancy-menus/core menu runtime swap-in, +~52 KB for the menu runtime + @floating-ui/react-dom + @dnd-kit/* — dnd-kit is the bulk and only pulled for sortable rows no current surface uses; revisit if the dep ships a sortable-free entry). Replaces the bespoke cheatsheet/launcher/icon-context/anchored-menu DOM that was deleted",
+ "name": "shell renderer JS bundle (gz) — per docs/shell/13-frontend-stack.md performance budgets; bumped 270→290 KB on 2026-05-28 for 12.1 (FormatJS ICU runtime, ~12.6 KB); 290→292 KB on 2026-06-01 for the new-vault onboarding pass (welcome redesign + Join/Devices first-run entry + theme-provider rework, 282.6→290.5 KB gz); 292→350 KB on 2026-06-01 for 8.8 (@react-fancy-menus/core menu runtime swap-in, +~52 KB for the menu runtime + @floating-ui/react-dom + @dnd-kit/* — dnd-kit is the bulk and only pulled for sortable rows no current surface uses; revisit if the dep ships a sortable-free entry). Replaces the bespoke cheatsheet/launcher/icon-context/anchored-menu DOM that was deleted. Bumped 350→385 KB on 2026-10-19 for the 0.15.0 market-demand bucket (main measured 369.3 KB, branch 375.4 KB gz); accepted by the owner as a re-baseline, NOT a fix: the renderer slimming rung is owed",
"path": "packages/shell/out/renderer/assets/index-*.js",
- "limit": "350 KB",
+ "limit": "385 KB",
"gzip": true
},
{
@@ -12,9 +12,9 @@
"gzip": true
},
{
- "name": "shell main process bundle (raw) — bumped 100→110 KB on 2026-05-23 for 10.2 (HPKE + member-wraps + device-x25519) + 10.3a (sync envelope codec/seal/pipeline/relay-port) + help/changelog + defaults-catalog scheme/extension expansion; bumped 110→210 KB on 2026-06-11 — the limit drifted ~86 KB under-budget across the sync stack (10.3b–10.9) + 9.12.3 (ListSource service-side resolution moved the predicate/source evaluators into main) + Mailbox-5 (the `mail` service + Gmail transport glue), measured 196 KB raw. This is a deliberate re-baseline, NOT a fix: a dedicated main-process trim pass is owed at G3 (split workers out / lazy-load the sync + connector subsystems). Revisit there. Bumped 210→214 KB for the Files storage-inventory surface (storage-inventory + gather + wallpaper-entries + the files.listStorageInventory wire, ~2.8 KB); folds into the same G3 trim. Bumped 214→220 KB on 2026-06-14 for the Storage-view owner-stamping surface (listAssetOwners + ownerByAsset entityId/entityType stamping so Storage rows open in Preview), measured 218.1 KB raw; same G3 trim. Bumped 220→245 KB on 2026-06-16 for the IE-1/IE-2 import-export spine (`packages/shell/src/main/{bundle,import}/` — tar/container codec, canonical-json, vault export/import + the shared import engine), measured 238.9 KB raw; same G3 trim (the import/export subsystem is a lazy-load candidate alongside sync + connectors). Bumped 245→305 KB on 2026-06-23 — accumulated drift across the always-on sync stack (10.12 live-sync engine, 10.13 selective-sync, 10.14 restore producer+consumer + RestoreEngine), the app-lifecycle catalog spine (14.29–14.34: CatalogClient / InstallEngine / UpdateEngine / `.brainstorm` bundle codec), SYNC-4b gated admission, and the cross-app DnD broker services (selection / dnd / ghost overlay), measured 296.5 KB; NOT a fix — the same G3 main-process trim is owed (sync + connector + import-export + catalog are all lazy-load candidates).",
+ "name": "shell main process bundle (raw) — bumped 100→110 KB on 2026-05-23 for 10.2 (HPKE + member-wraps + device-x25519) + 10.3a (sync envelope codec/seal/pipeline/relay-port) + help/changelog + defaults-catalog scheme/extension expansion; bumped 110→210 KB on 2026-06-11 — the limit drifted ~86 KB under-budget across the sync stack (10.3b–10.9) + 9.12.3 (ListSource service-side resolution moved the predicate/source evaluators into main) + Mailbox-5 (the `mail` service + Gmail transport glue), measured 196 KB raw. This is a deliberate re-baseline, NOT a fix: a dedicated main-process trim pass is owed at G3 (split workers out / lazy-load the sync + connector subsystems). Revisit there. Bumped 210→214 KB for the Files storage-inventory surface (storage-inventory + gather + wallpaper-entries + the files.listStorageInventory wire, ~2.8 KB); folds into the same G3 trim. Bumped 214→220 KB on 2026-06-14 for the Storage-view owner-stamping surface (listAssetOwners + ownerByAsset entityId/entityType stamping so Storage rows open in Preview), measured 218.1 KB raw; same G3 trim. Bumped 220→245 KB on 2026-06-16 for the IE-1/IE-2 import-export spine (`packages/shell/src/main/{bundle,import}/` — tar/container codec, canonical-json, vault export/import + the shared import engine), measured 238.9 KB raw; same G3 trim (the import/export subsystem is a lazy-load candidate alongside sync + connectors). Bumped 245→305 KB on 2026-06-23 — accumulated drift across the always-on sync stack (10.12 live-sync engine, 10.13 selective-sync, 10.14 restore producer+consumer + RestoreEngine), the app-lifecycle catalog spine (14.29–14.34: CatalogClient / InstallEngine / UpdateEngine / `.brainstorm` bundle codec), SYNC-4b gated admission, and the cross-app DnD broker services (selection / dnd / ghost overlay), measured 296.5 KB; NOT a fix — the same G3 main-process trim is owed (sync + connector + import-export + catalog are all lazy-load candidates). Bumped 305→365 KB on 2026-10-19 for the 0.15.0 market-demand bucket (version history, inverse relations, split view, backups, AI routing gates; main measured 330.6 KB, branch 355.7 KB, brotli — the \"raw\" label is wrong, size-limit compresses by default); accepted by the owner as a re-baseline, same G3 trim owed.",
"path": "packages/shell/out/main/index.js",
- "limit": "305 KB"
+ "limit": "365 KB"
},
{
"name": "shell preload bundle (raw)",
diff --git a/apps/agent/manifest.json b/apps/agent/manifest.json
index 2915c2d9..7f2f27bb 100644
--- a/apps/agent/manifest.json
+++ b/apps/agent/manifest.json
@@ -17,8 +17,10 @@
"ai.provider:anthropic",
"ai.provider:openai",
"ai.provider:glm",
+ "ai.provider:mistral",
"ai.provider:gemini",
"ai.provider:solheim",
+ "ai.provider:*",
"search.read",
"search.hybrid",
"platform.read",
diff --git a/apps/agent/src/app.test.tsx b/apps/agent/src/app.test.tsx
index 9a0c5db1..cf2c4866 100644
--- a/apps/agent/src/app.test.tsx
+++ b/apps/agent/src/app.test.tsx
@@ -460,3 +460,33 @@ describe("AgentApp — app-icon attention badge (7.14)", () => {
expect(handle.container.querySelector('[data-testid="agent-escalation"]')).not.toBeNull();
});
});
+
+describe("AgentApp — auto-hide sidebar (7.15)", () => {
+ afterEach(() => {
+ document.documentElement.removeAttribute("data-bs-autohide-sidebars");
+ window.localStorage.removeItem("agent:sidebar-open");
+ });
+
+ const sidebar = () => handle?.container.querySelector("#agent-sidebar") as HTMLElement;
+
+ it("without the preference, a collapsed sidebar is plainly hidden", async () => {
+ window.localStorage.setItem("agent:sidebar-open", "false");
+ installShell([]);
+ handle = await renderInto();
+ await flush();
+ expect(sidebar().hasAttribute("inert")).toBe(true);
+ expect(sidebar().hasAttribute("data-bs-autohide")).toBe(false);
+ expect(handle?.container.querySelector(".bs-autohide-edge")).toBeNull();
+ });
+
+ it("with the preference on, a collapsed sidebar becomes a hidden overlay with an edge hot-zone", async () => {
+ document.documentElement.setAttribute("data-bs-autohide-sidebars", "on");
+ window.localStorage.setItem("agent:sidebar-open", "false");
+ installShell([]);
+ handle = await renderInto();
+ await flush();
+ expect(sidebar().getAttribute("data-bs-autohide")).toBe("hidden");
+ expect(sidebar().hasAttribute("inert")).toBe(true);
+ expect(handle?.container.querySelector(".bs-autohide-edge")).not.toBeNull();
+ });
+});
diff --git a/apps/agent/src/app.tsx b/apps/agent/src/app.tsx
index ad2237be..0b6f2e3b 100644
--- a/apps/agent/src/app.tsx
+++ b/apps/agent/src/app.tsx
@@ -18,6 +18,7 @@ import {
AiContentPartKind,
type AiImagePart,
type AiProvenance,
+ type AiProviderInfo,
AttachmentKind,
COLLECTION_TYPE_URL,
CONVERSATION_TYPE_URL,
@@ -63,7 +64,7 @@ import {
type ObjectMenuRuntime,
ObjectMenuTrigger,
} from "@brainstorm-os/sdk/object-menu";
-import { PanelSide, PanelToggleButton } from "@brainstorm-os/sdk/panel-toggle";
+import { PanelSide, PanelToggleButton, useAutoHidePanel } from "@brainstorm-os/sdk/panel-toggle";
import { friendlyTypeName } from "@brainstorm-os/sdk/system-entities";
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
import type { ReactElement } from "react";
@@ -96,6 +97,7 @@ import {
estimateTurnTokens,
grantCapability,
grantsCover,
+ modelForRequest,
providerForRequest,
resolveProvider,
} from "./logic/conversation-settings";
@@ -155,6 +157,7 @@ import { buildVaultDataContextBlock } from "./logic/vault-data-context";
import { buildWorkspaceContextBlock, joinContextBlocks } from "./logic/workspace-context";
import { MemoryPopover } from "./memory-popover";
import { ProposalTray } from "./proposal-tray";
+import { RunChangesPanel } from "./run-changes-panel";
import { getBrainstorm } from "./runtime";
import { SaveAsAutomationPopover } from "./save-as-automation-popover";
import { TurnTimeline } from "./turn-timeline";
@@ -460,7 +463,23 @@ export function AgentApp(): ReactElement {
() => providerForRequest(resolveProvider(appCaps ?? [], str(activeConv?.properties.provider))),
[appCaps, activeConv],
);
- const conversationModel = str(activeConv?.properties.model) || undefined;
+ const conversationModel = modelForRequest(str(activeConv?.properties.model));
+
+ // Agent-14 — the shell's routable providers (incl. user-added custom
+ // endpoints), already filtered to this app's grants. `null` = an older shell
+ // without `ai.providers`; the picker then derives providers from caps.
+ const [aiProviders, setAiProviders] = useState(null);
+ useEffect(() => {
+ if (!settingsOpen || !aiSvc?.providers) return;
+ let live = true;
+ aiSvc
+ .providers()
+ .then((list) => live && setAiProviders(list))
+ .catch(() => live && setAiProviders(null));
+ return () => {
+ live = false;
+ };
+ }, [aiSvc, settingsOpen]);
// The conversation's token budget + spend so far (Agent-5).
const tokenBudget = activeConv ? posNum(activeConv.properties.tokenBudget) : undefined;
@@ -525,6 +544,7 @@ export function AgentApp(): ReactElement {
return next;
});
}, []);
+ const sidebarAutoHide = useAutoHidePanel({ side: PanelSide.Left, collapsed: !sidebarOpen });
// The composer is a CompactEditor (rich draft) — the latest payload rides in
// a ref (reading it doesn't need a render); `draftEmpty` drives the Send
// button's enabled state.
@@ -1598,7 +1618,13 @@ export function AgentApp(): ReactElement {
-