Skip to content

fix(tui_gateway): honor target profile's terminal.cwd on desktop profile switch#40892

Merged
OutThisLife merged 2 commits into
NousResearch:mainfrom
xxxigm:fix/desktop-profile-terminal-cwd
Jun 10, 2026
Merged

fix(tui_gateway): honor target profile's terminal.cwd on desktop profile switch#40892
OutThisLife merged 2 commits into
NousResearch:mainfrom
xxxigm:fix/desktop-profile-terminal-cwd

Conversation

@xxxigm

@xxxigm xxxigm commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a profile-isolation bug in the desktop app: after switching profiles, a newly-started session inherited the previous profile's working directory instead of the directory configured in the profile you switched to.

The desktop's app-global remote mode serves every local profile from one tui_gateway backend, so the process-global TERMINAL_CWD env var only ever reflects the launch profile. When a new session was bound to another profile, _completion_cwd() resolved its workspace straight from that stale env var and never consulted the target profile's own config.yaml.

This PR teaches the session-cwd resolver to read the bound profile's configured terminal.cwd first:

  • Adds _profile_configured_cwd(profile_home) — reads terminal.cwd from a non-launch profile's config.yaml, skipping placeholder (./auto/cwd), empty, missing, and non-existent-directory values so callers fall back cleanly (mirrors the placeholder handling in gateway/run.py's config bridge).
  • Wires it into _completion_cwd() with the correct precedence: explicit client cwd → existing session cwdbound profile's configured cwdTERMINAL_CWD env → os.getcwd().

When no profile is passed (single-profile / launch-profile sessions) _profile_home() returns None and behavior is unchanged.

Related Issue

Fixes #40334

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • tui_gateway/server.py:
    • Added _profile_configured_cwd() + a shared _CWD_PLACEHOLDERS constant (+34 lines).
    • _completion_cwd() now prefers the bound profile's configured terminal.cwd over the launch profile's TERMINAL_CWD (+6/-2 lines).
  • tests/test_tui_gateway_server.py — added 4 tests (+58 lines):
    • test_profile_configured_cwd_reads_target_profile — reads terminal.cwd from a profile's own config.yaml.
    • test_profile_configured_cwd_skips_placeholders_and_missingNone, missing dir, ./auto/cwd/empty placeholders, and non-existent paths all fall through.
    • test_completion_cwd_prefers_profile_over_stale_env — the Desktop: profile switch ignores terminal.cwd, uses wrong profile's working directory #40334 regression: a profile-bound session uses its own cwd, not the stale launch TERMINAL_CWD; no-profile still falls back to the env var.
    • test_completion_cwd_explicit_cwd_wins_over_profile — an explicit client cwd still wins.

How to Test

  1. Check out this branch and activate the venv: source .venv/bin/activate (or venv).
  2. Run the new tests:
    scripts/run_tests.sh tests/test_tui_gateway_server.py
    
    Expected: the four new *_cwd tests pass alongside the existing suite.
  3. Manual repro (desktop, app-global remote mode):
    • Two profiles A/B with different terminal.cwd.
    • Use A, switch to B, start a new session (/reset), ask the agent to pwd.
    • Before: B's terminal opens in A's directory. After: B opens in its own configured terminal.cwd.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(tui_gateway): ... / test(tui_gateway): ...)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix (no unrelated commits)
  • I've run scripts/run_tests.sh tests/test_tui_gateway_server.py and the new tests pass
  • I've added tests for my changes
  • I've tested on my platform: macOS (Darwin 24.6.0), Python 3.12

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A (internal resolver, no user-facing surface change)
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A (no new config keys)
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — issue is Windows-reported; the fix is pure path resolution and tests are hermetic (tmp_path + monkeypatch)
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

$ python -m pytest tests/test_tui_gateway_server.py -k "profile_configured_cwd or completion_cwd" -q
....                                                                     [100%]
4 passed, 205 deselected in 0.25s

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/gateway Gateway runner, session dispatch, delivery area/config Config system, migrations, profiles labels Jun 7, 2026
xxxigm added 2 commits June 9, 2026 06:46
…ile switch

