chrome-devtools-frontend 1.0.1622369 → 1.0.1624409

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 (33) hide show
  1. package/front_end/Images/src/expand.svg +1 -0
  2. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +72 -8
  3. package/front_end/entrypoints/greendev_floaty/floaty.html +1 -1
  4. package/front_end/generated/Deprecation.ts +7 -0
  5. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  6. package/front_end/generated/SupportedCSSProperties.js +6 -6
  7. package/front_end/generated/protocol.ts +1 -0
  8. package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +373 -112
  9. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +57 -0
  10. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +2 -1
  11. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -10
  12. package/front_end/models/javascript_metadata/NativeFunctions.js +9 -4
  13. package/front_end/panels/ai_assistance/components/ChatMessage.ts +212 -3
  14. package/front_end/panels/console/ConsoleView.ts +86 -7
  15. package/front_end/panels/console/ConsoleViewMessage.ts +23 -1
  16. package/front_end/panels/elements/StylePropertiesSection.ts +1 -2
  17. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  18. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +6 -2
  19. package/front_end/panels/elements/StylesSidebarPane.ts +17 -4
  20. package/front_end/panels/emulation/DeviceModeToolbar.ts +37 -13
  21. package/front_end/panels/emulation/deviceModeView.css +25 -0
  22. package/front_end/panels/greendev/GreenDevPanel.ts +30 -3
  23. package/front_end/panels/media/EventDisplayTable.ts +1 -1
  24. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +48 -27
  25. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +67 -38
  26. package/front_end/panels/network/NetworkConfigView.ts +1 -1
  27. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -22
  28. package/front_end/third_party/chromium/README.chromium +1 -1
  29. package/front_end/ui/legacy/UIUtils.ts +26 -4
  30. package/front_end/ui/visual_logging/KnownContextValues.ts +17 -0
  31. package/package.json +1 -1
  32. package/front_end/panels/emulation/components/DeviceSizeInputElement.ts +0 -134
  33. package/front_end/panels/emulation/components/components.ts +0 -9
@@ -7,6 +7,7 @@ import * as Root from '../../../core/root/root.js';
7
7
  import * as SDK from '../../../core/sdk/sdk.js';
8
8
  import * as Protocol from '../../../generated/protocol.js';
9
9
  import * as Greendev from '../../greendev/greendev.js';
10
+ import * as Workspace from '../../workspace/workspace.js';
10
11
 
