Skip to content

feat(ui): sessions sidebar redesign with agent sections, smart groups, and draft sessions#103498

Merged
steipete merged 9 commits into
mainfrom
feat/sessions-sidebar-redesign
Jul 10, 2026
Merged

feat(ui): sessions sidebar redesign with agent sections, smart groups, and draft sessions#103498
steipete merged 9 commits into
mainfrom
feat/sessions-sidebar-redesign

Conversation

@steipete

Copy link
Copy Markdown
Contributor

Related: #103431
Stacked on #103432 (protocol/server foundation).

What Problem This Solves

The sessions sidebar mixed three create affordances (New session, a git-branch worktree button, and a New group prompt), hid agent switching in a compact <select> that teleported navigation on change, and rendered channel-originated sessions in one flat list with raw peer ids ("Telegram · 491234567890") and token names ("slack:g-general"). Custom group names/order lived in browser localStorage, so they never followed the operator across devices. Worktree sessions were indistinguishable from plain chats, and there was no way to pick a branch, folder, or node when starting work.

Why This Change Was Made

Agents are now collapsible top-level sidebar sections: expanding an agent browses its sessions (scoped list fetch, cached per agent) without navigating away from the open chat, and collapsed agents show an unread indicator; single-agent installs keep the flat list. Within an agent, rows classify into built-in smart sections — one per channel (rows keep their human chat titles), a Work section for worktree/exec-node sessions with a repo ⎇ branch (plus node) subtitle — followed by custom groups and Chats; an explicit user category always beats smart classification. Naming never falls back to raw keys: DM fallbacks shorten peer ids, dashboard sessions read "New session" until their generated title lands, and unnamed work sessions read as their checkout. The three create buttons collapse into one + that opens a draft dialog (agent, exec host from paired system.run nodes, folder, worktree toggle with a worktrees.branches-backed base-branch picker and optional name); the first message creates the session and starts the run in a single sessions.create call, so abandoned drafts leave nothing behind. The custom group catalog moves onto the gateway (sessions.groups.*) with a one-time localStorage migration; rename/delete now update member sessions server-side, deleting the old client-side paging loop.

User Impact

Multi-agent operators see every agent in the sidebar and can browse one without losing their place. Telegram/Slack/WhatsApp sessions group under their channels with real names; work sessions surface their branch at a glance. Starting a session on a specific branch, folder, or node is a first-class flow. Custom groups follow you across browsers. Plain New Chat semantics (/new, main-session reset) are unchanged.

Evidence

  • Live end-to-end on a scratch gateway (isolated OPENCLAW_STATE_DIR, two agents, seeded Telegram/Slack/WhatsApp sessions): sidebar rendered agent sections Main/Work agent, channel groups with titles "Acme #general", "Peter Steinberger", "OpenClaw Devs", "WhatsApp · …345678"; the + dialog listed branches main/feature-base/topic-x from worktrees.branches, and submitting with base feature-base + name live-test created branch openclaw/live-test, checked out the worktree under the state dir, navigated to the new session, and listed it under Work with subtitle workspace-main ⎇ live-test. Agent expansion switched session lists without changing the URL; zero console errors.
  • pnpm test ui/src/lib/sessions ui/src/lib/session-display.test.ts ui/src/components/app-sidebar.test.ts ui/src/components/new-session-dialog.test.ts ui/src/lib/agents/index.test.ts — green, including new coverage for smart-section classification, channel-session detection, name fallbacks, and draft→create param mapping.
  • Full ui/src/pages/sessions, ui/src/components, chat page/pane sweep: green except lobster-pet/lobster-dex, which fail identically on the base branch (pre-existing, unrelated).
  • pnpm ui:build clean; env -u OPENAI_API_KEY pnpm ui:i18n:check clean (locale bundles regenerated raw-copy, matching the secretless CI lane); tsgo core + test lanes green.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: web-ui App: web-ui size: XL maintainer Maintainer-authored PR labels Jul 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bec30b1dff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let names = readGroupNames(listed);
