From 65bffd71ffdcdec154577a166536af0feb3db34d Mon Sep 17 00:00:00 2001 From: piyush0049 Date: Sat, 11 Jul 2026 17:47:17 +0530 Subject: [PATCH] refactor(server): decouple WorkingDir from RuntimeConfig in SessionManager Eliminates the hacky \.Clone()\ of the global CLI \RuntimeConfig\ inside the \SessionManager\'s per-session path. Instead of overriding the config's \WorkingDir\ and passing the mutated copy down to the team loader, the session's working directory is now passed cleanly via a new \ eamloader.WithWorkingDir()\ functional option. This treats the \RuntimeConfig\ as a proper read-only global context and resolves a lingering TODO left by maintainers regarding configuration leak. --- pkg/server/session_manager.go | 14 +++++--------- pkg/teamloader/teamloader.go | 13 +++++++++++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/pkg/server/session_manager.go b/pkg/server/session_manager.go index d6d77c05e..325ffe2f0 100644 --- a/pkg/server/session_manager.go +++ b/pkg/server/session_manager.go @@ -50,8 +50,6 @@ type SessionManager struct { sessionStore session.Store Sources config.Sources - // TODO: We have to do something about this, it's weird, session creation should send everything that is needed. - // This is only used for the working directory... runConfig *config.RuntimeConfig refreshInterval time.Duration @@ -614,16 +612,13 @@ func (sm *SessionManager) RunSession(ctx context.Context, sessionID, agentFilena return nil, err } - rc := sm.runConfig.Clone() - rc.WorkingDir = sess.WorkingDir - runtimeSession, exists := sm.runtimeSessions.Load(sessionID) streamCtx, cancel := context.WithCancel(ctx) var titleGen *sessiontitle.Generator if !exists { var rt runtime.Runtime - rt, titleGen, err = sm.runtimeForSession(ctx, sess, agentFilename, currentAgent, rc) + rt, titleGen, err = sm.runtimeForSession(ctx, sess, agentFilename, currentAgent, sm.runConfig) if err != nil { cancel() return nil, err @@ -1036,7 +1031,7 @@ func (sm *SessionManager) runtimeForSession(ctx context.Context, sess *session.S span.End() }() - loadResult, err := sm.loadTeamWithConfig(ctx, agentFilename, rc) + loadResult, err := sm.loadTeamWithConfig(ctx, agentFilename, rc, teamloader.WithWorkingDir(sess.WorkingDir)) if err != nil { return nil, nil, err } @@ -1112,13 +1107,14 @@ func (sm *SessionManager) loadTeam(ctx context.Context, agentFilename string, ru // loadTeamWithConfig is like loadTeam but also returns the loaded model and // provider configuration so the runtime can be wired for model switching. -func (sm *SessionManager) loadTeamWithConfig(ctx context.Context, agentFilename string, runConfig *config.RuntimeConfig) (*teamloader.LoadResult, error) { +func (sm *SessionManager) loadTeamWithConfig(ctx context.Context, agentFilename string, runConfig *config.RuntimeConfig, opts ...teamloader.Opt) (*teamloader.LoadResult, error) { agentSource, err := sm.resolveSource(agentFilename) if err != nil { return nil, err } - return teamloader.LoadWithConfig(ctx, agentSource, runConfig, loaderdefaults.Opts()...) + allOpts := append(loaderdefaults.Opts(), opts...) + return teamloader.LoadWithConfig(ctx, agentSource, runConfig, allOpts...) } // resolveSource looks up the agent source for agentFilename. diff --git a/pkg/teamloader/teamloader.go b/pkg/teamloader/teamloader.go index b1240f461..1387c3b85 100644 --- a/pkg/teamloader/teamloader.go +++ b/pkg/teamloader/teamloader.go @@ -42,6 +42,7 @@ import ( var defaultMaxTokens int64 = 32000 type loadOptions struct { + workingDir string modelOverrides []string promptFiles []string toolsetRegistry ToolsetRegistry @@ -51,6 +52,13 @@ type loadOptions struct { type Opt func(*loadOptions) error +func WithWorkingDir(dir string) Opt { + return func(opts *loadOptions) error { + opts.workingDir = dir + return nil + } +} + func WithModelOverrides(overrides []string) Opt { return func(opts *loadOptions) error { opts.modelOverrides = overrides @@ -226,7 +234,8 @@ func LoadWithConfig(ctx context.Context, agentSource config.Source, runConfig *c runConfig.ProviderRegistry = loadOpts.providerRegistry // Load agents - parentDir := cmp.Or(agentSource.ParentDir(), runConfig.WorkingDir) + workingDir := cmp.Or(loadOpts.workingDir, runConfig.WorkingDir) + parentDir := cmp.Or(agentSource.ParentDir(), workingDir) configName := configNameFromSource(agentSource.Name()) var agents []*agent.Agent agentsByName := make(map[string]*agent.Agent) @@ -358,7 +367,7 @@ func LoadWithConfig(ctx context.Context, agentSource config.Source, runConfig *c // always exposed and never subject to the include filter. loadedSkills = append(loadedSkills, inlineSkills(agentConfig.Skills.Inline)...) if len(loadedSkills) > 0 { - skillSet := skillstool.New(loadedSkills, runConfig.WorkingDir) + skillSet := skillstool.New(loadedSkills, workingDir) // Resolve the additional toolsets each fork skill exposes in // its sub-session from the top-level toolsets section. forkToolSets, forkWarnings := forkSkillToolSets(ctx, cfg, &agentConfig, loadedSkills, parentDir, runConfig, loadOpts.toolsetRegistry, configName, expander)