feat(agent/agentcontainers): implement sub agent injection (#18245)

This change adds support for sub agent creation and injection into dev
containers.

Updates coder/internal#621
This commit is contained in:
Mathias Fredriksson
2025-06-10 12:37:54 +03:00
committed by GitHub
parent 44fff54897
commit fca99174ad
15 changed files with 1218 additions and 146 deletions
+106 -6
View File
@@ -163,6 +163,14 @@ func (c *Client) GetConnectionReports() []*agentproto.ReportConnectionRequest {
return c.fakeAgentAPI.GetConnectionReports()
}
func (c *Client) GetSubAgents() []*agentproto.SubAgent {
return c.fakeAgentAPI.GetSubAgents()
}
func (c *Client) GetSubAgentDirectory(id uuid.UUID) (string, error) {
return c.fakeAgentAPI.GetSubAgentDirectory(id)
}
type FakeAgentAPI struct {
sync.Mutex
t testing.TB
@@ -177,6 +185,8 @@ type FakeAgentAPI struct {
metadata map[string]agentsdk.Metadata
timings []*agentproto.Timing
connectionReports []*agentproto.ReportConnectionRequest
subAgents map[uuid.UUID]*agentproto.SubAgent
subAgentDirs map[uuid.UUID]string
getAnnouncementBannersFunc func() ([]codersdk.BannerConfig, error)
getResourcesMonitoringConfigurationFunc func() (*agentproto.GetResourcesMonitoringConfigurationResponse, error)
@@ -365,16 +375,106 @@ func (f *FakeAgentAPI) GetConnectionReports() []*agentproto.ReportConnectionRequ
return slices.Clone(f.connectionReports)
}
func (*FakeAgentAPI) CreateSubAgent(_ context.Context, _ *agentproto.CreateSubAgentRequest) (*agentproto.CreateSubAgentResponse, error) {
panic("unimplemented")
func (f *FakeAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.CreateSubAgentRequest) (*agentproto.CreateSubAgentResponse, error) {
f.Lock()
defer f.Unlock()
f.logger.Debug(ctx, "create sub agent called", slog.F("req", req))
// Generate IDs for the new sub-agent.
subAgentID := uuid.New()
authToken := uuid.New()
// Create the sub-agent proto object.
subAgent := &agentproto.SubAgent{
Id: subAgentID[:],
Name: req.Name,
AuthToken: authToken[:],
}
// Store the sub-agent in our map.
if f.subAgents == nil {
f.subAgents = make(map[uuid.UUID]*agentproto.SubAgent)
}
f.subAgents[subAgentID] = subAgent
if f.subAgentDirs == nil {
f.subAgentDirs = make(map[uuid.UUID]string)
}
f.subAgentDirs[subAgentID] = req.GetDirectory()
// For a fake implementation, we don't create workspace apps.
// Real implementations would handle req.Apps here.
return &agentproto.CreateSubAgentResponse{
Agent: subAgent,
AppCreationErrors: nil,
}, nil
}
func (*FakeAgentAPI) DeleteSubAgent(_ context.Context, _ *agentproto.DeleteSubAgentRequest) (*agentproto.DeleteSubAgentResponse, error) {
panic("unimplemented")
func (f *FakeAgentAPI) DeleteSubAgent(ctx context.Context, req *agentproto.DeleteSubAgentRequest) (*agentproto.DeleteSubAgentResponse, error) {
f.Lock()
defer f.Unlock()
f.logger.Debug(ctx, "delete sub agent called", slog.F("req", req))
subAgentID, err := uuid.FromBytes(req.Id)
if err != nil {
return nil, err
}
// Remove the sub-agent from our map.
if f.subAgents != nil {
delete(f.subAgents, subAgentID)
}
return &agentproto.DeleteSubAgentResponse{}, nil
}
func (*FakeAgentAPI) ListSubAgents(_ context.Context, _ *agentproto.ListSubAgentsRequest) (*agentproto.ListSubAgentsResponse, error) {
panic("unimplemented")
func (f *FakeAgentAPI) ListSubAgents(ctx context.Context, req *agentproto.ListSubAgentsRequest) (*agentproto.ListSubAgentsResponse, error) {
f.Lock()
defer f.Unlock()
f.logger.Debug(ctx, "list sub agents called", slog.F("req", req))
var agents []*agentproto.SubAgent
if f.subAgents != nil {
agents = make([]*agentproto.SubAgent, 0, len(f.subAgents))
for _, agent := range f.subAgents {
agents = append(agents, agent)
}
}
return &agentproto.ListSubAgentsResponse{
Agents: agents,
}, nil
}
func (f *FakeAgentAPI) GetSubAgents() []*agentproto.SubAgent {
f.Lock()
defer f.Unlock()
var agents []*agentproto.SubAgent
if f.subAgents != nil {
agents = make([]*agentproto.SubAgent, 0, len(f.subAgents))
for _, agent := range f.subAgents {
agents = append(agents, agent)
}
}
return agents
}
func (f *FakeAgentAPI) GetSubAgentDirectory(id uuid.UUID) (string, error) {
f.Lock()
defer f.Unlock()
if f.subAgentDirs == nil {
return "", xerrors.New("no sub-agent directories available")
}
dir, ok := f.subAgentDirs[id]
if !ok {
return "", xerrors.New("sub-agent directory not found")
}
return dir, nil
}
func NewFakeAgentAPI(t testing.TB, logger slog.Logger, manifest *agentproto.Manifest, statsCh chan *agentproto.Stats) *FakeAgentAPI {