@newrelic/browser-agent 1.286.0 → 1.288.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/common/config/init-types.js +96 -0
  3. package/dist/cjs/common/config/init.js +9 -79
  4. package/dist/cjs/common/config/runtime.js +7 -6
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/harvest/harvester.js +2 -2
  8. package/dist/cjs/common/session/session-entity.js +10 -3
  9. package/dist/cjs/common/util/feature-flags.js +4 -5
  10. package/dist/cjs/common/util/target.js +34 -0
  11. package/dist/cjs/features/ajax/aggregate/index.js +2 -1
  12. package/dist/cjs/features/generic_events/aggregate/index.js +10 -6
  13. package/dist/cjs/features/jserrors/aggregate/index.js +44 -22
  14. package/dist/cjs/features/logging/aggregate/index.js +21 -13
  15. package/dist/cjs/features/logging/shared/utils.js +3 -2
  16. package/dist/cjs/features/metrics/aggregate/index.js +6 -4
  17. package/dist/cjs/features/page_view_event/aggregate/index.js +60 -11
  18. package/dist/cjs/features/page_view_event/instrument/index.js +4 -0
  19. package/dist/cjs/features/session_replay/aggregate/index.js +9 -7
  20. package/dist/cjs/features/session_replay/instrument/index.js +1 -1
  21. package/dist/cjs/features/session_replay/shared/recorder-events.js +4 -2
  22. package/dist/cjs/features/session_replay/shared/recorder.js +17 -11
  23. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +8 -1
  24. package/dist/cjs/features/soft_navigations/aggregate/index.js +13 -2
  25. package/dist/cjs/features/spa/aggregate/index.js +4 -3
  26. package/dist/cjs/features/spa/aggregate/interaction.js +6 -9
  27. package/dist/cjs/features/utils/aggregate-base.js +49 -20
  28. package/dist/cjs/features/utils/entity-manager.js +47 -0
  29. package/dist/cjs/features/utils/event-store-manager.js +79 -54
  30. package/dist/cjs/interfaces/registered-entity.js +114 -0
  31. package/dist/cjs/loaders/api/api-methods.js +1 -1
  32. package/dist/cjs/loaders/api/api.js +50 -18
  33. package/dist/cjs/loaders/api/register-api-types.js +35 -0
  34. package/dist/cjs/loaders/api/register-api.js +165 -0
  35. package/dist/cjs/loaders/micro-agent-base.js +16 -0
  36. package/dist/cjs/loaders/micro-agent.js +1 -0
  37. package/dist/esm/common/config/init-types.js +92 -0
  38. package/dist/esm/common/config/init.js +9 -79
  39. package/dist/esm/common/config/runtime.js +7 -6
  40. package/dist/esm/common/constants/env.cdn.js +1 -1
  41. package/dist/esm/common/constants/env.npm.js +1 -1
  42. package/dist/esm/common/harvest/harvester.js +2 -2
  43. package/dist/esm/common/session/session-entity.js +10 -3
  44. package/dist/esm/common/util/feature-flags.js +4 -5
  45. package/dist/esm/common/util/target.js +27 -0
  46. package/dist/esm/features/ajax/aggregate/index.js +2 -1
  47. package/dist/esm/features/generic_events/aggregate/index.js +10 -6
  48. package/dist/esm/features/jserrors/aggregate/index.js +44 -22
  49. package/dist/esm/features/logging/aggregate/index.js +21 -13
  50. package/dist/esm/features/logging/shared/utils.js +3 -2
  51. package/dist/esm/features/metrics/aggregate/index.js +6 -4
  52. package/dist/esm/features/page_view_event/aggregate/index.js +60 -11
  53. package/dist/esm/features/page_view_event/instrument/index.js +4 -0
  54. package/dist/esm/features/session_replay/aggregate/index.js +9 -7
  55. package/dist/esm/features/session_replay/instrument/index.js +1 -1
  56. package/dist/esm/features/session_replay/shared/recorder-events.js +4 -2
  57. package/dist/esm/features/session_replay/shared/recorder.js +17 -11
  58. package/dist/esm/features/session_trace/aggregate/trace/storage.js +8 -1
  59. package/dist/esm/features/soft_navigations/aggregate/index.js +13 -2
  60. package/dist/esm/features/spa/aggregate/index.js +4 -3
  61. package/dist/esm/features/spa/aggregate/interaction.js +6 -9
  62. package/dist/esm/features/utils/aggregate-base.js +49 -20
  63. package/dist/esm/features/utils/entity-manager.js +40 -0
  64. package/dist/esm/features/utils/event-store-manager.js +79 -54
  65. package/dist/esm/interfaces/registered-entity.js +107 -0
  66. package/dist/esm/loaders/api/api-methods.js +1 -1
  67. package/dist/esm/loaders/api/api.js +50 -18
  68. package/dist/esm/loaders/api/register-api-types.js +33 -0
  69. package/dist/esm/loaders/api/register-api.js +159 -0
  70. package/dist/esm/loaders/micro-agent-base.js +16 -0
  71. package/dist/esm/loaders/micro-agent.js +1 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/dist/types/common/config/init-types.d.ts +275 -0
  74. package/dist/types/common/config/init-types.d.ts.map +1 -0
  75. package/dist/types/common/config/init.d.ts +1 -262
  76. package/dist/types/common/config/init.d.ts.map +1 -1
  77. package/dist/types/common/config/runtime.d.ts.map +1 -1
  78. package/dist/types/common/session/session-entity.d.ts +3 -1
  79. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  80. package/dist/types/common/util/feature-flags.d.ts +1 -1
  81. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  82. package/dist/types/common/util/target.d.ts +18 -0
  83. package/dist/types/common/util/target.d.ts.map +1 -0
  84. package/dist/types/features/ajax/aggregate/index.d.ts +1 -1
  85. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  86. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -1
  87. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  88. package/dist/types/features/jserrors/aggregate/index.d.ts +9 -1
  89. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  90. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  91. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  92. package/dist/types/features/logging/shared/utils.d.ts +2 -1
  93. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  94. package/dist/types/features/metrics/aggregate/index.d.ts +1 -0
  95. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  96. package/dist/types/features/page_view_event/aggregate/index.d.ts +10 -2
  97. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  98. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  99. package/dist/types/features/session_replay/aggregate/index.d.ts +3 -12
  100. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  101. package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -0
  102. package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
  103. package/dist/types/features/session_replay/shared/recorder.d.ts +5 -5
  104. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  105. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +1 -0
  106. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  107. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  109. package/dist/types/features/spa/aggregate/interaction.d.ts +3 -4
  110. package/dist/types/features/spa/aggregate/interaction.d.ts.map +1 -1
  111. package/dist/types/features/utils/aggregate-base.d.ts +22 -5
  112. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  113. package/dist/types/features/utils/entity-manager.d.ts +15 -0
  114. package/dist/types/features/utils/entity-manager.d.ts.map +1 -0
  115. package/dist/types/features/utils/event-store-manager.d.ts +48 -24
  116. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  117. package/dist/types/interfaces/registered-entity.d.ts +72 -0
  118. package/dist/types/interfaces/registered-entity.d.ts.map +1 -0
  119. package/dist/types/loaders/agent.d.ts +1 -1
  120. package/dist/types/loaders/api/api.d.ts.map +1 -1
  121. package/dist/types/loaders/api/register-api-types.d.ts +56 -0
  122. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -0
  123. package/dist/types/loaders/api/register-api.d.ts +14 -0
  124. package/dist/types/loaders/api/register-api.d.ts.map +1 -0
  125. package/dist/types/loaders/micro-agent-base.d.ts +17 -0
  126. package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
  127. package/dist/types/loaders/micro-agent.d.ts +1 -0
  128. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  129. package/package.json +10 -1
  130. package/src/common/config/init-types.js +92 -0
  131. package/src/common/config/init.js +9 -79
  132. package/src/common/config/runtime.js +7 -6
  133. package/src/common/harvest/harvester.js +2 -2
  134. package/src/common/session/session-entity.js +10 -3
  135. package/src/common/util/feature-flags.js +4 -5
  136. package/src/common/util/target.js +27 -0
  137. package/src/features/ajax/aggregate/index.js +2 -1
  138. package/src/features/generic_events/aggregate/index.js +8 -6
  139. package/src/features/jserrors/aggregate/index.js +42 -20
  140. package/src/features/logging/aggregate/index.js +18 -12
  141. package/src/features/logging/shared/utils.js +3 -2
  142. package/src/features/metrics/aggregate/index.js +7 -5
  143. package/src/features/page_view_event/aggregate/index.js +50 -8
  144. package/src/features/page_view_event/instrument/index.js +4 -0
  145. package/src/features/session_replay/aggregate/index.js +7 -4
  146. package/src/features/session_replay/instrument/index.js +1 -1
  147. package/src/features/session_replay/shared/recorder-events.js +5 -2
  148. package/src/features/session_replay/shared/recorder.js +17 -11
  149. package/src/features/session_trace/aggregate/trace/storage.js +9 -1
  150. package/src/features/soft_navigations/aggregate/index.js +12 -2
  151. package/src/features/spa/aggregate/index.js +4 -3
  152. package/src/features/spa/aggregate/interaction.js +6 -9
  153. package/src/features/utils/aggregate-base.js +54 -22
  154. package/src/features/utils/entity-manager.js +45 -0
  155. package/src/features/utils/event-store-manager.js +72 -49
  156. package/src/interfaces/registered-entity.js +107 -0
  157. package/src/loaders/api/api-methods.js +1 -1
  158. package/src/loaders/api/api.js +49 -13
  159. package/src/loaders/api/register-api-types.js +33 -0
  160. package/src/loaders/api/register-api.js +152 -0
  161. package/src/loaders/micro-agent-base.js +16 -0
  162. package/src/loaders/micro-agent.js +1 -0
