function Agent(config) {
  EventEmitter.call(this)

  if (!config) {
    throw new Error('Agent must be created with a configuration!')
  }

  this.healthReporter = new HealthReporter({ agentConfig: config })

  // The agent base attributes which last throughout its lifetime.
  this._state = 'stopped'
  this.config = config
  this.environment = require('./environment')
  this.version = config.version

  if (config.serverless_mode.enabled) {
    this.collector = new ServerlessCollector(this)
  } else {
    this.collector = new CollectorAPI(this)
  }

  this.mapper = new MetricMapper()
  this.metricNameNormalizer = new MetricNormalizer(this.config, 'metric name')
  this.harvester = new Harvester()

  this.metrics = new MetricAggregator(
    {
      periodMs: DEFAULT_HARVEST_INTERVAL_MS,
      apdexT: this.config.apdex_t,
      mapper: this.mapper,
      normalizer: this.metricNameNormalizer
    },
    this.collector,
    this.harvester
  )

  this.metrics.on('starting_data_send-metric_data', this._beforeMetricDataSend.bind(this))

  this.spanEventAggregator = createSpanEventAggregator(config, this)

  this.transactionNameNormalizer = new MetricNormalizer(this.config, 'transaction name')
  // Segment term based tx renaming for MGI mitigation.
  this.txSegmentNormalizer = new TxSegmentNormalizer()

  // User naming and ignoring rules.
  this.urlNormalizer = new MetricNormalizer(this.config, 'URL')
  this.userNormalizer = new MetricNormalizer(this.config, 'user')
  this.userNormalizer.loadFromConfig()

  this.transactionEventAggregator = new TransactionEventAggregator(
    {
      periodMs: config.event_harvest_config.report_period_ms,
      limit: config.event_harvest_config.harvest_limits.analytic_event_data,
      config,
      enabled: (config) => config.transaction_events.enabled
    },
    this
  )

  this.customEventAggregator = new CustomEventAggregator(
    {
      periodMs: config.event_harvest_config.report_period_ms,
      limit: config.event_harvest_config.harvest_limits.custom_event_data,
      metricNames: NAMES.CUSTOM_EVENTS,
      config,
      enabled: (config) => config.custom_insights_events.enabled
    },
    this
  )

  const errorTraceAggregator = new ErrorTraceAggregator(
    {
      periodMs: DEFAULT_HARVEST_INTERVAL_MS,
      limit: MAX_ERROR_TRACES_DEFAULT,
      config,
      enabled: (config) => config.error_collector.enabled && config.collect_errors
    },
    this.collector,
    this.harvester
  )

  const errorEventAggregator = new ErrorEventAggregator(
    {
      periodMs: config.event_harvest_config.report_period_ms,
      limit: config.event_harvest_config.harvest_limits.error_event_data,
      config,
      enabled: (config) => config.error_collector.enabled && config.error_collector.capture_events
    },
    this
  )

  this.logs = new LogAggregator(
    {
      periodMs: config.event_harvest_config.report_period_ms,
      limit: config.event_harvest_config.harvest_limits.log_event_data,
      config,
      enabled: (config) =>
        config.application_logging.enabled && config.application_logging.forwarding.enabled
    },
    this
  )

  this.errors = new ErrorCollector(config, errorTraceAggregator, errorEventAggregator, this.metrics)

  // Transaction tracing.
  this.tracer = new Tracer(this)
  this.traces = new TransactionTraceAggregator(
    {
      periodMs: DEFAULT_HARVEST_INTERVAL_MS,
      config,
      isAsync: !config.serverless_mode.enabled,
      method: 'transaction_sample_data',
      enabled: (config) => config.transaction_tracer.enabled && config.collect_traces
    },
    this.collector,
    this.harvester
  )
  this.transactionSampler = new AdaptiveSampler({
    agent: this,
    serverless: config.serverless_mode.enabled,
    period: config.sampling_target_period_in_seconds * 1000,
    target: config.sampling_target
  })

  this.queries = new QueryTraceAggregator(
    {
      config,
      periodMs: DEFAULT_HARVEST_INTERVAL_MS,
      method: 'sql_trace_data',
      isAsync: !config.serverless_mode.enabled,
      enabled: (config) => config.slow_sql.enabled
    },
    this.collector,
    this.harvester
  )

  // Set up all the configuration events the agent needs to listen for.
  this._listenForConfigChanges()

  this.initCounters()
  this.llm = {}

  // Finally, add listeners for the agent's own events.
  this.on('transactionFinished', this._transactionFinished.bind(this))
}
There's a lot of stuff in this constructor, due to Agent acting as the
orchestrator for New Relic within instrumented applications.

This constructor can throw if, for some reason, the configuration isn't
available. Don't try to recover here, because without configuration the
agent can't be brought up to a useful state.

@param {object} config Agent configuration object