chrome-devtools-frontend 1.0.1635648 → 1.0.1635876

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 (60) hide show
  1. package/front_end/core/sdk/SourceMapCache.ts +13 -11
  2. package/front_end/core/sdk/SourceMapManager.ts +7 -3
  3. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +84 -22
  4. package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +68 -5
  5. package/front_end/models/ai_assistance/agents/GreenDevAgentAntigravityCliSocketClient.ts +53 -0
  6. package/front_end/models/ai_assistance/agents/GreenDevAgentGeminiCliSocketClient.ts +117 -0
  7. package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
  8. package/front_end/models/greendev/Prototypes.ts +17 -5
  9. package/front_end/panels/settings/SettingsScreen.ts +3 -2
  10. package/front_end/panels/whats_new/ReleaseNoteText.ts +9 -9
  11. package/front_end/panels/whats_new/resources/WNDT.md +9 -9
  12. package/front_end/third_party/puppeteer/README.chromium +2 -2
  13. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +8 -5
  14. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +35 -33
  15. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  16. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.js +0 -1
  17. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.js.map +1 -1
  18. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts.map +1 -1
  19. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js +0 -3
  20. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js.map +1 -1
  21. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.d.ts +8 -8
  22. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.d.ts.map +1 -1
  23. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.js +8 -8
  24. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/Debug.js.map +1 -1
  25. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
  26. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
  27. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
  28. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
  29. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/CustomQuerySelector.d.ts +2 -2
  30. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/CustomQuerySelector.d.ts.map +1 -1
  31. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/CustomQuerySelector.js +2 -1
  32. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/CustomQuerySelector.js.map +1 -1
  33. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/injected.d.ts +2 -7
  34. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/injected.d.ts.map +1 -1
  35. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/PuppeteerNode.d.ts +1 -1
  36. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/PuppeteerNode.js +1 -1
  37. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  38. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  39. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
  40. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/Mutex.d.ts +10 -7
  41. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/Mutex.d.ts.map +1 -1
  42. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/Mutex.js +16 -12
  43. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/Mutex.js.map +1 -1
  44. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  45. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  46. package/front_end/third_party/puppeteer/package/lib/types.d.ts +8 -5
  47. package/front_end/third_party/puppeteer/package/package.json +5 -7
  48. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +0 -1
  49. package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +0 -3
  50. package/front_end/third_party/puppeteer/package/src/common/Debug.ts +11 -11
  51. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  52. package/front_end/third_party/puppeteer/package/src/injected/CustomQuerySelector.ts +3 -2
  53. package/front_end/third_party/puppeteer/package/src/node/PuppeteerNode.ts +1 -1
  54. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  55. package/front_end/third_party/puppeteer/package/src/util/Mutex.ts +17 -12
  56. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  57. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +7 -0
  58. package/front_end/ui/legacy/components/source_frame/FontView.ts +11 -3
  59. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  60. package/package.json +1 -10
@@ -2,6 +2,8 @@
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 type * as Platform from '../platform/platform.js';
6
+
5
7
  import type {DebugId, SourceMapV3} from './SourceMap.js';
6
8
 
7
9
  /** A thin wrapper around the Cache API to store source map JSONs keyed on Debug IDs */
@@ -28,14 +30,14 @@ export class SourceMapCache {
28
30
  this.#name = name;
29
31
  }
30
32
 
