Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
169 commits
Select commit Hold shift + click to select a range
427c236
feat(editor): block Insert menu — gutter + opens the slash catalogue …
th3-br41n Oct 18, 2026
c4865d7
feat(notes): edit a transclusion in place — explicit Edit here mode (…
th3-br41n Oct 18, 2026
2ec1b6e
feat(database): contextual inline views — $host predicate ref + host-…
th3-br41n Oct 18, 2026
d88cba2
fix(predicate-eval): $contains matches multi-valued { value } envelop…
th3-br41n Oct 18, 2026
1f024f4
feat(editor): block + table-cell backgrounds from theme tokens (B11.23)
th3-br41n Oct 18, 2026
d961663
feat(entities): two-way relations via PropertyDef.inverseOf (9.12.25)
th3-br41n Oct 18, 2026
7010682
test(entities): typecheck fix for the inverse-relations service test
th3-br41n Oct 18, 2026
0afec95
feat(editor): /task — an inline task chip backed by a real Task/v1 (B…
th3-br41n Oct 18, 2026
1350248
feat(shell): split view — two objects side by side in one window (6.14)
th3-br41n Oct 18, 2026
5d48a88
feat(ai): custom OpenAI-compatible endpoints, OpenRouter preset, Olla…
th3-br41n Oct 18, 2026
76808ac
feat(agent): per-conversation model choice + custom endpoints in the …
th3-br41n Oct 18, 2026
fe1cc3f
Merge feat/md-ai: Agent-14 provider and model settings
th3-br41n Oct 18, 2026
fcfee75
Merge feat/md-split: 6.14 split view
th3-br41n Oct 18, 2026
778515b
feat(chat): message search within a channel and across channels (Chat…
th3-br41n Oct 18, 2026
567d22a
feat(files): property filters + Duplicates view (9.8.16)
th3-br41n Oct 18, 2026
df40310
feat(properties): rich-text property preset + editing cell (9.12.26)
th3-br41n Oct 18, 2026
9e95d75
feat(notes): Turn into → Page moves blocks into a new sub-page (B11.25)
th3-br41n Oct 18, 2026
2141cd7
feat(launcher): recents, saved searches and backlink counts on an emp…
th3-br41n Oct 18, 2026
7196e6b
feat(chat): read state follows the user; mark all as read; per-channe…
th3-br41n Oct 18, 2026
746a521
feat(sdk): os-file-drop — import dropped OS files as File/v1 objects …
th3-br41n Oct 18, 2026
81043c4
feat(chat): expandable composer and an absolute-timestamp preference …
th3-br41n Oct 18, 2026
7e4bef7
feat(database): editable collection descriptions, surfaced where coll…
th3-br41n Oct 18, 2026
6f41dd8
feat(shell): user-editable spellcheck languages, per device (B11.31)
th3-br41n Oct 18, 2026
555fb9d
Merge feat/md-notes: B11.21-25, B11.31 editor features
th3-br41n Oct 18, 2026
6888aac
feat(preview): context panel — caption, backlinks, Move to Bin (9.20.15)
th3-br41n Oct 18, 2026
f6d6380
feat(export): whole-vault Markdown export (IE-13)
th3-br41n Oct 18, 2026
7f4948b
Merge feat/md-db: 9.12.24-27 Database features
th3-br41n Oct 18, 2026
c4fd001
feat(chat): pinned messages and a typing indicator over presence (Cha…
th3-br41n Oct 18, 2026
22cd2fb
i18n(chat): de/es/fr/it/pt for the Chats-1..4 strings
th3-br41n Oct 18, 2026
c791696
feat(appearance): visibility preferences — higher contrast, text curs…
th3-br41n Oct 18, 2026
93ef7ef
feat(tasks): three-state checkbox — open / done / won't do (9.14.17)
th3-br41n Oct 18, 2026
3c476ea
Merge feat/md-chat: Chats-1..4
th3-br41n Oct 18, 2026
7216bd4
feat(backup): scheduled backups with restore points (IE-14)
th3-br41n Oct 18, 2026
3dbd06d
Merge feat/md-io: 9.22.7, IE-13, IE-14
th3-br41n Oct 18, 2026
07c5ef3
feat(agent): run diff + one-click undo for agent runs (Agent-15)
th3-br41n Oct 18, 2026
45fcc17
feat(sdk): auto-hide sidebars — edge-hover / shortcut overlay for col…
th3-br41n Oct 18, 2026
4c74d46
feat(journal): quick-capture stream on today's entry (9.16.14)
th3-br41n Oct 18, 2026
5f5ff32
chore: sort preload imports after the md-io merge
th3-br41n Oct 18, 2026
fbd8b05
feat(agent): show agent authorship + a filterable createdByAgent prop…
th3-br41n Oct 18, 2026
edff7a1
feat(whiteboard): restore each board's last viewport (9.17.24)
th3-br41n Oct 18, 2026
1797b09
feat(confirm): "Don't ask again" on move-to-Bin prompts, resettable i…
th3-br41n Oct 18, 2026
280d4fe
feat(notes): side Outline panel — live headings, click-to-scroll, act…
th3-br41n Oct 18, 2026
2bcd6bc
Merge feat/md-apps: 9.8.16/17, 9.20.15, 9.14.17, 9.16.14, 9.17.24
th3-br41n Oct 18, 2026
6961e3f
feat(shell): close to tray — opt-in "Keep running in the background" …
th3-br41n Oct 18, 2026
8505a78
feat(database): board swimlanes, group order by item count, collapse/…
th3-br41n Oct 18, 2026
c9af776
feat(shell): startup page — dashboard, today's journal entry, or a ch…
th3-br41n Oct 18, 2026
4424529
feat(collab): invite deep links, Viewer-by-default shares, last edite…
th3-br41n Oct 18, 2026
14436a1
feat(find): regex find & replace with a bounded match budget (B11.27,…
th3-br41n Oct 18, 2026
12ace5c
feat(database): column freedom — reorderable Name, per-view alias, ic…
th3-br41n Oct 18, 2026
6cd4328
Merge feat/md-agentcollab: Agent-15, Agent-16, Collab-C7
th3-br41n Oct 18, 2026
5b0e40a
feat(icon-picker): recent emoji and icons row (8.11)
th3-br41n Oct 18, 2026
6f8e21d
feat(database): bulk edit — Set property… and Add to collection… over…
th3-br41n Oct 18, 2026
6931fd3
wip(history): store, repos, retention, worker capture (pre-merge)
th3-br41n Oct 18, 2026
449a495
feat(database): next / previous object over the view's live order (9.…
th3-br41n Oct 18, 2026
a42380f
Merge feat/0.15.0-market-demand into feat/md-history (entity_versions…
th3-br41n Oct 18, 2026
d96a87a
fix(merge): resolve entities-repo index exports
th3-br41n Oct 18, 2026
a04c2da
feat(editor): media from the vault — reuse files, inline images, drag…
th3-br41n Oct 18, 2026
e8f515b
Merge feat/md-shellux: 6.15, 7.15-7.17, 8.11, 12.18
th3-br41n Oct 18, 2026
11f934e
feat(database): contextual-view follow-ups — New in the embed, hostle…
th3-br41n Oct 18, 2026
de3e6dc
feat(history): version checkpoints, change sets, restore + revert (10…
th3-br41n Oct 18, 2026
43da45a
feat(editor): tabs block — panes of blocks behind an ARIA tab strip (…
th3-br41n Oct 18, 2026
f7b7120
feat(sdk): shared VersionHistoryPanel + history runtime proxy (10.16 …
th3-br41n Oct 18, 2026
8d4a2a8
feat(editor): list indentation guides + outliner toggles (B11.30)
th3-br41n Oct 18, 2026
5d734b3
feat(database): Chart view (bar / line / pie / number) + a dashboard …
th3-br41n Oct 18, 2026
608a3ff
feat(notes): History tab in the shared right panel (10.16 step 4)
th3-br41n Oct 18, 2026
9a6ff76
feat(notes): block gutter + context menu inside the Edit here editor …
th3-br41n Oct 18, 2026
976c1fb
fix(history): history worker methods are shell-internal
th3-br41n Oct 18, 2026
d79f11e
feat(export): block backgrounds survive HTML export; Markdown drops t…
th3-br41n Oct 18, 2026
f661952
feat(database): gallery first-image covers, values-only cards, URL op…
th3-br41n Oct 18, 2026
3be54a8
feat(properties): the real rich-text editor in every app's properties…
th3-br41n Oct 18, 2026
eeb27ae
feat(editor): /task in Journal and Tasks — wire EditorHost.entities (…
th3-br41n Oct 18, 2026
8ee3d5f
feat(database): change a property's type with a per-row preview; numb…
th3-br41n Oct 18, 2026
a69e13b
feat(database): pin a view to the dashboard (9.12.35)
th3-br41n Oct 18, 2026
7e41c12
Merge branch 'feat/0.15.0-market-demand' into feat/md-db2
th3-br41n Oct 18, 2026
4f87e96
feat(database): drop OS files onto a view to import them into the col…
th3-br41n Oct 18, 2026
2743299
fix(database): lint — no assignment in the file-drop error callback
th3-br41n Oct 18, 2026
a0a1e29
Merge feat/md-history
th3-br41n Oct 18, 2026
ab347b2
Merge feat/md-notes2
th3-br41n Oct 18, 2026
840358e
Merge feat/md-db2
th3-br41n Oct 18, 2026
5e55277
test(journal): pin the merged write list (quick capture + /task)
th3-br41n Oct 18, 2026
c7be74c
fix(collab-c7): sanitize invite labels, first-wins invite slot, no de…
th3-br41n Oct 19, 2026
073dd33
feat(sdk): task-state — one isTaskClosed predicate for won't-do aware…
th3-br41n Oct 19, 2026
0489368
fix(editor): /task inserts at the caret; task chip verifies it is a T…
th3-br41n Oct 19, 2026
adade2e
fix(collections): a locked List takes no new members from quick captu…
th3-br41n Oct 19, 2026
48eb9af
fix(apps): reserve io.brainstorm.shell so no manifest or renderer can…
th3-br41n Oct 19, 2026
106c8cb
fix(i18n): translate every app key added this release, and gate pack …
th3-br41n Oct 19, 2026
b567aca
fix(tasks): won't do reads closed in the widget, alerts, Calendar, ti…
th3-br41n Oct 19, 2026
16bb651
fix(sdk): run-changes sizes its text from the type scale, not raw px
th3-br41n Oct 19, 2026
0163f3d
fix(chat): only the local member's own UserState rows apply
th3-br41n Oct 19, 2026
12c40ab
fix(chat): a pin re-reads the stored list instead of writing a stale …
th3-br41n Oct 19, 2026
b2cae1f
fix(ydoc): gate the broker's ydoc service to the shell and pin it to …
th3-br41n Oct 19, 2026
92776e0
fix(history): start version history for the boot vault and flush it b…
th3-br41n Oct 19, 2026
d2fce52
fix(notes): Edit-here body mounts Tabs; host format chords skip neste…
th3-br41n Oct 19, 2026
8631963
fix(ai): re-check ai.use and ai.provider:<id> against the ledger on e…
th3-br41n Oct 19, 2026
3bfa80e
fix(database): view settings and the embedded block speak the user's …
th3-br41n Oct 19, 2026
211f01f
feat(apps): Files, Journal and Mailbox honour the auto-hide sidebars …
th3-br41n Oct 19, 2026
4765e9a
fix(database): two-way picker excludes the unmarked side of an existi…
th3-br41n Oct 19, 2026
a84291e
feat(apps): Agent, Bookmarks and Books honour the auto-hide sidebars …
th3-br41n Oct 19, 2026
0d84f38
fix(entities): reserved keys never materialise from the doc channel
th3-br41n Oct 19, 2026
f398495
feat(editor): F2 renames a tab, and committing returns focus to the s…
th3-br41n Oct 19, 2026
946c8ba
fix(network): a strict redirect policy for credential-bearing calls
th3-br41n Oct 19, 2026
d2c1420
fix(agent-14): gate default routing, strict AI redirects, untrusted u…
th3-br41n Oct 19, 2026
607e3e9
fix(database): bulk-edit undo is targeted, lock-checked and expires
th3-br41n Oct 19, 2026
577f7bc
feat(apps): Calendar, Chat and Code Editor honour the auto-hide sideb…
th3-br41n Oct 19, 2026
9d2986d
fix(notes): the outline docks beside properties with a transform, not…
th3-br41n Oct 19, 2026
9212e53
feat(apps): Notes, Preview, Tasks and Whiteboard honour the auto-hide…
th3-br41n Oct 19, 2026
0015655
fix(split-view): sender-pane app, re-home on closeApp, no hidden side…
th3-br41n Oct 19, 2026
2e0ecda
fix(database): file drop pins into the current list and survives a fu…
th3-br41n Oct 19, 2026
ea45043
feat(chat): the mark-all-read menu rows show their Shift+Esc accelerator
th3-br41n Oct 19, 2026
8c23931
fix(database): prev/next walks the board in the order it is painted
th3-br41n Oct 19, 2026
3fc4d3f
fix(close-to-tray): lock a backgrounded vault on the idle timeout; fr…
th3-br41n Oct 19, 2026
a02d1a8
fix(database): pin-first-column pins Name only while Name is first
th3-br41n Oct 19, 2026
15a3ded
fix(editor): forget a destroyed tabs block's active pane
th3-br41n Oct 19, 2026
df33030
fix(ie-14): compress backups off the main thread; safe .partial; one …
th3-br41n Oct 19, 2026
94ba587
perf(database): embedded list fetches its view by id; concurrent scan…
th3-br41n Oct 19, 2026
15cae38
fix(agent-journal): purge journal rows with their entity and cap each…
th3-br41n Oct 19, 2026
41e8d62
fix(relations): gate inverseOf, harden far writes, and make agent run…
th3-br41n Oct 19, 2026
eb88c68
fix(ie-13): code-point-safe names, NFC/NFD collisions, no script link…
th3-br41n Oct 19, 2026
6f5ff5d
fix(editor): a rich-text state with unknown nodes degrades to text; s…
th3-br41n Oct 19, 2026
75e6bdb
fix(database): first-media cache closes only docs a load actually opened
th3-br41n Oct 19, 2026
98f13ef
test(network): give the default-policy redirect test an audit sink
th3-br41n Oct 19, 2026
a03e2fe
style(ie-13): mark the deliberate control-char strip for lint
th3-br41n Oct 19, 2026
f2fa309
fix(editor): toggle Tab falls through when it can't nest; regex repla…
th3-br41n Oct 19, 2026
ffc331e
fix(history): rate-limit restores, attribute agent restores, hide unr…
th3-br41n Oct 19, 2026
ec717ad
perf(history): memoise a doc's state key until the doc next changes
th3-br41n Oct 19, 2026
5643afe
fix(shell): theme preview keeps display prefs; a failed default-role …
th3-br41n Oct 19, 2026
ee7a6a9
style: biome format for the hardening pass
th3-br41n Oct 19, 2026
a4f4bed
Merge fix/0.15.0-harden-core
th3-br41n Oct 19, 2026
50834c3
Merge fix/0.15.0-harden-periph
th3-br41n Oct 19, 2026
1520c35
Merge fix/0.15.0-harden-design
th3-br41n Oct 19, 2026
fa42601
docs(ai): note the custom-default pin in the ledger-gate header
th3-br41n Oct 19, 2026
65c6749
Merge fix/0.15.0-harden-apps
th3-br41n Oct 19, 2026
2ea8e16
fix(relations): bound far writes by the pair's declared types and gat…
th3-br41n Oct 19, 2026
f51e9db
chore(size): re-baseline renderer and main budgets for 0.15.0
th3-br41n Oct 19, 2026
4688c8e
fix(bundle): strip reserved property keys on vault import
th3-br41n Oct 19, 2026
8df61ec
fix(import): every importer strips reserved property keys at one repo…
th3-br41n Oct 19, 2026
562684c
fix(entities): merge strips reserved keys from its patch and leaves l…
th3-br41n Oct 19, 2026
3cecf1b
fix(properties): reserved keys are never catalog keys; gate every way…
th3-br41n Oct 19, 2026
fda772a
fix(database): a locked List refuses every membership write (9.16.14)
th3-br41n Oct 19, 2026
2e53f84
Merge fix/0.15.0-harden-locks
th3-br41n Oct 19, 2026
cccc8b9
Merge fix/0.15.0-harden-relations
th3-br41n Oct 19, 2026
be34ed8
chore(ci): pin bun to 1.3.9 on both pipelines (SH-42a)
th3-br41n Oct 19, 2026
82a4448
release(0.15.0): version bump + the in-app What's New entry
th3-br41n Oct 19, 2026
10a6db2
fix(properties): close the pair-gate race and walk the whole pair com…
th3-br41n Oct 19, 2026
4c5502b
Merge fix/0.15.0-pair-gate-race (9.12.25 re-pentest fixes)
th3-br41n Oct 19, 2026
43119c8
fix(sdk): F-507 the menu layer stacks above every picker host
th3-br41n Oct 19, 2026
708f621
fix(database): F-508 a same-name relation adopts the new target and p…
th3-br41n Oct 19, 2026
9217391
fix(database): F-509 the pinned Name column stays opaque under hover …
th3-br41n Oct 19, 2026
117b526
fix(database): F-510 the inspector counter never wraps under a long t…
th3-br41n Oct 19, 2026
2b28d4d
fix(properties): a prototype key is never a catalog key, and the cata…
th3-br41n Oct 19, 2026
025df78
fix(database): a List lock holds at the commit, and gates rename/dele…
th3-br41n Oct 19, 2026
960ae61
fix(database): F-511 CSV columns get catalog defs, new views inherit …
th3-br41n Oct 19, 2026
377cfa2
Merge fix/0.15.0-lock-at-commit (9.16.14 re-pentest fixes + locked Li…
th3-br41n Oct 19, 2026
9d7f8a6
Merge fix/0.15.0-database-friction (F-507..F-511)
th3-br41n Oct 19, 2026
15641e5
perf(history): bound every retention step to the 8 ms main-thread budget
th3-br41n Oct 19, 2026
976de5a
Merge fix/0.15.0-history-retention (10.16 perf gate)
th3-br41n Oct 19, 2026
60f9c04
test(entities): tear down the agent-undo and inverse-relations vaults…
th3-br41n Oct 19, 2026
99902b2
fix(files): every list row renders a glyph on a fixed icon track
th3-br41n Oct 19, 2026
5fdcbb3
fix(preview): read Modified from the entity's own updatedAt
th3-br41n Oct 19, 2026
53414b3
fix(sdk): emoji picker search finds "coffee" through keyword aliases
th3-br41n Oct 19, 2026
00ebd28
fix(journal): resolve a day to ONE entry and capture through its live…
th3-br41n Oct 19, 2026
a944882
fix(shell): un-split a window whose pane renderer is destroyed from o…
th3-br41n Oct 19, 2026
53a669d
Merge fix/0.15.0-shots-files-preview (F-512, F-515, F-518)
th3-br41n Oct 19, 2026
22b3acf
fix(contacts): say a binned contact can be restored, not that it can'…
th3-br41n Oct 19, 2026
91f5f0d
Merge fix/0.15.0-shots-journal-split (F-516, F-517, F-519)
th3-br41n Oct 19, 2026
30bdb56
fix(editor): resolve a DOM selection in the tab strip without Lexical…
th3-br41n Oct 19, 2026
7884331
fix(chat): give the typing indicator its own row between the transcri…
th3-br41n Oct 19, 2026
45e2c31
fix(sdk): hide host bookkeeping in a run diff and read date stamps fo…
th3-br41n Oct 19, 2026
c9c63f0
Merge fix/0.15.0-shots-editor-chat (F-513, F-514, F-520)
th3-br41n Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/actions/setup-bun-rust/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 4 additions & 4 deletions .size-limit.json
Original file line number Diff line number Diff line change
@@ -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
},
{
Expand All @@ -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)",
Expand Down
2 changes: 2 additions & 0 deletions apps/agent/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
30 changes: 30 additions & 0 deletions apps/agent/src/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<AgentApp />);
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(<AgentApp />);
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();
});
});
43 changes: 39 additions & 4 deletions apps/agent/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
AiContentPartKind,
type AiImagePart,
type AiProvenance,
type AiProviderInfo,
AttachmentKind,
COLLECTION_TYPE_URL,
CONVERSATION_TYPE_URL,
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -96,6 +97,7 @@ import {
estimateTurnTokens,
grantCapability,
grantsCover,
modelForRequest,
providerForRequest,
resolveProvider,
} from "./logic/conversation-settings";
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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<readonly AiProviderInfo[] | null>(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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1598,7 +1618,13 @@ export function AgentApp(): ReactElement {
</header>

<div className="agent__body" data-sidebar-open={String(sidebarOpen)}>
<aside className="agent__sidebar" id="agent-sidebar">
<aside
{...sidebarAutoHide.panelProps}
className="agent__sidebar"
id="agent-sidebar"
aria-hidden={sidebarAutoHide.visible ? undefined : true}
inert={sidebarAutoHide.visible ? undefined : true}
>
<nav className="agent__convs">
{conversations.length === 0 ? (
<EmptyState icon={IconName.Chat} title={t("sidebar.empty")} tone={EmptyStateTone.Compact} />
Expand All @@ -1616,6 +1642,7 @@ export function AgentApp(): ReactElement {
)}
</nav>
</aside>
{sidebarAutoHide.edgeProps && <div {...sidebarAutoHide.edgeProps} />}

<section className="agent__main">
<div className="agent__transcript" data-testid="agent-transcript">
Expand Down Expand Up @@ -1808,6 +1835,12 @@ export function AgentApp(): ReactElement {
</div>
</section>
) : null}
<RunChangesPanel
trace={agentTraceSvc}
conversationId={activeId}
refreshKey={createdObjects.map((obj) => obj.id).join(",")}
onOpenEntity={openCitation}
/>
{insertNotice ? (
<div className="agent__error-row">
<div className="agent__notice" role="status" data-testid="agent-insert-notice">
Expand Down Expand Up @@ -1889,6 +1922,7 @@ export function AgentApp(): ReactElement {
{settingsOpen && activeConv ? (
<ConversationSettingsPopover
appCaps={appCaps ?? []}
providers={aiProviders}
settings={{
grants: conversationGrants,
provider: str(activeConv.properties.provider) || undefined,
Expand All @@ -1898,7 +1932,8 @@ export function AgentApp(): ReactElement {
}}
onClose={() => setSettingsOpen(false)}
onGrantsChange={(grants) => persistConversation({ toolGrants: grants })}
onProviderChange={(provider) => persistConversation({ provider: provider ?? "" })}
onProviderChange={(provider) => persistConversation({ provider: provider ?? "", model: "" })}
onModelChange={(model) => persistConversation({ model: model ?? "" })}
onBudgetChange={(budget) => persistConversation({ tokenBudget: budget ?? 0 })}
/>
) : null}
Expand Down
92 changes: 92 additions & 0 deletions apps/agent/src/conversation-settings-popover.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// @vitest-environment jsdom
/**
* Agent-14 — the conversation settings popover's model field: custom endpoints
* from `ai.providers()` appear by name when the app holds `ai.provider:*`, the
* placeholder names the provider's default model, typing a sane id persists it
* (trimmed), and a control-character id is flagged and never persisted.
*/

import { act } from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
type ConversationSettings,
ConversationSettingsPopover,
} from "./conversation-settings-popover";
import { type RenderHandle, renderInto } from "./test/render";

const CAPS = ["ai.use", "ai.provider:ollama", "ai.provider:*"];
const PROVIDERS = [
{ id: "ollama", label: "Ollama", defaultModel: "llama3.2", models: ["llama3.2"], custom: false },
{
id: "custom-0123abcd",
label: "Studio box",
defaultModel: "qwen3",
models: ["qwen3"],
custom: true,
},
];

const settings = (over: Partial<ConversationSettings> = {}): ConversationSettings => ({
grants: CAPS,
provider: "custom-0123abcd",
model: undefined,
tokenBudget: undefined,
tokensSpent: 0,
...over,
});

function typeInto(input: HTMLInputElement, value: string): void {
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
setter?.call(input, value);
input.dispatchEvent(new Event("input", { bubbles: true }));
}

let handle: RenderHandle | null = null;
afterEach(async () => {
await handle?.unmount();
handle = null;
});

async function mount(onModelChange = vi.fn(), over: Partial<ConversationSettings> = {}) {
handle = await renderInto(
<ConversationSettingsPopover
appCaps={CAPS}
providers={PROVIDERS}
settings={settings(over)}
onClose={() => {}}
onGrantsChange={() => {}}
onProviderChange={() => {}}
onModelChange={onModelChange}
onBudgetChange={() => {}}
/>,
);
const model = document.querySelector<HTMLInputElement>('[data-testid="agent-settings-model"]');
if (!model) throw new Error("model field missing");
return { model, onModelChange };
}

describe("ConversationSettingsPopover — model field", () => {
it("shows the pinned custom endpoint by name and its default model as the placeholder", async () => {
const { model } = await mount();
const provider = document.querySelector('[data-testid="agent-settings-provider"]');
expect(provider?.textContent).toContain("Studio box");
expect(model.placeholder).toContain("qwen3");
expect(document.querySelector('[data-testid="agent-settings-model-suggestions"]')).not.toBeNull();
});

it("persists a typed model id trimmed, and clears on empty", async () => {
const { model, onModelChange } = await mount();
await act(async () => typeInto(model, " qwen3-coder "));
expect(onModelChange).toHaveBeenLastCalledWith("qwen3-coder");
await act(async () => typeInto(model, ""));
expect(onModelChange).toHaveBeenLastCalledWith(undefined);
});

it("flags a control-character id and does not persist it", async () => {
const { model, onModelChange } = await mount();
await act(async () => typeInto(model, "bad\u0007id"));
expect(onModelChange).not.toHaveBeenCalled();
expect(model.getAttribute("aria-invalid")).toBe("true");
expect(document.querySelector('[role="alert"]')).not.toBeNull();
});
});
Loading
Loading