@@ -19,6 +19,8 @@ import { AggregateBase } from '../../utils/aggregate-base'
19
19
  import { now } from '../../../common/timing/now'
20
20
  import { applyFnToProps } from '../../../common/util/traverse'
21
21
  import { evaluateInternalError } from './internal-errors'
22
+ import { isContainerAgentTarget } from '../../../common/util/target'
23
+ import { warn } from '../../../common/util/console'
22
24
 
23
25
  /**
24
26
  * @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
@@ -107,10 +109,13 @@ export class Aggregate extends AggregateBase {
107
109
  * @param {object=} customAttributes any custom attributes to be included in the error payload
108
110
  * @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
109
111
  * @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
112
+ * @param {object=} target the target to buffer and harvest to, if undefined the default configuration target is used
110
113
  * @returns
111
114
  */
112
- storeError (err, time, internal, customAttributes, hasReplay, swallowReason) {
115
+ storeError (err, time, internal, customAttributes, hasReplay, swallowReason, targetEntityGuid) {
113
116
  if (!err) return
117
+ const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
118
+ if (!target) return warn(56, this.featureName)
114
119
  // are we in an interaction
115
120
  time = time || now()
116
121
  let filterOutput
@@ -145,7 +150,8 @@ export class Aggregate extends AggregateBase {
145
150
  // Do not modify the name ('errorGroup') of params without DEM approval!
146
151
  if (filterOutput?.group) params.errorGroup = filterOutput.group
147
152
 
148
- if (hasReplay) params.hasReplay = hasReplay
153
+ // Should only decorate "hasReplay" for the container agent, so check if the target matches the config
154
+ if (hasReplay && isContainerAgentTarget(target, this.agentRef)) params.hasReplay = hasReplay
149
155
  /**
150
156
  * The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
151
157
  * stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
@@ -181,7 +187,7 @@ export class Aggregate extends AggregateBase {
181
187
 
182
188
  // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
183
189
  const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes]
184
- handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
190
+ if (this.shouldAllowMainAgentToCapture(targetEntityGuid)) handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
185
191
  // still send EE events for other features such as above, but stop this one from aggregating internal data
186
192
  if (this.blocked) return
187
193
 
@@ -190,27 +196,32 @@ export class Aggregate extends AggregateBase {
190
196
  params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId
191
197
  }
192
198
 
193
- const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav])
194
- // Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
195
- // They each will also tack on their respective properties to the params object as part of the decision flow.
196
- if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee)
197
- else handle('spa-jserror', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee)
198
-
199
- if (params.browserInteractionId && !params._softNavFinished) { // hold onto the error until the in-progress interaction is done, eithered saved or discarded
200
- this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= []
201
- this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent)
202
- } else if (params._interactionId != null) { // same as above, except tailored for the way old spa does it
203
- this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || []
204
- this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent)
205
- } else {
199
+ if (this.shouldAllowMainAgentToCapture(targetEntityGuid)) {
200
+ const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav])
201
+ // Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
202
+ // They each will also tack on their respective properties to the params object as part of the decision flow.
203
+ if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee)
204
+ else handle('spa-jserror', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee)
205
+
206
+ if (params.browserInteractionId && !params._softNavFinished) { // hold onto the error until the in-progress interaction is done, eithered saved or discarded
207
+ this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= []
208
+ this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent)
209
+ } else if (params._interactionId != null) { // same as above, except tailored for the way old spa does it
210
+ this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || []
211
+ this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent)
212
+ } else {
206
213
  // Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
207
214
  // The old spa does not look up completed interactions at all, so there's no need to consider it.
208
- this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes)
215
+ this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes)
216
+ }
209
217
  }
218
+
219
+ // always add directly if scoped to a sub-entity, the other pathways above will be deterministic if the main agent should procede
220
+ if (targetEntityGuid) this.#storeJserrorForHarvest([...jsErrorEvent, targetEntityGuid], false, params._softNavAttributes)
210
221
  }
211
222
 
212
223
  #storeJserrorForHarvest (errorInfoArr, softNavOccurredFinished, softNavCustomAttrs = {}) {
213
- let [type, bucketHash, params, newMetrics, localAttrs] = errorInfoArr
224
+ let [type, bucketHash, params, newMetrics, localAttrs, targetEntityGuid] = errorInfoArr
214
225
  const allCustomAttrs = {}
215
226
 
216
227
  if (softNavOccurredFinished) {
@@ -227,13 +238,24 @@ export class Aggregate extends AggregateBase {
227
238
 
228
239
  const jsAttributesHash = stringHashCode(stringify(allCustomAttrs))
229
240
  const aggregateHash = bucketHash + ':' + jsAttributesHash
230
- this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs])
241
+
242
+ this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs], targetEntityGuid)
231
243
 
232
244
  function setCustom (key, val) {
233
245
  allCustomAttrs[key] = (val && typeof val === 'object' ? stringify(val) : val)
234
246
  }
235
247
  }
236
248
 
249
+ /**
250
+ * If the event lacks an entityGuid (the default behavior), the main agent should capture the data. If the data is assigned to a sub-entity target
251
+ * the main agent should not capture events unless it is configured to do so.
252
+ * @param {string} entityGuid - the context object for the event
253
+ * @returns {boolean} - whether the main agent should capture the event to its internal target
254
+ */
255
+ shouldAllowMainAgentToCapture (entityGuid) {
256
+ return (!entityGuid || this.agentRef.init.api.duplicate_registered_data)
257
+ }
258
+
237
259
  // TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
238
260
  onInteractionDone (interaction, wasSaved) {
239
261
  if (!this.bufferedErrorsUnderSpa[interaction.id] || this.blocked) return
@@ -257,7 +279,7 @@ export class Aggregate extends AggregateBase {
257
279
  var jsAttributesHash = stringHashCode(stringify(allCustomAttrs))
258
280
  var aggregateHash = hash + ':' + jsAttributesHash
259
281
 
260
- this.events.add([item[0], aggregateHash, params, item[3], allCustomAttrs])
282
+ this.events.add([item[0], aggregateHash, params, item[3], allCustomAttrs], item[5])
261
283
 
262
284
  function setCustom ([key, val]) {
263
285
  allCustomAttrs[key] = (val && typeof val === 'object' ? stringify(val) : val)
@@ -11,6 +11,7 @@ import { Log } from '../shared/log'
11
11
  import { isValidLogLevel } from '../shared/utils'
12
12
  import { applyFnToProps } from '../../../common/util/traverse'
13
13
  import { MAX_PAYLOAD_SIZE } from '../../../common/constants/agent-constants'
14
+ import { isContainerAgentTarget } from '../../../common/util/target'
14
15
  import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../../../common/session/constants'
15
16
  import { ABORT_REASONS } from '../../session_replay/constants'
16
17
  import { canEnableSessionTracking } from '../../utils/feature-gates'
@@ -61,7 +62,8 @@ export class Aggregate extends AggregateBase {
61
62
  })
62
63
  }
63
64
 
64
- handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO) {
65
+ handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO, targetEntityGuid) {
66
+ if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return warn(56, this.featureName)
65
67
  if (this.blocked || !this.loggingMode) return
66
68
 
67
69
  if (!attributes || typeof attributes !== 'object') attributes = {}
@@ -105,12 +107,12 @@ export class Aggregate extends AggregateBase {
105
107
  return
106
108
  }
107
109
 
108
- if (this.events.wouldExceedMaxSize(logBytes)) {
110
+ if (this.events.wouldExceedMaxSize(logBytes, targetEntityGuid)) {
109
111
  this.reportSupportabilityMetric('Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes)
110
- this.agentRef.runtime.harvester.triggerHarvestFor(this) // force a harvest synchronously to try adding again
112
+ this.agentRef.runtime.harvester.triggerHarvestFor(this, { targetEntityGuid }) // force a harvest synchronously to try adding again
111
113
  }
112
114
 
113
- if (!this.events.add(log)) { // still failed after a harvest attempt despite not being too large would mean harvest failed with options.retry
115
+ if (!this.events.add(log, targetEntityGuid)) { // still failed after a harvest attempt despite not being too large would mean harvest failed with options.retry
114
116
  this.reportSupportabilityMetric(failToHarvestMessage, logBytes)
115
117
  warn(31, log.message.slice(0, 25) + '...')
116
118
  } else {
@@ -118,20 +120,21 @@ export class Aggregate extends AggregateBase {
118
120
  }
119
121
  }
120
122
 
121
- serializer (eventBuffer) {
123
+ serializer (eventBuffer, targetEntityGuid) {
124
+ const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
122
125
  const sessionEntity = this.agentRef.runtime.session
123
126
  return [{
124
127
  common: {
125
128
  /** Attributes in the `common` section are added to `all` logs generated in the payload */
126
129
  attributes: {
127
- 'entity.guid': this.agentRef.runtime.appMetadata?.agents?.[0]?.entityGuid, // browser entity guid as provided from RUM response
130
+ 'entity.guid': target.entityGuid, // browser entity guid as provided API target OR the default from RUM response if not supplied
128
131
  ...(sessionEntity && {
129
132
  session: sessionEntity.state.value || '0', // The session ID that we generate and keep across page loads
130
- hasReplay: sessionEntity.state.sessionReplayMode === 1, // True if a session replay recording is running
133
+ hasReplay: sessionEntity.state.sessionReplayMode === 1 && isContainerAgentTarget(target, this.agentRef), // True if a session replay recording is running
131
134
  hasTrace: sessionEntity.state.sessionTraceMode === 1 // True if a session trace recording is running
132
135
  }),
133
136
  ptid: this.agentRef.runtime.ptid, // page trace id
134
- appId: this.agentRef.info.applicationID, // Application ID from info object,
137
+ appId: target.applicationID || this.agentRef.info.applicationID, // Application ID from info object,
135
138
  standalone: Boolean(this.agentRef.info.sa), // copy paste (true) vs APM (false)
136
139
  agentVersion: this.agentRef.runtime.version, // browser agent version
137
140
  // The following 3 attributes are evaluated and dropped at ingest processing time and do not get stored on NRDB:
@@ -150,16 +153,19 @@ export class Aggregate extends AggregateBase {
150
153
  }]
151
154
  }
152
155
 
153
- queryStringsBuilder () {
154
- return { browser_monitoring_key: this.agentRef.info.licenseKey }
156
+ queryStringsBuilder (_, targetEntityGuid) {
157
+ const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
158
+ return { browser_monitoring_key: target.licenseKey }
155
159
  }
156
160
 
157
161
  /** Abort the feature, once aborted it will not resume */
158
162
  abort (reason = {}) {
159
163
  this.reportSupportabilityMetric(`Logging/Abort/${reason.sm}`)
160
164
  this.blocked = true
161
- this.events.clear()
162
- this.events.clearSave()
165
+ if (this.events) {
166
+ this.events.clear()
167
+ this.events.clearSave()
168
+ }
163
169
  this.updateLoggingMode(LOGGING_MODE.OFF)
164
170
  this.deregisterDrain()
165
171
  }
@@ -13,10 +13,11 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
13
13
  * @param {string} message - the log message string
14
14
  * @param {{[key: string]: *}} customAttributes - The log's custom attributes if any
15
15
  * @param {enum} level - the log level enum
16
+ * @param {object=} targetEntityGuid - the optional target entity guid provided by an api call
16
17
  */
17
- export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO) {
18
+ export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO, targetEntityGuid, timestamp = now()) {
18
19
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/logging/${level.toLowerCase()}/called`], undefined, FEATURE_NAMES.metrics, ee)
19
- handle(LOGGING_EVENT_EMITTER_CHANNEL, [now(), message, customAttributes, level], undefined, FEATURE_NAMES.logging, ee)
20
+ handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, targetEntityGuid], undefined, FEATURE_NAMES.logging, ee)
20
21
  }
21
22
 
22
23
  /**
@@ -21,8 +21,14 @@ export class Aggregate extends AggregateBase {
21
21
  this.harvestOpts.aggregatorTypes = ['cm', 'sm'] // the types in EventAggregator this feature cares about
22
22
  // This feature only harvests once per potential EoL of the page, which is handled by the central harvester.
23
23
 
24
+ // this must be read/stored synchronously, as the currentScript is removed from the DOM after this script is executed and this lookup will be void
25
+ // its used to report a SM later in the lifecycle
26
+ this.agentNonce = isBrowserScope && document.currentScript?.nonce
27
+
24
28
  this.waitForFlags(['err']).then(([errFlag]) => {
25
29
  if (errFlag) {
30
+ this.singleChecks() // checks that are run only one time, at script load
31
+ this.eachSessionChecks() // the start of every time user engages with page
26
32
  this.drain()
27
33
  } else {
28
34
  this.blocked = true // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
@@ -33,9 +39,6 @@ export class Aggregate extends AggregateBase {
33
39
  // Allow features external to the metrics feature to capture SMs and CMs through the event emitter
34
40
  registerHandler(SUPPORTABILITY_METRIC_CHANNEL, this.storeSupportabilityMetrics.bind(this), this.featureName, this.ee)
35
41
  registerHandler(CUSTOM_METRIC_CHANNEL, this.storeEventMetrics.bind(this), this.featureName, this.ee)
36
-
37
- this.singleChecks() // checks that are run only one time, at script load
38
- this.eachSessionChecks() // the start of every time user engages with page
39
42
  }
40
43
 
41
44
  preHarvestChecks (opts) { return this.drained && opts.isFinalHarvest } // only allow any metrics to be sent after we get the right RUM flag and only on EoL
@@ -65,8 +68,7 @@ export class Aggregate extends AggregateBase {
65
68
  if (isBrowserScope) {
66
69
  this.storeSupportabilityMetrics('Generic/Runtime/Browser/Detected')
67
70
 
68
- const nonce = document?.currentScript?.nonce
69
- if (nonce && nonce !== '') {
71
+ if (this.agentNonce && this.agentNonce !== '') {
70
72
  this.storeSupportabilityMetrics('Generic/Runtime/Nonce/Detected')
71
73
  }
72
74
 
@@ -17,6 +17,8 @@ import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
17
17
  import { now } from '../../../common/timing/now'
18
18
  import { TimeKeeper } from '../../../common/timing/time-keeper'
19
19
  import { applyFnToProps } from '../../../common/util/traverse'
20
+ import { registerHandler } from '../../../common/event-emitter/register-handler'
21
+ import { isContainerAgentTarget } from '../../../common/util/target'
20
22
 
21
23
  export class Aggregate extends AggregateBase {
22
24
  static featureName = CONSTANTS.FEATURE_NAME
@@ -27,6 +29,10 @@ export class Aggregate extends AggregateBase {
27
29
  this.firstByteToWindowLoad = 0 // our "frontend" duration
28
30
  this.firstByteToDomContent = 0 // our "dom processing" duration
29
31
 
32
+ registerHandler('send-rum', (customAttibutes, target) => {
33
+ this.sendRum(customAttibutes, target)
34
+ }, this.featureName, this.ee)
35
+
30
36
  if (!isValid(agentRef.agentIdentifier)) {
31
37
  this.ee.abort()
32
38
  return warn(43)
@@ -48,7 +54,13 @@ export class Aggregate extends AggregateBase {
48
54
  }
49
55
  }
50
56
 
51
- sendRum () {
57
+ /**
58
+ *
59
+ * @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
60
+ * @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
61
+ * @param {*} target The target to harvest to - Since we will not know the entityGuid before harvesting, this must be an object directly supplied from the info object or API, not an entityGuid string for lookup with the entityManager - Defaults to { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }
62
+ */
63
+ sendRum (customAttributes = this.agentRef.info.jsAttributes, target = { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }) {
52
64
  const info = this.agentRef.info
53
65
  const measures = {}
54
66
 
@@ -77,8 +89,8 @@ export class Aggregate extends AggregateBase {
77
89
  if (this.agentRef.runtime.session) queryParameters.fsh = Number(this.agentRef.runtime.session.isNew) // "first session harvest" aka RUM request or PageView event of a session
78
90
 
79
91
  let body
80
- if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
81
- body = applyFnToProps({ ja: info.jsAttributes }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
92
+ if (typeof customAttributes === 'object' && Object.keys(customAttributes).length > 0) {
93
+ body = applyFnToProps({ ja: customAttributes }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
82
94
  }
83
95
 
84
96
  if (globalScope.performance) {
@@ -107,9 +119,10 @@ export class Aggregate extends AggregateBase {
107
119
  }
108
120
 
109
121
  this.rumStartTime = now()
122
+
110
123
  this.agentRef.runtime.harvester.triggerHarvestFor(this, {
111
124
  directSend: {
112
- targetApp: this.agentRef.mainAppKey,
125
+ targetApp: target,
113
126
  payload: { qs: queryParameters, body }
114
127
  },
115
128
  needResponse: true,
@@ -117,8 +130,19 @@ export class Aggregate extends AggregateBase {
117
130
  })
118
131
  }
119
132
 
120
- postHarvestCleanup ({ status, responseText, xhr }) {
133
+ postHarvestCleanup ({ status, responseText, xhr, targetApp }) {
121
134
  const rumEndTime = now()
135
+ let app, flags
136
+ try {
137
+ ({ app, ...flags } = JSON.parse(responseText))
138
+ this.processEntities(app.agents, targetApp)
139
+ } catch (error) {
140
+ // wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
141
+ warn(53, error)
142
+ }
143
+
144
+ /** Only run agent-wide side-effects if the harvest was for the main agent */
145
+ if (!isContainerAgentTarget(targetApp, this.agentRef)) return
122
146
 
123
147
  if (status >= 400 || status === 0) {
124
148
  warn(18, status)
@@ -127,8 +151,8 @@ export class Aggregate extends AggregateBase {
127
151
  return
128
152
  }
129
153
 
130
- const { app, ...flags } = JSON.parse(responseText)
131
154
  try {
155
+ // will do nothing if already done
132
156
  this.agentRef.runtime.timeKeeper.processRumRequest(xhr, this.rumStartTime, rumEndTime, app.nrServerTime)
133
157
  if (!this.agentRef.runtime.timeKeeper.ready) throw new Error('TimeKeeper not ready')
134
158
  } catch (error) {
@@ -136,9 +160,27 @@ export class Aggregate extends AggregateBase {
136
160
  warn(17, error)
137
161
  return
138
162
  }
139
- this.agentRef.runtime.appMetadata = app
140
- activateFeatures(flags, this.agentIdentifier)
163
+
164
+ // set the agent runtime objects that require the rum response or entity guid
165
+ if (!Object.keys(this.agentRef.runtime.appMetadata).length) this.agentRef.runtime.appMetadata = app
166
+
141
167
  this.drain()
142
168
  this.agentRef.runtime.harvester.startTimer()
169
+ activateFeatures(flags, this.agentRef)
170
+ }
171
+
172
+ processEntities (entities, targetApp) {
173
+ if (!entities || !targetApp) return
174
+ entities.forEach(agent => {
175
+ const entityManager = this.agentRef.runtime.entityManager
176
+ const entityGuid = agent.entityGuid
177
+ const entity = entityManager.get(entityGuid)
178
+ if (entity) return // already processed
179
+
180
+ if (isContainerAgentTarget(targetApp, this.agentRef)) {
181
+ entityManager.setDefaultEntity({ ...targetApp, entityGuid })
182
+ }
183
+ entityManager.set(agent.entityGuid, { ...targetApp, entityGuid })
184
+ })
143
185
  }
144
186
  }
@@ -2,6 +2,7 @@
2
2
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
+ import { handle } from '../../../common/event-emitter/handle'
5
6
  import { InstrumentBase } from '../../utils/instrument-base'
6
7
  import * as CONSTANTS from '../constants'
7
8
 
@@ -10,6 +11,9 @@ export class Instrument extends InstrumentBase {
10
11
  constructor (agentRef, auto = true) {
11
12
  super(agentRef, CONSTANTS.FEATURE_NAME, auto)
12
13
 
14
+ /** messages from the register API that can trigger a new RUM call */
15
+ this.ee.on('api-send-rum', (attrs, target) => handle('send-rum', [attrs, target], undefined, this.featureName, this.ee))
16
+
13
17
  this.importAggregator(agentRef)
14
18
  }
15
19
  }
@@ -48,7 +48,7 @@ export class Aggregate extends AggregateBase {
48
48
  this.errorNoticed = args?.errorNoticed || false
49
49
  this.harvestOpts.raw = true
50
50
 
51
- this.isSessionTrackingEnabled = canEnableSessionTracking(this.agentIdentifier) && this.agentRef.runtime.session
51
+ this.isSessionTrackingEnabled = canEnableSessionTracking(this.agentIdentifier) && !!this.agentRef.runtime.session
52
52
 
53
53
  this.reportSupportabilityMetric('Config/SessionReplay/Enabled')
54
54
 
@@ -213,6 +213,7 @@ export class Aggregate extends AggregateBase {
213
213
  }
214
214
 
215
215
  makeHarvestPayload (shouldRetryOnFail) {
216
+ const payloadOutput = { targetApp: undefined, payload: undefined }
216
217
  if (this.mode !== MODE.FULL || this.blocked) return
217
218
  if (!this.recorder || !this.timeKeeper?.ready || !this.recorder.hasSeenSnapshot) return
218
219
 
@@ -223,7 +224,7 @@ export class Aggregate extends AggregateBase {
223
224
  const payload = this.getHarvestContents(recorderEvents)
224
225
  if (!payload.body.length) {
225
226
  this.recorder.clearBuffer()
226
- return
227
+ return [payloadOutput]
227
228
  }
228
229
 
229
230
  this.reportSupportabilityMetric('SessionReplay/Harvest/Attempts')
@@ -253,13 +254,15 @@ export class Aggregate extends AggregateBase {
253
254
 
254
255
  if (len > MAX_PAYLOAD_SIZE) {
255
256
  this.abort(ABORT_REASONS.TOO_BIG, len)
256
- return
257
+ return [payloadOutput]
257
258
  }
258
259
  // TODO -- Gracefully handle the buffer for retries.
259
260
  if (!this.agentRef.runtime.session.state.sessionReplaySentFirstChunk) this.syncWithSessionManager({ sessionReplaySentFirstChunk: true })
260
261
  this.recorder.clearBuffer()
261
262
  if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this)
262
- return [{ targetApp: undefined, payload }] // SR doesn't need a targetApp as it only works for the main, but format needs to make AggregateBase
263
+ payloadOutput.payload = payload
264
+
265
+ return [payloadOutput]
263
266
  }
264
267
 
265
268
  getCorrectedTimestamp (node) {
@@ -80,7 +80,7 @@ export class Instrument extends InstrumentBase {
80
80
  this.recorder ??= new Recorder({ mode: this.#mode, agentIdentifier: this.agentIdentifier, trigger, ee: this.ee, agentRef: this.#agentRef })
81
81
  this.recorder.startRecording()
82
82
  this.abortHandler = this.recorder.stopRecording
83
- } catch (e) {}
83
+ } catch (e) {} // TODO add internal error handling
84
84
  this.importAggregator(this.#agentRef, { recorder: this.recorder, errorNoticed: this.errorNoticed })
85
85
  }
86
86
 
@@ -20,8 +20,11 @@ export class RecorderEvents {
20
20
  hasMeta = false
21
21
  /** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
22
22
  hasError = false
23
- /** Payload metadata -- Denotes whether all stylesheet elements were able to be inlined */
24
- inlinedAllStylesheets = true
23
+
24
+ constructor (shouldInlineStylesheets = true) {
25
+ /** Payload metadata -- Denotes whether all stylesheet elements were able to be inlined */
26
+ this.inlinedAllStylesheets = shouldInlineStylesheets
27
+ }
25
28
 
26
29
  add (event) {
27
30
  this.#events.add(event)
@@ -30,9 +30,14 @@ export class Recorder {
30
30
  #warnCSSOnce = single(() => warn(47)) // notifies user of potential replayer issue if fix_stylesheets is off
31
31
 
32
32
  constructor (parent) {
33
- this.#events = new RecorderEvents()
34
- this.#backloggedEvents = new RecorderEvents()
35
- this.#preloaded = [new RecorderEvents()]
33
+ /** The parent class that instantiated the recorder */
34
+ this.parent = parent
35
+ /** A flag that can be set to false by failing conversions to stop the fetching process */
36
+ this.shouldFix = this.parent.agentRef.init.session_replay.fix_stylesheets
37
+ /** Event Buffers */
38
+ this.#events = new RecorderEvents(this.shouldFix)
39
+ this.#backloggedEvents = new RecorderEvents(this.shouldFix)
40
+ this.#preloaded = [new RecorderEvents(this.shouldFix)]
36
41
  /** True when actively recording, false when paused or stopped */
37
42
  this.recording = false
38
43
  /** The pointer to the current bucket holding rrweb events */
@@ -41,10 +46,6 @@ export class Recorder {
41
46
  this.hasSeenSnapshot = false
42
47
  /** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
43
48
  this.lastMeta = false
44
- /** The parent class that instantiated the recorder */
45
- this.parent = parent
46
- /** A flag that can be set to false by failing conversions to stop the fetching process */
47
- this.shouldFix = this.parent.agentRef.init.session_replay.fix_stylesheets
48
49
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
49
50
  this.stopRecording = () => { /* no-op until set by rrweb initializer */ }
50
51
  }
@@ -74,8 +75,8 @@ export class Recorder {
74
75
  clearBuffer () {
75
76
  if (this.#preloaded[0]?.events.length) this.#preloaded.shift()
76
77
  else if (this.parent.mode === MODE.ERROR) this.#backloggedEvents = this.#events
77
- else this.#backloggedEvents = new RecorderEvents()
78
- this.#events = new RecorderEvents()
78
+ else this.#backloggedEvents = new RecorderEvents(this.shouldFix)
79
+ this.#events = new RecorderEvents(this.shouldFix)
79
80
  }
80
81
 
81
82
  /** Begin recording using configured recording lib */
@@ -105,6 +106,7 @@ export class Recorder {
105
106
 
106
107
  this.stopRecording = () => {
107
108
  this.recording = false
109
+ this.notified = false
108
110
  this.parent.ee.emit(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode])
109
111
  stop?.()
110
112
  }
@@ -118,7 +120,7 @@ export class Recorder {
118
120
  */
119
121
  audit (event, isCheckout) {
120
122
  /** An count of stylesheet objects that were blocked from accessing contents via JS */
121
- const incompletes = stylesheetEvaluator.evaluate()
123
+ const incompletes = this.parent.agentRef.init.session_replay.fix_stylesheets ? stylesheetEvaluator.evaluate() : 0
122
124
  const missingInlineSMTag = 'SessionReplay/Payload/Missing-Inline-Css/'
123
125
  /** only run the full fixing behavior (more costly) if fix_stylesheets is configured as on (default behavior) */
124
126
  if (!this.shouldFix) {
@@ -152,6 +154,10 @@ export class Recorder {
152
154
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
153
155
  store (event, isCheckout) {
154
156
  if (!event) return
157
+ if (this.parent.agentRef.runtime?.session?.isAfterSessionExpiry(event.timestamp)) {
158
+ this.parent.reportSupportabilityMetric('Session/Expired/SessionReplay/Seen')
159
+ return
160
+ }
155
161
 
156
162
  if (!(this.parent instanceof AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1]
157
163
  else this.currentBufferTarget = this.#events
@@ -199,7 +205,7 @@ export class Recorder {
199
205
  this.parent.agentRef.runtime.harvester.triggerHarvestFor(this.parent)
200
206
  } else {
201
207
  // we are still in "preload" and it triggered a "stop point". Make a new set, which will get pointed at on next cycle
202
- this.#preloaded.push(new RecorderEvents())
208
+ this.#preloaded.push(new RecorderEvents(this.shouldFix))
203
209
  }
204
210
  }
205
211
  }
@@ -41,6 +41,10 @@ export class TraceStorage {
41
41
  this.parent = parent
42
42
  }
43
43
 
44
+ isAfterSessionExpiry (entryTimestamp) {
45
+ return this.parent.agentRef.runtime?.session?.isAfterSessionExpiry((this.parent.timeKeeper?.ready && this.parent.timeKeeper.convertRelativeTimestamp(entryTimestamp)) ?? undefined)
46
+ }
47
+
44
48
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
45
49
  storeSTN (stn) {
46
50
  if (this.parent.blocked) return
@@ -49,6 +53,10 @@ export class TraceStorage {
49
53
  const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW) // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
50
54
  if (openedSpace === 0) return
51
55
  }
56
+ if (this.isAfterSessionExpiry(stn.s)) {
57
+ this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen')
58
+ return
59
+ }
52
60
 
53
61
  if (this.trace[stn.n]) this.trace[stn.n].push(stn)
54
62
  else this.trace[stn.n] = [stn]
@@ -259,7 +267,7 @@ export class TraceStorage {
259
267
  }
260
268
 
261
269
  get () {
262
- return [{ targetApp: this.parent.agentRef.mainAppKey, data: this.takeSTNs() }]
270
+ return [{ targetApp: this.parent.agentRef.runtime.entityManager.get(), data: this.takeSTNs() }]
263
271
  }
264
272
 
265
273
  clear () {
@@ -23,8 +23,18 @@ export class Aggregate extends AggregateBase {
23
23
 
24
24
  this.initialPageLoadInteraction = new InitialPageLoadInteraction(agentRef.agentIdentifier)
25
25
  this.initialPageLoadInteraction.onDone.push(() => { // this ensures the .end() method also works with iPL
26
+ if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true // mark the hard page load as first of its session
26
27
  this.initialPageLoadInteraction.forceSave = true // unless forcibly ignored, iPL always finish by default
27
- this.interactionsToHarvest.add(this.initialPageLoadInteraction)
28
+ const ixn = this.initialPageLoadInteraction
29
+ /** this.events (ixns to harvest) has already been set up, use it immediately */
30
+ if (this.interactionsToHarvest) this.interactionsToHarvest.add(ixn)
31
+ else {
32
+ /** this.events (ixns to harvest) hasnt been initialized yet... wait for it */
33
+ this.ee.on('entity-added', () => {
34
+ this.interactionsToHarvest = this.events
35
+ this.interactionsToHarvest.add(ixn)
36
+ })
37
+ }
28
38
  this.initialPageLoadInteraction = null
29
39
  })
30
40
  timeToFirstByte.subscribe(({ attrs }) => {
@@ -131,7 +141,7 @@ export class Aggregate extends AggregateBase {
131
141
  */
132
142
  if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress
133
143
  let saveIxn
134
- const interactionsBuffer = this.interactionsToHarvest.get(this.agentRef.mainAppKey)[0].data
144
+ const [{ data: interactionsBuffer }] = this.interactionsToHarvest.get()
135
145
  for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) { // reverse search for the latest completed interaction for efficiency
136
146
  const finishedInteraction = interactionsBuffer[idx]
137
147
  if (finishedInteraction.isActiveDuring(timestamp)) {
@@ -108,8 +108,9 @@ export class Aggregate extends AggregateBase {
108
108
 
109
109
  if (agentRef.init.spa.enabled !== true) return
110
110
 
111
- state.initialPageLoad = new Interaction('initialPageLoad', 0, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef.agentIdentifier)
111
+ state.initialPageLoad = new Interaction('initialPageLoad', 0, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef)
112
112
  state.initialPageLoad.save = true
113
+ if (agentRef.runtime.session?.isNew) state.initialPageLoad.root.attrs.custom.isFirstOfSession = true // mark the hard page load as first of its session
113
114
  state.prevInteraction = state.initialPageLoad
114
115
  state.currentNode = state.initialPageLoad.root // hint
115
116
  // ensure that checkFinish calls are safe during initialPageLoad
@@ -199,7 +200,7 @@ export class Aggregate extends AggregateBase {
199
200
  // Otherwise, if no interaction is currently active, create a new node ID,
200
201
  // and let the aggregator know that we entered a new event handler callback
201
202
  // so that it has a chance to possibly start an interaction.
202
- var ixn = new Interaction(evName, this[FN_START], state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef.agentIdentifier)
203
+ var ixn = new Interaction(evName, this[FN_START], state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef)
203
204
 
204
205
  // Store the interaction as prevInteraction in case it is prematurely discarded
205
206
  state.prevInteraction = ixn
@@ -535,7 +536,7 @@ export class Aggregate extends AggregateBase {
535
536
  var interaction
536
537
  if (state?.currentNode?.[INTERACTION]) interaction = this.ixn = state.currentNode[INTERACTION]
537
538
  else if (state?.prevNode?.end === null && state?.prevNode?.[INTERACTION]?.root?.[INTERACTION]?.eventName !== 'initialPageLoad') interaction = this.ixn = state.prevNode[INTERACTION]
538
- else interaction = this.ixn = new Interaction('api', t, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef.agentIdentifier)
539
+ else interaction = this.ixn = new Interaction('api', t, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef)
539
540
  if (!state.currentNode) {
540
541
  interaction.checkFinish()
541
542
  if (state.depth) setCurrentNode(interaction.root)