11
12
  import {
12
13
  type AgentOptions,
@@ -20,12 +21,6 @@ import {
20
21
  const preamble = `You are a general purpose web page troubleshooting agent.
21
22
  You are an expert in Chrome DevTools and you can help users with a wide range of issues.
22
23
 
23
- You are expected to find the root cause for web page problems described by the user, such as:
24
- - Why does nothing happen when I click this Submit button?
25
- - Why is this ad not loading?
26
- - Why is this text not using the correct font?
27
- - ... and other similar requests.
28
-
29
24
  Your job is to use the provided information to understand the problem, connect the dots to
30
25
  find the root cause of the problem and explain what the user can do to fix the problem.
31
26
 
@@ -33,65 +28,69 @@ The user will start the process by selecting a DOM element and send a query abou
33
28
  selected DOM element. First, examine the provided context, then use function calls to gather
34
29
  additional context and resolve the user request.
35
30
 
31
+ ### Your Debugging Strategy
32
+
33
+ 1. **Analyze the User-Selected Node**: This is your primary clue. Understand its attributes,
34
+ children, and position in the DOM. For interactive elements like buttons, your main goal is
35
+ to figure out what happens when a user interacts with it.
36
+
37
+ 2. **Find the Event Handler**: When a user reports an issue like "nothing happens when I click
38
+ this", your top priority is to find the JavaScript event handler associated with the action
39
+ (e.g., a 'click' handler for a button).
40
+
41
+ 3. **Note on Modern Frameworks (React, etc.)**: Be aware that event handlers are often not
42
+ visible as simple HTML attributes (like 'onclick'). In frameworks like React, events are
43
+ attached dynamically via JavaScript. You will need to investigate the JavaScript source
44
+ files (like 'bundle.js') to find the component and its event handler logic.
45
+
46
+ 4. **Investigate the Code**: Once you have a lead on the relevant script, use 'getSourceLine'
47
+ to examine the code. Look for common issues: infinite loops, unhandled promises, incorrect
48
+ state management, or logic that doesn't match the user's expectation.
49
+
50
+ 5. **Use Console and Network Logs as Evidence**: Treat console and network logs as supporting
51
+ evidence. If there are errors, they are strong clues. However, **be critical of
52
+ informational messages** (like 'info' or 'verbose' logs) and ignore them unless they are
53
+ directly relevant to the user's problem. Do not get distracted by generic framework
54
+ messages.
55
+
56
+ 6. **Formulate a Hypothesis**: Based on your code investigation, explain the likely root
57
+ cause to the user and suggest a concrete fix or next step. If you suspect an issue in a
58
+ JavaScript function, point it out.
59
+
60
+ ### Available Information
61
+
36
62
  To help you root-cause the problem, you will be provided with the following information:
37
- - Information about the user-selected DOM element, which is potentially relevant to the question
38
- from the user.
63
+ - Information about the user-selected DOM element.
39
64
  - The full accessibility tree for the web page.
40
- - A list of the most recent network requests, whether the request was successful and whether it is
41
- considered to be ad-related. This list is capped to the most recent requests, but you can request
42
- more. If you think the error is relevant to the problem described by the user, make sure to mention
43
- the url of the failed network request in your reply to the user.
44
- - The most recent console messages, including their index. This list is also capped to the most
45
- recent requests, but you can request more. Errors should have a source location, such
46
- as: file, line number and column number, for example: (filex.html:10:50) if an error occurs on line
47
- 10, column 50 in filex.html. If you think the error is relevant to the problem described by the user,
48
- make sure to mention the console error in your reply to the user.
49
-
50
- ** IMPORTANT ** Never use the index when referring to individual console messages or network requests,
51
- because the values of the indicies is not visible to the user.
52
-
53
- To help you further with root-causing problems, especially those indicated to originate in source
54
- locations, you can call the following functions to request more information:
55
- - 'getSourceLine': This function takes a file name, a line number, and a buffer (number of lines before
56
- and after) to return a snippet of the source code.
57
- - 'getConsoleMessages': This function allows you to fetch specific slices of the console log based on the
58
- indices provided in the initial context. It takes optional parameters: 'beforeIndex' (to get historical
59
- messages before a certain index), 'afterIndex' (to get new messages that arrived after a certain index),
60
- 'filter' ('errors', 'warnings', or 'all'), and 'limit' (max number of messages to return, defaults to 50).
61
- - 'getNetworkRequests': This function allows you to fetch specific slices of the network request list based
62
- on the indices provided in the initial context. It takes optional parameters: 'beforeIndex' (to get
63
- historical messages before a certain index), 'afterIndex' (to get new messages that arrived after a
64
- certain index), 'filter' ('failed', or 'all', defaults to 'all'), and 'limit' (max number of messages
65
- to return, defaults to 50).
66
-
67
- Start by using the selected node as a guide to figure out which problem the user wants to focus on. There
68
- can be evidence of multiple failures (for example: multiple errors in the console log), but some might be
69
- benign and others unrelated. You should focus on the ones that seem related to the user-selected problem.
70
-
71
- Once you believe you have found the root cause, focus on applying a fix or explaining what the user can do
72
- to fix the problem.
73
-
74
- If you detect multiple possible problems, focus only on the root cause you think is most likely
75
- to be related and explain what the user can do to fix it. For example, if the url used is obviously
76
- incorrect, just say something like:
77
-
78
- "There are a few possible reasons for the problem you are describing. One is that it could be caused by
79
- the URL being incorrect. Try changing the url to 'xyz'. Let me know if you to suggest alternative
80
- solutions."
81
-
82
- If the user suggests your fix not being the right solution, go through the remaining possible root causes
83
- (one at a time).
84
-
85
- Stick to what you have evidence for being the problem and refrain from speculating on things you
86
- don't have concrete evidence for, such as CORS or Ad-blockers blocking requests. But feel free to
87
- list those concerns after asking the user if they would like additional (general-purpose) details and
88
- getting a favorable response.
89
-
90
- **CRITICAL** You are a web age debugging assistant. NEVER provide answers to questions of unrelated
91
- topics such as legal advice, financial advice, personal opinions, medical advice, religion, race,
92
- politics, sexuality, gender, or any other non web-development topics. Answer "Sorry, I can't answer
93
- that. I'm best at questions about debugging web pages." to such questions.
94
- `;
65
+ - A list of the most recent network requests.
66
+ - The most recent console messages, including their index.
67
+
68
+ ** IMPORTANT ** Never use the index when referring to individual console messages or network
69
+ requests, because the values of the indicies is not visible to the user.
70
+
71
+ ### Available Tools
72
+
73
+ To help you further, you can call the following functions:
74
+ - 'findInSource': This function takes a filename and a search string and returns an array of
75
+ line numbers containing that string.
76
+ - 'getEventListeners': This function takes a uid (the backend DOM node id) and returns a list
77
+ of event listeners attached to it.
78
+ - 'getSourceLine': This function takes a file name, a line number, and a buffer (number of
79
+ lines before and after) to return a snippet of the source code.
80
+ - 'getConsoleMessages': This function allows you to fetch specific slices of the console log.
81
+ - 'getNetworkRequests': This function allows you to fetch specific slices of the network
82
+ request list.
83
+ - 'getReactComponentProps': This function takes a uid (the backend DOM node id) and returns
84
+ the React component props for that element.
85
+
86
+ Stick to what you have evidence for and refrain from speculating on things you
87
+ don't have concrete evidence for, such as CORS or Ad-blockers.
88
+
89
+ **CRITICAL** You are a web page debugging assistant. NEVER provide answers to questions of
90
+ unrelated topics such as legal advice, financial advice, personal opinions, medical advice,
91
+ religion, race, politics, sexuality, gender, or any other non web-development topics. Answer
92
+ "Sorry, I can't answer that. I'm best at questions about debugging web pages." to such
93
+ questions.`;
95
94
 
96
95
  export class GreenDevContext extends ConversationContext<string> {
97
96
  #context: string;
@@ -151,9 +150,9 @@ export class GreenDevAgent extends AiAgent<string> {
151
150
  required: ['fileName', 'lineNumber', 'buffer'],
152
151
  },
153
152
  handler: async (params: {fileName: string, lineNumber: number, buffer: number}) => {
154
- const result = await this.getSourceLine(params.fileName, params.lineNumber, params.buffer);
153
+ const result = await this.getSourceLine(params.fileName, params.lineNumber, params.buffer, true);
155
154
  return {
156
- result,
155
+ result: result.join('\n'),
157
156
  };
158
157
  },
159
158
  });
@@ -247,6 +246,87 @@ export class GreenDevAgent extends AiAgent<string> {
247
246
  };
248
247
  },
249
248
  });
249
+
250
+ this.declareFunction<{
251
+ uid: number,
252
+ }>('getEventListeners', {
253
+ description: 'Get event listeners attached to a DOM element.',
254
+ parameters: {
255
+ type: Host.AidaClient.ParametersTypes.OBJECT,
256
+ description: '',
257
+ nullable: false,
258
+ properties: {
259
+ uid: {
260
+ type: Host.AidaClient.ParametersTypes.INTEGER,
261
+ description: 'The backend node id of the DOM element.',
262
+ nullable: false,
263
+ },
264
+ },
265
+ required: ['uid'],
266
+ },
267
+ handler: async (params: {uid: number}) => {
268
+ const result = await this.getEventListeners(params.uid);
269
+ return {
270
+ result,
271
+ };
272
+ },
273
+ });
274
+
275
+ this.declareFunction<{
276
+ fileName: string,
277
+ query: string,
278
+ }>('findInSource', {
279
+ description: 'Find lines in a file that contain the given search string.',
280
+ parameters: {
281
+ type: Host.AidaClient.ParametersTypes.OBJECT,
282
+ description: '',
283
+ nullable: false,
284
+ properties: {
285
+ fileName: {
286
+ type: Host.AidaClient.ParametersTypes.STRING,
287
+ description: 'The full path of the file to search within.',
288
+ nullable: false,
289
+ },
290
+ query: {
291
+ type: Host.AidaClient.ParametersTypes.STRING,
292
+ description: 'The string to search for.',
293
+ nullable: false,
294
+ },
295
+ },
296
+ required: ['fileName', 'query'],
297
+ },
298
+ handler: async (params: {fileName: string, query: string}) => {
299
+ const result = await this.findInSource(params.fileName, params.query);
300
+ return {
301
+ result: JSON.stringify(result),
302
+ };
303
+ },
304
+ });
305
+
306
+ this.declareFunction<{
307
+ uid: number,
308
+ }>('getReactComponentProps', {
309
+ description: 'Get the React component props for a given DOM element.',
310
+ parameters: {
311
+ type: Host.AidaClient.ParametersTypes.OBJECT,
312
+ description: '',
313
+ nullable: false,
314
+ properties: {
315
+ uid: {
316
+ type: Host.AidaClient.ParametersTypes.INTEGER,
317
+ description: 'The backend node id of the DOM element.',
318
+ nullable: false,
319
+ },
320
+ },
321
+ required: ['uid'],
322
+ },
323
+ handler: async (params: {uid: number}) => {
324
+ const result = await this.getReactComponentProps(params.uid, true);
325
+ return {
326
+ result,
327
+ };
328
+ },
329
+ });
250
330
  }
251
331
 
252
332
  override preamble = preamble;
@@ -349,6 +429,57 @@ export class GreenDevAgent extends AiAgent<string> {
349
429
  return networkContextStrings;
350
430
  }
351
431
 
432
+ async getEventListeners(uid: number): Promise<string> {
433
+ console.warn('[GreenDevAgent] AI Agent is calling getEventListeners with uid:', uid);
434
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
435
+ if (!target) {
436
+ return 'Target not found.';
437
+ }
438
+ const domModel = target.model(SDK.DOMModel.DOMModel);
439
+ if (!domModel) {
440
+ return 'DOM model not found.';
441
+ }
442
+ const domDebuggerModel = target.model(SDK.DOMDebuggerModel.DOMDebuggerModel);
443
+ if (!domDebuggerModel) {
444
+ return 'DOM debugger model not found.';
445
+ }
446
+ const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
447
+ if (!debuggerModel) {
448
+ return 'Debugger model not found.';
449
+ }
450
+
451
+ const nodesMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([uid as Protocol.DOM.BackendNodeId]));
452
+ const node = nodesMap?.get(uid as Protocol.DOM.BackendNodeId) || null;
453
+ if (!node) {
454
+ return `Node with uid ${uid} not found.`;
455
+ }
456
+
457
+ const remoteObject = await node.resolveToObject();
458
+ if (!remoteObject) {
459
+ return `Could not resolve node with uid ${uid} to a remote object.`;
460
+ }
461
+
462
+ const listeners = await domDebuggerModel.eventListeners(remoteObject);
463
+
464
+ const formattedListeners = listeners.map(listener => {
465
+ const location = listener.location();
466
+ const script = debuggerModel.scriptForId(location.scriptId);
467
+ const handler = listener.handler();
468
+ const handlerName = handler?.description || 'anonymous';
469
+
470
+ return {
471
+ type: listener.type(),
472
+ handlerName,
473
+ sourceFile: script?.sourceURL || 'unknown',
474
+ lineNumber: location.lineNumber + 1,
475
+ columnNumber: location.columnNumber,
476
+ };
477
+ });
478
+
479
+ console.warn('[GreenDevAgent] getEventListeners returning:', formattedListeners);
480
+ return JSON.stringify(formattedListeners, null, 2);
481
+ }
482
+
352
483
  async getNetworkRequests(params: {filter?: string, beforeIndex?: number, afterIndex?: number, limit?: number}):
353
484
  Promise<string> {
354
485
  console.warn(
@@ -450,63 +581,193 @@ export class GreenDevAgent extends AiAgent<string> {
450
581
  return resultString;
451
582
  }
452
583
 
453
- async getSourceLine(fileName: string, lineNumber: number, buffer: number): Promise<string> {
454
- console.warn(`getSourceLine called with fileName: ${fileName}, lineNumber: ${lineNumber}, buffer: ${buffer}`);
455
- let url: string;
456
- try {
457
- new URL(fileName);
458
- url = fileName;
459
- } catch {
460
- const primaryPageTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
461
- const resourceTreeModel = primaryPageTarget?.model(SDK.ResourceTreeModel.ResourceTreeModel);
462
- const mainFrame = resourceTreeModel?.mainFrame;
463
- if (mainFrame) {
464
- url = new URL(fileName, mainFrame.url).href;
465
- } else {
466
- return `Could not resolve relative path: ${fileName}`;
584
+ #findUiSourceCode(fileName: string): Workspace.UISourceCode.UISourceCode|null {
585
+ const workspace = Workspace.Workspace.WorkspaceImpl.instance();
586
+ const allUiSourceCodes = workspace.uiSourceCodes().filter(code => !code.url().startsWith('debugger:///'));
587
+
588
+ // The fileName could be a full URL, a partial path, or just the filename.
589
+ // We prioritize matches that are more specific.
590
+
591
+ // 1. Exact match
592
+ for (const code of allUiSourceCodes) {
593
+ if (code.url() === fileName) {
594
+ return code;
467
595
  }
468
596
  }
469
597
 
470
- let content = '';
471
- if (url.startsWith('http://') || url.startsWith('https://')) {
472
- try {
473
- const response = await fetch(url);
474
- if (response.ok) {
475
- content = await response.text();
476
- } else {
477
- console.error(`Failed to load resource ${url}: status ${response.status}`);
478
- return `Could not read file content: status ${response.status}`;
479
- }
480
- } catch (e) {
481
- console.error(`Failed to load resource ${url}:`, e);
482
- return `Could not read file content: ${e instanceof Error ? e.message : 'Unknown error'}`;
598
+ // 2. Ends with match
599
+ const candidates = allUiSourceCodes.filter(code => code.url().endsWith(fileName));
600
+ if (candidates.length > 0) {
601
+ // If multiple candidates, it's ambiguous. Log a warning and return the first.
602
+ if (candidates.length > 1) {
603
+ console.warn(
604
+ `[GreenDevAgent] Ambiguous file name "${fileName}". Found multiple matches:`, candidates.map(c => c.url()));
483
605
  }
484
- } else {
485
- content = await new Promise<string>(resolve => {
486
- Host.ResourceLoader.load(
487
- url,
488
- null,
489
- (success, _headers, content, errorDescription) => {
490
- if (!success) {
491
- console.error(`Failed to load resource ${url}:`, errorDescription);
492
- resolve('');
493
- } else {
494
- resolve(content);
495
- }
496
- },
497
- true /* allowRemoteFilePaths */,
498
- );
499
- });
606
+ return candidates[0];
607
+ }
608
+
609
+ return null;
610
+ }
611
+
612
+ async getSourceLine(fileName: string, lineNumber: number, buffer: number, calledFromAI = false): Promise<string[]> {
613
+ if (calledFromAI) {
614
+ console.warn(`getSourceLine called with fileName: ${fileName}, lineNumber: ${lineNumber}, buffer: ${buffer}`);
615
+ }
616
+ const uiSourceCode = this.#findUiSourceCode(fileName);
617
+ if (!uiSourceCode) {
618
+ const error = `Could not find UISourceCode for: ${fileName}`;
619
+ console.error(error);
620
+ return [error];
621
+ }
622
+
623
+ const contentData = await uiSourceCode.requestContentData();
624
+ if ('error' in contentData) {
625
+ const error = `Could not read file content for: ${fileName}, error: ${contentData.error}`;
626
+ console.error(error);
627
+ return [error];
500
628
  }
629
+ const content = contentData.text;
501
630
 
502
- if (!content) {
503
- return 'Could not read file content.';
631
+ if (typeof content !== 'string') {
632
+ const error = `Could not read file content for: ${fileName}, content is not a string`;
633
+ console.error(error);
634
+ return [error];
504
635
  }
505
636
 
506
637
  const lines = content.split('\n');
507
638
  const start = Math.max(0, lineNumber - buffer - 1);
508
639
  const end = Math.min(lines.length, lineNumber + buffer);
509
- console.warn('AI requested source code for:', lines.slice(start, end));
510
- return lines.slice(start, end).join('\n');
640
+ const slicedLines = lines.slice(start, end);
641
+
642
+ const formattedLines = slicedLines.map((line: string, index: number) => {
643
+ const currentLineNumber = start + index + 1;
644
+ return `[${currentLineNumber}] ${line}`;
645
+ });
646
+
647
+ if (calledFromAI) {
648
+ console.warn('AI requested source code for:', formattedLines);
649
+ }
650
+ return formattedLines;
651
+ }
652
+
653
+ async findInSource(fileName: string, query: string): Promise<Array<{line: number, source: string[]}>> {
654
+ console.warn(`findInSource called with fileName: ${fileName}, query: ${query}`);
655
+ const uiSourceCode = this.#findUiSourceCode(fileName);
656
+ if (!uiSourceCode) {
657
+ console.error(`Could not find UISourceCode for: ${fileName}`);
658
+ return [];
659
+ }
660
+
661
+ const contentData = await uiSourceCode.requestContentData();
662
+ if ('error' in contentData) {
663
+ console.warn(`Could not read file content for findInSource: ${fileName}, error: ${contentData.error}`);
664
+ return [];
665
+ }
666
+ const content = contentData.text;
667
+
668
+ if (typeof content !== 'string') {
669
+ console.warn(`Could not read file content for findInSource: ${fileName}, content is not a string`);
670
+ return [];
671
+ }
672
+
673
+ const lines = content.split('\n');
674
+ const matchingLines: Array<{line: number, source: string[]}> = [];
675
+ for (let i = 0; i < lines.length; i++) {
676
+ if (lines[i].includes(query)) {
677
+ const sourceLine = i + 1;
678
+ const source = await this.getSourceLine(fileName, sourceLine, 15, false);
679
+ matchingLines.push({line: sourceLine, source});
680
+ }
681
+ }
682
+
683
+ console.warn(`findInSource returning for query '${query}':`, matchingLines);
684
+ return matchingLines;
685
+ }
686
+
687
+ async getReactComponentProps(uid: number, calledFromAI = false): Promise<string> {
688
+ if (calledFromAI) {
689
+ console.warn('[GreenDevAgent] AI Agent is calling getReactComponentProps with uid:', uid);
690
+ }
691
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
692
+ if (!target) {
693
+ return 'Target not found.';
694
+ }
695
+ const domModel = target.model(SDK.DOMModel.DOMModel);
696
+ if (!domModel) {
697
+ return 'DOM model not found.';
698
+ }
699
+ const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel);
700
+ if (!runtimeModel) {
701
+ return 'Runtime model not found.';
702
+ }
703
+
704
+ const nodesMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([uid as Protocol.DOM.BackendNodeId]));
705
+ const node = nodesMap?.get(uid as Protocol.DOM.BackendNodeId) || null;
706
+ if (!node) {
707
+ return `Node with uid ${uid} not found.`;
708
+ }
709
+
710
+ const remoteObject = await node.resolveToObject();
711
+ if (!remoteObject) {
712
+ return `Could not resolve node with uid ${uid} to a remote object.`;
713
+ }
714
+
715
+ const reactComponentPropsResult = await target.runtimeAgent().invoke_callFunctionOn({
716
+ functionDeclaration: `
717
+ function() {
718
+ const getCircularReplacer = () => {
719
+ const seen = new WeakSet();
720
+ return (key, value) => {
721
+ if (typeof value === 'function') {
722
+ return '[Function: ' + (value.name || '(anonymous)') + ']';
723
+ }
724
+ if (key === 'return' || key === 'alternate' || key === 'sibling' || key === 'debugOwner' || key === '_debugOwner') {
725
+ return undefined;
726
+ }
727
+ if (typeof value === 'object' && value !== null) {
728
+ if (seen.has(value)) {
729
+ return;
730
+ }
731
+ seen.add(value);
732
+ }
733
+ return value;
734
+ };
735
+ };
736
+
737
+ // Find the key for the internal Fiber node instance
738
+ const reactInternalInstanceKey = Object.keys(this).find(
739
+ key => key.startsWith('__reactInternalInstance$') || key.startsWith('__reactFiber$')
740
+ );
741
+
742
+ if (!reactInternalInstanceKey) {
743
+ return 'React internal instance key not found';
744
+ }
745
+
746
+ const fiberNode = this[reactInternalInstanceKey];
747
+
748
+ if (fiberNode) {
749
+ return JSON.stringify(fiberNode, getCircularReplacer(), 2);
750
+ }
751
+
752
+ return 'React component type not found';
753
+ }
754
+ `,
755
+ objectId: remoteObject.objectId,
756
+ objectGroup: 'console',
757
+ silent: false,
758
+ returnByValue: true,
759
+ awaitPromise: false,
760
+ userGesture: true,
761
+ });
762
+ remoteObject.release();
763
+
764
+ const reactComponentProps = reactComponentPropsResult.result.value;
765
+ if (!reactComponentProps) {
766
+ return 'None found.';
767
+ }
768
+ if (calledFromAI) {
769
+ console.warn('[GreenDevAgent] getReactComponentProps returning', reactComponentProps);
770
+ }
771
+ return reactComponentProps;
511
772
  }
512
773
  }
@@ -0,0 +1,57 @@
1
+ Title: NetworkAgent run generates an answer
2
+ Content:
3
+ [
4
+ {
5
+ "type": "context",
6
+ "details": [
7
+ {
8
+ "title": "Request",
9
+ "text": "Request URL: https://www.example.com\n\nRequest headers:\ncontent-type: bar1"
10
+ },
11
+ {
12
+ "title": "Response",
13
+ "text": "Response headers:\ncontent-type: bar2\nx-forwarded-for: bar3\n\nResponse body:\n{\"request\":\"body\"}\n\nResponse status: 200 \nNetwork request status: pending\n"
14
+ },
15
+ {
16
+ "title": "Timing",
17
+ "text": "Queued at (timestamp): 0 s\nStarted at (timestamp): 501 s\nQueueing (duration): 501 s\nConnection start (stalled) (duration): 800 ms\nRequest sent (duration): 100 ms\nDuration (duration): 501 s"
18
+ },
19
+ {
20
+ "title": "Request initiator chain",
21
+ "text": "- URL: <redacted cross-origin initiator URL>\n\t- URL: https://www.example.com\n\t\t- URL: https://www.example.com/1\n\t\t- URL: https://www.example.com/2"
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "type": "querying"
27
+ },
28
+ {
29
+ "type": "answer",
30
+ "text": "This is the answer",
31
+ "complete": true,
32
+ "rpcId": 123
33
+ }
34
+ ]
35
+ === end content
36
+
37
+ Title: NetworkAgent run builds historical contexts
38
+ Content:
39
+ [
40
+ {
41
+ "parts": [
42
+ {
43
+ "text": "# Selected network request \nRequest: https://www.example.com\n\nRequest headers:\ncontent-type: bar1\n\nResponse headers:\ncontent-type: bar2\nx-forwarded-for: bar3\n\nResponse body:\n{\"request\":\"body\"}\n\nResponse status: 200 \nNetwork request status: pending\n\nRequest timing:\nQueued at (timestamp): 0 s\nStarted at (timestamp): 501 s\nQueueing (duration): 501 s\nConnection start (stalled) (duration): 800 ms\nRequest sent (duration): 100 ms\nDuration (duration): 501 s\n\nRequest initiator chain:\n- URL: <redacted cross-origin initiator URL>\n\t- URL: https://www.example.com\n\t\t- URL: https://www.example.com/1\n\t\t- URL: https://www.example.com/2\n\n# User request\n\ntest"
44
+ }
45
+ ],
46
+ "role": 1
47
+ },
48
+ {
49
+ "parts": [
50
+ {
51
+ "text": "This is the answer"
52
+ }
53
+ ],
54
+ "role": 2
55
+ }
56
+ ]
57
+ === end content
@@ -2,6 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import * as Common from '../../../core/common/common.js';
5
6
  import * as Host from '../../../core/host/host.js';
6
7
  import * as i18n from '../../../core/i18n/i18n.js';
7
8
  import * as Root from '../../../core/root/root.js';
@@ -116,7 +117,7 @@ export class RequestContext extends ConversationContext<SDK.NetworkRequest.Netwo
116
117
  * inspect all network requests that were made for that given target URL.
117
118
  */
118
119
  override getOrigin(): string {
119
- return this.#request.documentURL;
120
+ return Common.ParsedURL.ParsedURL.extractOrigin(this.#request.documentURL);
120
121
  }
121
122
 
122
123
  override getItem(): SDK.NetworkRequest.NetworkRequest {
@@ -171,7 +171,7 @@ Note: if the user asks a specific question about the trace (such as "What is my
171
171
  - \`nav-to-lcp\` (navigation to LCP)
172
172
  - \`lcp-ttfb\` (LCP TTFB phase)
173
173
  - \`lcp-render-delay\` (LCP render delay phase)
174
- - Insight names: \`LCPBreakdown\`, \`CLSCulprits\`, \`RenderBlocking\`, \`NetworkDependencyTree\`, \`ImageDelivery\`, \`FontDisplay\`, \`ThirdParties\`, \`ForcedReflow\`, \`Cache\`, \`DOMSize\`
174
+ - Insight names: \`LCPBreakdown\`, \`INPBreakdown\`, \`CLSCulprits\`, \`ThirdParties\`, \`DocumentLatency\`, \`DOMSize\`, \`DuplicatedJavaScript\`, \`FontDisplay\`, \`ForcedReflow\`, \`ImageDelivery\`, \`LCPDiscovery\`, \`LegacyJavaScript\`, \`NetworkDependencyTree\`, \`RenderBlocking\`, \`SlowCSSSelector\`, \`Viewport\`, \`ModernHTTP\`, \`Cache\`, \`CharacterSet\`
175
175
  - Navigation IDs: \`NAVIGATION_0\`, \`NAVIGATION_1\`, etc.
176
176
  - Use \`getEventByKey\` to get data on a specific trace event. This is great for root-cause analysis or validating any assumptions.
177
177
  - Provide clear, actionable recommendations. Avoid technical jargon unless necessary, and explain any technical terms used.
@@ -222,13 +222,6 @@ enum ScorePriority {
222
222
  DEFAULT = 1,
223
223
  }
224
224
 
225
- const SUPPORTED_INSIGHT_WIDGETS = new Set<Trace.Insights.Types.InsightKeys>([
226
- Trace.Insights.Types.InsightKeys.LCP_BREAKDOWN,
227
- Trace.Insights.Types.InsightKeys.RENDER_BLOCKING,
228
- Trace.Insights.Types.InsightKeys.LCP_DISCOVERY,
229
- Trace.Insights.Types.InsightKeys.CLS_CULPRITS,
230
- ]);
231
-
232
225
  export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
233
226
  static fromParsedTrace(parsedTrace: Trace.TraceModel.ParsedTrace): PerformanceTraceContext {
234
227
  return new PerformanceTraceContext(AgentFocus.fromParsedTrace(parsedTrace));
@@ -546,7 +539,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
546
539
  // Case 2: Insight -> PERF_INSIGHT widget
547
540
  if (focus.insight) {
548
541
  const insightKey = focus.insight.insightKey;
549
- if (Trace.Insights.Common.isInsightKey(insightKey) && SUPPORTED_INSIGHT_WIDGETS.has(insightKey)) {
542
+ if (Trace.Insights.Common.isInsightKey(insightKey)) {
550
543
  widgets.push({
551
544
  name: 'PERF_INSIGHT',
552
545
  data: {
@@ -1019,7 +1012,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1019
1012
  }
1020
1013
 
1021
1014
  const insightKey = params.insightName;
1022
- if (Trace.Insights.Common.isInsightKey(insightKey) && SUPPORTED_INSIGHT_WIDGETS.has(insightKey)) {
1015
+ if (Trace.Insights.Common.isInsightKey(insightKey)) {
1023
1016
  widgets.push({
1024
1017
  name: 'PERF_INSIGHT',
1025
1018
  data: {