chrome-devtools-frontend 1.0.1590494 → 1.0.1592129
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/docs/ui_engineering.md +34 -1
- package/front_end/core/common/Gzip.ts +17 -2
- package/front_end/core/host/AidaClient.ts +38 -15
- package/front_end/core/host/DispatchHttpRequestClient.ts +7 -4
- package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/root/ExperimentNames.ts +1 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/CSSMetadata.ts +18 -0
- package/front_end/core/sdk/EmulationModel.ts +6 -1
- package/front_end/core/sdk/NetworkManager.ts +49 -2
- package/front_end/core/sdk/NetworkRequest.ts +4 -0
- package/front_end/core/sdk/RuntimeModel.ts +2 -1
- package/front_end/core/sdk/sdk-meta.ts +26 -0
- package/front_end/entrypoints/inspector_main/RenderingOptions.ts +34 -5
- 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 +10 -0
- package/front_end/generated/protocol-proxy-api.d.ts +8 -0
- package/front_end/generated/protocol.ts +6 -2
- package/front_end/models/ai_assistance/AiConversation.ts +9 -0
- package/front_end/models/ai_assistance/AiHistoryStorage.ts +1 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +6 -1
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +722 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +12 -12
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +7 -7
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +7 -7
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
- package/front_end/models/greendev/Prototypes.ts +7 -1
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +3 -3
- package/front_end/models/trace/helpers/Network.ts +1 -1
- package/front_end/models/trace/insights/NetworkDependencyTree.ts +1 -1
- package/front_end/models/trace/insights/RenderBlocking.ts +5 -5
- package/front_end/models/trace/lantern/metrics/FirstContentfulPaint.ts +6 -6
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +63 -0
- package/front_end/panels/ai_assistance/README.md +14 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +1 -4
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +45 -27
- package/front_end/panels/ai_assistance/components/chatInput.css +0 -8
- package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +25 -46
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +9 -4
- package/front_end/panels/console/ConsoleViewMessage.ts +100 -4
- package/front_end/panels/elements/ComputedStyleWidget.ts +46 -18
- package/front_end/panels/elements/ElementsPanel.ts +2 -2
- package/front_end/panels/elements/StyleEditorWidget.ts +10 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
- package/front_end/panels/elements/StylesContainer.ts +2 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +8 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -1
- package/front_end/panels/network/NetworkLogViewColumns.ts +1 -1
- package/front_end/panels/network/RequestPayloadView.ts +7 -6
- package/front_end/panels/network/RequestTimingView.ts +3 -3
- package/front_end/panels/search/SearchResultsPane.ts +2 -1
- package/front_end/panels/settings/SettingsScreen.ts +3 -2
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
- package/front_end/panels/sources/BreakpointsView.ts +1 -1
- package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
- package/front_end/panels/timeline/NetworkTrackAppender.ts +1 -1
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +4 -4
- package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +2 -2
- package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +4 -4
- package/front_end/third_party/chromium/README.chromium +4 -2
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +5 -1
- package/front_end/third_party/lighthouse/locales/en-GB.json +2 -2
- package/front_end/third_party/lighthouse/locales/en-US.json +2 -2
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +1 -1
- package/front_end/ui/legacy/Treeoutline.ts +39 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +31 -8
- package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +10 -3
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +20 -5
- package/front_end/ui/visual_logging/Debugging.ts +51 -34
- package/front_end/ui/visual_logging/KnownContextValues.ts +7 -1
- package/front_end/ui/visual_logging/LoggingEvents.ts +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,722 @@
|
|
|
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
|
+
import type * as Common from '../../../core/common/common.js';
|
|
6
|
+
import * as Host from '../../../core/host/host.js';
|
|
7
|
+
import type * as Platform from '../../../core/platform/platform.js';
|
|
8
|
+
import * as SDK from '../../../core/sdk/sdk.js';
|
|
9
|
+
import * as Bindings from '../../bindings/bindings.js';
|
|
10
|
+
import * as Breakpoints from '../../breakpoints/breakpoints.js';
|
|
11
|
+
import {formatterWorkerPool, ScopeKind, type ScopeTreeNode} from '../../formatter/FormatterWorkerPool.js';
|
|
12
|
+
import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
|
|
13
|
+
import * as TextUtils from '../../text_utils/text_utils.js';
|
|
14
|
+
import * as Workspace from '../../workspace/workspace.js';
|
|
15
|
+
import {debugLog} from '../debug.js';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
type AgentOptions,
|
|
19
|
+
AiAgent,
|
|
20
|
+
type ContextResponse,
|
|
21
|
+
ConversationContext,
|
|
22
|
+
type FunctionCallHandlerResult,
|
|
23
|
+
type RequestOptions,
|
|
24
|
+
ResponseType,
|
|
25
|
+
} from './AiAgent.js';
|
|
26
|
+
|
|
27
|
+
// This is a temporary agent for a GreenDev prototype.
|
|
28
|
+
// The preamble is not on the server and you should not build on top of this.
|
|
29
|
+
const preamble = `You are an expert Root Cause Analysis (RCA) specialist.
|
|
30
|
+
Your sole objective is to find the **root cause** of why an error was thrown or why a bug occurred.
|
|
31
|
+
You must not stop at the surface level. You must dig deep to understand the exact sequence of events and state changes that led to the failure.
|
|
32
|
+
|
|
33
|
+
**Excessively use all available tools** to gather as much information as possible. Do not make assumptions.
|
|
34
|
+
|
|
35
|
+
You have two modes of operation that you can switch between and control:
|
|
36
|
+
1. **STATIC MODE** (Default): You can read code but cannot see variables. You must analyze the logic to determine where to place breakpoints.
|
|
37
|
+
2. **RUNTIME MODE**: You are paused at a breakpoint. You can inspect variables and the call stack.
|
|
38
|
+
|
|
39
|
+
**Workflow**:
|
|
40
|
+
1. **Hypothesize**: Read the code ('getFunctionSource', 'getPreviousLines', 'getNextLines') to understand the logic.
|
|
41
|
+
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 'waitForBreakpoint'. This will suspend your execution until the user triggers the breakpoint. You CANNOT proceed until this tool returns.
|
|
43
|
+
4. **Inspect**: Using 'getExecutionLocation' check exactly where you are paused.
|
|
44
|
+
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
|
+
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
|
+
7. **Trace Back**: If the current function isn't the root cause, use 'getCallStack' to find the caller, and repeat the analysis there.
|
|
47
|
+
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.
|
|
48
|
+
|
|
49
|
+
**Rules**:
|
|
50
|
+
- **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 'waitForBreakpoint'. This tool will block and wait for the user to act.
|
|
52
|
+
- **STATIC MODE**: If you are in STATIC MODE and need to see variables: 1. 'setBreakpoint', 2. 'waitForBreakpoint'. **DO NOT STOP** to ask the user. Investigate code and set breakpoints to find the root cause.
|
|
53
|
+
- **ALREADY PAUSED?**: If 'setBreakpoint' warns you that you are already paused, **DO NOT** call 'waitForBreakpoint'. Start inspecting immediately. You can set more breakpoints while paused, but to call 'waitForBreakpoint' again you MUST be in static state.
|
|
54
|
+
- **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 'waitForBreakpoint' or any step command to confirm where you are.
|
|
56
|
+
|
|
57
|
+
**Execution Control when you are currently on a breakpoint**:
|
|
58
|
+
- **stepInto**: ESSENTIAL for entering function calls on the current line. Use this heavily when you suspect the issue is inside a called function.
|
|
59
|
+
- **stepOver**: Use to proceed line-by-line. If you are currently on a breakpoint, 'stepOver' will move you to the next line and pause again.
|
|
60
|
+
- **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
|
+
- **stepInto, stepOver, stepOut**: After any step command, always call 'getScopeVariables' to see how the state evolved.
|
|
62
|
+
- **listBreakpoints**: Use this to see all active breakpoints. Do not try to set a breakpoint that is already active.
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
export class BreakpointContext extends ConversationContext<Workspace.UISourceCode.UILocation> {
|
|
66
|
+
#focus: Workspace.UISourceCode.UILocation;
|
|
67
|
+
|
|
68
|
+
constructor(focus: Workspace.UISourceCode.UILocation) {
|
|
69
|
+
super();
|
|
70
|
+
this.#focus = focus;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override getOrigin(): string {
|
|
74
|
+
return new URL(this.#focus.uiSourceCode.url()).origin;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override getItem(): Workspace.UISourceCode.UILocation {
|
|
78
|
+
return this.#focus;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override getTitle(): string {
|
|
82
|
+
return `Breakpoint at ${this.#focus.uiSourceCode.displayName()}:${this.#focus.lineNumber + 1}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class BreakpointDebuggerAgent extends AiAgent<Workspace.UISourceCode.UILocation> {
|
|
87
|
+
readonly preamble = preamble;
|
|
88
|
+
// Using file agent as a base for now since it is the closest one logic wise.
|
|
89
|
+
// Since the user tier is forced to TESTERS, it should not mess up the stats.
|
|
90
|
+
// If this code is taken to production, we should create a new client feature.
|
|
91
|
+
readonly clientFeature = Host.AidaClient.ClientFeature.CHROME_FILE_AGENT;
|
|
92
|
+
constructor(opts: AgentOptions) {
|
|
93
|
+
super(opts);
|
|
94
|
+
this.declareFunction('getFunctionSource', {
|
|
95
|
+
description: 'Retrieve the source code of a function given a code line within it.',
|
|
96
|
+
parameters: {
|
|
97
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
98
|
+
description: 'The location to find the function source for',
|
|
99
|
+
properties: {
|
|
100
|
+
url: {
|
|
101
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
102
|
+
description: 'The URL of the file',
|
|
103
|
+
},
|
|
104
|
+
lineNumber: {
|
|
105
|
+
type: Host.AidaClient.ParametersTypes.INTEGER,
|
|
106
|
+
description: 'The 1-based line number of the code to look for',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ['url', 'lineNumber'],
|
|
110
|
+
},
|
|
111
|
+
handler: async (args: {url: string, lineNumber: number}) => {
|
|
112
|
+
const result = await this.#getFunctionSource(args);
|
|
113
|
+
debugLog('getFunctionSource for ', JSON.stringify(args), '->', JSON.stringify(result));
|
|
114
|
+
return result;
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
// In case the function source returns error or it is not enough to understand the code, the
|
|
118
|
+
// agent can use this function to get the code lines before or after a specific line.
|
|
119
|
+
this.declareFunction('getCodeLines', {
|
|
120
|
+
description: 'Retrieve the 10 lines of code before or after a specific line.',
|
|
121
|
+
parameters: {
|
|
122
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
123
|
+
description: 'The location and direction to look for code',
|
|
124
|
+
properties: {
|
|
125
|
+
url: {
|
|
126
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
127
|
+
description: 'The URL of the file',
|
|
128
|
+
},
|
|
129
|
+
lineNumber: {
|
|
130
|
+
type: Host.AidaClient.ParametersTypes.INTEGER,
|
|
131
|
+
description: 'The 1-based line number of the code to look for',
|
|
132
|
+
},
|
|
133
|
+
direction: {
|
|
134
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
135
|
+
description: 'The direction to look for code (before or after)',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
required: ['url', 'lineNumber', 'direction'],
|
|
139
|
+
},
|
|
140
|
+
handler: async (args: {url: string, lineNumber: number, direction: 'before'|'after'}) => {
|
|
141
|
+
const result = await this.#getCodeLines(args);
|
|
142
|
+
debugLog('getCodeLines result', JSON.stringify(result));
|
|
143
|
+
return result;
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
this.declareFunction('getCallStack', {
|
|
147
|
+
description: 'Retrieve the current call stack frames. Only call while debugger is paused.',
|
|
148
|
+
parameters: {
|
|
149
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
150
|
+
description: 'No parameters required',
|
|
151
|
+
properties: {},
|
|
152
|
+
required: [],
|
|
153
|
+
},
|
|
154
|
+
handler: async () => {
|
|
155
|
+
const result = await this.#getCallStack();
|
|
156
|
+
debugLog('getCallStack result', JSON.stringify(result));
|
|
157
|
+
return result;
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
this.declareFunction('getScopeVariables', {
|
|
161
|
+
description: 'Retrieve variables from all frames in the current call stack. Only call while debugger is paused.',
|
|
162
|
+
parameters: {
|
|
163
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
164
|
+
description: 'No parameters required',
|
|
165
|
+
properties: {},
|
|
166
|
+
required: [],
|
|
167
|
+
},
|
|
168
|
+
handler: async () => {
|
|
169
|
+
const result = await this.#getScopeVariables();
|
|
170
|
+
debugLog('getScopeVariables result', JSON.stringify(result));
|
|
171
|
+
return result;
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
this.declareFunction('listBreakpoints', {
|
|
175
|
+
description: 'List all active breakpoints.',
|
|
176
|
+
parameters: {
|
|
177
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
178
|
+
description: 'No parameters required',
|
|
179
|
+
properties: {},
|
|
180
|
+
required: [],
|
|
181
|
+
},
|
|
182
|
+
handler: async () => {
|
|
183
|
+
const result = await this.#listBreakpoints();
|
|
184
|
+
debugLog('listBreakpoints result', JSON.stringify(result));
|
|
185
|
+
return result;
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
this.declareFunction('setBreakpoint', {
|
|
189
|
+
description: 'Set a breakpoint at a specific location.',
|
|
190
|
+
parameters: {
|
|
191
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
192
|
+
description: 'Location to set the breakpoint',
|
|
193
|
+
properties: {
|
|
194
|
+
url: {
|
|
195
|
+
type: Host.AidaClient.ParametersTypes.STRING,
|
|
196
|
+
description: 'The URL of the file',
|
|
197
|
+
},
|
|
198
|
+
lineNumber: {
|
|
199
|
+
type: Host.AidaClient.ParametersTypes.INTEGER,
|
|
200
|
+
description: 'The 1-based line number to set the breakpoint on',
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
required: ['url', 'lineNumber'],
|
|
204
|
+
},
|
|
205
|
+
handler: async (args: {url: string, lineNumber: number}) => {
|
|
206
|
+
debugLog('setBreakpoint requested', args);
|
|
207
|
+
const result = await this.#setBreakpoint(args);
|
|
208
|
+
debugLog('setBreakpoint result', JSON.stringify(result));
|
|
209
|
+
return result;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
this.declareFunction('resume', {
|
|
213
|
+
description: 'Resume execution until the next breakpoint is hit.',
|
|
214
|
+
parameters: {
|
|
215
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
216
|
+
description: 'No parameters required',
|
|
217
|
+
properties: {},
|
|
218
|
+
required: [],
|
|
219
|
+
},
|
|
220
|
+
handler: async () => {
|
|
221
|
+
const result = await this.#debuggerAction(model => model.resume());
|
|
222
|
+
debugLog('resume result', JSON.stringify(result));
|
|
223
|
+
return result;
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
this.declareFunction('stepOver', {
|
|
227
|
+
description: 'Execute the current line and pause at the next line in the same function.',
|
|
228
|
+
parameters: {
|
|
229
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
230
|
+
description: 'No parameters required',
|
|
231
|
+
properties: {},
|
|
232
|
+
required: [],
|
|
233
|
+
},
|
|
234
|
+
handler: async () => {
|
|
235
|
+
const result = await this.#debuggerAction(model => model.stepOver());
|
|
236
|
+
debugLog('stepOver result', JSON.stringify(result));
|
|
237
|
+
return result;
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
this.declareFunction('stepInto', {
|
|
241
|
+
description:
|
|
242
|
+
'Step into the function call on the current line. REQUIRED when you want to investigate the code inside a function call.',
|
|
243
|
+
parameters: {
|
|
244
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
245
|
+
description: 'No parameters required',
|
|
246
|
+
properties: {},
|
|
247
|
+
required: [],
|
|
248
|
+
},
|
|
249
|
+
handler: async () => {
|
|
250
|
+
const result = await this.#debuggerAction(model => model.stepInto());
|
|
251
|
+
debugLog('stepInto result', JSON.stringify(result));
|
|
252
|
+
return result;
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
this.declareFunction('stepOut', {
|
|
257
|
+
description: 'Finish the current function and pause at the caller.',
|
|
258
|
+
parameters: {
|
|
259
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
260
|
+
description: 'No parameters required',
|
|
261
|
+
properties: {},
|
|
262
|
+
required: [],
|
|
263
|
+
},
|
|
264
|
+
handler: async () => {
|
|
265
|
+
const result = await this.#debuggerAction(model => model.stepOut());
|
|
266
|
+
debugLog('stepOut result', JSON.stringify(result));
|
|
267
|
+
return result;
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
this.declareFunction('waitForUserActionToTriggerBreakpoint', {
|
|
272
|
+
description: 'Resume execution and wait for the user to trigger a breakpoint.',
|
|
273
|
+
parameters: {
|
|
274
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
275
|
+
description: 'No parameters required',
|
|
276
|
+
properties: {},
|
|
277
|
+
required: [],
|
|
278
|
+
},
|
|
279
|
+
displayInfoFromArgs: () => {
|
|
280
|
+
return {
|
|
281
|
+
title: 'Waiting for user action...',
|
|
282
|
+
thought: 'I am waiting for you to trigger a breakpoint in the application.',
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
handler: async () => {
|
|
286
|
+
debugLog('waitForUserActionToTriggerBreakpoint requested');
|
|
287
|
+
const result = await this.#waitForUserActionToTriggerBreakpoint();
|
|
288
|
+
debugLog('waitForUserActionToTriggerBreakpoint result', JSON.stringify(result));
|
|
289
|
+
return result;
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
this.declareFunction('getExecutionLocation', {
|
|
294
|
+
description: 'Get the current location (line number, source code line and url) where the debugger is paused.',
|
|
295
|
+
parameters: {
|
|
296
|
+
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
297
|
+
description: 'No parameters required',
|
|
298
|
+
properties: {},
|
|
299
|
+
required: [],
|
|
300
|
+
},
|
|
301
|
+
handler: async () => {
|
|
302
|
+
const result = await this.#getExecutionLocation();
|
|
303
|
+
debugLog('getExecutionLocation ', JSON.stringify(result));
|
|
304
|
+
return result;
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async #getFunctionSource(args: {url: string, lineNumber: number}):
|
|
310
|
+
Promise<FunctionCallHandlerResult<{functionSource: string}>> {
|
|
311
|
+
const uiSourceCode =
|
|
312
|
+
Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(args.url as Platform.DevToolsPath.UrlString);
|
|
313
|
+
if (!uiSourceCode) {
|
|
314
|
+
return {error: `File not found: ${args.url}`};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const contentData = await uiSourceCode.requestContentData();
|
|
318
|
+
if ('error' in contentData) {
|
|
319
|
+
return {error: `Could not read content for file: ${args.url}`};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const textContent = contentData.text;
|
|
323
|
+
const scopeTree = await formatterWorkerPool().javaScriptScopeTree(textContent);
|
|
324
|
+
if (!scopeTree) {
|
|
325
|
+
return {error: `Could not parse scope tree for file: ${args.url}`};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const text = new TextUtils.Text.Text(textContent);
|
|
329
|
+
const selectedLineIndex = args.lineNumber - 1;
|
|
330
|
+
|
|
331
|
+
if (selectedLineIndex < 0 || selectedLineIndex >= text.lineCount()) {
|
|
332
|
+
return {error: `Line number ${args.lineNumber} is out of range`};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const selectedOffset = text.offsetFromPosition(selectedLineIndex, 0);
|
|
336
|
+
let currentNode: ScopeTreeNode|undefined = scopeTree;
|
|
337
|
+
let functionNode: ScopeTreeNode = scopeTree;
|
|
338
|
+
|
|
339
|
+
// loop through the scope tree children to find the function node that contains the selected line
|
|
340
|
+
while (currentNode) {
|
|
341
|
+
if (currentNode.kind === ScopeKind.FUNCTION || currentNode.kind === ScopeKind.ARROW_FUNCTION) {
|
|
342
|
+
functionNode = currentNode;
|
|
343
|
+
}
|
|
344
|
+
const child: ScopeTreeNode|undefined =
|
|
345
|
+
currentNode.children.find(c => c.start <= selectedOffset && c.end > selectedOffset);
|
|
346
|
+
currentNode = child;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const startLocation = text.positionFromOffset(functionNode.start);
|
|
350
|
+
const endLocation = text.positionFromOffset(functionNode.end);
|
|
351
|
+
|
|
352
|
+
return {result: {functionSource: this.#formatLines(text, startLocation.lineNumber, endLocation.lineNumber + 1)}};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Gets 10 lines of code before or after a specific line.
|
|
356
|
+
async #getCodeLines(args: {url: string, lineNumber: number, direction: 'before'|'after'}):
|
|
357
|
+
Promise<FunctionCallHandlerResult<{codeLines: string}>> {
|
|
358
|
+
const uiSourceCode =
|
|
359
|
+
Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(args.url as Platform.DevToolsPath.UrlString);
|
|
360
|
+
if (!uiSourceCode) {
|
|
361
|
+
return {error: `File not found: ${args.url}`};
|
|
362
|
+
}
|
|
363
|
+
const contentData = await uiSourceCode.requestContentData();
|
|
364
|
+
if ('error' in contentData) {
|
|
365
|
+
return {error: `Could not read content for file: ${args.url}`};
|
|
366
|
+
}
|
|
367
|
+
const text = new TextUtils.Text.Text(contentData.text);
|
|
368
|
+
const lineNumber = args.lineNumber - 1;
|
|
369
|
+
const count = 10;
|
|
370
|
+
if (args.direction === 'before') {
|
|
371
|
+
const startLine = Math.max(0, lineNumber - count);
|
|
372
|
+
const endLine = Math.max(0, lineNumber);
|
|
373
|
+
return {result: {codeLines: this.#formatLines(text, startLine, endLine)}};
|
|
374
|
+
}
|
|
375
|
+
const startLine = Math.min(text.lineCount(), lineNumber + 1);
|
|
376
|
+
const endLine = Math.min(text.lineCount(), lineNumber + 1 + count);
|
|
377
|
+
return {result: {codeLines: this.#formatLines(text, startLine, endLine)}};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#formatLines(text: TextUtils.Text.Text, startLine: number, endLine: number): string {
|
|
381
|
+
let lines = '';
|
|
382
|
+
for (let i = startLine; i < endLine; i++) {
|
|
383
|
+
// Maybe there is a better format to send to the llm in
|
|
384
|
+
lines += `${i + 1}: ${text.lineAt(i)}\n`;
|
|
385
|
+
}
|
|
386
|
+
return lines;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
override async *
|
|
390
|
+
handleContextDetails(selectedBreakpoint: ConversationContext<Workspace.UISourceCode.UILocation>|null):
|
|
391
|
+
AsyncGenerator<ContextResponse, void, void> {
|
|
392
|
+
if (!selectedBreakpoint) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
yield {
|
|
396
|
+
type: ResponseType.CONTEXT,
|
|
397
|
+
title: 'Analyzing breakpoint location',
|
|
398
|
+
details: [{title: 'Location', text: selectedBreakpoint.getTitle()}]
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async #getCallStack(): Promise<FunctionCallHandlerResult<
|
|
403
|
+
{callFrames: Array<{functionName: string, url: string, lineNumber: number, id: string}>}>> {
|
|
404
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
405
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
406
|
+
|
|
407
|
+
if (!debuggerModel) {
|
|
408
|
+
return {
|
|
409
|
+
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 waitForBreakpoint to enter RUNTIME MODE.'
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const details = debuggerModel.debuggerPausedDetails();
|
|
415
|
+
if (!details) {
|
|
416
|
+
return {error: 'Internal error: debugger is paused but no details available.'};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const stackTrace =
|
|
420
|
+
await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createStackTraceFromDebuggerPaused(
|
|
421
|
+
details, debuggerModel.target());
|
|
422
|
+
const callFrames = stackTrace.syncFragment.frames.map(frame => {
|
|
423
|
+
return {
|
|
424
|
+
functionName: frame.name || frame.sdkFrame.functionName,
|
|
425
|
+
url: frame.uiSourceCode ? frame.uiSourceCode.url() : (frame.url || frame.sdkFrame.script.contentURL()),
|
|
426
|
+
lineNumber: frame.line + 1,
|
|
427
|
+
id: frame.sdkFrame.id,
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
return {result: {callFrames}};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async #getScopeVariables(): Promise<FunctionCallHandlerResult<
|
|
435
|
+
{frames: Array<{functionName: string, scopes: Array<{type: string, object: unknown}>}>}>> {
|
|
436
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
437
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
438
|
+
|
|
439
|
+
if (!debuggerModel) {
|
|
440
|
+
return {
|
|
441
|
+
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 waitForBreakpoint to enter RUNTIME MODE.'
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const details = debuggerModel.debuggerPausedDetails();
|
|
447
|
+
if (!details) {
|
|
448
|
+
return {error: 'Internal error: debugger is paused but no details available.'};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const stackTrace =
|
|
452
|
+
await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createStackTraceFromDebuggerPaused(
|
|
453
|
+
details, debuggerModel.target());
|
|
454
|
+
const helperFrames = stackTrace.syncFragment.frames;
|
|
455
|
+
|
|
456
|
+
const frames = [];
|
|
457
|
+
|
|
458
|
+
for (const frame of helperFrames) {
|
|
459
|
+
const callFrame = frame.sdkFrame;
|
|
460
|
+
const scopeChain = await SourceMapScopes.NamesResolver.resolveScopeChain(callFrame);
|
|
461
|
+
const resultScopeChain = [];
|
|
462
|
+
|
|
463
|
+
for (const scope of scopeChain) {
|
|
464
|
+
const type = scope.type();
|
|
465
|
+
// Filter interesting scopes
|
|
466
|
+
if (type !== 'local' && type !== 'closure' && type !== 'module' && type !== 'block' && type !== 'catch') {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const remoteObject = scope.object();
|
|
471
|
+
const {properties} =
|
|
472
|
+
await remoteObject.getAllProperties(false /* accessorPropertiesOnly */, true /* generatePreview */);
|
|
473
|
+
|
|
474
|
+
const variables: Record<string, string> = {};
|
|
475
|
+
if (properties) {
|
|
476
|
+
for (const prop of properties) {
|
|
477
|
+
if (!prop.name) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
let value = 'undefined';
|
|
481
|
+
if (prop.value) {
|
|
482
|
+
if (prop.value.type === 'string') {
|
|
483
|
+
value = `"${prop.value.value}"`;
|
|
484
|
+
} else if (prop.value.value !== undefined) {
|
|
485
|
+
value = String(prop.value.value);
|
|
486
|
+
} else if (prop.value.preview) {
|
|
487
|
+
const props = prop.value.preview.properties.map(p => `${p.name}: ${p.value}`).join(', ');
|
|
488
|
+
value = prop.value.subtype === 'array' ? `[${props}]` : `{${props}}`;
|
|
489
|
+
} else {
|
|
490
|
+
value = prop.value.description ?? prop.value.type;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
variables[prop.name] = value;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
resultScopeChain.push({
|
|
498
|
+
type,
|
|
499
|
+
object: variables,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
frames.push({
|
|
503
|
+
functionName: frame.name || frame.sdkFrame.functionName,
|
|
504
|
+
scopes: resultScopeChain,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return {result: {frames}};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async #listBreakpoints():
|
|
512
|
+
Promise<FunctionCallHandlerResult<{breakpoints: Array<{url: string, lineNumber: number}>}>> {
|
|
513
|
+
const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
|
|
514
|
+
const allBreakpoints = breakpointManager.allBreakpointLocations();
|
|
515
|
+
const breakpoints = allBreakpoints.map(bp => {
|
|
516
|
+
return {url: bp.uiLocation.uiSourceCode.url(), lineNumber: bp.uiLocation.lineNumber + 1};
|
|
517
|
+
});
|
|
518
|
+
return {result: {breakpoints}};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async #setBreakpoint(args: {url: string, lineNumber: number}): Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
522
|
+
const uiSourceCode =
|
|
523
|
+
Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(args.url as Platform.DevToolsPath.UrlString);
|
|
524
|
+
if (!uiSourceCode) {
|
|
525
|
+
return {error: `File not found: ${args.url}`};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Check if breakpoint already exists
|
|
529
|
+
const breakpointLocations =
|
|
530
|
+
Breakpoints.BreakpointManager.BreakpointManager.instance().breakpointLocationsForUISourceCode(uiSourceCode);
|
|
531
|
+
const alreadyExists = breakpointLocations.some(bp => bp.uiLocation.lineNumber === args.lineNumber - 1);
|
|
532
|
+
if (alreadyExists) {
|
|
533
|
+
return {result: {status: `Breakpoint already exists at ${args.url}:${args.lineNumber}.`}};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const breakpoint = await Breakpoints.BreakpointManager.BreakpointManager.instance().setBreakpoint(
|
|
537
|
+
uiSourceCode,
|
|
538
|
+
args.lineNumber - 1,
|
|
539
|
+
0,
|
|
540
|
+
Breakpoints.BreakpointManager.EMPTY_BREAKPOINT_CONDITION,
|
|
541
|
+
true,
|
|
542
|
+
false,
|
|
543
|
+
Breakpoints.BreakpointManager.BreakpointOrigin.USER_ACTION,
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
// If the agent asks to set a breakpoint on a line that is not executable,
|
|
547
|
+
// the breakpoint will be set on the next executable line.
|
|
548
|
+
// Calculate the actual line number and inform the agent about this.
|
|
549
|
+
let actualLineNumber = args.lineNumber;
|
|
550
|
+
if (breakpoint) {
|
|
551
|
+
const resolvedState = breakpoint.getLastResolvedState();
|
|
552
|
+
if (resolvedState && resolvedState.length > 0) {
|
|
553
|
+
// resolvedState locations are 0-indexed
|
|
554
|
+
actualLineNumber = resolvedState[0].lineNumber + 1;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
559
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
560
|
+
let warning = '';
|
|
561
|
+
// Sometimes it gets confusing for the agent if it is already paused at a breakpoint.
|
|
562
|
+
// We should inform the agent that it is paused at the moment.
|
|
563
|
+
if (debuggerModel) {
|
|
564
|
+
const details = debuggerModel.debuggerPausedDetails();
|
|
565
|
+
const callFrame = details?.callFrames[0];
|
|
566
|
+
if (callFrame) {
|
|
567
|
+
const pausedLoc = `${callFrame.script.contentURL()}:${callFrame.location().lineNumber + 1}`;
|
|
568
|
+
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 'waitForBreakpoint' (which will resume execution).`;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (actualLineNumber !== args.lineNumber) {
|
|
574
|
+
return {
|
|
575
|
+
result: {
|
|
576
|
+
status: `Breakpoint requested at ${args.url}:${args.lineNumber}, but ACTUALLY resolved to line ${
|
|
577
|
+
actualLineNumber}.${
|
|
578
|
+
warning ? '\n' + warning :
|
|
579
|
+
' You must now call waitForBreakpoint and ask the user to trigger the action.'}`
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return {
|
|
585
|
+
result: {
|
|
586
|
+
status: `Breakpoint set at ${args.url}:${args.lineNumber}.${
|
|
587
|
+
warning ? '\n' + warning : ' You must now call waitForBreakpoint and ask the user to trigger the action.'}`
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
async #debuggerAction(action: (model: SDK.DebuggerModel.DebuggerModel) => void):
|
|
593
|
+
Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
594
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
595
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
596
|
+
|
|
597
|
+
if (!debuggerModel) {
|
|
598
|
+
return {error: 'Execution is not paused. I cannot step or resume in STATIC MODE.'};
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Only resolve when next pause event is triggered
|
|
602
|
+
return await this.#waitForNextPause(() => action(debuggerModel));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async #waitForUserActionToTriggerBreakpoint(): Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
606
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
607
|
+
const debuggerModels = targetManager.models(SDK.DebuggerModel.DebuggerModel);
|
|
608
|
+
|
|
609
|
+
if (debuggerModels.length === 0) {
|
|
610
|
+
return {error: 'No debugger attached'};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return await this.#waitForNextPause(() => {
|
|
614
|
+
// Resume all paused models before waiting for the next breakpoint
|
|
615
|
+
for (const model of debuggerModels) {
|
|
616
|
+
if (model.isPaused()) {
|
|
617
|
+
model.resume();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Helper that waits for the next debugger pause event.
|
|
625
|
+
* It sets up the listener *before* executing the trigger action to avoid race conditions.
|
|
626
|
+
*
|
|
627
|
+
* @param triggerAction Optional action to execute (e.g. resume, step) that is expected to lead to a pause.
|
|
628
|
+
*/
|
|
629
|
+
async #waitForNextPause(triggerAction: () => void = () => {}): Promise<FunctionCallHandlerResult<{status: string}>> {
|
|
630
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
631
|
+
|
|
632
|
+
return await new Promise(resolve => {
|
|
633
|
+
const listener =
|
|
634
|
+
async(event: Common.EventTarget.EventTargetEvent<SDK.DebuggerModel.DebuggerModel>): Promise<void> => {
|
|
635
|
+
targetManager.removeModelListener(
|
|
636
|
+
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, listener);
|
|
637
|
+
const model = event.data;
|
|
638
|
+
const details = model.debuggerPausedDetails();
|
|
639
|
+
const callFrame = details?.callFrames[0];
|
|
640
|
+
let location = 'unknown location';
|
|
641
|
+
if (callFrame) {
|
|
642
|
+
const rawLocation = callFrame.location();
|
|
643
|
+
const uiLocation =
|
|
644
|
+
await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().rawLocationToUILocation(
|
|
645
|
+
rawLocation);
|
|
646
|
+
if (uiLocation) {
|
|
647
|
+
location = `${uiLocation.uiSourceCode.url()}:${uiLocation.lineNumber + 1}`;
|
|
648
|
+
} else {
|
|
649
|
+
location = `${callFrame.script.contentURL()}:${rawLocation.lineNumber + 1}`;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
resolve({result: {status: `Paused at ${location}`}});
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
targetManager.addModelListener(
|
|
656
|
+
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, listener);
|
|
657
|
+
|
|
658
|
+
// Execute the action that will eventually trigger the pause
|
|
659
|
+
triggerAction();
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async #getExecutionLocation():
|
|
664
|
+
Promise<FunctionCallHandlerResult<{url: string, lineNumber: number, lineContent: string}>> {
|
|
665
|
+
const targetManager = SDK.TargetManager.TargetManager.instance();
|
|
666
|
+
const debuggerModel = targetManager.models(SDK.DebuggerModel.DebuggerModel).find(m => m.isPaused());
|
|
667
|
+
|
|
668
|
+
if (!debuggerModel) {
|
|
669
|
+
return {error: 'Execution is not paused. I cannot determine execution location in STATIC MODE.'};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const details = debuggerModel.debuggerPausedDetails();
|
|
673
|
+
if (!details) {
|
|
674
|
+
return {error: 'Internal error: debugger is paused but no details available.'};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const stackTrace =
|
|
678
|
+
await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createStackTraceFromDebuggerPaused(
|
|
679
|
+
details, debuggerModel.target());
|
|
680
|
+
const currentFrame = stackTrace.syncFragment.frames[0];
|
|
681
|
+
if (!currentFrame) {
|
|
682
|
+
return {error: 'Internal error: no frames available.'};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const url = currentFrame.uiSourceCode ? currentFrame.uiSourceCode.url() :
|
|
686
|
+
(currentFrame.url || currentFrame.sdkFrame.script.contentURL());
|
|
687
|
+
const lineNumber = currentFrame.line + 1;
|
|
688
|
+
let lineContent = '';
|
|
689
|
+
|
|
690
|
+
if (currentFrame.uiSourceCode) {
|
|
691
|
+
const contentData = await currentFrame.uiSourceCode.requestContentData();
|
|
692
|
+
if (!('error' in contentData)) {
|
|
693
|
+
const text = new TextUtils.Text.Text(contentData.text);
|
|
694
|
+
// Get current paused line content from the file
|
|
695
|
+
lineContent = text.lineAt(lineNumber - 1);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return {result: {url, lineNumber, lineContent}};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
override async enhanceQuery(
|
|
703
|
+
query: string, selectedBreakpoint: ConversationContext<Workspace.UISourceCode.UILocation>|null): Promise<string> {
|
|
704
|
+
const item = selectedBreakpoint?.getItem();
|
|
705
|
+
if (!item) {
|
|
706
|
+
return query;
|
|
707
|
+
}
|
|
708
|
+
const locationPart =
|
|
709
|
+
`I am investigating a breakpoint that is already set at ${item.uiSourceCode.url()}:${item.lineNumber + 1}${
|
|
710
|
+
item.columnNumber !== undefined ? ':' + (item.columnNumber + 1) :
|
|
711
|
+
''}. The execution is currently in STATIC MODE.`;
|
|
712
|
+
return `${locationPart}\n\n${query}`;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
get userTier(): string|undefined {
|
|
716
|
+
return 'TESTERS';
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
get options(): RequestOptions {
|
|
720
|
+
return {temperature: 0, modelId: undefined};
|
|
721
|
+
}
|
|
722
|
+
}
|