The desktop's app-global remote mode serves every profile from one
tui_gateway backend, so the process-global TERMINAL_CWD only reflects the
launch profile. After switching profiles, a new session resolved its
workspace from that stale env var and inherited the previous profile's
directory.

Add _profile_configured_cwd() to read a non-launch profile's own
terminal.cwd from its config.yaml (skipping placeholder/empty/missing and
non-existent paths so callers fall back cleanly), and wire it into
_completion_cwd() with precedence: explicit client cwd -> existing session
cwd -> bound profile's configured cwd -> TERMINAL_CWD -> os.getcwd().

Fixes NousResearch#40334
Pin the new contract: _profile_configured_cwd reads a profile's own
terminal.cwd and rejects placeholders/missing paths, and _completion_cwd
prefers a bound profile's cwd over a stale launch-profile TERMINAL_CWD
while still letting an explicit client cwd win.
@xxxigm xxxigm force-pushed the fix/desktop-profile-terminal-cwd branch from 7dc9617 to fc69e57 Compare June 8, 2026 23:46
@szqjl

szqjl commented Jun 9, 2026

Copy link
Copy Markdown

感谢该 PR 解决了网关端读取 Profile 自身 的问题。从文档(profile-switch-terminal-cwd-fix.md)中可以看到,Hermes 原本的设计意图是 Profile 与 Workspace 正交(模式 B),即 Profile 决定 AI/工具,Workspace 决定当前工作目录。用户实际上也存在一种 “Profile‑Project 一对一” 的使用习惯(模式 A),即切 Profile 期望自动跳到该 Profile 配置的 。当前补丁在网关端实现了 profile‑first 的优先级,能够完美修复模式 A 下的 bug。但正如文档所列的对比表所示,这会在模式 B 下引入新问题:用户在侧边栏工作区列表点击 “+” 时,所选的 Workspace 会被 Profile 默认目录覆盖。为了同时兼容两种使用模式,文档提出了一种 “温和版 scheme D + explicit_cwd 标记” 的完整方案:1. Desktop 端在显式选择 Workspace 时设置 = true,切 Profile 时复位;2. 创建会话时把该标记随 RPC 发送;3. 网关端在收到 profile 参数时,只有当 explicit_cwd 为 false 时才使用 Profile 自身的 terminal.cwd;否则尊重 params.cwd。这样既保证了模式 A 的自动跳转,又不破坏模式 B 的灵活性。请问是否可以在后续的迭代中考虑加入这一标记机制?如果目前的补丁已经足够满足当前的紧急需求,我们也可以先合并此 PR,并在后续的 Issue 中跟进完整的正交方案。期待维护者的意见,并愿意为实现该标记提供代码或测试上的帮助。

@szqjl

szqjl commented Jun 9, 2026

Copy link
Copy Markdown

感谢该 PR 解决了网关端读取 Profile 自身 terminal.cwd 的问题。我们理解Hermes 原本的设计意图是 Profile 与 Workspace 正交(模式 B),即 Profile 决定 AI/工具,Workspace 决定当前工作目录。用户实际上也存在一种 “Profile‑Project 一对一” 的使用习惯(模式 A),即切 Profile 期望自动跳到该 Profile 配置的 terminal.cwd。当前补丁在网关端实现了 profile‑first 的优先级,能够完美修复模式 A 下的 bug。这会在模式 B 下引入新问题:当用户在侧边栏工作区列表点击 “+” 显式选择 Workspace 时,该选择会被 Profile 默认的 terminal.cwd 覆盖,导致工作区被强制切回 Profile 配置的目录。为了同时兼容两种使用模式,我们提出了一种 “温和版 scheme D + explicit_cwd 标记” 的完整方案:1. Desktop 端在显式选择 Workspace 时设置 $explicitCwd = true,切 Profile 时复位;2. 创建会话时把该标记随 RPC 发送;3. 网关端在收到 profile 参数时,只有当 explicit_cwd 为 false 时才使用 Profile 自身的 terminal.cwd;否则尊重 params.cwd。这样既保证了模式 A 的自动跳转,又不破坏模式 B 的灵活性。请问是否可以在后续的迭代中考虑加入这一标记机制?我们愿意在后续的 Issue 中跟进完整的正交方案。并也可为实现该标记提供代码或测试上的协助。

