chrome-devtools-frontend 1.0.1592129 → 1.0.1592362
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.
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/root/ExperimentNames.ts +1 -0
- package/front_end/core/sdk/EmulationModel.ts +41 -1
- package/front_end/entrypoints/main/MainImpl.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.ts +1 -0
- package/front_end/generated/protocol-mapping.d.ts +6 -0
- package/front_end/generated/protocol-proxy-api.d.ts +7 -0
- package/front_end/generated/protocol.ts +16 -0
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +379 -25
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgentOverlay.ts +87 -0
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +8 -8
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +65 -44
- package/front_end/models/computed_style/ComputedStyleModel.ts +40 -6
- package/front_end/models/emulation/DeviceModeModel.ts +47 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +11 -2
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +55 -1
- package/front_end/panels/application/CookieItemsView.ts +194 -134
- package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +1 -1
- package/front_end/panels/application/StorageItemsToolbar.ts +25 -2
- package/front_end/panels/application/cookieItemsView.css +28 -26
- package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
- package/front_end/panels/elements/ComputedStyleWidget.ts +11 -25
- package/front_end/panels/elements/ElementsPanel.ts +26 -3
- package/front_end/panels/elements/StylePropertyTreeElement.ts +33 -0
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +148 -1
- package/front_end/panels/elements/elements-meta.ts +3 -5
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +6 -0
- package/front_end/panels/emulation/DeviceModeToolbar.ts +25 -0
- package/front_end/panels/timeline/components/metricCard.css +0 -4
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +13 -2
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +44 -24
- package/front_end/ui/visual_logging/Debugging.ts +4 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -20,9 +20,16 @@ import {
|
|
|
20
20
|
type ContextResponse,
|
|
21
21
|
ConversationContext,
|
|
22
22
|
type FunctionCallHandlerResult,
|
|
23
|
+
type FunctionHandlerOptions,
|
|
24
|
+
type MultimodalInput,
|
|
23
25
|
type RequestOptions,
|
|
26
|
+
type ResponseData,
|
|
24
27
|
ResponseType,
|
|
25
28
|
} from './AiAgent.js';
|
|
29
|
+
import {
|
|
30
|
+
injectOverlay,
|
|
31
|
+
removeOverlay,
|
|
32
|
+
} from './BreakpointDebuggerAgentOverlay.js';
|
|
26
33
|
|
|
27
34
|
// This is a temporary agent for a GreenDev prototype.
|
|
28
35
|
// The preamble is not on the server and you should not build on top of this.
|
|
@@ -39,20 +46,23 @@ You have two modes of operation that you can switch between and control:
|
|
|
39
46
|
**Workflow**:
|
|
40
47
|
1. **Hypothesize**: Read the code ('getFunctionSource', 'getPreviousLines', 'getNextLines') to understand the logic.
|
|
41
48
|
2. **Set Trap**: Identify the critical line where state corruption likely occurred or lines that can lead you to that place. Use 'setBreakpoint' on that line.
|
|
42
|
-
3. **Wait**: Call '
|
|
49
|
+
3. **Wait**: Call 'waitForUserActionToTriggerBreakpoint'. This will suspend your execution until the user triggers the breakpoint. You CANNOT proceed until this tool returns.
|
|
43
50
|
4. **Inspect**: Using 'getExecutionLocation' check exactly where you are paused.
|
|
44
51
|
5. **Analyze**: When paused (Runtime Mode), use 'getScopeVariables' and 'getCallStack' to verify your hypothesis. Check variables in multiple scopes and look up the call stack to see where bad data came from.
|
|
45
52
|
6. **Step**: Use 'stepInto' to investigate function calls on the current line. Use 'stepOut' to return to the caller. Use 'stepOver' to move to the next line.
|
|
46
53
|
7. **Trace Back**: If the current function isn't the root cause, use 'getCallStack' to find the caller, and repeat the analysis there.
|
|
47
54
|
8. **Root Cause**: Explain exactly how the runtime state contradicts the expected logic and point to the specific line of code that is the root cause.
|
|
55
|
+
9. **Test Fix**: Use the 'testFixInConsole' tool to run a JavaScript snippet in the current execution context to test your fix before making permanent changes. This will overwrite the problematic function or state. Follow the instructions to get the user's approval.
|
|
56
|
+
10. **Verify**: IMMEDIATELY AFTER 'testFixInConsole' succeeds, you MUST call 'waitForUserActionToTriggerBreakpoint' (to pause and inspect) or 'resume' (if no inspection is needed), AND ask the user to trigger the buggy action again to verify the fix.
|
|
57
|
+
11. **Patch**: Once the fix is successfully verified by the user, you MUST use the 'suggestFix' tool to apply the patch to the source code file. Provide the URL, the exact original code to change, and the new code. Finish the execution then.
|
|
48
58
|
|
|
49
59
|
**Rules**:
|
|
50
60
|
- **NEVER FINISH** execution until you have found the root cause or answer.
|
|
51
|
-
- **ACTION OVER TALK**: If you need the user to trigger a breakpoint, do NOT just ask them in text. You **MUST** call '
|
|
52
|
-
- **STATIC MODE**: If you are in STATIC MODE and need to see variables: 1. 'setBreakpoint', 2. '
|
|
53
|
-
- **ALREADY PAUSED?**: If 'setBreakpoint' warns you that you are already paused, **DO NOT** call '
|
|
61
|
+
- **ACTION OVER TALK**: If you need the user to trigger a breakpoint, do NOT just ask them in text. You **MUST** call 'waitForUserActionToTriggerBreakpoint'. This tool will block and wait for the user to act.
|
|
62
|
+
- **STATIC MODE**: If you are in STATIC MODE and need to see variables: 1. 'setBreakpoint', 2. 'waitForUserActionToTriggerBreakpoint'. **DO NOT STOP** to ask the user. Investigate code and set breakpoints to find the root cause.
|
|
63
|
+
- **ALREADY PAUSED?**: If 'setBreakpoint' warns you that you are already paused, **DO NOT** call 'waitForUserActionToTriggerBreakpoint'. Start inspecting immediately. You can set more breakpoints while paused, but to call 'waitForUserActionToTriggerBreakpoint' again you MUST be in static state.
|
|
54
64
|
- **USE TOOLS EXCESSIVELY**: checking one thing is often not enough. Check everything you can thinks of.
|
|
55
|
-
- **CHECK LOCATION**: If you are not sure where you are, call 'getExecutionLocation' after '
|
|
65
|
+
- **CHECK LOCATION**: If you are not sure where you are, call 'getExecutionLocation' after 'waitForUserActionToTriggerBreakpoint' or any step command to confirm where you are.
|
|
56
66
|
|
|
57
67
|
**Execution Control when you are currently on a breakpoint**:
|
|
58
68
|
- **stepInto**: ESSENTIAL for entering function calls on the current line. Use this heavily when you suspect the issue is inside a called function.
|
|
@@ -60,6 +70,8 @@ You have two modes of operation that you can switch between and control:
|
|
|
60
70
|
- **stepOut**: Return to the caller. If you are currently on a breakpoint, 'stepOut' will move you to the caller and pause again. **It often makes sense to 'stepOut' after you have investigated a function with 'stepInto' and verified it is correct.**
|
|
61
71
|
- **stepInto, stepOver, stepOut**: After any step command, always call 'getScopeVariables' to see how the state evolved.
|
|
62
72
|
- **listBreakpoints**: Use this to see all active breakpoints. Do not try to set a breakpoint that is already active.
|
|
73
|
+
- **removeBreakpoint / removeAllBreakpoints**: Use this to remove breakpoints. This is especially useful when you want to speed up verifying a fix.
|
|
74
|
+
- **CLEANUP AFTER FIX**: After a fix is suggested and worked, you MUST remove all breakpoints and call 'resume' to resume the execution of the page.
|
|
63
75
|
`;
|
|
64
76
|
|
|
65
77
|
export class BreakpointContext extends ConversationContext<Workspace.UISourceCode.UILocation> {
|
|
@@ -108,6 +120,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
108
120
|
},
|
|
109
121
|
required: ['url', 'lineNumber'],
|
|
110
122
|
},
|
|
123
|
+
displayInfoFromArgs: (args: {url: string, lineNumber: number}) => {
|
|
124
|
+
return {
|
|
125
|
+
title: `Reading function source for ${args.url}:${args.lineNumber}`,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
111
128
|
handler: async (args: {url: string, lineNumber: number}) => {
|
|
112
129
|
const result = await this.#getFunctionSource(args);
|
|
113
130
|
debugLog('getFunctionSource for ', JSON.stringify(args), '->', JSON.stringify(result));
|
|
@@ -137,6 +154,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
137
154
|
},
|
|
138
155
|
required: ['url', 'lineNumber', 'direction'],
|
|
139
156
|
},
|
|
157
|
+
displayInfoFromArgs: (args: {url: string, lineNumber: number, direction: 'before'|'after'}) => {
|
|
158
|
+
return {
|
|
159
|
+
title: `Reading code ${args.direction} ${args.url}:${args.lineNumber}`,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
140
162
|
handler: async (args: {url: string, lineNumber: number, direction: 'before'|'after'}) => {
|
|
141
163
|
const result = await this.#getCodeLines(args);
|
|
142
164
|
debugLog('getCodeLines result', JSON.stringify(result));
|
|
@@ -151,6 +173,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
151
173
|
properties: {},
|
|
152
174
|
required: [],
|
|
153
175
|
},
|
|
176
|
+
displayInfoFromArgs: () => {
|
|
177
|
+
return {
|
|
178
|
+
title: 'Reading call stack',
|
|
179
|
+
};
|
|
180
|
+
},
|
|
154
181
|
handler: async () => {
|
|
155
182
|
const result = await this.#getCallStack();
|
|
156
183
|
debugLog('getCallStack result', JSON.stringify(result));
|
|
@@ -165,6 +192,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
165
192
|
properties: {},
|
|
166
193
|
required: [],
|
|
167
194
|
},
|
|
195
|
+
displayInfoFromArgs: () => {
|
|
196
|
+
return {
|
|
197
|
+
title: 'Reading scope variables',
|
|
198
|
+
};
|
|
199
|
+
},
|
|
168
200
|
handler: async () => {
|
|
169
201
|
const result = await this.#getScopeVariables();
|
|
170
202
|
debugLog('getScopeVariables result', JSON.stringify(result));
|
|
@@ -179,6 +211,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
179
211
|
properties: {},
|
|
180
212
|
required: [],
|
|
181
213
|
},
|
|
214
|
+
displayInfoFromArgs: () => {
|
|
215
|
+
return {
|
|
216
|
+
title: 'Listing breakpoints',
|
|
217
|
+
};
|
|
218
|
+
},
|
|
182
219
|
handler: async () => {
|
|
183
220
|
const result = await this.#listBreakpoints();
|
|
184
221
|
debugLog('listBreakpoints result', JSON.stringify(result));
|
|
@@ -202,6 +239,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
202
239
|
},
|
|
203
240
|
required: ['url', 'lineNumber'],
|
|
204
241
|
},
|
|
242
|
+
displayInfoFromArgs: (args: {url: string, lineNumber: number}) => {
|
|
243
|
+
return {
|
|
244
|
+
title: `Setting breakpoint at ${args.url}:${args.lineNumber}`,
|
|
245
|
+
};
|
|
246
|
+
},
|
|
205
247
|
handler: async (args: {url: string, lineNumber: number}) => {
|
|
206
248
|
debugLog('setBreakpoint requested', args);
|
|
207
249
|
const result = await this.#setBreakpoint(args);
|
|
@@ -209,18 +251,78 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
209
251
|
return result;
|
|
210
252
|
},
|
|
211
253
|
});
|
|
254
|
+
this.declareFunction('removeBreakpoint', {
|
|
255
|
+
description: 'Remove a breakpoint at a specific location.',
|
|
256
|
+
parameters: {
|
|
257
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
258
|
+
description: 'Location to remove the breakpoint from',
|
|
259
|
+
properties: {
|
|
260
|
+
url: {
|
|
261
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
262
|
+
description: 'The URL of the file',
|
|
263
|
+
},
|
|
264
|
+
lineNumber: {
|
|
265
|
+
type: Host.AidaClient.ParametersTypes.INTEGER,
|
|
266
|
+
description: 'The 1-based line number to remove the breakpoint from',
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
required: ['url', 'lineNumber'],
|
|
270
|
+
},
|
|
271
|
+
displayInfoFromArgs: (args: {url: string, lineNumber: number}) => {
|
|
272
|
+
return {
|
|
273
|
+
title: `Removing breakpoint at ${args.url}:${args.lineNumber}`,
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
handler: async (args: {url: string, lineNumber: number}) => {
|
|
277
|
+
debugLog('removeBreakpoint requested', args);
|
|
278
|
+
const result = await this.#removeBreakpoint(args);
|
|
279
|
+
debugLog('removeBreakpoint result', JSON.stringify(result));
|
|
280
|
+
return result;
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
this.declareFunction('removeAllBreakpoints', {
|
|
284
|
+
description: 'Remove all active breakpoints.',
|
|
285
|
+
parameters: {
|
|
286
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
287
|
+
description: 'No parameters required',
|
|
288
|
+
properties: {},
|
|
289
|
+
required: [],
|
|
290
|
+
},
|
|
291
|
+
displayInfoFromArgs: () => {
|
|
292
|
+
return {
|
|
293
|
+
title: 'Removing all breakpoints',
|
|
294
|
+
};
|
|
295
|
+
},
|
|
296
|
+
handler: async () => {
|
|
297
|
+
debugLog('removeAllBreakpoints requested');
|
|
298
|
+
const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
|
|
299
|
+
const allBreakpoints = breakpointManager.allBreakpointLocations();
|
|
300
|
+
for (const bp of allBreakpoints) {
|
|
301
|
+
await bp.breakpoint.remove(false);
|
|
302
|
+
}
|
|
303
|
+
return {result: {status: 'All breakpoints removed.'}};
|
|
304
|
+
},
|
|
305
|
+
});
|
|
212
306
|
this.declareFunction('resume', {
|
|
213
|
-
description: 'Resume execution
|
|
307
|
+
description: 'Resume execution. Always use this after applying a fix to resume the page execution.',
|
|
214
308
|
parameters: {
|
|
215
309
|
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
216
310
|
description: 'No parameters required',
|
|
217
311
|
properties: {},
|
|
218
312
|
required: [],
|
|
219
313
|
},
|
|
314
|
+
displayInfoFromArgs: () => {
|
|
315
|
+
return {
|
|
316
|
+
title: 'Resuming execution',
|
|
317
|
+
};
|
|
318
|
+
},
|
|
220
319
|
handler: async () => {
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
320
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
321
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
322
|
+
if (debuggerModel) {
|
|
323
|
+
debuggerModel.resume();
|
|
324
|
+
}
|
|
325
|
+
return {result: {status: 'Execution resumed.'}};
|
|
224
326
|
},
|
|
225
327
|
});
|
|
226
328
|
this.declareFunction('stepOver', {
|
|
@@ -231,6 +333,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
231
333
|
properties: {},
|
|
232
334
|
required: [],
|
|
233
335
|
},
|
|
336
|
+
displayInfoFromArgs: () => {
|
|
337
|
+
return {
|
|
338
|
+
title: 'Stepping over',
|
|
339
|
+
};
|
|
340
|
+
},
|
|
234
341
|
handler: async () => {
|
|
235
342
|
const result = await this.#debuggerAction(model => model.stepOver());
|
|
236
343
|
debugLog('stepOver result', JSON.stringify(result));
|
|
@@ -246,6 +353,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
246
353
|
properties: {},
|
|
247
354
|
required: [],
|
|
248
355
|
},
|
|
356
|
+
displayInfoFromArgs: () => {
|
|
357
|
+
return {
|
|
358
|
+
title: 'Stepping into',
|
|
359
|
+
};
|
|
360
|
+
},
|
|
249
361
|
handler: async () => {
|
|
250
362
|
const result = await this.#debuggerAction(model => model.stepInto());
|
|
251
363
|
debugLog('stepInto result', JSON.stringify(result));
|
|
@@ -261,6 +373,11 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
261
373
|
properties: {},
|
|
262
374
|
required: [],
|
|
263
375
|
},
|
|
376
|
+
displayInfoFromArgs: () => {
|
|
377
|
+
return {
|
|
378
|
+
title: 'Stepping out',
|
|
379
|
+
};
|
|
380
|
+
},
|
|
264
381
|
handler: async () => {
|
|
265
382
|
const result = await this.#debuggerAction(model => model.stepOut());
|
|
266
383
|
debugLog('stepOut result', JSON.stringify(result));
|
|
@@ -298,12 +415,169 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
298
415
|
properties: {},
|
|
299
416
|
required: [],
|
|
300
417
|
},
|
|
418
|
+
displayInfoFromArgs: () => {
|
|
419
|
+
return {
|
|
420
|
+
title: 'Getting execution location',
|
|
421
|
+
};
|
|
422
|
+
},
|
|
301
423
|
handler: async () => {
|
|
302
424
|
const result = await this.#getExecutionLocation();
|
|
303
425
|
debugLog('getExecutionLocation ', JSON.stringify(result));
|
|
304
426
|
return result;
|
|
305
427
|
},
|
|
306
428
|
});
|
|
429
|
+
|
|
430
|
+
this.declareFunction('testFixInConsole', {
|
|
431
|
+
description:
|
|
432
|
+
'Tests a JavaScript code snippet in the current execution context to overwrite the problematic code or state. Use this to verify the fix works before patching the source file.',
|
|
433
|
+
parameters: {
|
|
434
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
435
|
+
description: 'Provide the code to evaluate to test the fix',
|
|
436
|
+
properties: {
|
|
437
|
+
code: {
|
|
438
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
439
|
+
description: 'The JavaScript code to evaluate in the console to test the fix.',
|
|
440
|
+
},
|
|
441
|
+
explanation: {
|
|
442
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
443
|
+
description: 'Explanation for why this code fixes the issue.',
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
required: ['code', 'explanation'],
|
|
447
|
+
},
|
|
448
|
+
displayInfoFromArgs: (args: {code: string, explanation: string}) => {
|
|
449
|
+
return {
|
|
450
|
+
title: 'Testing a fix in console',
|
|
451
|
+
thought: args.explanation,
|
|
452
|
+
action: args.code,
|
|
453
|
+
};
|
|
454
|
+
},
|
|
455
|
+
handler: async (args: {code: string, explanation: string}, options?: FunctionHandlerOptions) => {
|
|
456
|
+
debugLog('testFixInConsole requested', args);
|
|
457
|
+
if (options?.approved === false) {
|
|
458
|
+
return {error: 'Fix rejected by the user.'};
|
|
459
|
+
}
|
|
460
|
+
if (!options?.approved) {
|
|
461
|
+
return {requiresApproval: true};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
465
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
466
|
+
|
|
467
|
+
if (!debuggerModel) {
|
|
468
|
+
return {error: 'Execution is not paused.'};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const details = debuggerModel.debuggerPausedDetails();
|
|
472
|
+
const callFrame = details?.callFrames[0];
|
|
473
|
+
if (!callFrame) {
|
|
474
|
+
return {error: 'No call frame available.'};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const result = await callFrame.evaluate({
|
|
478
|
+
expression: args.code,
|
|
479
|
+
objectGroup: 'console',
|
|
480
|
+
includeCommandLineAPI: true,
|
|
481
|
+
silent: false,
|
|
482
|
+
returnByValue: false,
|
|
483
|
+
generatePreview: true
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
if (!result) {
|
|
487
|
+
return {error: 'Failed to evaluate the fix.'};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if ('error' in result) {
|
|
491
|
+
return {error: 'Error applying fix: ' + (result as unknown as {error: string}).error};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (result.exceptionDetails) {
|
|
495
|
+
return {error: 'Fix threw an exception: ' + result.exceptionDetails.text};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
result: {
|
|
500
|
+
status:
|
|
501
|
+
'Code evaluated successfully. You MUST now call waitForUserActionToTriggerBreakpoint or resume, and ask the user to trigger the action again to verify the fix.'
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
this.declareFunction('suggestFix', {
|
|
508
|
+
description:
|
|
509
|
+
'Suggests a JavaScript code snippet to fix the root cause of the issue and applies it to the source code file if the user approves. Call this function AFTER you have verified the fix works using testFixInConsole.',
|
|
510
|
+
parameters: {
|
|
511
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
512
|
+
description: 'Provide the fix to the user',
|
|
513
|
+
properties: {
|
|
514
|
+
url: {
|
|
515
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
516
|
+
description: 'The URL of the file to fix.',
|
|
517
|
+
},
|
|
518
|
+
originalCode: {
|
|
519
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
520
|
+
description:
|
|
521
|
+
'The exact original code to replace. Must match the file content exactly, including whitespace.',
|
|
522
|
+
},
|
|
523
|
+
suggestion: {
|
|
524
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
525
|
+
description: 'The new JavaScript code to replace the original code with.',
|
|
526
|
+
},
|
|
527
|
+
explanation: {
|
|
528
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
529
|
+
description: 'Explanation for why this code fixes the issue.',
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
required: ['url', 'originalCode', 'suggestion', 'explanation'],
|
|
533
|
+
},
|
|
534
|
+
displayInfoFromArgs: (args: {suggestion: string, explanation: string}) => {
|
|
535
|
+
return {
|
|
536
|
+
title: 'Suggesting a fix',
|
|
537
|
+
thought: args.explanation,
|
|
538
|
+
action: args.suggestion,
|
|
539
|
+
};
|
|
540
|
+
},
|
|
541
|
+
handler: async (
|
|
542
|
+
args: {url: string, originalCode: string, suggestion: string, explanation: string},
|
|
543
|
+
options?: FunctionHandlerOptions) => {
|
|
544
|
+
debugLog('suggestFix requested', args);
|
|
545
|
+
if (options?.approved === false) {
|
|
546
|
+
return {error: 'Fix rejected by the user.'};
|
|
547
|
+
}
|
|
548
|
+
if (!options?.approved) {
|
|
549
|
+
return {requiresApproval: true};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(
|
|
553
|
+
args.url as Platform.DevToolsPath.UrlString);
|
|
554
|
+
if (!uiSourceCode) {
|
|
555
|
+
return {error: `File not found: ${args.url}`};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const contentData = await uiSourceCode.requestContentData();
|
|
559
|
+
if ('error' in contentData || !contentData.isTextContent) {
|
|
560
|
+
return {error: `Could not read content for file: ${args.url}`};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
let currentContent = uiSourceCode.workingCopy();
|
|
564
|
+
if (!uiSourceCode.isDirty()) {
|
|
565
|
+
currentContent = contentData.text;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (!currentContent.includes(args.originalCode)) {
|
|
569
|
+
return {
|
|
570
|
+
error: 'originalCode not found in the file. Ensure the originalCode is an exact match including whitespace.'
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const newContent = currentContent.replace(args.originalCode, args.suggestion);
|
|
575
|
+
uiSourceCode.setWorkingCopy(newContent);
|
|
576
|
+
uiSourceCode.commitWorkingCopy();
|
|
577
|
+
|
|
578
|
+
return {result: {status: 'Fix applied to the source file successfully.'}};
|
|
579
|
+
},
|
|
580
|
+
});
|
|
307
581
|
}
|
|
308
582
|
|
|
309
583
|
async #getFunctionSource(args: {url: string, lineNumber: number}):
|
|
@@ -407,7 +681,7 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
407
681
|
if (!debuggerModel) {
|
|
408
682
|
return {
|
|
409
683
|
error:
|
|
410
|
-
'Execution is not paused. I cannot access runtime variables or the call stack. I am currently in STATIC MODE. I must set a breakpoint and use
|
|
684
|
+
'Execution is not paused. I cannot access runtime variables or the call stack. I am currently in STATIC MODE. I must set a breakpoint and use waitForUserActionToTriggerBreakpoint to enter RUNTIME MODE.'
|
|
411
685
|
};
|
|
412
686
|
}
|
|
413
687
|
|
|
@@ -439,7 +713,7 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
439
713
|
if (!debuggerModel) {
|
|
440
714
|
return {
|
|
441
715
|
error:
|
|
442
|
-
'Execution is not paused. I cannot access runtime variables or the call stack. I am currently in STATIC MODE. I must set a breakpoint and use
|
|
716
|
+
'Execution is not paused. I cannot access runtime variables or the call stack. I am currently in STATIC MODE. I must set a breakpoint and use waitForUserActionToTriggerBreakpoint to enter RUNTIME MODE.'
|
|
443
717
|
};
|
|
444
718
|
}
|
|
445
719
|
|
|
@@ -566,7 +840,7 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
566
840
|
if (callFrame) {
|
|
567
841
|
const pausedLoc = `${callFrame.script.contentURL()}:${callFrame.location().lineNumber + 1}`;
|
|
568
842
|
warning = ` WARNING: You are already PAUSED at ${
|
|
569
|
-
pausedLoc}. \n1. If this is where you want to be, call 'getExecutionLocation' and inspect variables. \n2. If you want to wait for the NEW breakpoint, you MUST call '
|
|
843
|
+
pausedLoc}. \n1. If this is where you want to be, call 'getExecutionLocation' and inspect variables. \n2. If you want to wait for the NEW breakpoint, you MUST call 'waitForUserActionToTriggerBreakpoint' (which will resume execution).`;
|
|
570
844
|
}
|
|
571
845
|
}
|
|
572
846
|
|
|
@@ -575,8 +849,9 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
575
849
|
result: {
|
|
576
850
|
status: `Breakpoint requested at ${args.url}:${args.lineNumber}, but ACTUALLY resolved to line ${
|
|
577
851
|
actualLineNumber}.${
|
|
578
|
-
warning ?
|
|
579
|
-
|
|
852
|
+
warning ?
|
|
853
|
+
'\n' + warning :
|
|
854
|
+
' You must now call waitForUserActionToTriggerBreakpoint and ask the user to trigger the action.'}`
|
|
580
855
|
}
|
|
581
856
|
};
|
|
582
857
|
}
|
|
@@ -584,11 +859,33 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
584
859
|
return {
|
|
585
860
|
result: {
|
|
586
861
|
status: `Breakpoint set at ${args.url}:${args.lineNumber}.${
|
|
587
|
-
warning ?
|
|
862
|
+
warning ?
|
|
863
|
+
'\n' + warning :
|
|
864
|
+
' You must now call waitForUserActionToTriggerBreakpoint and ask the user to trigger the action.'}`
|
|
588
865
|
}
|
|
589
866
|
};
|
|
590
867
|
}
|
|
591
868
|
|
|
869
|
+
async #removeBreakpoint(args: {url: string, lineNumber: number}):
|
|
870
|
+
Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
871
|
+
const uiSourceCode =
|
|
872
|
+
Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(args.url as Platform.DevToolsPath.UrlString);
|
|
873
|
+
if (!uiSourceCode) {
|
|
874
|
+
return {error: `File not found: ${args.url}`};
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const breakpointLocations =
|
|
878
|
+
Breakpoints.BreakpointManager.BreakpointManager.instance().breakpointLocationsForUISourceCode(uiSourceCode);
|
|
879
|
+
const breakpointLocation = breakpointLocations.find(bp => bp.uiLocation.lineNumber === args.lineNumber - 1);
|
|
880
|
+
|
|
881
|
+
if (!breakpointLocation) {
|
|
882
|
+
return {result: {status: `Breakpoint not found at ${args.url}:${args.lineNumber}.`}};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
await breakpointLocation.breakpoint.remove(false);
|
|
886
|
+
return {result: {status: `Breakpoint removed at ${args.url}:${args.lineNumber}.`}};
|
|
887
|
+
}
|
|
888
|
+
|
|
592
889
|
async #debuggerAction(action: (model: SDK.DebuggerModel.DebuggerModel) => void):
|
|
593
890
|
Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
594
891
|
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
@@ -598,8 +895,8 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
598
895
|
return {error: 'Execution is not paused. I cannot step or resume in STATIC MODE.'};
|
|
599
896
|
}
|
|
600
897
|
|
|
601
|
-
// Only resolve when next pause event is triggered
|
|
602
|
-
return await this.#waitForNextPause(() => action(debuggerModel));
|
|
898
|
+
// Only resolve when next pause event is triggered (with a 3 second timeout so the agent doesn't hang)
|
|
899
|
+
return await this.#waitForNextPause(() => action(debuggerModel), 3000);
|
|
603
900
|
}
|
|
604
901
|
|
|
605
902
|
async #waitForUserActionToTriggerBreakpoint(): Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
@@ -610,14 +907,23 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
610
907
|
return {error: 'No debugger attached'};
|
|
611
908
|
}
|
|
612
909
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
910
|
+
// While waiting for the user to trigger the breakpoint, show an overlay on top of the page.
|
|
911
|
+
// This is to make it clear to the user that they need to trigger the
|
|
912
|
+
// breakpoint for the agent to continue the debugging process.
|
|
913
|
+
void injectOverlay();
|
|
914
|
+
|
|
915
|
+
try {
|
|
916
|
+
return await this.#waitForNextPause(() => {
|
|
917
|
+
// Resume all paused models before waiting for the next breakpoint
|
|
918
|
+
for (const model of debuggerModels) {
|
|
919
|
+
if (model.isPaused()) {
|
|
920
|
+
model.resume();
|
|
921
|
+
}
|
|
618
922
|
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
923
|
+
});
|
|
924
|
+
} finally {
|
|
925
|
+
void removeOverlay();
|
|
926
|
+
}
|
|
621
927
|
}
|
|
622
928
|
|
|
623
929
|
/**
|
|
@@ -626,14 +932,20 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
626
932
|
*
|
|
627
933
|
* @param triggerAction Optional action to execute (e.g. resume, step) that is expected to lead to a pause.
|
|
628
934
|
*/
|
|
629
|
-
async #waitForNextPause(triggerAction: () => void = () => {}):
|
|
935
|
+
async #waitForNextPause(triggerAction: () => void = () => {}, timeoutMs?: number):
|
|
936
|
+
Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
630
937
|
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
631
938
|
|
|
632
939
|
return await new Promise(resolve => {
|
|
940
|
+
let timeoutId: ReturnType<typeof setTimeout>;
|
|
941
|
+
|
|
633
942
|
const listener =
|
|
634
943
|
async(event: Common.EventTarget.EventTargetEvent<SDK.DebuggerModel.DebuggerModel>): Promise<void> => {
|
|
635
944
|
targetManager.removeModelListener(
|
|
636
945
|
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, listener);
|
|
946
|
+
if (timeoutId) {
|
|
947
|
+
clearTimeout(timeoutId);
|
|
948
|
+
}
|
|
637
949
|
const model = event.data;
|
|
638
950
|
const details = model.debuggerPausedDetails();
|
|
639
951
|
const callFrame = details?.callFrames[0];
|
|
@@ -655,6 +967,19 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
655
967
|
targetManager.addModelListener(
|
|
656
968
|
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, listener);
|
|
657
969
|
|
|
970
|
+
if (timeoutMs !== undefined) {
|
|
971
|
+
timeoutId = setTimeout(() => {
|
|
972
|
+
targetManager.removeModelListener(
|
|
973
|
+
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, listener);
|
|
974
|
+
resolve({
|
|
975
|
+
result: {
|
|
976
|
+
status:
|
|
977
|
+
'Execution resumed but did not pause again. There is nothing to step into or the execution finished.'
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
}, timeoutMs);
|
|
981
|
+
}
|
|
982
|
+
|
|
658
983
|
// Execute the action that will eventually trigger the pause
|
|
659
984
|
triggerAction();
|
|
660
985
|
});
|
|
@@ -719,4 +1044,33 @@ export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILo
|
|
|
719
1044
|
get options(): RequestOptions {
|
|
720
1045
|
return {temperature: 0, modelId: undefined};
|
|
721
1046
|
}
|
|
1047
|
+
|
|
1048
|
+
override async *
|
|
1049
|
+
run(
|
|
1050
|
+
initialQuery: string,
|
|
1051
|
+
options: {
|
|
1052
|
+
selected: ConversationContext<Workspace.UISourceCode.UILocation>|null,
|
|
1053
|
+
signal?: AbortSignal,
|
|
1054
|
+
},
|
|
1055
|
+
multimodalInput?: MultimodalInput,
|
|
1056
|
+
): AsyncGenerator<ResponseData, void, void> {
|
|
1057
|
+
try {
|
|
1058
|
+
yield* super.run(initialQuery, options, multimodalInput);
|
|
1059
|
+
} finally {
|
|
1060
|
+
// When the agent is done, remove all breakpoints and exit paused state.
|
|
1061
|
+
const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
|
|
1062
|
+
const allBreakpoints = breakpointManager.allBreakpointLocations();
|
|
1063
|
+
for (const bp of allBreakpoints) {
|
|
1064
|
+
await bp.breakpoint.remove(false);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
1068
|
+
const debuggerModels = targetManager.models(SDK.DebuggerModel.DebuggerModel);
|
|
1069
|
+
for (const model of debuggerModels) {
|
|
1070
|
+
if (model.isPaused()) {
|
|
1071
|
+
model.resume();
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
722
1076
|
}
|