Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions backend/internal/session_manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,15 @@ func (m *Manager) Spawn(ctx context.Context, cfg ports.SpawnConfig) (domain.Sess
m.markSpawnFailedTerminated(ctx, id)
return domain.SessionRecord{}, fmt.Errorf("spawn %s: %w", id, err)
}
agentConfig := effectiveAgentConfig(cfg.Kind, project.Config)
argv, err := agent.GetLaunchCommand(ctx, ports.LaunchConfig{
SessionID: string(id),
WorkspacePath: ws.Path,
Prompt: prompt,
SystemPrompt: systemPrompt,
IssueID: string(cfg.IssueID),
Config: effectiveAgentConfig(cfg.Kind, project.Config),
Config: agentConfig,
Permissions: agentConfig.Permissions,
})
if err != nil {
_ = m.workspace.Destroy(ctx, ws)
Expand Down Expand Up @@ -823,7 +825,7 @@ func restoreArgv(ctx context.Context, agent ports.Agent, id domain.SessionID, wo
WorkspacePath: workspacePath,
Metadata: map[string]string{ports.MetadataKeyAgentSessionID: meta.AgentSessionID},
}
cmd, ok, err := agent.GetRestoreCommand(ctx, ports.RestoreConfig{Session: ref, Config: agentConfig})
cmd, ok, err := agent.GetRestoreCommand(ctx, ports.RestoreConfig{Session: ref, Config: agentConfig, Permissions: agentConfig.Permissions})
if err != nil {
return nil, fmt.Errorf("restore command: %w", err)
}
Expand All @@ -835,6 +837,7 @@ func restoreArgv(ctx context.Context, agent ports.Agent, id domain.SessionID, wo
WorkspacePath: workspacePath,
Prompt: meta.Prompt,
Config: agentConfig,
Permissions: agentConfig.Permissions,
})
if err != nil {
return nil, fmt.Errorf("launch command: %w", err)
Expand Down
57 changes: 55 additions & 2 deletions backend/internal/session_manager/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,9 @@ func (fakeAgents) Agent(domain.AgentHarness) (ports.Agent, bool) { return fakeAg
// session manager resolved and forwarded a project's agent config.
type recordingAgent struct {
fakeAgent
lastConfig ports.AgentConfig
lastLaunch ports.LaunchConfig
lastConfig ports.AgentConfig
lastLaunch ports.LaunchConfig
lastRestore ports.RestoreConfig
}

func (a *recordingAgent) GetLaunchCommand(_ context.Context, cfg ports.LaunchConfig) ([]string, error) {
Expand All @@ -166,6 +167,7 @@ func (a *recordingAgent) GetLaunchCommand(_ context.Context, cfg ports.LaunchCon

func (a *recordingAgent) GetRestoreCommand(_ context.Context, cfg ports.RestoreConfig) ([]string, bool, error) {
a.lastConfig = cfg.Config
a.lastRestore = cfg
return []string{"resume"}, true, nil
}

Expand Down Expand Up @@ -497,6 +499,57 @@ func TestSpawn_DefaultsBranchFromSessionID(t *testing.T) {
}
}

func TestSpawn_ForwardsResolvedAgentConfigPermissions(t *testing.T) {
st := newFakeStore()
st.projects["mer"] = domain.ProjectRecord{ID: "mer", Config: domain.ProjectConfig{
AgentConfig: domain.AgentConfig{Permissions: domain.PermissionModeAuto},
Worker: domain.RoleOverride{AgentConfig: domain.AgentConfig{Permissions: domain.PermissionModeBypassPermissions}},
}}
agent := &recordingAgent{}
lookPath := func(string) (string, error) { return "/bin/true", nil }
m := New(Deps{Runtime: &fakeRuntime{}, Agents: singleAgent{agent: agent}, Workspace: &fakeWorkspace{}, Store: st, Messenger: &fakeMessenger{}, Lifecycle: &fakeLCM{store: st}, LookPath: lookPath})

_, err := m.Spawn(ctx, ports.SpawnConfig{ProjectID: "mer", Kind: domain.KindWorker})
if err != nil {
t.Fatal(err)
}

if agent.lastLaunch.Config.Permissions != domain.PermissionModeBypassPermissions {
t.Fatalf("launch config permissions = %q, want bypass", agent.lastLaunch.Config.Permissions)
}
if agent.lastLaunch.Permissions != domain.PermissionModeBypassPermissions {
t.Fatalf("launch permissions = %q, want bypass", agent.lastLaunch.Permissions)
}
}

func TestRestore_ForwardsResolvedAgentConfigPermissions(t *testing.T) {
st := newFakeStore()
st.projects["mer"] = domain.ProjectRecord{ID: "mer", Config: domain.ProjectConfig{
AgentConfig: domain.AgentConfig{Permissions: domain.PermissionModeBypassPermissions},
}}
st.sessions["mer-1"] = domain.SessionRecord{
ID: "mer-1",
ProjectID: "mer",
Kind: domain.KindWorker,
IsTerminated: true,
Metadata: domain.SessionMetadata{Branch: "ao/mer-1", WorkspacePath: "/tmp/ws", AgentSessionID: "native-1"},
}
agent := &recordingAgent{}
m := New(Deps{Runtime: &fakeRuntime{}, Agents: singleAgent{agent: agent}, Workspace: &fakeWorkspace{}, Store: st, Messenger: &fakeMessenger{}, Lifecycle: &fakeLCM{store: st}, LookPath: func(string) (string, error) { return "/bin/true", nil }})

_, err := m.Restore(ctx, "mer-1")
if err != nil {
t.Fatal(err)
}

if agent.lastRestore.Config.Permissions != domain.PermissionModeBypassPermissions {
t.Fatalf("restore config permissions = %q, want bypass", agent.lastRestore.Config.Permissions)
}
if agent.lastRestore.Permissions != domain.PermissionModeBypassPermissions {
t.Fatalf("restore permissions = %q, want bypass", agent.lastRestore.Permissions)
}
}

func TestSpawnWorker_AppendsActiveOrchestratorContact(t *testing.T) {
st := newFakeStore()
st.num = 1
Expand Down
Loading