* fix(api): paginate the bank list
GET /v1/default/banks returned every bank in the system: no limit, no
offset, and a query with no LIMIT clause. Beyond the unbounded payload, the
per-bank work — config resolution and a live store count for banks whose
memories live outside SQL — ran for every bank rather than the ones being
shown.
The endpoint now takes limit/offset (defaults 100/0, matching
list_documents) plus a `q` substring filter on bank id and name, and
returns total/limit/offset alongside `banks`. Paging happens after
filter_bank_list rather than in SQL: that extension hook can drop any bank,
so a SQL page would hand back short pages and a total counting banks the
caller can't see.
Consumers page instead of taking the first 100: the control-plane bank
selector scrolls infinitely and searches server-side, the CLI walks every
page, and the Zapier bank dropdown became canPaginate.
* fix(api): bound the bank-list probes and keep the selected bank's name
Follow-ups from reviewing the pagination change:
- the control-plane health probe and the Zapier credential test only need to
know the endpoint answers, so they ask for limit=1 instead of a default page
- the header showed the raw bank id whenever the selected bank sat past the
first page, so its name is fetched directly
- limit/offset are clamped in the engine: the page is a Python slice, and the
MCP tool takes both straight from a model with no HTTP-layer validation
* docs(mcp): document list_banks query/limit/offset
* fix(control-plane): make the bank selector actually page and report empty searches
Verified against a 130-bank instance: the infinite scroll never fired. The
observer effect read listRef.current/sentinelRef.current on the commit that
flips the popover open, but Radix mounts the content in a portal afterwards, so
both refs were null and nothing re-ran the effect — the selector sat on its
first 50 banks forever. Tracking the nodes as state through callback refs
re-runs the effect when they attach; paging now walks offset 0/50/100 and stops
at the total.
An empty result also read "No memory banks yet." after a search that simply
matched nothing, so searches get their own message.
* feat(control-plane): smooth the bank selector as pages land and searches narrow
The list is paged and searched server-side now, so rows appear and vanish in
batches — every page landed as a hard 50-row pop, and a search that narrowed to
one bank snapped the popover shut from 300px.
- rows fade and lift in, staggered within their page and capped so the tail of a
50-row page doesn't crawl; only rows that actually mount animate, so appending
page 2 leaves page 1 still
- the list height follows cmdk's --cmdk-list-height, easing down to the filtered
set instead of jumping
- the previous results hold their place and dim while the next set is in flight,
rather than blanking on every keystroke
The animations are defined in globals.css next to the existing logo keyframes:
tailwindcss-animate is a Tailwind v3 plugin declared in tailwind.config.ts, but
this app runs Tailwind v4 with the CSS-first config, so `animate-in` and friends
compile to nothing here.
* refactor(control-plane): tidy the bank row className and import