@OutThisLife OutThisLife left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving. I traced the resolution path rather than going off the description:

  • _completion_cwd is the session cwd seed, not just autocomplete: session.create stores _completion_cwd(params) as the session's cwd and passes profile in params, so this lands on the exact path #40334 exercises (/reset / new session under a switched-to profile).
  • Precedence reads correctly: explicit client cwd > existing session cwd > bound profile config > TERMINAL_CWD > os.getcwd(). The bound profile's explicit config should beat the launch profile's stale env var, and it does.
  • No-profile / launch sessions: _profile_home(None) returns None, the resolver returns None, and it falls through to TERMINAL_CWD unchanged. No regression for single-profile use.
  • _CWD_PLACEHOLDERS matches gateway/run.py exactly, and empty/missing/non-existent dirs fall back cleanly.
  • The 4 hermetic tests cover the #40334 regression and the explicit-cwd-wins case. CI is green and the branch is clean against main.

LGTM.

@OutThisLife OutThisLife merged commit 93340fa into NousResearch:main Jun 10, 2026
23 checks passed
wachoo pushed a commit to wachoo/hermes-agent that referenced this pull request Jun 10, 2026
…ile switch (NousResearch#40892)

* fix(tui_gateway): honor target profile's terminal.cwd on desktop profile switch

The desktop's app-global remote mode serves every profile from one
tui_gateway backend, so the process-global TERMINAL_CWD only reflects the
launch profile. After switching profiles, a new session resolved its
workspace from that stale env var and inherited the previous profile's
directory.

Add _profile_configured_cwd() to read a non-launch profile's own
terminal.cwd from its config.yaml (skipping placeholder/empty/missing and
non-existent paths so callers fall back cleanly), and wire it into
_completion_cwd() with precedence: explicit client cwd -> existing session
cwd -> bound profile's configured cwd -> TERMINAL_CWD -> os.getcwd().

Fixes NousResearch#40334

* test(tui_gateway): cover per-profile cwd resolution (NousResearch#40334)

Pin the new contract: _profile_configured_cwd reads a profile's own
terminal.cwd and rejects placeholders/missing paths, and _completion_cwd
prefers a bound profile's cwd over a stale launch-profile TERMINAL_CWD
while still letting an explicit client cwd win.
@szqjl

szqjl commented Jun 10, 2026 via email

Copy link
Copy Markdown

changman pushed a commit to changman/hermes-agent that referenced this pull request Jun 10, 2026
…ile switch (NousResearch#40892)

* fix(tui_gateway): honor target profile's terminal.cwd on desktop profile switch

The desktop's app-global remote mode serves every profile from one
tui_gateway backend, so the process-global TERMINAL_CWD only reflects the
launch profile. After switching profiles, a new session resolved its
workspace from that stale env var and inherited the previous profile's
directory.

Add _profile_configured_cwd() to read a non-launch profile's own
terminal.cwd from its config.yaml (skipping placeholder/empty/missing and
non-existent paths so callers fall back cleanly), and wire it into
_completion_cwd() with precedence: explicit client cwd -> existing session
cwd -> bound profile's configured cwd -> TERMINAL_CWD -> os.getcwd().

Fixes NousResearch#40334

* test(tui_gateway): cover per-profile cwd resolution (NousResearch#40334)

Pin the new contract: _profile_configured_cwd reads a profile's own
terminal.cwd and rejects placeholders/missing paths, and _completion_cwd
prefers a bound profile's cwd over a stale launch-profile TERMINAL_CWD
while still letting an explicit client cwd win.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop: profile switch ignores terminal.cwd, uses wrong profile's working directory

4 participants