// One-time migration: browser-local catalogs predate the gateway store.
const legacy = readLegacyStoredGroups();
if (names.length === 0 && legacy.length > 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Merge legacy groups into non-empty catalogs

When a browser still has legacy localStorage custom groups and the gateway already has at least one group, this condition skips migration and the later removeItem clears the local catalog. That loses local-only empty groups and ordering for that browser instead of merging the legacy names into the gateway catalog before deleting the old storage.

Useful? React with 👍 / 👎.

const cached = this.sessionRowsByAgent[normalized] ?? [];
return filterVisibleSessionRows(cached, {
agentId: normalized,
defaultAgentId: navigationState.selectedAgentId,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Use the real default agent for cached-row filtering

When the current chat belongs to a non-default agent and the user expands the default agent section, cached legacy/unprefixed rows are filtered with defaultAgentId set to the current selected agent. isSessionKeyTiedToAgent then treats those unprefixed default-agent sessions as belonging to the non-default agent and drops them; this should pass navigationState.defaultAgentId.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04016c5670

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +45 to +49
const keyChannel = key.match(CHANNEL_SESSION_KEY_RE)?.[1];
const channel =
normalizeOptionalString(keyChannel && keyChannel !== "direct" ? keyChannel : undefined) ??
normalizeOptionalString(rowChannel);
return { channel, channelSession: Boolean(channel) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Exclude dashboard keys from channel grouping

For persistent dashboard sessions with keys such as agent:ops-agent:dashboard:direct:subagent-orchestrator (covered by src/gateway/server.sessions.create.test.ts), this parser extracts dashboard as the channel and returns channelSession: true. The new sidebar then places those non-message sessions under a bogus Dashboard channel section instead of the normal chat/dashboard area, despite the comment saying dashboard sessions stay out. Please short-circuit dashboard keys before peer-channel classification or teach the parser that dashboard is not a transport channel.

Useful? React with 👍 / 👎.

Comment on lines +1530 to +1531
const rows = this.sessionRowsByAgent[normalizeAgentId(agentId)] ?? [];
return rows.filter((row) => row.unread === true && row.archived !== true).length;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Populate unread state before rendering collapsed agents

In multi-agent sidebars, this only counts rows already present in sessionRowsByAgent, but that cache is filled for the currently loaded/expanded agent and expandAgent fetches one agent at a time. A collapsed agent with unread sessions that has not been expanded in this browser therefore gets [] here, so the advertised unread dot is missing until after the user opens that agent. Please hydrate per-agent unread state up front or derive it from an unscoped result when available.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/sessions-worktree-foundation branch from 38123d3 to a6afa58 Compare July 10, 2026 08:19
@steipete
steipete force-pushed the feat/sessions-sidebar-redesign branch from 04016c5 to 0ee43cb Compare July 10, 2026 08:35

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ee43cb85d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

saveStoredSessionCustomGroups(this.customGroups);
const known = this.knownCategories();
if (!known.includes(name)) {
void this.context?.sessions.groupsPut([...this.customGroups(), name]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Re-render when the group catalog changes

When the Sessions page toolbar creates an empty group (onRequestNewCategory() without a session key), this only calls groupsPut. That publishes state.groups without changing state.result, but this page's sessions subscription only schedules work when the result object changes, so the new catalog entry does not appear as an empty category section or menu option until some unrelated session refresh occurs. Please request an update or keep a local catalog snapshot when groupsPut completes.

Useful? React with 👍 / 👎.

Comment on lines +219 to +221
private canSubmit(): boolean {
if (this.submitting || !this.message.trim() || !this.context?.gateway.snapshot.connected) {
return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Gate draft creation while sessions are loading

When a foreground sessions refresh is running, this still enables the draft Start button as long as the gateway is connected. SessionCapability.create returns null while state.loading is true (ui/src/lib/sessions/index.ts:761), so submitting a draft during a Sessions page load or similar refresh shows a generic create failure without sending sessions.create. Either keep the loading gate here or allow create to proceed/queue during refresh.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle Bot added the gateway Gateway runtime label Jul 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee148418a7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const eventReason = (event.payload as { reason?: unknown } | null)?.reason;
if (eventReason === "groups") {
groupsLoadedEpoch = -1;
void groupsLoad();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Refresh rows on remote group mutations

When another browser renames or deletes a group that has member sessions, the gateway emits a keyless sessions.changed event with reason: "groups". This branch only reloads the catalog; the keyless payload is not reconciled into state.result.sessions, so this client keeps rendering member rows with the old category and the old group section until some unrelated full refresh happens. Please force a sessions refresh for group events that can mutate member categories.

Useful? React with 👍 / 👎.

showDraft:
Boolean(this.draftSessionAgentId) &&
normalizeAgentId(this.draftSessionAgentId) === agentId,
showFallback: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Target the empty agent fallback at that agent

In multi-agent setups, an expanded agent with zero visible sessions reaches this showFallback: true path, which renders renderChatFallback(). That fallback calls onNavigate("chat") without an agent-specific session search, so app-host fills in the current active session and clicking the only row under an empty/non-default agent reopens the previous chat instead of that agent's main chat. Pass the target agent/main session into the fallback.

Useful? React with 👍 / 👎.

Comment on lines +181 to +183
if (!repoRoot || (usesWorkspace && agent?.workspaceGit !== true)) {
this.branches = null;
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Cancel stale branch lookups on invalid targets

If the dialog starts a branch lookup for a git-backed workspace, then the user switches to a non-git agent before that request resolves, this early return leaves the old request token valid. The stale response can later populate baseRef for the previous repo; if an admin then enters a custom folder and submits before its fresh branch lookup completes, worktreeBaseRef is sent for the wrong checkout. Bump the token and clear baseRef/loading whenever branch loading is abandoned.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/sessions-sidebar-redesign branch from ee14841 to d79a2a6 Compare July 10, 2026 09:37

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d79a2a6bf1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +59 to +61
...(normalizeOptionalString(draft.execNode)
? { execNode: normalizeOptionalString(draft.execNode) }
: {}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Do not combine node execution with gateway worktrees

When an operator selects a node and also enables Worktree/custom folder, these params submit both execNode and the worktree fields. The create handler provisions the managed worktree on the gateway host, but host=node exec workdir resolution ignores the session default cwd unless the model passes an explicit workdir (src/agents/bash-tools.exec-workdir.ts:347-350), so the new session is displayed as a branch-backed Work session while commands run on the node outside that checkout. Disable this combination or provision/route the checkout on the selected node before sending execNode with worktree.

Useful? React with 👍 / 👎.

Comment on lines 657 to 660
const groups = this.knownSessionGroups();
if (!groups.includes(name)) {
saveStoredSessionCustomGroups([...groups, name]);
void this.context?.sessions.groupsPut([...groups, name]);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Load groups before replacing the catalog

If the user creates or reorders a group before groupsLoad() has completed (or after a transient list failure), knownSessionGroups() is only the current list window plus an empty state.groups, and sessions.groups.put replaces the entire gateway catalog. In that state this call can drop existing empty groups or groups outside the current agent/limit, even though the user only added one name; wait for the catalog or merge against a fresh sessions.groups.list before issuing the replace.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jul 10, 2026
@clawsweeper

clawsweeper Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed July 10, 2026, 1:35 PM ET / 17:35 UTC.

Summary
The branch redesigns the Control UI sessions sidebar with agent sections and smart groups, adds a target-aware draft-session dialog, consumes gateway-owned group/worktree APIs, and updates related tests, documentation, and locale data.

Reproducibility: yes. The exact PR head provides deterministic source-level reproduction paths for the remaining migration, navigation, cache, key-parsing, and asynchronous draft-state defects, although this read-only review did not execute the UI.

Review metrics: 2 noteworthy metrics.

  • Persisted-state migration: 1 browser catalog migrated into 1 replace-all gateway catalog. Upgrade safety depends on preserving empty groups and ordering when both stores already contain data.
  • Execution target dimensions: 3 independent selectors: agent, worktree/folder, and exec node. The dialog currently permits combinations that do not resolve to one coherent runtime working directory.

Stored data model
Persistent data-model change detected: persistent cache schema: ui/src/i18n/.i18n/es.tm.jsonl, persistent cache schema: ui/src/i18n/.i18n/fr.tm.jsonl, persistent cache schema: ui/src/i18n/.i18n/hi.tm.jsonl, persistent cache schema: ui/src/i18n/.i18n/it.tm.jsonl, persistent cache schema: ui/src/i18n/.i18n/ja-JP.tm.jsonl, persistent cache schema: ui/src/i18n/.i18n/nl.tm.jsonl, and 23 more. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #103431
Summary: This PR remains the unique UI implementation candidate for the canonical redesign issue; the merged foundation and dependent Worktrees PR neither supersede nor implement its sidebar behavior.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦞 diamond lobster
Patch quality: 🦪 silver shellfish
Result: blocked by patch quality or review findings.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Fix the complete current-head finding set in one round.
  • Resolve the merge conflict and rerun the live multi-agent, group migration, and worktree scenario.

Risk before merge

  • [P1] Existing browser-local custom groups and ordering can be lost during migration or replace-all writes issued before canonical catalog hydration completes.
  • [P1] Browsing another agent can mutate global selection, while incomplete per-agent caches can hide legacy rows and unread indicators.
  • [P1] Draft creation can target the wrong agent, overwrite user-entered branch input, or display a gateway-host worktree for commands actually executed elsewhere on a node.
  • [P1] The branch is merge-conflicted; conflict resolution can invalidate this line-level review and requires a fresh exact-head pass.

Maintainer options:

  1. Repair the complete blocker set (recommended)
    Resolve conflicts and every exact-head finding together, then refresh the live proof on the resolved head.
  2. Split into safer slices
    Separate sidebar grouping/navigation from draft targeting if the coupled branch cannot be stabilized without repeated state regressions.
  3. Pause the UI branch
    Retain the merged gateway foundation and pause this branch if maintainers no longer want to carry the redesign as one stack.

Next step before merge

  • [P2] The protected, merge-conflicted branch has a concrete but broad coupled repair set; the contributor should resolve the findings and conflicts before another exact-head review.

Security
Cleared: The diff introduces no concrete third-party execution, secret-handling, permission, dependency, or supply-chain regression.

Review findings

  • [P1] Merge legacy groups before deleting browser state — ui/src/lib/sessions/index.ts:839-848
  • [P1] Load the canonical catalog before replace-all writes — ui/src/components/app-sidebar.ts:704-708
  • [P1] Keep agent expansion out of shared selection — ui/src/components/app-sidebar.ts:499-504
Review details

Best possible solution:

Keep browse expansion local to the sidebar, make group migration and replace-all writes lossless against a fully loaded canonical catalog, and validate agent, branch, folder, worktree, and node selections against the runtime's actual execution semantics before creating a session.

Do we have a high-confidence way to reproduce the issue?

Yes. The exact PR head provides deterministic source-level reproduction paths for the remaining migration, navigation, cache, key-parsing, and asynchronous draft-state defects, although this read-only review did not execute the UI.

Is this the best way to solve the issue?

No. The feature direction is valuable and supported by the merged foundation, but the current patch is not the best implementation until browse/selection ownership, catalog integrity, and execution-target semantics are corrected.

Full review comments:

  • [P1] Merge legacy groups before deleting browser state — ui/src/lib/sessions/index.ts:839-848
    When the gateway already contains any group, this skips uploading the browser's legacy groups but still removes the local catalog. Merge both ordered catalogs and only clear local storage after the merged gateway write succeeds, or upgrades lose local-only empty groups and ordering.
    Confidence: 0.99
  • [P1] Load the canonical catalog before replace-all writes — ui/src/components/app-sidebar.ts:704-708
    Creating a group can call sessions.groups.put while groupsLoad() is still pending or failed, using only the current session window plus an empty catalog. Because put replaces the entire catalog, wait for or reload the canonical list before issuing this write.
    Confidence: 0.98
  • [P1] Keep agent expansion out of shared selection — ui/src/components/app-sidebar.ts:499-504
    Expanding a section is documented as browse-only, but this mutates agentSelection, which disambiguates unqualified global/main sessions and other agent-scoped actions. Track the expanded section locally and update shared selection only when a session is actually selected.
    Confidence: 0.99
  • [P1] Reject node execution with gateway worktrees — ui/src/components/new-session-dialog.ts:248-254
    The dialog can submit execNode together with gateway-provisioned worktree and cwd fields. Node commands do not automatically enter that gateway-host checkout, so the UI reports a branch-backed Work session while execution occurs elsewhere; disable this combination or provision the checkout on the node.
    Confidence: 0.98
  • [P2] Use the configured default agent for cached rows — ui/src/components/app-sidebar.ts:1612-1615
    Legacy or unprefixed rows are assigned using defaultAgentId, but this passes the currently selected agent instead of the configured default. Expanding the real default agent while another agent is active can therefore filter out its legacy sessions.
    Confidence: 0.97
  • [P2] Hydrate unread state for collapsed agents — ui/src/components/app-sidebar.ts:1621-1623
    Unread counts use only sessionRowsByAgent, which is populated for loaded or previously expanded agents. A never-expanded collapsed agent therefore shows no unread indicator until opened; hydrate summaries for every agent or derive them from a canonical unscoped result.
    Confidence: 0.97
  • [P2] Gate draft creation during session refreshes — ui/src/components/new-session-dialog.ts:219-223
    The dialog enables submission while the session capability is loading, but SessionCapability.create returns null in that state without sending the RPC. Disable or queue submission during refreshes so users do not receive a spurious create failure.
    Confidence: 0.98
  • [P2] Target the empty fallback at its agent — ui/src/components/app-sidebar.ts:1689-1695
    An empty expanded agent renders the generic chat fallback, whose click navigates without an agent-specific session key and can reopen the previously active agent. Build the fallback link from this section's agent main-session key.
    Confidence: 0.96
  • [P2] Handle every generated DM key shape — ui/src/lib/session-display.ts:114-121
    The fallback parser recognizes only agent:<id>:<channel>:direct:<peer>, but routing also emits per-peer and per-account-channel-peer keys. Unnamed rows using those valid shapes can fall through to the complete raw key; parse the canonical routing shapes and shorten only the peer component.
    Confidence: 0.99
  • [P2] Exclude dashboard direct keys from channel grouping — ui/src/lib/session-display.ts:42-49
    Keys such as agent:<id>:dashboard:direct:<name> match this channel regex and are classified under a synthetic Dashboard channel, despite dashboard sessions being intended for Chats. Explicitly reject dashboard/main key families before transport classification.
    Confidence: 0.98
  • [P2] Preserve the requested agent before list hydration — ui/src/components/new-session-dialog.ts:125-131
    If the dialog opens before agents.ensureList() populates the list, a valid non-main initialAgentId fails membership validation and is replaced with the fallback. Because openedFor prevents a later reset, the draft can create a session for the wrong agent.
    Confidence: 0.98
  • [P2] Cancel stale branch requests on invalid targets — ui/src/components/new-session-dialog.ts:177-183
    Switching to a non-git or empty target returns without incrementing the request token or clearing all branch state. An earlier lookup can later repopulate a base ref for the wrong repository; invalidate the request and clear baseRef and loading state before returning.
    Confidence: 0.97
  • [P2] Do not overwrite user-entered base refs — ui/src/components/new-session-dialog.ts:193-199
    A pending branch lookup unconditionally replaces baseRef when it resolves, even if the user typed a custom ref meanwhile. Apply the discovered default only if the field is still untouched for that request.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.96

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5aea34ad8c21.

Label changes

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦞 diamond lobster and patch quality is 🦪 silver shellfish.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🦪 silver shellfish, so this older rating label is no longer current.

Label justifications:

  • P2: The PR is a substantial operator workflow improvement with multiple bounded correctness blockers but no emergency impact.
  • merge-risk: 🚨 compatibility: Merging can discard existing custom-group catalogs and ordering or unexpectedly change established sidebar navigation behavior.
  • merge-risk: 🚨 session-state: Merging can mis-associate agent selection, unread state, session creation targets, and worktree/node execution metadata.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦞 diamond lobster and patch quality is 🦪 silver shellfish.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (live_output): The PR body includes convincing after-fix scratch-gateway output for the multi-agent sidebar, channel groups, branch picker, worktree creation, and navigation happy path.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes convincing after-fix scratch-gateway output for the multi-agent sidebar, channel groups, branch picker, worktree creation, and navigation happy path.
Evidence reviewed

What I checked:

  • Live PR state: GitHub reports the PR open, non-draft, protected by the maintainer label, and merge-conflicted at head 06c824d against main 5aea34a. (06c824d5c059)
  • Current-main necessity: Current main includes sessions.groups gateway APIs from the merged foundation but does not contain the new-session dialog or agent-section sidebar, so this PR's central UI capability is not implemented on main. (src/gateway/server-methods/sessions.ts:2627, 5aea34ad8c21)
  • Lossy legacy migration: Legacy browser groups are uploaded only when the gateway catalog is empty, but local storage is deleted whenever legacy groups exist; local-only empty groups and ordering are therefore lost when both catalogs are populated. (ui/src/lib/sessions/index.ts:839, 06c824d5c059)
  • Browse state mutates selection: The browse-only expand action writes into shared agentSelection, which resolveSessionNavigation uses to disambiguate global/main sessions. (ui/src/components/app-sidebar.ts:503, 06c824d5c059)
  • Incompatible execution targets: Draft parameter construction sends execNode alongside gateway-provisioned worktree and cwd fields, while node execution does not automatically enter that gateway-host checkout. (ui/src/components/new-session-dialog.ts:253, 06c824d5c059)
  • Generated key contract mismatch: Routing generates per-peer and per-account-channel-peer DM keys that the sidebar fallback parser does not recognize, allowing complete raw keys or identifiers to remain visible. (ui/src/lib/session-display.ts:116, 06c824d5c059)

Likely related people:

  • steipete: Introduced this redesign and authored most recent merged sidebar, grouping, unread, worktree, and session-capability changes on current main. (role: feature owner and recent area contributor; confidence: high; commits: 88f1ec38d4a5, ecc2cffad8aa, deb7faf7b066; files: ui/src/components/app-sidebar.ts, ui/src/lib/sessions/index.ts, ui/src/lib/session-display.ts)
  • Shakker: Introduced the current Control UI architecture and much of the shared sidebar navigation and session-capability structure affected by the patch. (role: architecture introducer and adjacent owner; confidence: high; commits: 65e12328aa20; files: ui/src/components/app-sidebar.ts, ui/src/lib/sessions/index.ts)
  • maweibin: Recently repaired sidebar session preservation across reconnect in the same caching and refresh paths extended by this PR. (role: recent session-state contributor; confidence: medium; commits: 8dd45e864e04; files: ui/src/components/app-sidebar.ts, ui/src/lib/sessions/index.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (4 earlier review cycles)
  • reviewed 2026-07-10T11:10:54.471Z sha d79a2a6 :: needs real behavior proof before merge. :: [P1] Merge legacy groups before deleting browser state | [P1] Load the catalog before issuing replace-all writes | [P2] Use the configured default agent for cached rows | [P2] Exclude dashboard keys from channel grouping | [P2] Hydrate unread state for collapsed agents | [P2] Re-render when the shared group catalog changes
  • reviewed 2026-07-10T15:19:37.761Z sha 8a9b34a :: needs real behavior proof before merge. :: [P1] Merge legacy groups before deleting browser state | [P1] Load the catalog before issuing replace-all writes | [P2] Gate draft creation during session refreshes | [P2] Target the empty agent fallback at that agent | [P2] Hydrate unread state before rendering collapsed agents | [P1] Do not combine node execution with gateway worktrees
  • reviewed 2026-07-10T15:38:23.431Z sha 19a4731 :: found issues before merge. :: [P1] Merge legacy groups before deleting browser state | [P1] Load the catalog before replace-all writes | [P1] Keep agent expansion out of global selection | [P1] Do not combine node execution with gateway worktrees | [P2] Use the configured default agent for cached rows | [P2] Hydrate unread state for collapsed agents
  • reviewed 2026-07-10T15:45:03.989Z sha 19a4731 :: found issues before merge. :: [P1] Merge legacy groups before deleting browser state | [P1] Load the full catalog before replace-all writes | [P1] Keep agent expansion out of global selection | [P1] Do not combine node execution with gateway worktrees | [P2] Use the configured default agent for cached rows | [P2] Hydrate unread state for collapsed agents

Base automatically changed from feat/sessions-worktree-foundation to main July 10, 2026 14:51
@openclaw-barnacle openclaw-barnacle Bot added the agents Agent runtime and tooling label Jul 10, 2026
@steipete
steipete force-pushed the feat/sessions-sidebar-redesign branch from d79a2a6 to 8a9b34a Compare July 10, 2026 15:06
@openclaw-barnacle openclaw-barnacle Bot removed the agents Agent runtime and tooling label Jul 10, 2026
@clawsweeper clawsweeper Bot removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jul 10, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 10, 2026
@steipete
steipete force-pushed the feat/sessions-sidebar-redesign branch from 19a4731 to 1f875b8 Compare July 10, 2026 16:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f875b8585

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +128 to +131
const fallback = this.context?.agents.state.agentsList?.defaultId ?? agents[0]?.id ?? "main";
this.agentId = agents.some((agent) => normalizeAgentId(agent.id) === requested)
? requested
: normalizeAgentId(fallback);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Preserve the requested agent before the list loads

When the dialog opens before agents.ensureList() has populated agentsList, agents is empty, so even a non-main initialAgentId from the expanded/deep-linked agent fails this membership check and falls back to main; because openedFor then suppresses another reset, the draft can submit sessions.create for the wrong agent until the user closes and reopens it. Keep the requested agent id as the fallback when the list is not loaded yet, and only reject it once there is an actual list to validate against.

Useful? React with 👍 / 👎.

Comment on lines +197 to +198
this.branches = result ? { ...result, repoRoot } : null;
this.baseRef = result?.defaultBranch ?? result?.headBranch ?? "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Do not overwrite typed base refs after branch load

If worktrees.branches is still pending after the user enables Worktree and types a custom base ref, the async response unconditionally resets baseRef to the default/head branch. On slow branch lookups this silently changes the checkout that will be sent in worktreeBaseRef; only apply the discovered default while the field is still untouched for that request.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/sessions-sidebar-redesign branch from 487f718 to 9a35da8 Compare July 10, 2026 17:21
steipete added 8 commits July 10, 2026 10:33
…, and draft sessions

- agents become collapsible top-level sidebar sections; expanding an agent
  browses its sessions without navigating, replacing the hidden scope select
- built-in smart groups: one section per channel (rows keep their chat
  titles), a Work section for worktree/exec-node sessions with a
  repo/branch/node subtitle, custom groups, and Chats
- session names never show raw keys or peer ids; DM fallbacks shorten ids,
  dashboard sessions read 'New session', unnamed work sessions read as
  their checkout
- one + opens the new-session draft dialog: agent, exec host (paired
  system.run nodes), folder, worktree toggle with base-branch picker
  (worktrees.branches) and optional name; the first message creates the
  session and starts the run in one sessions.create call
- custom group catalog/order moves to the gateway (sessions.groups.*) with
  a one-time localStorage migration; rename/delete update members
  server-side instead of client-side paging

Part of #103431
Dropping a row dragged out of a non-active agent section now finds the
session in the per-agent row cache instead of only the active scope, so
the category patch is applied instead of silently doing nothing.

Part of #103431
sessions.groups.put/rename/delete now always broadcast a groups-change
event, and the session capability reloads the gateway-owned catalog when
one arrives, so another browser's group create/reorder/rename/delete no
longer leaves this client on a stale snapshot for the rest of the
connection.

Part of #103431
Real locale translations for the new-session dialog and sidebar keys
(the fallback gate requires zero recorded English fallbacks), plus the
regenerated docs map for the new Control UI section.

Part of #103431
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06c824d5c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1738 to +1740
${multiAgent
? agents.map((agent) => this.renderAgentSection(agent, navigationState))
: this.renderSessionListBody(visibleSessions, {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Render the active unconfigured agent session

When the current chat/deep link is for an agent that is not present in agentsList while a multi-agent config exists, resolveSessionNavigation() still adds a fallback active row to visibleSessions, but this branch ignores visibleSessions and renders only agents.map(...). That makes stale/unconfigured-agent chats disappear from the sidebar entirely in multi-agent setups instead of showing the active fallback row the navigation layer prepared.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/sessions-sidebar-redesign branch from 06c824d to 11ea65a Compare July 10, 2026 17:45
@steipete
steipete merged commit fd2a241 into main Jul 10, 2026
98 checks passed
@steipete
steipete deleted the feat/sessions-sidebar-redesign branch July 10, 2026 17:54
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11ea65ad15

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

this.error = context.sessions.state.error ?? t("newSession.createFailed");
return;
}
this.onCreated?.(key);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Surface create-time send failures

When the draft includes an initial message and sessions.create creates the session but the embedded chat.send fails (for example provider/model unavailable), the gateway still returns an ok payload with a key plus runError; this path treats any key as success, closes the dialog, and navigates away without showing that the submitted prompt did not start. Message-backed creates need to inspect the create result/run error (or have SessionCapability.create return null for that case) before calling onCreated.

Useful? React with 👍 / 👎.

Comment on lines +854 to +856
} catch {
// Older gateways without the groups RPC keep observed-category grouping.
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Clear stale groups when group RPCs are unavailable

When a browser has already loaded a group catalog, then reconnects to an older/different Gateway where sessions.groups.list is unavailable or fails, this catch leaves the previous state.groups intact even though the comment says older Gateways should fall back to observed categories. Since the disconnect path preserves groups: state.groups, the sidebar and Sessions page can render an empty catalog from the previous server and later replace the wrong catalog; publish an empty catalog on this failure or reset groups when the client changes.

Useful? React with 👍 / 👎.

@steipete

Copy link
Copy Markdown
Contributor Author

Before/after screenshots for this PR (deterministic mock-gateway harness, identical seeded sessions):

Before — agent dropdown, separate "New session" entry + worktree button, second plus on the group row, one flat "Ungrouped" list:

sidebar before

After — agent sections, single plus on the Sessions header, Slack/Telegram smart groups, Work section with repo ⎇ branch / node subtitles, custom groups:

sidebar after

New Session dialog (agent · where · folder · worktree + base branch · name · first message):

new session dialog

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 11, 2026
…, and draft sessions (openclaw#103498)

* feat(ui): sessions sidebar redesign with agent sections, smart groups, and draft sessions

- agents become collapsible top-level sidebar sections; expanding an agent
  browses its sessions without navigating, replacing the hidden scope select
- built-in smart groups: one section per channel (rows keep their chat
  titles), a Work section for worktree/exec-node sessions with a
  repo/branch/node subtitle, custom groups, and Chats
- session names never show raw keys or peer ids; DM fallbacks shorten ids,
  dashboard sessions read 'New session', unnamed work sessions read as
  their checkout
- one + opens the new-session draft dialog: agent, exec host (paired
  system.run nodes), folder, worktree toggle with base-branch picker
  (worktrees.branches) and optional name; the first message creates the
  session and starts the run in one sessions.create call
- custom group catalog/order moves to the gateway (sessions.groups.*) with
  a one-time localStorage migration; rename/delete update members
  server-side instead of client-side paging

Part of openclaw#103431

* fix(ui): resolve dragged sessions across browsed agent sections

Dropping a row dragged out of a non-active agent section now finds the
session in the per-agent row cache instead of only the active scope, so
the category patch is applied instead of silently doing nothing.

Part of openclaw#103431

* fix(ui): propagate group catalog changes to open clients

sessions.groups.put/rename/delete now always broadcast a groups-change
event, and the session capability reloads the gateway-owned catalog when
one arrives, so another browser's group create/reorder/rename/delete no
longer leaves this client on a stale snapshot for the rest of the
connection.

Part of openclaw#103431

* docs: restore new session dialog section after rebase

* fix(ui): translate new sidebar/session strings and refresh docs map

Real locale translations for the new-session dialog and sidebar keys
(the fallback gate requires zero recorded English fallbacks), plus the
regenerated docs map for the new Control UI section.

Part of openclaw#103431

* fix(ui): repair locale metadata after rebase conflict resolution

* fix(ui): merge mainline locale keys with the sidebar redesign strings

* fix(ui): refresh raw-copy baseline for mainline tool-card strings

* fix(ui): reconcile generated locale artifacts after rebase
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant