cleanupSubAgents removes subagents that are no longer managed by this agent. This is usually only run at startup to ensure a clean slate. This method has an internal timeout to prevent blocking indefinitely if something goes wrong with the subagent deletion.
(ctx context.Context)
| 1612 | // slate. This method has an internal timeout to prevent blocking |
| 1613 | // indefinitely if something goes wrong with the subagent deletion. |
| 1614 | func (api *API) cleanupSubAgents(ctx context.Context) error { |
| 1615 | client := *api.subAgentClient.Load() |
| 1616 | agents, err := client.List(ctx) |
| 1617 | if err != nil { |
| 1618 | return xerrors.Errorf("list agents: %w", err) |
| 1619 | } |
| 1620 | if len(agents) == 0 { |
| 1621 | return nil |
| 1622 | } |
| 1623 | |
| 1624 | api.mu.Lock() |
| 1625 | defer api.mu.Unlock() |
| 1626 | |
| 1627 | // Collect all subagent IDs that should be kept: |
| 1628 | // 1. Subagents currently tracked by injectedSubAgentProcs |
| 1629 | // 2. Subagents referenced by known devcontainers from the manifest |
| 1630 | var keep []uuid.UUID |
| 1631 | for _, proc := range api.injectedSubAgentProcs { |
| 1632 | keep = append(keep, proc.agent.ID) |
| 1633 | } |
| 1634 | for _, dc := range api.knownDevcontainers { |
| 1635 | if dc.SubagentID.Valid { |
| 1636 | keep = append(keep, dc.SubagentID.UUID) |
| 1637 | } |
| 1638 | } |
| 1639 | |
| 1640 | ctx, cancel := context.WithTimeout(ctx, defaultOperationTimeout) |
| 1641 | defer cancel() |
| 1642 | |
| 1643 | var errs []error |
| 1644 | for _, agent := range agents { |
| 1645 | if slices.Contains(keep, agent.ID) { |
| 1646 | continue |
| 1647 | } |
| 1648 | client := *api.subAgentClient.Load() |
| 1649 | err := client.Delete(ctx, agent.ID) |
| 1650 | if err != nil { |
| 1651 | api.logger.Error(ctx, "failed to delete agent", |
| 1652 | slog.Error(err), |
| 1653 | slog.F("agent_id", agent.ID), |
| 1654 | slog.F("agent_name", agent.Name), |
| 1655 | ) |
| 1656 | errs = append(errs, xerrors.Errorf("delete agent %s (%s): %w", agent.Name, agent.ID, err)) |
| 1657 | } |
| 1658 | } |
| 1659 | |
| 1660 | return errors.Join(errs...) |
| 1661 | } |
| 1662 | |
| 1663 | // maybeInjectSubAgentIntoContainerLocked injects a subagent into a dev |
| 1664 | // container and starts the subagent process. This method assumes that |