You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Multiplexed Discord profiles now start each routed session in that profile's configured terminal.cwd instead of inheriting the gateway launch profile's directory. This prevents terminal and file operations from running against another profile's workspace while preserving the session's later cd changes and existing conversation identity.
Symptom
When one gateway multiplexes profiles with different terminal.cwd values, a correctly routed profile can report and use the launch profile's working directory. The same profile behaves correctly when used outside the multiplexed gateway path.
Impact
Affected multiplexed sessions can run terminal commands and resolve relative file paths in another profile's workspace. Profile routing, credentials, skills, memory, and transcript isolation remain correct, but workspace isolation is lost.
Bug Cause
Trigger:gateway/run.py / GatewayRunner._set_session_env() when gateway.multiplex_profiles routes a message to a secondary profile.
Causal chain:
The gateway resolves the incoming Discord message to the correct profile and enters that profile's runtime scope.
_set_session_env() binds the routed profile and session key but does not bind the profile's configured working directory.
Runtime cwd resolution falls back to the process-wide TERMINAL_CWD set by the launch profile, so terminal and relative file operations use the wrong workspace.
Why it is wrong: A process-global cwd fallback cannot represent multiple concurrently routed profiles. Mutating it per message would also introduce a cross-session race.
Working sibling / contrast: Single-profile and TUI launches load their own profile configuration at process startup, so their process-level fallback already points at the intended workspace.
Ruled out: Session-key routing was not the cause. The multiplexed path already selected the correct profile, credentials, skills, memory, and transcript; exact-base verification reproduced the cwd mismatch after routing succeeded.
Fix
The gateway now resolves the routed profile's explicit terminal.cwd, seeds the existing per-session cwd record only when that session has no live cwd, and binds it through the existing cwd ContextVar. Concurrent profiles remain isolated, while a later session-local cd stays authoritative on subsequent turns. Placeholder and absent cwd values retain the legacy fallback behavior.
gateway/run.py - resolve routed profile cwd and bind it into multiplexed session context without mutating process-global state.
tests/gateway/test_multiplex_profile_cwd.py - cover concurrent profile isolation, runtime/file cwd consistency, and later cd persistence.
How to Test
Configure two multiplexed profiles with distinct terminal.cwd directories and route a Discord session to each profile.
Confirm each session's runtime cwd and relative file resolution stay in its configured directory, then cd one session and confirm the new directory persists without affecting the other profile.
The related suite passed locally: 22 tests passed. Exact-base REAL_ENV comparison reproduced the original cross-profile cwd inheritance; the committed tip isolated concurrent routed profile terminal/runtime/file paths and preserved later cd state.
AI code review — automated review for reference, author can ignore or act on any point.
fix(gateway): keep multiplexed profiles in their own workspaces
Seeded cwd is not validated to exist — _profile_terminal_cwd (gateway/run.py:9-47) resolves and returns terminal.cwd from a profile's config without checking that the directory actually exists, and _set_session_env then record_session_cwds it. If a profile's config points at a deleted/moved path, the session is seeded with a dead cwd and file tools resolve against it with no fallback to the process-level TERMINAL_CWD. Suggest Path(raw).is_dir() check → None so the existing fallback stays intact.
Non-multiplexed path passes cwd="" — when multiplex_profiles is false, _session_cwd stays "" and is passed to set_session_vars. Worth verifying set_session_vars treats an empty cwd as "no override" (not an actual chdir target), since other callers of the gateway path now hit this argument too.
Test couples to a private module global — the regression test monkeypatches terminal_tool._session_cwd directly (tests/gateway/test_multiplex_profile_cwd.py:143). Acceptable for a regression test, but if the cwd store ever moves to a per-session structure the test silently stops exercising the real path.
Thanks for this PR. Merged via #101242 (4a7f228) on current main — routed multiplex profiles get their own terminal cwd/backend/docker config; container boot honors config multiplex_profiles.
#101242 won as the consolidated fix because it covers the whole multiplex-profile bug class in one change (with tests) rather than the single symptom addressed here; this PR is superseded by it.
Your PR is referenced in #101242's body as prior work on this bug.
If anything from your original change is still missing on main >= 4a7f228, please open a fresh PR/issue against main and tag it. Thanks again.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
area/configConfig system, migrations, profilesarea/profilesMulti-profile isolation, HERMES_HOME scopingarea/sessionsSession lifecycle, resume, persistence, historycomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointcomp/gatewayGateway runner, session dispatch, deliveryP2Medium — degraded but workaround existssweeper:risk-compatibilitySweeper risk: may break existing users, config, migrations, defaults, or upgradessweeper:risk-message-deliverySweeper risk: may drop, duplicate, misroute, or suppress messagessweeper:risk-session-stateSweeper risk: may lose/corrupt/mis-associate session or context statetool/terminalTerminal execution and process managementtype/bugSomething isn't working
4 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Multiplexed Discord profiles now start each routed session in that profile's configured
terminal.cwdinstead of inheriting the gateway launch profile's directory. This prevents terminal and file operations from running against another profile's workspace while preserving the session's latercdchanges and existing conversation identity.Symptom
When one gateway multiplexes profiles with different
terminal.cwdvalues, a correctly routed profile can report and use the launch profile's working directory. The same profile behaves correctly when used outside the multiplexed gateway path.Impact
Affected multiplexed sessions can run terminal commands and resolve relative file paths in another profile's workspace. Profile routing, credentials, skills, memory, and transcript isolation remain correct, but workspace isolation is lost.
Bug Cause
Trigger:
gateway/run.py/GatewayRunner._set_session_env()whengateway.multiplex_profilesroutes a message to a secondary profile.Causal chain:
_set_session_env()binds the routed profile and session key but does not bind the profile's configured working directory.TERMINAL_CWDset by the launch profile, so terminal and relative file operations use the wrong workspace.Why it is wrong: A process-global cwd fallback cannot represent multiple concurrently routed profiles. Mutating it per message would also introduce a cross-session race.
Working sibling / contrast: Single-profile and TUI launches load their own profile configuration at process startup, so their process-level fallback already points at the intended workspace.
Ruled out: Session-key routing was not the cause. The multiplexed path already selected the correct profile, credentials, skills, memory, and transcript; exact-base verification reproduced the cwd mismatch after routing succeeded.
Fix
The gateway now resolves the routed profile's explicit
terminal.cwd, seeds the existing per-session cwd record only when that session has no live cwd, and binds it through the existing cwd ContextVar. Concurrent profiles remain isolated, while a later session-localcdstays authoritative on subsequent turns. Placeholder and absent cwd values retain the legacy fallback behavior.Related Issue
Closes #84517
Type of Change
Changes Made
gateway/run.py- resolve routed profile cwd and bind it into multiplexed session context without mutating process-global state.tests/gateway/test_multiplex_profile_cwd.py- cover concurrent profile isolation, runtime/file cwd consistency, and latercdpersistence.How to Test
terminal.cwddirectories and route a Discord session to each profile.cdone session and confirm the new directory persists without affecting the other profile.The related suite passed locally: 22 tests passed. Exact-base REAL_ENV comparison reproduced the original cross-profile cwd inheritance; the committed tip isolated concurrent routed profile terminal/runtime/file paths and preserved later
cdstate.Checklist
Code
fix(scope):,feat(scope):, etc.)Documentation & Housekeeping
cli-config.yaml.exampleupdate: N/A - no configuration keys changedCONTRIBUTING.mdorAGENTS.mdupdate: N/A - no architecture or workflow contract changedScreenshots / Logs
N/A - the regression is covered by exact-base runtime verification and automated tests.