# NousResearch/hermes-agent · File browser sidebar auto-opens unexpectedly on desktop

## Open now

- Nothing open on GitHub.

Multiple reports and related fixes address the desktop file browser/sidebar panel automatically reopening on new session creation or first reply, ignoring user-collapsed state.

## TL;DR

- 9 member(s): 0 open issue(s), 5 closed issue(s), 0 open PR(s), 2 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #65375. Verify it addresses your case.
- **Recommended:** read PR #65375 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-65375,pr-122369`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.25`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #65173: File browser panel reopens on session start despite persisted closed state; no settings toggle for default
- #65376: Right sidebar (file browser) re-opens after Cmd+J collapse when a new session is created
- #66059: \[Bug\]: The file tree keeps opening automatically.
- #66917: \[desktop\] File browser sidebar auto-opens on every new session when no project is active
- #67286: \[Bug\]: desktop files panel auto-opens after first reply in a new session

## Prior & competing PRs

- **#65375: fix(desktop): don't auto-expand user-collapsed side on reactive unhide**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `apps/desktop/src/components/pane-shell/tree/reactive-unhide.test.ts`, `apps/desktop/src/components/pane-shell/tree/store.ts`, `apps/desktop/src/store/review.ts`
  - fix hunks:
  - `apps/desktop/src/components/pane-shell/tree/reactive-unhide.test.ts`

    ```diff
    @@ -0,0 +1,147 @@
    +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
    +
    +// Ground-truth repro for "reactive unhides silently re-open a user-collapsed
    +// side": `setTreePaneHidden` is the primitive that backs `bindPaneVisibility`
    +// (e.g. `bindPaneVisibility('files', $hasWorkspace)`). The original
    +// implementation auto-called `revealTreePane` on every unhide, which expanded
    +// the parent column even when the user had explicitly collapsed it — so a
    +// workspace transition (`$currentCwd` flipping on session create / resume)
    +// forced the right sidebar open and persisted that open state to localStorage.
    +//
    +// We mirror controller.tsx exactly: register the `files` pane with
    +// `placement: 'right'`, declare the default tree, bind the right side to
    +// the file-browser open store, then collapse the right sidebar and trigger
    +// the reactive unhide via the same primitive `bindPaneVisibility` invokes.
    +
    +describe('reactive pane unhide', () => {
    +  beforeEach(() => {
    +    window.localStorage.clear()
    +    vi.resetModules()
    +  })
    +
    +  afterEach(() => {
    +    vi.resetModules()
    +  })
    +
    +  async function setupWithFiles() {
    +    const tree = await import('@/components/pane-shell/tree/store')
    +    const layout = await import('@/store/layout')
    +    const model = await import('@/components/pane-shell/tree/model')
    +    const { registry } = await import('@/contrib/registry')
    +
    +    // Register `files` like controller.tsx does — placement 'right' is what
    +    // makes `treeSideOfPane('files')` return 'right' (and therefore what
    +    // makes the buggy `revealTreePane` auto-expand the right column).
    +    registry.register({
    +      id: 'files',
    +      area: 'panes',
    +      title: 'files',
    +      data: { placement: 'right' },
    +      render: () => null
    +    })
    +
    +    // Declare a minimal default tree mirroring the production DEFAULT_TREE's
    +    // row shape (sessions | workspace | right-column-with-files).
    +    tree.declareDefaultTree(
    +      model.split('row', [
    +        model.group(['sessions'], { id: 'grp-sessions' }),
    +        model.group(['workspace'], { id: 'grp-main' }),
    +        model.split('column', [
    +          model.split('row', [
    +            model.group(['files'], { id: 'grp-files' })
    +          ], [1], 'spl-rail'),
    +          model.group(['terminal'], { id: 'grp-terminal' })
    +        ], [1.6, 1], 'spl-right')
    +      ], [0.85, 3, 1.6])
    +    )
    +
    +    // Mirror controller.tsx:512.
    +    tree.bindTreeSideVisibility('right', layout.$fileBrowserOpen, layout.setFileBrowserOpen)
    +
    +    return { tree, layout }
    +  }
    +
    +  it('reactive unhide does NOT expand a user-collapsed side', async () => {
    +    const { tree, layout } = await setupWithFiles()
    +
    +    // Simulate the production scenario: a detached chat has no cwd, so
    +    // `bindPaneVisibility('files', $hasWorkspace)` calls `setTreePaneHidden(
    +    // 'files', true)` (hidden). Then the user collapses the right sidebar.
    +    // Then a new session acquires a cwd and the workspace wiring unhides
    +    // `files`. That last step is what the bug corrupts.
    +    tree.setTreePaneHidden('files', true)
    +    expect(tree.$hiddenTreePanes.get().has('files')).toBe(true)
    +
    +    layout.setFileBrowserOpen(false)
    +    expect(layout.$fileBrowserOpen.get()).toBe(false)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(true)
    +
    +    // Workspace flips: `bindPaneVisibility('files', $hasWorkspace)` calls
    +    // `setTreePaneHidden('files', false)` — model it directly.
    +    tree.setTreePaneHidden('files', false)
    +
    +    // The pane unhides…
    +    expect(tree.$hiddenTreePanes.get().has('files')).toBe(false)
    +
    +    // …but the user-collapsed side MUST stay collapsed. Before the fix, the
    +    // unhide called `revealTreePane`, which saw the side was collapsed and
    +    // called `setFileBrowserOpen(true)` — silently re-opening the right
    +    // sidebar on every session create / resume.
    +    expect(layout.$fileBrowserOpen.get()).toBe(false)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(true)
    +  })
    +
    +  it('reactive HIDE (hidden=true) leaves the side flag alone', async () => {
    +    const { tree, layout } = await setupWithFiles()
    +
    +    layout.setFileBrowserOpen(false)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(true)
    +
    +    // A reactive HIDE (workspace goes away) must not flip the side either —
    +    // collapsing the side is the user's domain; hiding individual panes
    +    // inside it is the workspace domain.
    +    tree.setTreePaneHidden('files', true)
    +
    +    expect(tree.$hiddenTreePanes.get().has('files')).toBe(true)
    +    expect(layout.$fileBrowserOpen.get()).toBe(false)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(true)
    +  })
    +
    +  it('explicit setFileBrowserOpen still expands the side (user toggle)', async () => {
    +    const { tree, layout } = await setupWithFiles()
    +
    +    layout.setFileBrowserOpen(false)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(true)
    +
    +    // The user toggles the sidebar back open — this MUST still work; the
    +    // binding mirrors `$fileBrowserOpen` to `$collapsedTreeSides`.
    +    layout.setFileBrowserOpen(true)
    +    expect(layout.$fileBrowserOpen.get()).toBe(true)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(false)
    +  })
    +
    +  it('reactive unhide does not invoke the right side opener directly', async () => {
    +    const { tree, layout } = await setupWithFiles()
    +
    +    // Spy on the opener that `revealTreePane` would call when expanding a
    +    // collapsed side — the bug is exactly this call firing on reactive unhide.
    +    const openerSpy = vi.fn()
    +    tree.bindTreeSideVisibility('right', layout.$fileBrowserOpen, openerSpy)
    +
    +    layout.setFileBrowserOpen(false)
    +    expect(tree.$collapsedTreeSides.get().has('right')).toBe(true)
    +
    +    tree.setTreePaneHidden('files', true)
    +    expect(tree.$hiddenTreePanes.get().has('files')).toBe(true)
    +
    +    openerSpy.mockClear()
    +
    +    // Reactive unhide fires via the same primitive the workspace wiring uses.
    +    tree.setTreePaneHidden('files', false)
    +
    +    // The opener MUST NOT be called — before the fix, the auto-reveal would
    +    // call `setFileBrowserOpen(true)` via this opener.
    +    expect(tree.$hiddenTreePanes.get().has('files')).toBe(false)
    +    expect(openerSpy).not.toHaveBeenCalled()
    +  })
    +})
    ```
  - `apps/desktop/src/components/pane-shell/tree/store.ts`

    ```diff
    @@ -126,9 +126,33 @@ export function setTreePaneHidden(paneId: string, hidden: boolean) {
     
       $hiddenTreePanes.set(next)
     
    -  // Unhiding is an intent to SEE the pane — front it in its group.
    +  // Reactive unhides (e.g. `bindPaneVisibility('files', $hasWorkspace)`) are
    +  // state-driven, not user intent — opening the side or fronting the tab in
    +  // response to an environmental flag change would clobber an explicit user
    +  // collapse (Cmd+J) and silently re-open the rail after every session create.
    +  // Callers that want user-intent semantics (open the side, front the tab)
    +  // must call `revealTreePane` explicitly. We still front the pane in its
    +  // group so it's visible the next time the column is shown.
       if (!hidden) {
    -    revealTreePane(paneId)
    +    frontPaneInGroup(paneId)
    +  }
    +}
    +
    +/** Make `paneId` the active tab in its group without touching side collapse
    + *  or zone-minimized state — the safe "make it visible next time the column
    + *  is shown" primitive that reactive unhides need. */
    +function frontPaneInGroup(paneId: string) {
    +  const tree = $layoutTree.get()
    +  const group = tree ? findGroupOfPane(tree, paneId) : null
    +
    +  if (!tree || !group || group.active === paneId) {
    +    return
    +  }
    +
    +  const next = setActivePaneOp(tree, group.id, paneId)
    +
    +  if (next !== tree) {
    +    commit(next)
       }
     }
     
    ```
  - `apps/desktop/src/store/review.ts`

    ```diff
    @@ -2,6 +2,7 @@ import { atom, computed } from 'nanostores'
     
     import { SIDEBAR_COLLAPSE_MEDIA_QUERY } from '@/app/layout-constants'
     import { PANE_TOGGLE_REVEAL_EVENT } from '@/components/pane-shell'
    +import { revealTreePane } from '@/components/pane-shell/tree/store'
     import type { HermesReviewFile, HermesReviewShipInfo } from '@/global'
     import { matchesQuery } from '@/hooks/use-media-query'
     import { desktopGit } from '@/lib/desktop-git'
    ```

    ```diff
    @@ -274,6 +275,7 @@ export function toggleReview(): void {
         closeReview()
       } else {
         openReview()
    +    revealTreePane(REVIEW_PANE_ID)
       }
     }
     
    ```
- **#122369: fix(desktop): add a File Browser toggle to Appearance settings**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `apps/desktop/src/app/settings/appearance-settings.tsx`, `apps/desktop/src/app/settings/file-browser-setting.test.tsx`, `apps/desktop/src/app/settings/settings-manifest.ts`, `apps/desktop/src/i18n/ar.ts`, `apps/desktop/src/i18n/de.ts`, `apps/desktop/src/i18n/en.ts`, `apps/desktop/src/i18n/es.ts`, `apps/desktop/src/i18n/fr.ts`, `apps/desktop/src/i18n/ja.ts`, `apps/desktop/src/i18n/ru.ts`, `apps/desktop/src/i18n/types.ts`, `apps/desktop/src/i18n/zh-hant.ts`, `apps/desktop/src/i18n/zh.ts`
- **#66079: fix(desktop): stop auto-opening file tree on new session creation (#66059)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#65375`, `#65375`
- **#67288: fix(desktop): stop files panel auto-opening on first reply**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#65375`, `#65375`, `#65375`, `#67286`, `#65375`

## Fix status

- Merged to main in PR #65375 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-09-26`.
- Subsystem: `apps/desktop`