31
- async set(debugId: DebugId, sourceMap: SourceMapV3): Promise<void> {
33
+ async set(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString, sourceMap: SourceMapV3): Promise<void> {
32
34
  const cache = await this.#cache();
33
- await cache.put(SourceMapCache.#urlForDebugId(debugId), new Response(JSON.stringify(sourceMap)));
35
+ await cache.put(SourceMapCache.#urlForDebugId(debugId, securityOrigin), new Response(JSON.stringify(sourceMap)));
34
36
  }
35
37
 
36
- async get(debugId: DebugId): Promise<SourceMapV3|null> {
38
+ async get(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString): Promise<SourceMapV3|null> {
37
39
  const cache = await this.#cache();
38
- const response = await cache.match(SourceMapCache.#urlForDebugId(debugId));
40
+ const response = await cache.match(SourceMapCache.#urlForDebugId(debugId, securityOrigin));
39
41
  return await response?.json() ?? null;
40
42
  }
41
43
 
@@ -49,8 +51,8 @@ export class SourceMapCache {
49
51
  }
50
52
 
51
53
  /** The Cache API only allows URL as keys, so we construct a simple one. Given that we have our own cache, we have no risk of conflicting URLs */
52
- static #urlForDebugId(debugId: DebugId): string {
53
- return 'http://debug.id/' + encodeURIComponent(debugId);
54
+ static #urlForDebugId(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString): string {
55
+ return `http://debug.id/${encodeURIComponent(debugId)}?origin=${encodeURIComponent(securityOrigin)}`;
54
56
  }
55
57
 
56
58
  async disposeForTest(): Promise<void> {
@@ -59,14 +61,14 @@ export class SourceMapCache {
59
61
  }
60
62
 
61
63
  const IN_MEMORY_INSTANCE = new (class implements Pick<SourceMapCache, 'get'|'set'|'disposeForTest'> {
62
- readonly #cache = new Map<DebugId, SourceMapV3>();
64
+ readonly #cache = new Map<string, SourceMapV3>();
63
65
 
64
- async set(debugId: DebugId, sourceMap: SourceMapV3): Promise<void> {
65
- this.#cache.set(debugId, sourceMap);
66
+ async set(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString, sourceMap: SourceMapV3): Promise<void> {
67
+ this.#cache.set(`${debugId}|${securityOrigin}`, sourceMap);
66
68
  }
67
69
 
68
- async get(debugId: DebugId): Promise<SourceMapV3|null> {
69
- return this.#cache.get(debugId) ?? null;
70
+ async get(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString): Promise<SourceMapV3|null> {
71
+ return this.#cache.get(`${debugId}|${securityOrigin}`) ?? null;
70
72
  }
71
73
 
72
74
  async disposeForTest(): Promise<void> {
@@ -190,7 +190,9 @@ async function loadSourceMap(
190
190
  initiator: PageResourceLoadInitiator): Promise<SourceMapV3> {
191
191
  try {
192
192
  if (debugId) {
193
- const cachedSourceMap = await SourceMapCache.instance().get(debugId);
193
+ const securityOrigin = initiator.initiatorUrl ? Common.ParsedURL.ParsedURL.extractOrigin(initiator.initiatorUrl) :
194
+ Platform.DevToolsPath.EmptyUrlString;
195
+ const cachedSourceMap = await SourceMapCache.instance().get(debugId, securityOrigin);
194
196
  if (cachedSourceMap) {
195
197
  return cachedSourceMap;
196
198
  }
@@ -198,9 +200,11 @@ async function loadSourceMap(
198
200
 
199
201
  const {content} = await resourceLoader.loadResource(url, initiator);
200
202
  const sourceMap = parseSourceMap(content);
201
- if ('debugId' in sourceMap && sourceMap.debugId) {
203
+ if (debugId && 'debugId' in sourceMap && sourceMap.debugId === debugId) {
202
204
  // In case something goes wrong with updating the cache, we still want to use the source map.
203
- await SourceMapCache.instance().set(sourceMap.debugId as DebugId, sourceMap).catch();
205
+ const securityOrigin = initiator.initiatorUrl ? Common.ParsedURL.ParsedURL.extractOrigin(initiator.initiatorUrl) :
206
+ Platform.DevToolsPath.EmptyUrlString;
207
+ await SourceMapCache.instance().set(sourceMap.debugId as DebugId, securityOrigin, sourceMap).catch();
204
208
  }
205
209
  return sourceMap;
206
210
  } catch (cause) {
@@ -45,6 +45,9 @@ class GreenDevFloaty {
45
45
  #backendNodeId?: Protocol.DOM.BackendNodeId;
46
46
  #syncChannel: BroadcastChannel;
47
47
  #isFloatyWindow: boolean;
48
+ #socketClient:
49
+ AiAssistance.GreenDevAgentAntigravityCliSocketClient.GreenDevAgentAntigravityCliSocketClient|
50
+ AiAssistance.GreenDevAgentGeminiCliSocketClient.GreenDevAgentGeminiCliSocketClient;
48
51
 
49
52
  constructor(document: Document) {
50
53
  const params = new URLSearchParams(window.location.hash.substring(1));
@@ -57,6 +60,12 @@ class GreenDevFloaty {
57
60
  };
58
61
 
59
62
  this.#initFloatyMode(document);
63
+ if (Greendev.Prototypes.instance().isEnabled('beyondStylingAntigravity')) {
64
+ this.#socketClient =
65
+ new AiAssistance.GreenDevAgentAntigravityCliSocketClient.GreenDevAgentAntigravityCliSocketClient();
66
+ } else {
67
+ this.#socketClient = new AiAssistance.GreenDevAgentGeminiCliSocketClient.GreenDevAgentGeminiCliSocketClient();
68
+ }
60
69
  }
61
70
 
62
71
  #initFloatyMode(doc: Document): void {
@@ -269,12 +278,67 @@ class GreenDevFloaty {
269
278
  const query = this.#textField.value || this.#textField.placeholder;
270
279
  this.#textField.value = '';
271
280
 
272
- const useGreenDevAgent = Greendev.Prototypes.instance().isEnabled('beyondStyling');
281
+ const useAntigravity = Greendev.Prototypes.instance().isEnabled('beyondStylingAntigravity');
282
+ const useGemini = Greendev.Prototypes.instance().isEnabled('beyondStylingGemini');
283
+ const useGreenDevAgent = useAntigravity || useGemini;
284
+ const isAntigravity = useAntigravity;
273
285
 
274
286
  if (!this.#agent) {
275
287
  const aidaClient = new AidaClient();
276
288
  if (useGreenDevAgent) {
289
+ if (isAntigravity) {
290
+ this.#socketClient =
291
+ new AiAssistance.GreenDevAgentAntigravityCliSocketClient.GreenDevAgentAntigravityCliSocketClient();
292
+ } else {
293
+ this.#socketClient = new AiAssistance.GreenDevAgentGeminiCliSocketClient.GreenDevAgentGeminiCliSocketClient();
294
+ }
295
+
277
296
  this.#agent = new AiAssistance.GreenDevAgent.GreenDevAgent({aidaClient});
297
+ this.#agent.addEventListener(
298
+ AiAssistance.GreenDevAgent.Events.CLI_PROMPT_REQUESTED,
299
+ async (
300
+ event: Common.EventTarget.EventTargetEvent<
301
+ AiAssistance.GreenDevAgent.EventTypes[AiAssistance.GreenDevAgent.Events.CLI_PROMPT_REQUESTED]>) => {
302
+ const {prompt} = event.data;
303
+ const remoteName = isAntigravity ? 'Antigravity' : 'Gemini';
304
+ const aiContent =
305
+ this.#addMessageInternal(`Fix has been submitted to ${remoteName} CLI, please wait...`, false);
306
+
307
+ if (isAntigravity) {
308
+ let fullResponse = '';
309
+ void (
310
+ this.#socketClient as
311
+ AiAssistance.GreenDevAgentAntigravityCliSocketClient.GreenDevAgentAntigravityCliSocketClient)
312
+ .sendPrompt(prompt, (chunk: string) => {
313
+ fullResponse += chunk;
314
+ const responseText = `Antigravity CLI responds: ${fullResponse}`;
315
+ const markdown = new MarkdownView.MarkdownView.MarkdownView();
316
+ markdown.data = {
317
+ tokens: Marked.Marked.lexer(responseText),
318
+ };
319
+
320
+ const style = document.createElement('style');
321
+ style.textContent =
322
+ '.message { font-size: 1.0rem; } .message code { font-size: 1.0rem; font-family: \'Roboto Mono\', ' +
323
+ 'monospace; color: green; } .message ul { margin-left: 20px; }';
324
+ markdown.shadowRoot?.appendChild(style);
325
+
326
+ this.#updateAiMessage(aiContent, markdown, responseText);
327
+ });
328
+ } else {
329
+ const response =
330
+ await (this.#socketClient as
331
+ AiAssistance.GreenDevAgentGeminiCliSocketClient.GreenDevAgentGeminiCliSocketClient)
332
+ .sendPrompt(prompt);
333
+
334
+ const responseText = `Gemini CLI responds: ${response}`;
335
+ const markdown = new MarkdownView.MarkdownView.MarkdownView();
336
+ markdown.data = {
337
+ tokens: Marked.Marked.lexer(responseText),
338
+ };
339
+ this.#updateAiMessage(aiContent, markdown, responseText);
340
+ }
341
+ });
278
342
  } else {
279
343
  this.#agent = new AiAssistance.StylingAgent.StylingAgent({aidaClient});
280
344
  this.#nodeContext = new AiAssistance.StylingAgent.NodeContext(this.#node);
@@ -282,22 +346,7 @@ class GreenDevFloaty {
282
346
  }
283
347
 
284
348
  this.#addMessageInternal(query, true);
285
- this.#syncChannel.postMessage({
286
- type: 'new-message',
287
- text: query,
288
- isUser: true,
289
- sessionId: this.#backendNodeId,
290
- nodeDescription: document.querySelector('.green-dev-floaty-dialog-node-description')?.textContent,
291
- });
292
-
293
349
  const aiContent = this.#addMessageInternal('Thinking...', false);
294
- this.#syncChannel.postMessage({
295
- type: 'new-message',
296
- text: 'Thinking...',
297
- isUser: false,
298
- sessionId: this.#backendNodeId,
299
- nodeDescription: document.querySelector('.green-dev-floaty-dialog-node-description')?.textContent,
300
- });
301
350
 
302
351
  let agentFinished = false;
303
352
  let steps = 0;
@@ -433,7 +482,8 @@ ${axTree}`;
433
482
  markdown.data = {
434
483
  tokens: Marked.Marked.lexer(sanitizedText),
435
484
  };
436
- aiContent.append(markdown);
485
+ this.#updateAiMessage(aiContent, markdown, result.text);
486
+
437
487
  void new Promise(resolve => setTimeout(resolve, 0)).then(() => {
438
488
  const style = document.createElement('style');
439
489
  style.textContent =
@@ -456,15 +506,12 @@ ${axTree}`;
456
506
  }
457
507
  }
458
508
  });
459
- this.#syncChannel.postMessage(
460
- {type: 'update-last-message', text: result.text, sessionId: this.#backendNodeId});
461
509
  agentFinished = true;
462
510
  break;
463
511
  }
464
512
  case ResponseType.ERROR:
465
- aiContent.textContent = this.#formatError(result.error);
466
- this.#syncChannel.postMessage(
467
- {type: 'update-last-message', text: this.#formatError(result.error), sessionId: this.#backendNodeId});
513
+ this.#updateAiMessage(
514
+ aiContent, document.createTextNode(this.#formatError(result.error)), this.#formatError(result.error));
468
515
  agentFinished = true;
469
516
  break;
470
517
  case ResponseType.SIDE_EFFECT:
@@ -503,8 +550,23 @@ ${axTree}`;
503
550
  this.#chatContainer.appendChild(messageElement);
504
551
  this.#chatContainer.scrollTop = this.#chatContainer.scrollHeight;
505
552
  }
553
+
554
+ this.#syncChannel.postMessage({
555
+ type: 'new-message',
556
+ text,
557
+ isUser,
558
+ sessionId: this.#backendNodeId,
559
+ nodeDescription: document.querySelector('.green-dev-floaty-dialog-node-description')?.textContent,
560
+ });
561
+
506
562
  return content;
507
563
  }
564
+
565
+ #updateAiMessage(aiContentElement: HTMLDivElement, newContent: Node, newText: string): void {
566
+ aiContentElement.textContent = '';
567
+ aiContentElement.append(newContent);
568
+ this.#syncChannel.postMessage({type: 'update-last-message', text: newText, sessionId: this.#backendNodeId});
569
+ }
508
570
  }
509
571
 
510
572
  let greenDevFloatyInstance: GreenDevFloaty;
@@ -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 Root from '../../../core/root/root.js';
7
8
  import * as SDK from '../../../core/sdk/sdk.js';
@@ -53,9 +54,9 @@ additional context and resolve the user request.
53
54
  directly relevant to the user's problem. Do not get distracted by generic framework
54
55
  messages.
55
56
 
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.
57
+ 6. **Formulate a Hypothesis**: Based on your code investigation, and if you have a promising
58
+ fix, ALWAYS apply it using the provided 'applyFix' function. If you can identify more than
59
+ one fix, ask the user which one to apply.
59
60
 
60
61
  ### Available Information
61
62
 
@@ -82,6 +83,7 @@ To help you further, you can call the following functions:
82
83
  request list.
83
84
  - 'getReactComponentProps': This function takes a uid (the backend DOM node id) and returns
84
85
  the React component props for that element.
86
+ - 'applyFix': This function accepts a code diff to apply to the code base.
85
87
 
86
88
  Stick to what you have evidence for and refrain from speculating on things you
87
89
  don't have concrete evidence for, such as CORS or Ad-blockers.
@@ -112,11 +114,38 @@ export class GreenDevContext extends ConversationContext<string> {
112
114
  }
113
115
  }
114
116
 
117
+ export const enum Events {
118
+ CLI_PROMPT_REQUESTED = 'CliPromptRequested',
119
+ }
120
+
121
+ export interface EventTypes {
122
+ [Events.CLI_PROMPT_REQUESTED]: {prompt: string};
123
+ }
124
+
125
+ export const enum RemoteEndpoint {
126
+ GEMINI_CLI_SOCKET = 'GeminiCliSocket',
127
+ ANTIGRAVITY_CLI_SOCKET = 'AntigravityCliSocket',
128
+ }
129
+
115
130
  /**
116
131
  * This agent is a general-purpose web page troubleshooting agent for GreenDev
117
132
  * prototypes.
118
133
  */
119
134
  export class GreenDevAgent extends AiAgent<string> {
135
+ #eventTarget = new Common.ObjectWrapper.ObjectWrapper<EventTypes>();
136
+
137
+ addEventListener<T extends keyof EventTypes>(
138
+ eventType: T, listener: (arg0: Common.EventTarget.EventTargetEvent<EventTypes[T]>) => void,
139
+ thisObject?: Object): Common.EventTarget.EventDescriptor<EventTypes, T> {
140
+ return this.#eventTarget.addEventListener(eventType, listener, thisObject);
141
+ }
142
+
143
+ removeEventListener<T extends keyof EventTypes>(
144
+ eventType: T, listener: (arg0: Common.EventTarget.EventTargetEvent<EventTypes[T]>) => void,
145
+ thisObject?: Object): void {
146
+ this.#eventTarget.removeEventListener(eventType, listener, thisObject);
147
+ }
148
+
120
149
  constructor(options: AgentOptions) {
121
150
  super(options);
122
151
 
@@ -327,6 +356,38 @@ export class GreenDevAgent extends AiAgent<string> {
327
356
  };
328
357
  },
329
358
  });
359
+
360
+ this.declareFunction<{
361
+ codeSuggestionDiff: string,
362
+ }>('applyFix', {
363
+ description: 'Apply a code fix for the user to review.',
364
+ parameters: {
365
+ type: Host.AidaClient.ParametersTypes.OBJECT,
366
+ description: '',
367
+ nullable: false,
368
+ properties: {
369
+ codeSuggestionDiff: {
370
+ type: Host.AidaClient.ParametersTypes.STRING,
371
+ description: 'The diff of the suggested code change.',
372
+ nullable: false,
373
+ },
374
+ },
375
+ required: ['codeSuggestionDiff'],
376
+ },
377
+ handler: async (params: {codeSuggestionDiff: string}) => {
378
+ const result = await this.applyFix(params.codeSuggestionDiff);
379
+ return {
380
+ result,
381
+ };
382
+ },
383
+ });
384
+ }
385
+
386
+ async applyFix(codeSuggestionDiff: string): Promise<string> {
387
+ console.warn('[GreenDevAgent] applyFix called with:', codeSuggestionDiff);
388
+ this.#eventTarget.dispatchEventToListeners(
389
+ Events.CLI_PROMPT_REQUESTED, {prompt: `Apply this diff:\n${codeSuggestionDiff}`});
390
+ return 'The fix suggestion has been submitted.';
330
391
  }
331
392
 
332
393
  override preamble = preamble;
@@ -377,8 +438,10 @@ export class GreenDevAgent extends AiAgent<string> {
377
438
  }
378
439
 
379
440
  static isEnabled(): boolean {
380
- console.warn('BeyondStyling prototype is enabled:', Greendev.Prototypes.instance().isEnabled('beyondStyling'));
381
- return Greendev.Prototypes.instance().isEnabled('beyondStyling');
441
+ const isGeminiEnabled = Greendev.Prototypes.instance().isEnabled('beyondStylingGemini');
442
+ const isAntigravityEnabled = Greendev.Prototypes.instance().isEnabled('beyondStylingAntigravity');
443
+ console.warn('BeyondStyling prototype is enabled:', isGeminiEnabled || isAntigravityEnabled);
444
+ return isGeminiEnabled || isAntigravityEnabled;
382
445
  }
383
446
 
384
447
  static formatConsoleMessage(message: SDK.ConsoleModel.ConsoleMessage, index: number): string {
@@ -0,0 +1,53 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ export class GreenDevAgentAntigravityCliSocketClient {
6
+ #websocket: WebSocket;
7
+ sessionReady: Promise<void>;
8
+ #sessionReadyResolve: ((value: void|PromiseLike<void>) => void)|null = null;
9
+
10
+ constructor() {
11
+ this.sessionReady = new Promise(resolve => {
12
+ this.#sessionReadyResolve = resolve;
13
+ });
14
+ this.#websocket = new WebSocket('ws://localhost:5566');
15
+ this.#websocket.onopen = this.#onOpen.bind(this);
16
+ this.#websocket.onmessage = this.#onMessage.bind(this);
17
+ this.#websocket.onclose = this.#onClose.bind(this);
18
+ this.#websocket.onerror = this.#onError.bind(this);
19
+ }
20
+
21
+ #onOpen(): void {
22
+ console.warn('WebSocket connected (Antigravity).');
23
+ if (this.#sessionReadyResolve) {
24
+ this.#sessionReadyResolve();
25
+ this.#sessionReadyResolve = null;
26
+ }
27
+ }
28
+
29
+ #onMessage(event: MessageEvent): void {
30
+ console.warn('Antigravity WebSocket message received:', event.data);
31
+ if (this.#onChunkCallback) {
32
+ this.#onChunkCallback(event.data);
33
+ }
34
+ }
35
+
36
+ #onClose(): void {
37
+ console.warn('WebSocket disconnected (Antigravity).');
38
+ }
39
+
40
+ #onError(error: Event): void {
41
+ console.error('WebSocket error (Antigravity):', error);
42
+ }
43
+
44
+ #onChunkCallback: ((chunk: string) => void)|null = null;
45
+
46
+ sendPrompt(promptText: string, onChunk: (chunk: string) => void): Promise<void> {
47
+ this.#onChunkCallback = onChunk;
48
+
49
+ console.warn(`Sending Antigravity prompt: "${promptText}"`);
50
+ this.#websocket.send(promptText);
51
+ return Promise.resolve();
52
+ }
53
+ }
@@ -0,0 +1,117 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ export class GreenDevAgentGeminiCliSocketClient {
6
+ #websocket: WebSocket;
7
+ #sessionId?: string;
8
+ #activeMessage = '';
9
+ #messageLog: string[] = [];
10
+ #promptResolve: ((response: string) => void)|null = null;
11
+ sessionReady: Promise<void>;
12
+ #sessionReadyResolve: ((value: void|PromiseLike<void>) => void)|null = null;
13
+
14
+ constructor() {
15
+ this.sessionReady = new Promise(resolve => {
16
+ this.#sessionReadyResolve = resolve;
17
+ });
18
+ this.#websocket = new WebSocket('ws://localhost:6655');
19
+ this.#websocket.onopen = this.#onOpen.bind(this);
20
+ this.#websocket.onmessage = this.#onMessage.bind(this);
21
+ this.#websocket.onclose = this.#onClose.bind(this);
22
+ this.#websocket.onerror = this.#onError.bind(this);
23
+ }
24
+
25
+ #onOpen(): void {
26
+ console.warn('WebSocket connected.');
27
+ this.#websocket.send(
28
+ JSON.stringify({jsonrpc: '2.0', method: 'session/new', params: {cwd: '.', mcpServers: []}, id: 14}));
29
+ }
30
+
31
+ #onMessage(event: MessageEvent): void {
32
+ this.#messageLog.push(event.data);
33
+
34
+ try {
35
+ const data = JSON.parse(event.data);
36
+
37
+ if (data?.result?.sessionId && !this.#sessionId) {
38
+ this.#sessionId = data.result.sessionId;
39
+ console.warn(`Successfully created new session with ID: ${this.#sessionId}`);
40
+ if (this.#sessionReadyResolve) {
41
+ this.#sessionReadyResolve();
42
+ this.#sessionReadyResolve = null;
43
+ }
44
+ }
45
+
46
+ const update = data?.params?.update;
47
+ if (update?.sessionUpdate === 'agent_message_chunk') {
48
+ this.#activeMessage += update.content?.text || '';
49
+ }
50
+
51
+ if (data?.result?.stopReason) {
52
+ if (this.#activeMessage) {
53
+ console.warn(this.#activeMessage);
54
+ }
55
+ console.warn(`Stop Reason: ${data.result.stopReason}`, this.#messageLog);
56
+
57
+ if (this.#promptResolve) {
58
+ this.#promptResolve(this.#activeMessage);
59
+ this.#promptResolve = null;
60
+ }
61
+
62
+ this.#activeMessage = '';
63
+ this.#messageLog = [];
64
+ } else if (data?.method === 'session/request_permission') {
65
+ // TODO(finnur): Needs augmenting once I turn off YOLO in the bridge.
66
+ this.#websocket.send(JSON.stringify({
67
+ jsonrpc: '2.0',
68
+ id: data.id,
69
+ result: {
70
+ outcome: {
71
+ selected: {
72
+ optionId: 'proceed_always',
73
+ },
74
+ },
75
+ },
76
+ }));
77
+ }
78
+ } catch (e) {
79
+ // Log the raw data if it's not valid JSON.
80
+ console.error('Failed to parse WebSocket message or process data:', event.data, e);
81
+ }
82
+ }
83
+
84
+ #onClose(): void {
85
+ console.warn('WebSocket disconnected.');
86
+ }
87
+
88
+ #onError(error: Event): void {
89
+ console.error('WebSocket error:', error);
90
+ }
91
+
92
+ sendPrompt(promptText: string): Promise<string> {
93
+ return new Promise((resolve, reject) => {
94
+ if (this.#promptResolve) {
95
+ reject(new Error('Another prompt is already in progress.'));
96
+ return;
97
+ }
98
+ if (!this.#sessionId) {
99
+ reject(new Error('Cannot send prompt without a session ID.'));
100
+ return;
101
+ }
102
+ this.#promptResolve = resolve;
103
+
104
+ console.warn(`Sending prompt: "${promptText}"`);
105
+ console.warn('Thinking...');
106
+ this.#websocket.send(JSON.stringify({
107
+ jsonrpc: '2.0',
108
+ method: 'session/prompt',
109
+ params: {
110
+ prompt: [{type: 'text', text: promptText}],
111
+ sessionId: this.#sessionId,
112
+ },
113
+ id: 15,
114
+ }));
115
+ });
116
+ }
117
+ }
@@ -10,6 +10,8 @@ import * as ContextSelectionAgent from './agents/ContextSelectionAgent.js';
10
10
  import * as ConversationSummaryAgent from './agents/ConversationSummaryAgent.js';
11
11
  import * as FileAgent from './agents/FileAgent.js';
12
12
  import * as GreenDevAgent from './agents/GreenDevAgent.js';
13
+ import * as GreenDevAgentAntigravityCliSocketClient from './agents/GreenDevAgentAntigravityCliSocketClient.js';
14
+ import * as GreenDevAgentGeminiCliSocketClient from './agents/GreenDevAgentGeminiCliSocketClient.js';
13
15
  import * as NetworkAgent from './agents/NetworkAgent.js';
14
16
  import * as PatchAgent from './agents/PatchAgent.js';
15
17
  import * as PerformanceAgent from './agents/PerformanceAgent.js';
@@ -57,6 +59,8 @@ export {
57
59
  FileAgent,
58
60
  FileFormatter,
59
61
  GreenDevAgent,
62
+ GreenDevAgentAntigravityCliSocketClient,
63
+ GreenDevAgentGeminiCliSocketClient,
60
64
  Injected,
61
65
  LighthouseFormatter,
62
66
  NetworkAgent,
@@ -9,7 +9,8 @@ let instance: Prototypes|null = null;
9
9
 
10
10
  export interface GreenDevSettings {
11
11
  aiAnnotations: Common.Settings.Setting<boolean>;
12
- beyondStyling: Common.Settings.Setting<boolean>;
12
+ beyondStylingGemini: Common.Settings.Setting<boolean>;
13
+ beyondStylingAntigravity: Common.Settings.Setting<boolean>;
13
14
  breakpointDebuggerAgent: Common.Settings.Setting<boolean>;
14
15
  emulationCapabilities: Common.Settings.Setting<boolean>;
15
16
  }
@@ -42,10 +43,15 @@ export class Prototypes {
42
43
  false,
43
44
  Common.Settings.SettingStorageType.LOCAL,
44
45
  );
45
- const beyondStyling = settings.createSetting(
46
- 'greendev-beyond-styling-enabled',
46
+ const beyondStylingGemini = settings.createSetting(
47
+ 'greendev-beyond-styling-gemini-enabled',
47
48
  false,
48
- Common.Settings.SettingStorageType.LOCAL,
49
+ Common.Settings.SettingStorageType.GLOBAL,
50
+ );
51
+ const beyondStylingAntigravity = settings.createSetting(
52
+ 'greendev-beyond-styling-antigravity-enabled',
53
+ false,
54
+ Common.Settings.SettingStorageType.GLOBAL,
49
55
  );
50
56
  const breakpointDebuggerAgent = settings.createSetting(
51
57
  'greendev-breakpoint-debugger-agent-enabled',
@@ -58,6 +64,12 @@ export class Prototypes {
58
64
  Common.Settings.SettingStorageType.LOCAL,
59
65
  );
60
66
 
61
- return {aiAnnotations, beyondStyling, breakpointDebuggerAgent, emulationCapabilities};
67
+ return {
68
+ aiAnnotations,
69
+ beyondStylingGemini,
70
+ beyondStylingAntigravity,
71
+ breakpointDebuggerAgent,
72
+ emulationCapabilities
73
+ };
62
74
  }
63
75
  }
@@ -636,7 +636,7 @@ const GREENDEV_VIEW: View = (input, _output, target) => {
636
636
  <span>${i18nString(UIStrings.greenDevUnstable)}</span>
637
637
  </div>
638
638
  <div class="settings-experiments-block">
639
- ${renderPrototypeCheckboxes(input.settings, ['aiAnnotations', 'beyondStyling', 'breakpointDebuggerAgent', 'emulationCapabilities'])}
639
+ ${renderPrototypeCheckboxes(input.settings, ['aiAnnotations', 'beyondStylingGemini', 'beyondStylingAntigravity', 'breakpointDebuggerAgent', 'emulationCapabilities'])}
640
640
  </div>
641
641
  </devtools-card>
642
642
  </div>
@@ -646,7 +646,8 @@ const GREENDEV_VIEW: View = (input, _output, target) => {
646
646
 
647
647
  const GREENDEV_PROTOTYPE_NAMES: Record<keyof GreenDev.GreenDevSettings, string> = {
648
648
  aiAnnotations: 'AI auto-annotations',
649
- beyondStyling: 'Beyond Styling',
649
+ beyondStylingGemini: 'Beyond Styling (Gemini CLI)',
650
+ beyondStylingAntigravity: 'Beyond Styling (Antigravity CLI)',
650
651
  breakpointDebuggerAgent: 'Breakpoint Debugger Agent',
651
652
  emulationCapabilities: 'Emulation Capabilities',
652
653
  };
@@ -40,22 +40,22 @@ export function getReleaseNote(): ReleaseNote {
40
40
  }
41
41
 
42
42
  let releaseNote: ReleaseNote = {
43
- version: 148,
44
- header: 'What\'s new in DevTools 148',
43
+ version: 149,
44
+ header: 'Whats new in DevTools 149',
45
45
  markdownLinks: [
46
46
  {
47
- key: 'extension-debugging',
48
- link: 'https://developer.chrome.com/blog/new-in-devtools-148/#extension-debugging',
47
+ key: 'devtools-for-agents',
48
+ link: 'https://developer.chrome.com/blog/new-in-devtools-149/#devtools-for-agents',
49
49
  },
50
50
  {
51
- key: 'sort-network-requests',
52
- link: 'https://developer.chrome.com/blog/new-in-devtools-148/#sort-network-requests',
51
+ key: 'ai-assistance',
52
+ link: 'https://developer.chrome.com/blog/new-in-devtools-149/#ai-assistance',
53
53
  },
54
54
  {
55
- key: 'accessibility-tree',
56
- link: 'https://developer.chrome.com/blog/new-in-devtools-148/#accessibility-tree',
55
+ key: 'css-code-completion',
56
+ link: 'https://developer.chrome.com/blog/new-in-devtools-149/#css-code-completion',
57
57
  }
58
58
  ],
59
59
  videoLinks: [],
60
- link: 'https://developer.chrome.com/blog/new-in-devtools-148/',
60
+ link: 'https://developer.chrome.com/blog/new-in-devtools-149/',
61
61
  };