Skip to content

feat(agents): add manual drag-and-drop sorting for agent and session lists#13460

Merged
kangfenmao merged 11 commits intomainfrom
feat/agent-manual-sort
Mar 15, 2026
Merged

feat(agents): add manual drag-and-drop sorting for agent and session lists#13460
kangfenmao merged 11 commits intomainfrom
feat/agent-manual-sort

Conversation

@EurFelux
Copy link
Copy Markdown
Collaborator

@EurFelux EurFelux commented Mar 14, 2026

What this PR does

Before this PR:
Agents and sessions in the sidebar are listed in fixed order with no way to manually reorder them.

After this PR:
Both agents and sessions can be reordered via drag-and-drop. The order is persisted to the SQLite database via a new sort_order column, and new items are automatically placed at the top of the list. Session sorting is scoped per-agent (each agent's sessions are independently sortable).

Why we need it and why it was done in this way

The following tradeoffs were made:

  • Used sort_order integer column in SQLite (backend persistence) rather than Redux/localStorage, because agents/sessions data lives in the backend DB — consistent with the existing architecture.
  • New agents/sessions get sort_order = 0 and all existing items are shifted up by 1, matching the assistant list behavior where new items appear at the top.
  • Reused DraggableList component for agents and DraggableVirtualList for sessions (both using @hello-pangea/dnd) for consistency with existing drag-and-drop patterns.
  • Added dedicated PUT /v1/agents/reorder and PUT /v1/agents/{agentId}/sessions/reorder endpoints rather than updating sort_order through individual PATCH calls, to ensure atomic reorder operations.

The following alternatives were considered:

  • Storing order in Redux/localStorage — rejected because agents/sessions are fetched from the backend SQLite DB via HTTP API, so the order would diverge.
  • Using @dnd-kit/sortable instead of @hello-pangea/dnd — rejected to maintain consistency with existing components.

Breaking changes

None. The new sort_order column defaults to 0 for all existing agents and sessions, so they will share the same sort order initially and fall back to created_at DESC as tie-breaker.

Special notes for your reviewer

Database migration:

  • 0003_slippery_wild_pack.sql adds sort_order INTEGER NOT NULL DEFAULT 0 to both agents and sessions tables
  • Schema defines indexes (idx_agents_sort_order, idx_sessions_sort_order) for query performance
  • Existing rows get sort_order = 0 by default; the first reorder operation will assign proper values

API changes:

  • PUT /v1/agents/reorder — accepts { ordered_ids: string[] } to reorder agents
  • PUT /v1/agents/{agentId}/sessions/reorder — accepts { ordered_ids: string[] } to reorder sessions (scoped to agent)
  • GET /v1/agents — default sort changed to sort_order ASC, created_at DESC
  • GET /v1/agents/{agentId}/sessions — default sort changed to sort_order ASC, created_at DESC

Frontend:

  • useAgents hook exposes reorderAgents() with optimistic SWR cache update
  • useSessions hook exposes reorderSessions() with optimistic SWRInfinite cache update (preserves real total for pagination)
  • AgentSidePanel uses DraggableList for agent drag-and-drop
  • Sessions component swapped from DynamicVirtualList to DraggableVirtualList for session drag-and-drop

Atomicity:

  • createSession wraps sort_order shift + insert in a transaction
  • reorderAgents and reorderSessions both use transactions

Checklist

  • PR: The PR description is expressive enough and will help future contributors
  • Code: Write code that humans can understand and Keep it simple
  • Refactor: You have left the code cleaner than you found it (Boy Scout Rule)
  • Upgrade: Impact of this change on upgrade flows was considered and addressed if required
  • Documentation: A user-guide update was considered and is present (link) or not required. Check this only when the PR introduces or changes a user-facing feature or behavior.
  • Self-review: I have reviewed my own code before requesting review from others

Release note

Added drag-and-drop manual sorting for agent and session lists in the sidebar panel.

Add sort_order column to agents table and implement drag-and-drop
reordering in the agent sidebar panel, consistent with assistant list
behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
@EurFelux EurFelux requested a review from kangfenmao March 14, 2026 09:19
Copy link
Copy Markdown
Collaborator Author

@EurFelux EurFelux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review: overall implementation looks good. Two minor suggestions:

  1. Transaction safetyreorderAgents should use a DB transaction to prevent partial updates on failure
  2. Error feedback — Reorder failure should show a toast to the user instead of silently reverting

Copy link
Copy Markdown
Contributor

@cherry-ai-bot cherry-ai-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice feature overall. I’m adding one follow-up comment about initial post-migration ordering for pre-existing agents.

EurFelux and others added 2 commits March 14, 2026 17:24
- Use database.transaction() for atomic sort_order updates
- Show error toast on reorder failure instead of silent revert
- Add i18n keys for reorder error message

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
When sort_order values are equal (e.g., after migration when all
existing agents have sort_order=0), use created_at DESC as a
deterministic tie-breaker to maintain stable ordering.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
@EurFelux
Copy link
Copy Markdown
Collaborator Author

Note on migration from unified list order:

Previously, when Agents and Assistants were mixed together, their display order was stored in unifiedListOrder (Redux). After #13420 separated Agents into an independent module, there is no clean migration path from unifiedListOrder to the new sort_order column in SQLite — the old order interleaved agents and assistants, making it meaningless for an agent-only list.

As a result, after upgrading, existing agents will all start with sort_order = 0 and fall back to created_at DESC ordering (newest first) until the user manually reorders them.

Copy link
Copy Markdown
Contributor

@cherry-ai-bot cherry-ai-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the latest revision. The follow-up issues raised in review are now addressed: reorder writes are wrapped in a transaction, reorder failures surface an error toast instead of silently reverting, and sort_order ties now use created_at DESC as a deterministic secondary sort after migration. I don't see new blocking issues in the current revision. LGTM.

@EurFelux
Copy link
Copy Markdown
Collaborator Author

Sessions also need to be sorted.

@EurFelux EurFelux marked this pull request as draft March 14, 2026 09:44
Mirror the agent sorting implementation for sessions, scoped per-agent.
Add sort_order column to sessions table, reorder API endpoint, and
swap DynamicVirtualList for DraggableVirtualList in the Sessions UI.
Combine agent and session sort_order into a single migration (0003).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
@EurFelux EurFelux changed the title feat(agents): add manual drag-and-drop sorting for agent list feat(agents): add manual drag-and-drop sorting for agent and session lists Mar 14, 2026
@EurFelux EurFelux marked this pull request as ready for review March 14, 2026 12:00
@EurFelux EurFelux requested review from DeJeune and vaayne March 14, 2026 12:01
Copy link
Copy Markdown
Contributor

@cherry-ai-bot cherry-ai-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the updated PR after the session-sorting expansion. The earlier review findings remain addressed, and the new session-side implementation follows the same persistence/error-handling pattern: transactional reorder writes, deterministic sort_order tie-breaking with created_at DESC, and user-visible failure feedback on optimistic rollback. I don't see a new blocking issue in the current revision. LGTM.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
Copy link
Copy Markdown
Collaborator Author

@EurFelux EurFelux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this is a well-designed feature with clean architecture — sort_order column + transactional reorder APIs + optimistic frontend updates is the right approach.

Main issue to fix:

  • AgentService.createAgent — the sort_order shift + insert needs to be wrapped in a transaction (like SessionService.createSession already does), otherwise a failed insert leaves all sort_orders incremented with no rollback.

Minor suggestions:

  • Swagger doc orderBy default doesn't reflect the conditional asc for sort_order
  • agentId! non-null assertion could use an early return guard

Everything else looks good — DB migration, API design, frontend DnD integration, and optimistic caching are all solid. 👍

- Wrap createAgent shift+insert in transaction for atomicity
- Replace agentId! non-null assertion with early return in reorderSessions
- Update Swagger doc to note conditional orderBy default for sort_order

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
Copy link
Copy Markdown
Collaborator Author

@EurFelux EurFelux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Well-structured PR! The implementation follows existing patterns nicely (DraggableList reuse, SWR optimistic updates, Drizzle transactions). The backend/frontend integration is clean and the route ordering to prevent "reorder" being captured as :agentId is a nice touch.

What looks great

  • Transactions for both create (sort_order shift + insert) and reorder operations
  • Optimistic SWR updates with proper rollback on error
  • Secondary sort by created_at DESC for tie-breaking after migration
  • Scoped session sorting per agent
  • Route placement comments explaining why /reorder must precede /:id

Minor suggestions (non-blocking)

  • Element-level type validation for ordered_ids array entries
  • Future optimization opportunity: batch UPDATE via CASE/WHEN instead of N individual UPDATEs
  • Confirm autoHideScrollbar removal in Sessions is intentional
  • Confirm no-revalidation-on-success pattern is intentional for both hooks

Overall: clean implementation, no blockers found. 👍

Ensure each element in ordered_ids is a non-empty string, not just
that the array itself is non-empty.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
Copy link
Copy Markdown
Collaborator Author

@EurFelux EurFelux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after a30aad9

The new commit a30aad9 correctly addresses the element-type validation feedback — both reorderAgents and reorderSessions handlers now validate that each entry in ordered_ids is a non-empty string via .every((id: unknown) => typeof id === 'string' && id.length > 0).

All previously raised review threads are resolved. No new issues found.

Remaining non-blocking suggestions from earlier review (for future consideration):

  • Batch UPDATE via CASE/WHEN for reorder performance optimization
  • Confirm autoHideScrollbar removal in Sessions component is intentional
  • Consider adding server revalidation after successful reorder

LGTM overall 👍

@EurFelux
Copy link
Copy Markdown
Collaborator Author

EurFelux commented Mar 14, 2026

⚠️ Dev environment migration note

If you have previously run pnpm dev on the feat/agent-manual-sort branch (before session sorting was added), the old 0003_agent_sort_order migration (which only added sort_order to the agents table) is already recorded as applied with idx=3. The new combined migration 0003_slippery_wild_pack (which adds sort_order to both tables) shares the same idx and will be silently skipped.

Additionally, if you have worked on the v2 branch (or any branch with data migrations like idx=10001), MigrationService may log Latest applied migration: v10001 > latest available: v3 and skip all pending schema migrations.

Symptom: SQLITE_ERROR: no such column: sort_order when loading sessions.

Fix: Manually add the missing column and update the migration record:

# Find your dev DB path in the startup log: "[DatabaseManager] Initializing database at: ..."
# Then run:
sqlite3 "<your-agents.db-path>" \
  "ALTER TABLE sessions ADD COLUMN sort_order integer NOT NULL DEFAULT 0; \
   UPDATE migrations SET tag = '0003_slippery_wild_pack' WHERE version = 3;"

If agents.sort_order is also missing (fresh clone):

sqlite3 "<your-agents.db-path>" \
  "ALTER TABLE agents ADD COLUMN sort_order integer NOT NULL DEFAULT 0; \
   ALTER TABLE sessions ADD COLUMN sort_order integer NOT NULL DEFAULT 0; \
   INSERT INTO migrations (version, tag, executed_at) VALUES (3, '0003_slippery_wild_pack', strftime('%s','now') * 1000);"

Then restart pnpm dev.

@EurFelux EurFelux requested a review from 0xfullex March 14, 2026 14:46
with Tailwind

- Extract agent list from AgentSidePanel into dedicated Agents
  component,
  mirroring the Sessions component structure
- Replace styled-components StyledVirtualList with Tailwind classes in
  both Agents and Sessions components

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: icarus <[email protected]>
@EurFelux EurFelux force-pushed the feat/agent-manual-sort branch from 1d35a78 to f106798 Compare March 14, 2026 14:50
@kangfenmao
Copy link
Copy Markdown
Collaborator

kangfenmao commented Mar 14, 2026

image

When the scrollbar appears, this section may be truncated.

image image image

@EurFelux
Copy link
Copy Markdown
Collaborator Author

Fixed?

@kangfenmao
Copy link
Copy Markdown
Collaborator

Fixed?

Yes

@DeJeune
Copy link
Copy Markdown
Collaborator

DeJeune commented Mar 14, 2026

merge?

@EurFelux
Copy link
Copy Markdown
Collaborator Author

EurFelux commented Mar 14, 2026

Note

This issue/comment/review was translated by Claude.

I think it's OK now. Do we need to review it further?


Original Content

我觉得 OK 了,还要再看看吗

@kangfenmao kangfenmao merged commit e860dd4 into main Mar 15, 2026
7 checks passed
@kangfenmao kangfenmao deleted the feat/agent-manual-sort branch March 15, 2026 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants