@codingame/monaco-vscode-telemetry-service-override 36.2.7 → 37.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -1
- package/package.json +2 -2
- package/vscode/src/vs/base/common/editArcTracker.d.ts +29 -0
- package/vscode/src/vs/base/common/editArcTracker.js +291 -0
- package/vscode/src/vs/platform/agentHost/common/diffComputeService.d.ts +19 -0
- package/vscode/src/vs/platform/agentHost/common/fileEditAttribution.d.ts +107 -0
- package/vscode/src/vs/platform/agentHost/common/fileEditAttribution.js +59 -0
- package/vscode/src/vs/platform/agentHost/common/fileEditAttribution.service.d.ts +12 -0
- package/vscode/src/vs/platform/telemetry/browser/errorTelemetry.js +5 -5
- package/vscode/src/vs/platform/telemetry/common/1dsAppender.d.ts +2 -0
- package/vscode/src/vs/platform/telemetry/common/1dsAppender.js +12 -10
- package/vscode/src/vs/platform/telemetry/common/editArcTelemetry.d.ts +141 -0
- package/vscode/src/vs/platform/telemetry/common/editArcTelemetry.js +5 -0
- package/vscode/src/vs/platform/telemetry/common/editTelemetry.d.ts +49 -0
- package/vscode/src/vs/platform/telemetry/common/editTelemetry.js +10 -0
- package/vscode/src/vs/platform/telemetry/common/telemetryLogAppender.d.ts +5 -2
- package/vscode/src/vs/platform/telemetry/common/telemetryLogAppender.js +4 -3
- package/vscode/src/vs/platform/telemetry/common/telemetryService.d.ts +14 -4
- package/vscode/src/vs/platform/telemetry/common/telemetryService.js +50 -34
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsChart.js +1 -1
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar.js +8 -8
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/editTelemetry.contribution.js +12 -7
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/editTelemetryContribution.js +2 -1
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/helpers/documentWithAnnotatedEdits.d.ts +7 -1
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/helpers/documentWithAnnotatedEdits.js +15 -2
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/settingIds.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/settingIds.js +1 -2
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/agentHostEditMarkerService.d.ts +83 -0
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/agentHostEditMarkerService.js +690 -0
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/arcTelemetryReporter.d.ts +2 -2
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/arcTelemetrySender.d.ts +3 -3
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/arcTelemetrySender.js +3 -0
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingFeature.js +11 -4
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingImpl.d.ts +15 -4
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingImpl.js +185 -83
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editTracker.d.ts +21 -5
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editTracker.js +128 -12
- package/vscode/src/vs/workbench/contrib/editTelemetry/browser/telemetry/scmAdapter.d.ts +7 -2
- package/vscode/src/vs/workbench/contrib/editTelemetry/common/arcTracker.d.ts +3 -11
- package/vscode/src/vs/workbench/contrib/editTelemetry/common/arcTracker.js +17 -47
- package/vscode/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.js +13 -2
- package/vscode/src/vs/workbench/services/telemetry/browser/telemetryService.d.ts +1 -1
- package/vscode/src/vs/workbench/services/dataChannel/browser/dataChannelService.d.ts +0 -10
- package/vscode/src/vs/workbench/services/dataChannel/browser/dataChannelService.js +0 -30
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
|
|
2
|
+
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
|
|
3
|
+
import { raceTimeout } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
4
|
+
import { Emitter, Event } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
|
|
5
|
+
import { Disposable, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
6
|
+
import { Schemas } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
|
|
7
|
+
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
|
|
8
|
+
import { generateUuid } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uuid';
|
|
9
|
+
import { ActionType } from '@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/state/protocol/common/actions';
|
|
10
|
+
import '@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/state/sessionState';
|
|
11
|
+
import { IAgentHostConnectionsService } from '@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/agentHostConnectionsService.service';
|
|
12
|
+
import { toAgentHostUri } from '@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/agentHostUri';
|
|
13
|
+
import { buildPrepareEditAttributionResource, buildCommitEditAttributionResource, buildCancelEditAttributionResource, getFileEditAttributionMarker, createFileEditContentDigest } from '../../../../../platform/agentHost/common/fileEditAttribution.js';
|
|
14
|
+
import { IUriIdentityService } from '@codingame/monaco-vscode-api/vscode/vs/platform/uriIdentity/common/uriIdentity.service';
|
|
15
|
+
import { EditSources } from '@codingame/monaco-vscode-api/vscode/vs/editor/common/textModelEditSource';
|
|
16
|
+
import { ToolResultContentType } from '@codingame/monaco-vscode-api/vscode/vs/platform/agentHost/common/state/protocol/channels-chat/state';
|
|
17
|
+
|
|
18
|
+
const MARKER_TTL = 5 * 60 * 1000;
|
|
19
|
+
const ROUTE_TTL = 10 * 60 * 60 * 1000;
|
|
20
|
+
const MAX_MARKERS_PER_RESOURCE = 128;
|
|
21
|
+
const MAX_OBSERVATIONS_PER_RESOURCE = 128;
|
|
22
|
+
const MAX_ROUTES = 1_000;
|
|
23
|
+
const MAX_COVERAGE_GAP_ACKNOWLEDGEMENTS = 1_000;
|
|
24
|
+
const COORDINATION_TIMEOUT = 15_000;
|
|
25
|
+
class AgentHostEditAttributionUnknownOutcomeError extends Error {
|
|
26
|
+
constructor(cause) {
|
|
27
|
+
super("The Agent Host edit attribution outcome is unknown", {
|
|
28
|
+
cause
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class AgentHostEditAttributionDeferredError extends Error {
|
|
33
|
+
constructor(cause) {
|
|
34
|
+
super("The Agent Host edit attribution was deferred", {
|
|
35
|
+
cause
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
let AgentHostEditMarkerService = class AgentHostEditMarkerService extends Disposable {
|
|
40
|
+
constructor(_connectionsService, _uriIdentityService) {
|
|
41
|
+
super();
|
|
42
|
+
this._connectionsService = _connectionsService;
|
|
43
|
+
this._uriIdentityService = _uriIdentityService;
|
|
44
|
+
this._markers = ( new Map());
|
|
45
|
+
this._observations = ( new Map());
|
|
46
|
+
this._routes = ( new Map());
|
|
47
|
+
this._coverageGaps = ( new Map());
|
|
48
|
+
this._acknowledgedCoverageGapIds = ( new Map());
|
|
49
|
+
this._pendingCoverageGapAcknowledgements = ( new Map());
|
|
50
|
+
this._onDidSuppress = this._register(( new Emitter()));
|
|
51
|
+
this._onDidResolve = this._register(( new Emitter()));
|
|
52
|
+
this._onDidInvalidate = this._register(( new Emitter()));
|
|
53
|
+
this._onDidReceiveMarker = this._register(( new Emitter()));
|
|
54
|
+
this._connectionListeners = this._register(( new DisposableStore()));
|
|
55
|
+
this._updateConnectionListeners();
|
|
56
|
+
this._register(
|
|
57
|
+
this._connectionsService.onDidChangeConnections(() => this._updateConnectionListeners())
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
createCorrelation(resource) {
|
|
61
|
+
const resourceKey = this._key(resource);
|
|
62
|
+
let currentRegistration;
|
|
63
|
+
let clearRegistrationScheduled = false;
|
|
64
|
+
return {
|
|
65
|
+
onDidSuppress: this._onDidSuppress.event,
|
|
66
|
+
onDidResolve: this._onDidResolve.event,
|
|
67
|
+
onDidInvalidate: this._onDidInvalidate.event,
|
|
68
|
+
register: (before, after) => {
|
|
69
|
+
if (currentRegistration?.before === before && currentRegistration.after === after) {
|
|
70
|
+
this._retainObservation(resourceKey, currentRegistration.id);
|
|
71
|
+
return currentRegistration.id;
|
|
72
|
+
}
|
|
73
|
+
const id = this._registerObservation(resourceKey, before, after);
|
|
74
|
+
currentRegistration = {
|
|
75
|
+
before,
|
|
76
|
+
after,
|
|
77
|
+
id
|
|
78
|
+
};
|
|
79
|
+
if (!clearRegistrationScheduled) {
|
|
80
|
+
clearRegistrationScheduled = true;
|
|
81
|
+
queueMicrotask(() => {
|
|
82
|
+
currentRegistration = undefined;
|
|
83
|
+
clearRegistrationScheduled = false;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return id;
|
|
87
|
+
},
|
|
88
|
+
isSuppressed: id => this._getObservation(resourceKey, id)?.resolution !== undefined,
|
|
89
|
+
getResolution: id => this._getObservation(resourceKey, id)?.resolution,
|
|
90
|
+
waitForResolution: (ids, timeoutMs) => this._waitForResolution(resourceKey, ids, timeoutMs),
|
|
91
|
+
release: id => this._releaseObservation(resourceKey, id)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
takeCoverageGap(resource, throughSequence = Number.MAX_SAFE_INTEGER) {
|
|
95
|
+
const resourceKey = this._key(resource);
|
|
96
|
+
this._prune(resourceKey);
|
|
97
|
+
const state = this._coverageGaps.get(resourceKey);
|
|
98
|
+
if (!state) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const included = state.entries.filter(entry => entry.sequence <= throughSequence);
|
|
102
|
+
const remaining = state.entries.filter(entry => entry.sequence > throughSequence);
|
|
103
|
+
const editCount = included.reduce((sum, entry) => sum + entry.editCount, 0);
|
|
104
|
+
const insertedCount = included.reduce((sum, entry) => sum + entry.insertedCount, 0);
|
|
105
|
+
if (remaining.length > 0) {
|
|
106
|
+
this._coverageGaps.set(resourceKey, {
|
|
107
|
+
entries: remaining,
|
|
108
|
+
timestamp: state.timestamp
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
this._coverageGaps.delete(resourceKey);
|
|
112
|
+
}
|
|
113
|
+
return editCount > 0 || insertedCount > 0 ? {
|
|
114
|
+
editCount,
|
|
115
|
+
insertedCount
|
|
116
|
+
} : undefined;
|
|
117
|
+
}
|
|
118
|
+
async prepareFlush(resource, trigger, statsUuid, isDirty, languageId = "plaintext") {
|
|
119
|
+
const resourceKey = this._key(resource);
|
|
120
|
+
this._prune(resourceKey);
|
|
121
|
+
const route = this._routes.get(resourceKey);
|
|
122
|
+
if (!route) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
const flushToken = generateUuid();
|
|
126
|
+
try {
|
|
127
|
+
const result = await this._resourceRead(route.connection, buildPrepareEditAttributionResource({
|
|
128
|
+
resource: route.resource,
|
|
129
|
+
trigger,
|
|
130
|
+
statsUuid,
|
|
131
|
+
isDirty,
|
|
132
|
+
flushToken,
|
|
133
|
+
languageId
|
|
134
|
+
}));
|
|
135
|
+
const prepared = JSON.parse(result.data);
|
|
136
|
+
if (prepared && (prepared.flushToken !== flushToken || !Number.isSafeInteger(prepared.agentModifiedCount) || prepared.agentModifiedCount < 0 || (prepared.lastSequence !== undefined && (!Number.isSafeInteger(prepared.lastSequence) || prepared.lastSequence < 0)) || (prepared.coverageGapThroughSequence !== undefined && (!Number.isSafeInteger(prepared.coverageGapThroughSequence) || prepared.coverageGapThroughSequence < 0 || prepared.lastSequence === undefined || prepared.coverageGapThroughSequence > prepared.lastSequence)) || (prepared.standaloneCoverageGapAcknowledgements !== undefined && prepared.lastSequence === undefined) || !isValidCoverageGapAcknowledgements(prepared.standaloneCoverageGapAcknowledgements, prepared.lastSequence))) {
|
|
137
|
+
throw ( new Error("Agent Host edit attribution returned an invalid prepared flush"));
|
|
138
|
+
}
|
|
139
|
+
if (prepared?.lastSequence !== undefined) {
|
|
140
|
+
await this._waitForMarker(resourceKey, route.connection, prepared.lastSequence);
|
|
141
|
+
}
|
|
142
|
+
if (prepared?.standaloneCoverageGapAcknowledgements !== undefined) {
|
|
143
|
+
this._acknowledgeCoverageGaps(resourceKey, prepared.standaloneCoverageGapAcknowledgements);
|
|
144
|
+
}
|
|
145
|
+
return prepared ? {
|
|
146
|
+
...prepared,
|
|
147
|
+
commit: async totalModifiedCount => {
|
|
148
|
+
let commitError = ( new Error(`Agent Host edit attribution commit failed: ${prepared.flushToken}`));
|
|
149
|
+
try {
|
|
150
|
+
const result = await this._readOutcome(route.connection, buildCommitEditAttributionResource({
|
|
151
|
+
flushToken: prepared.flushToken,
|
|
152
|
+
totalModifiedCount
|
|
153
|
+
}));
|
|
154
|
+
if (result.outcome === "committed") {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
commitError = ( new Error(`Agent Host edit attribution commit was not found: ${prepared.flushToken}`));
|
|
158
|
+
} catch (error) {
|
|
159
|
+
commitError = error;
|
|
160
|
+
}
|
|
161
|
+
let cancelResult;
|
|
162
|
+
try {
|
|
163
|
+
cancelResult = await this._readOutcome(route.connection, buildCancelEditAttributionResource({
|
|
164
|
+
flushToken: prepared.flushToken
|
|
165
|
+
}));
|
|
166
|
+
} catch (cancelError) {
|
|
167
|
+
throw ( new AgentHostEditAttributionUnknownOutcomeError(( new AggregateError(
|
|
168
|
+
[commitError, cancelError],
|
|
169
|
+
"Failed to commit or cancel Agent Host edit attribution"
|
|
170
|
+
))));
|
|
171
|
+
}
|
|
172
|
+
if (cancelResult.outcome === "committed") {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
throw ( new AgentHostEditAttributionDeferredError(commitError));
|
|
176
|
+
}
|
|
177
|
+
} : undefined;
|
|
178
|
+
} catch (prepareError) {
|
|
179
|
+
return this._recoverFailedPrepare(route.connection, resourceKey, flushToken, prepareError);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async _recoverFailedPrepare(connection, resourceKey, flushToken, prepareError) {
|
|
183
|
+
let cancelResult;
|
|
184
|
+
try {
|
|
185
|
+
cancelResult = await this._readOutcome(connection, buildCancelEditAttributionResource({
|
|
186
|
+
flushToken
|
|
187
|
+
}));
|
|
188
|
+
} catch (cancelError) {
|
|
189
|
+
throw ( new AgentHostEditAttributionUnknownOutcomeError(( new AggregateError(
|
|
190
|
+
[prepareError, cancelError],
|
|
191
|
+
"Failed to prepare or cancel Agent Host edit attribution"
|
|
192
|
+
))));
|
|
193
|
+
}
|
|
194
|
+
if (cancelResult.outcome === "committed") {
|
|
195
|
+
let deferCoverageGap = false;
|
|
196
|
+
if (cancelResult.lastSequence !== undefined) {
|
|
197
|
+
try {
|
|
198
|
+
await this._waitForMarker(resourceKey, connection, cancelResult.lastSequence);
|
|
199
|
+
} catch (markerError) {
|
|
200
|
+
throw ( new AgentHostEditAttributionUnknownOutcomeError(( new AggregateError(
|
|
201
|
+
[prepareError, markerError],
|
|
202
|
+
"Committed Agent Host attribution markers did not arrive"
|
|
203
|
+
))));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (cancelResult.standaloneCoverageGapAcknowledgements?.length) {
|
|
207
|
+
try {
|
|
208
|
+
await this._waitForMarker(
|
|
209
|
+
resourceKey,
|
|
210
|
+
connection,
|
|
211
|
+
getLastAcknowledgedSequence(cancelResult.standaloneCoverageGapAcknowledgements)
|
|
212
|
+
);
|
|
213
|
+
this._acknowledgeCoverageGaps(resourceKey, cancelResult.standaloneCoverageGapAcknowledgements);
|
|
214
|
+
} catch {
|
|
215
|
+
this._queuePendingCoverageGapAcknowledgements(resourceKey, cancelResult.standaloneCoverageGapAcknowledgements);
|
|
216
|
+
deferCoverageGap = true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
flushToken,
|
|
221
|
+
agentModifiedCount: cancelResult.agentModifiedCount,
|
|
222
|
+
lastSequence: cancelResult.lastSequence,
|
|
223
|
+
coverageGapThroughSequence: cancelResult.coverageGapThroughSequence,
|
|
224
|
+
deferCoverageGap,
|
|
225
|
+
commit: async () => {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
throw ( new AgentHostEditAttributionDeferredError(prepareError));
|
|
229
|
+
}
|
|
230
|
+
async _waitForMarker(resourceKey, connection, sequence) {
|
|
231
|
+
const isCaughtUp = () => {
|
|
232
|
+
const route = this._routes.get(resourceKey);
|
|
233
|
+
return route?.connection === connection && route.lastSequence >= sequence;
|
|
234
|
+
};
|
|
235
|
+
if (isCaughtUp()) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const marker = await raceTimeout(Event.toPromise(Event.filter(
|
|
239
|
+
this._onDidReceiveMarker.event,
|
|
240
|
+
event => event.resourceKey === resourceKey && event.connection === connection && event.sequence >= sequence
|
|
241
|
+
)), COORDINATION_TIMEOUT);
|
|
242
|
+
if (!marker && !isCaughtUp()) {
|
|
243
|
+
throw ( new Error(`Timed out waiting for Agent Host edit attribution marker: ${sequence}`));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async _resourceRead(connection, resource) {
|
|
247
|
+
const result = await raceTimeout(connection.resourceRead(resource), COORDINATION_TIMEOUT);
|
|
248
|
+
if (!result) {
|
|
249
|
+
throw ( new Error(`Agent Host edit attribution request timed out: ${resource.path}`));
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
async _readOutcome(connection, resource) {
|
|
254
|
+
const result = await this._resourceRead(connection, resource);
|
|
255
|
+
const parsed = JSON.parse(result.data);
|
|
256
|
+
if ((parsed.outcome !== "committed" && parsed.outcome !== "cancelled" && parsed.outcome !== "missing") || typeof parsed.agentModifiedCount !== "number" || (parsed.lastSequence !== undefined && (!Number.isSafeInteger(parsed.lastSequence) || parsed.lastSequence < 0)) || (parsed.coverageGapThroughSequence !== undefined && (!Number.isSafeInteger(parsed.coverageGapThroughSequence) || parsed.coverageGapThroughSequence < 0 || parsed.lastSequence === undefined || parsed.coverageGapThroughSequence > parsed.lastSequence)) || (parsed.standaloneCoverageGapAcknowledgements !== undefined && parsed.lastSequence === undefined) || !isValidCoverageGapAcknowledgements(parsed.standaloneCoverageGapAcknowledgements)) {
|
|
257
|
+
throw ( new Error(`Invalid Agent Host edit attribution outcome: ${resource.path}`));
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
outcome: parsed.outcome,
|
|
261
|
+
agentModifiedCount: parsed.agentModifiedCount,
|
|
262
|
+
lastSequence: parsed.lastSequence,
|
|
263
|
+
coverageGapThroughSequence: parsed.coverageGapThroughSequence,
|
|
264
|
+
standaloneCoverageGapAcknowledgements: parsed.standaloneCoverageGapAcknowledgements
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
_updateConnectionListeners() {
|
|
268
|
+
this._connectionListeners.clear();
|
|
269
|
+
const activeConnections = ( new Set(
|
|
270
|
+
this._connectionsService.connections.flatMap(info => info.connection ? [info.connection] : [])
|
|
271
|
+
));
|
|
272
|
+
for (const [resourceKey, route] of this._routes) {
|
|
273
|
+
if (!( activeConnections.has(route.connection))) {
|
|
274
|
+
this._invalidateObservations(resourceKey);
|
|
275
|
+
this._routes.delete(resourceKey);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
for (const connectionInfo of this._connectionsService.connections) {
|
|
279
|
+
const connection = connectionInfo.connection;
|
|
280
|
+
if (!connection) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
this._connectionListeners.add(connection.onDidAction(envelope => {
|
|
284
|
+
const action = envelope.action;
|
|
285
|
+
if (action.type !== ActionType.ChatToolCallComplete) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
for (const content of action.result.content ?? []) {
|
|
289
|
+
if (content.type !== ToolResultContentType.FileEdit) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const marker = getFileEditAttributionMarker(content);
|
|
293
|
+
const resourceUri = content.after?.uri ?? content.before?.uri;
|
|
294
|
+
if (!marker || !resourceUri) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const resource = toAgentHostUri(( URI.parse(resourceUri)), connectionInfo.authority);
|
|
298
|
+
const resourceKey = this._key(resource);
|
|
299
|
+
const previousRoute = this._routes.get(resourceKey);
|
|
300
|
+
if (previousRoute && (previousRoute.connection !== connection || marker.sequence <= previousRoute.lastSequence)) {
|
|
301
|
+
this._invalidateObservations(resourceKey);
|
|
302
|
+
}
|
|
303
|
+
this._routes.delete(resourceKey);
|
|
304
|
+
this._routes.set(resourceKey, {
|
|
305
|
+
connection,
|
|
306
|
+
resource: ( URI.parse(resourceUri)),
|
|
307
|
+
timestamp: Date.now(),
|
|
308
|
+
lastSequence: marker.sequence
|
|
309
|
+
});
|
|
310
|
+
this._onDidReceiveMarker.fire({
|
|
311
|
+
resourceKey,
|
|
312
|
+
connection,
|
|
313
|
+
sequence: marker.sequence
|
|
314
|
+
});
|
|
315
|
+
while (this._routes.size > MAX_ROUTES) {
|
|
316
|
+
const oldestKey = ( this._routes.keys()).next().value;
|
|
317
|
+
if (oldestKey === undefined) {
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
this._invalidateObservations(oldestKey);
|
|
321
|
+
this._routes.delete(oldestKey);
|
|
322
|
+
}
|
|
323
|
+
if (marker.status === "skipped") {
|
|
324
|
+
this._recordCoverageGap(
|
|
325
|
+
resourceKey,
|
|
326
|
+
marker.sequence,
|
|
327
|
+
marker.untrackedEditCount ?? 1,
|
|
328
|
+
marker.insertedCount
|
|
329
|
+
);
|
|
330
|
+
} else {
|
|
331
|
+
this._recordMarker(resourceKey, marker);
|
|
332
|
+
}
|
|
333
|
+
this._applyPendingCoverageGapAcknowledgements(resourceKey);
|
|
334
|
+
}
|
|
335
|
+
}));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
_recordCoverageGap(resourceKey, sequence, editCount, insertedCount) {
|
|
339
|
+
const existing = this._coverageGaps.get(resourceKey) ?? {
|
|
340
|
+
entries: [],
|
|
341
|
+
timestamp: Date.now()
|
|
342
|
+
};
|
|
343
|
+
if (( existing.entries.some(entry => entry.sequence === sequence))) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
existing.entries.push({
|
|
347
|
+
sequence,
|
|
348
|
+
editCount,
|
|
349
|
+
insertedCount
|
|
350
|
+
});
|
|
351
|
+
existing.timestamp = Date.now();
|
|
352
|
+
this._coverageGaps.delete(resourceKey);
|
|
353
|
+
this._coverageGaps.set(resourceKey, existing);
|
|
354
|
+
while (this._coverageGaps.size > MAX_ROUTES) {
|
|
355
|
+
const oldestKey = ( this._coverageGaps.keys()).next().value;
|
|
356
|
+
if (oldestKey === undefined) {
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
this._coverageGaps.delete(oldestKey);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
_acknowledgeCoverageGaps(resourceKey, acknowledgements) {
|
|
363
|
+
const remaining = [];
|
|
364
|
+
for (const acknowledgement of acknowledgements) {
|
|
365
|
+
const acknowledgementKey = coverageGapAcknowledgementKey(resourceKey, acknowledgement.id);
|
|
366
|
+
if (( this._acknowledgedCoverageGapIds.has(acknowledgementKey))) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const state = this._coverageGaps.get(resourceKey);
|
|
370
|
+
if (!state) {
|
|
371
|
+
this._recordCoverageGapAcknowledgement(acknowledgementKey);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const acknowledgedSequences = ( new Set(acknowledgement.sequences));
|
|
375
|
+
const matched = state.entries.filter(entry => ( acknowledgedSequences.has(entry.sequence)));
|
|
376
|
+
const matchedEditCount = matched.reduce((sum, entry) => sum + entry.editCount, 0);
|
|
377
|
+
const matchedInsertedCount = matched.reduce((sum, entry) => sum + entry.insertedCount, 0);
|
|
378
|
+
if (matched.length !== acknowledgement.sequences.length || matchedEditCount !== acknowledgement.editCount || matchedInsertedCount !== acknowledgement.insertedCount) {
|
|
379
|
+
remaining.push(acknowledgement);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
state.entries.splice(0, state.entries.length, ...state.entries.filter(entry => !( acknowledgedSequences.has(entry.sequence))));
|
|
383
|
+
if (state.entries.length > 0) {
|
|
384
|
+
this._coverageGaps.set(resourceKey, state);
|
|
385
|
+
} else {
|
|
386
|
+
this._coverageGaps.delete(resourceKey);
|
|
387
|
+
}
|
|
388
|
+
this._recordCoverageGapAcknowledgement(acknowledgementKey);
|
|
389
|
+
}
|
|
390
|
+
return remaining;
|
|
391
|
+
}
|
|
392
|
+
_recordCoverageGapAcknowledgement(acknowledgementKey) {
|
|
393
|
+
this._acknowledgedCoverageGapIds.delete(acknowledgementKey);
|
|
394
|
+
this._acknowledgedCoverageGapIds.set(acknowledgementKey, Date.now());
|
|
395
|
+
while (this._acknowledgedCoverageGapIds.size > MAX_COVERAGE_GAP_ACKNOWLEDGEMENTS) {
|
|
396
|
+
const oldestKey = ( this._acknowledgedCoverageGapIds.keys()).next().value;
|
|
397
|
+
if (oldestKey === undefined) {
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
this._acknowledgedCoverageGapIds.delete(oldestKey);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
_queuePendingCoverageGapAcknowledgements(resourceKey, acknowledgements) {
|
|
404
|
+
const pending = ( new Map(( (this._pendingCoverageGapAcknowledgements.get(resourceKey)?.acknowledgements ?? []).map(acknowledgement => [acknowledgement.id, acknowledgement]))));
|
|
405
|
+
for (const acknowledgement of acknowledgements) {
|
|
406
|
+
pending.set(acknowledgement.id, acknowledgement);
|
|
407
|
+
}
|
|
408
|
+
this._pendingCoverageGapAcknowledgements.delete(resourceKey);
|
|
409
|
+
this._pendingCoverageGapAcknowledgements.set(resourceKey, {
|
|
410
|
+
acknowledgements: Array.from(( pending.values())),
|
|
411
|
+
timestamp: Date.now()
|
|
412
|
+
});
|
|
413
|
+
while (this._pendingCoverageGapAcknowledgements.size > MAX_ROUTES) {
|
|
414
|
+
const oldestKey = ( this._pendingCoverageGapAcknowledgements.keys()).next().value;
|
|
415
|
+
if (oldestKey === undefined) {
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
this._pendingCoverageGapAcknowledgements.delete(oldestKey);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
_applyPendingCoverageGapAcknowledgements(resourceKey) {
|
|
422
|
+
const pending = this._pendingCoverageGapAcknowledgements.get(resourceKey);
|
|
423
|
+
const route = this._routes.get(resourceKey);
|
|
424
|
+
if (!pending || !route || route.lastSequence < getLastAcknowledgedSequence(pending.acknowledgements)) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
this._pendingCoverageGapAcknowledgements.delete(resourceKey);
|
|
428
|
+
const remaining = this._acknowledgeCoverageGaps(resourceKey, pending.acknowledgements);
|
|
429
|
+
if (remaining.length > 0) {
|
|
430
|
+
this._queuePendingCoverageGapAcknowledgements(resourceKey, remaining);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
_registerObservation(resourceKey, before, after) {
|
|
434
|
+
this._prune(resourceKey);
|
|
435
|
+
const observation = {
|
|
436
|
+
id: generateUuid(),
|
|
437
|
+
beforeDigest: createFileEditContentDigest(before),
|
|
438
|
+
afterDigest: createFileEditContentDigest(after),
|
|
439
|
+
timestamp: Date.now(),
|
|
440
|
+
referenceCount: 1
|
|
441
|
+
};
|
|
442
|
+
const observations = this._observations.get(resourceKey) ?? [];
|
|
443
|
+
if (observations.length >= MAX_OBSERVATIONS_PER_RESOURCE) {
|
|
444
|
+
return observation.id;
|
|
445
|
+
}
|
|
446
|
+
observations.push(observation);
|
|
447
|
+
this._observations.set(resourceKey, observations);
|
|
448
|
+
this._tryResolve(resourceKey, observation);
|
|
449
|
+
return observation.id;
|
|
450
|
+
}
|
|
451
|
+
_recordMarker(resourceKey, marker) {
|
|
452
|
+
this._prune(resourceKey);
|
|
453
|
+
const markers = this._markers.get(resourceKey) ?? [];
|
|
454
|
+
if (!( markers.some(candidate => candidate.editId === marker.editId))) {
|
|
455
|
+
markers.push({
|
|
456
|
+
...marker,
|
|
457
|
+
timestamp: Date.now()
|
|
458
|
+
});
|
|
459
|
+
markers.sort((a, b) => a.sequence - b.sequence);
|
|
460
|
+
removeCompletedCycle(markers, marker.editId);
|
|
461
|
+
while (markers.length > MAX_MARKERS_PER_RESOURCE) {
|
|
462
|
+
markers.shift();
|
|
463
|
+
}
|
|
464
|
+
this._markers.set(resourceKey, markers);
|
|
465
|
+
}
|
|
466
|
+
for (const observation of this._observations.get(resourceKey) ?? []) {
|
|
467
|
+
this._tryResolve(resourceKey, observation);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
_tryResolve(resourceKey, observation) {
|
|
471
|
+
if (observation.resolution) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const markers = this._markers.get(resourceKey);
|
|
475
|
+
if (!markers) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
for (let startIndex = 0; startIndex < markers.length; startIndex++) {
|
|
479
|
+
const first = markers[startIndex];
|
|
480
|
+
if (first.beforeDigest !== observation.beforeDigest) {
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
const consumed = [startIndex];
|
|
484
|
+
let afterDigest = first.afterDigest;
|
|
485
|
+
let sequence = first.sequence;
|
|
486
|
+
while (afterDigest !== observation.afterDigest) {
|
|
487
|
+
const nextIndex = markers.findIndex(
|
|
488
|
+
(marker, index) => index !== startIndex && !consumed.includes(index) && marker.sequence > sequence && marker.beforeDigest === afterDigest
|
|
489
|
+
);
|
|
490
|
+
if (nextIndex < 0) {
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
consumed.push(nextIndex);
|
|
494
|
+
afterDigest = markers[nextIndex].afterDigest;
|
|
495
|
+
sequence = markers[nextIndex].sequence;
|
|
496
|
+
}
|
|
497
|
+
if (afterDigest !== observation.afterDigest) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const sources = ( consumed.map(index => markers[index].source));
|
|
501
|
+
const firstSource = sources[0];
|
|
502
|
+
const source = firstSource && sources.every(
|
|
503
|
+
candidate => candidate !== undefined && candidate.modelId === firstSource.modelId && candidate.harness === firstSource.harness
|
|
504
|
+
) ? EditSources.agentHostChatApplyEdits({
|
|
505
|
+
modelId: firstSource.modelId,
|
|
506
|
+
sessionId: firstSource.conversationId,
|
|
507
|
+
requestId: firstSource.requestId,
|
|
508
|
+
harness: firstSource.harness
|
|
509
|
+
}) : undefined;
|
|
510
|
+
observation.resolution = {
|
|
511
|
+
id: observation.id,
|
|
512
|
+
source
|
|
513
|
+
};
|
|
514
|
+
for (const index of consumed.toSorted((a, b) => b - a)) {
|
|
515
|
+
markers.splice(index, 1);
|
|
516
|
+
}
|
|
517
|
+
if (markers.length === 0) {
|
|
518
|
+
this._markers.delete(resourceKey);
|
|
519
|
+
}
|
|
520
|
+
this._onDidSuppress.fire(observation.id);
|
|
521
|
+
this._onDidResolve.fire(observation.resolution);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
_retainObservation(resourceKey, id) {
|
|
526
|
+
const observation = this._getObservation(resourceKey, id);
|
|
527
|
+
if (observation) {
|
|
528
|
+
observation.referenceCount++;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
_releaseObservation(resourceKey, id) {
|
|
532
|
+
const observations = this._observations.get(resourceKey);
|
|
533
|
+
if (!observations) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const index = observations.findIndex(observation => observation.id === id);
|
|
537
|
+
if (index >= 0) {
|
|
538
|
+
const observation = observations[index];
|
|
539
|
+
observation.referenceCount--;
|
|
540
|
+
if (observation.referenceCount <= 0) {
|
|
541
|
+
observations.splice(index, 1);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (observations.length === 0) {
|
|
545
|
+
this._observations.delete(resourceKey);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
_invalidateObservations(resourceKey) {
|
|
549
|
+
this._markers.delete(resourceKey);
|
|
550
|
+
this._coverageGaps.delete(resourceKey);
|
|
551
|
+
this._pendingCoverageGapAcknowledgements.delete(resourceKey);
|
|
552
|
+
const acknowledgementPrefix = coverageGapAcknowledgementKey(resourceKey, "");
|
|
553
|
+
for (const acknowledgementKey of ( this._acknowledgedCoverageGapIds.keys())) {
|
|
554
|
+
if (acknowledgementKey.startsWith(acknowledgementPrefix)) {
|
|
555
|
+
this._acknowledgedCoverageGapIds.delete(acknowledgementKey);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const observations = this._observations.get(resourceKey);
|
|
559
|
+
if (!observations) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
for (const observation of observations) {
|
|
563
|
+
if (observation.resolution) {
|
|
564
|
+
this._onDidInvalidate.fire(observation.id);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
this._observations.delete(resourceKey);
|
|
568
|
+
}
|
|
569
|
+
_prune(resourceKey) {
|
|
570
|
+
const now = Date.now();
|
|
571
|
+
const minimumTimestamp = now - MARKER_TTL;
|
|
572
|
+
const markers = this._markers.get(resourceKey)?.filter(marker => marker.timestamp >= minimumTimestamp);
|
|
573
|
+
if (markers?.length) {
|
|
574
|
+
this._markers.set(resourceKey, markers);
|
|
575
|
+
} else {
|
|
576
|
+
this._markers.delete(resourceKey);
|
|
577
|
+
}
|
|
578
|
+
const observations = this._observations.get(resourceKey)?.filter(
|
|
579
|
+
observation => observation.resolution !== undefined || observation.timestamp >= minimumTimestamp
|
|
580
|
+
);
|
|
581
|
+
if (observations?.length) {
|
|
582
|
+
this._observations.set(resourceKey, observations);
|
|
583
|
+
} else {
|
|
584
|
+
this._observations.delete(resourceKey);
|
|
585
|
+
}
|
|
586
|
+
if ((this._routes.get(resourceKey)?.timestamp ?? now) < now - ROUTE_TTL) {
|
|
587
|
+
this._invalidateObservations(resourceKey);
|
|
588
|
+
this._routes.delete(resourceKey);
|
|
589
|
+
}
|
|
590
|
+
if ((this._coverageGaps.get(resourceKey)?.timestamp ?? now) < now - ROUTE_TTL) {
|
|
591
|
+
this._coverageGaps.delete(resourceKey);
|
|
592
|
+
}
|
|
593
|
+
if ((this._pendingCoverageGapAcknowledgements.get(resourceKey)?.timestamp ?? now) < now - ROUTE_TTL) {
|
|
594
|
+
this._pendingCoverageGapAcknowledgements.delete(resourceKey);
|
|
595
|
+
}
|
|
596
|
+
for (const [acknowledgementKey, timestamp] of this._acknowledgedCoverageGapIds) {
|
|
597
|
+
if (timestamp < now - ROUTE_TTL) {
|
|
598
|
+
this._acknowledgedCoverageGapIds.delete(acknowledgementKey);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
_key(resource) {
|
|
603
|
+
const normalizedResource = resource.scheme === Schemas.vscodeRemote ? ( URI.from({
|
|
604
|
+
scheme: Schemas.file,
|
|
605
|
+
path: resource.path
|
|
606
|
+
})) : resource;
|
|
607
|
+
return this._uriIdentityService.extUri.getComparisonKey(this._uriIdentityService.asCanonicalUri(normalizedResource));
|
|
608
|
+
}
|
|
609
|
+
_getObservation(resourceKey, id) {
|
|
610
|
+
return this._observations.get(resourceKey)?.find(observation => observation.id === id);
|
|
611
|
+
}
|
|
612
|
+
async _waitForResolution(resourceKey, ids, timeoutMs) {
|
|
613
|
+
const unresolved = ( new Set(ids.filter(id => {
|
|
614
|
+
const observation = this._getObservation(resourceKey, id);
|
|
615
|
+
return observation !== undefined && observation.resolution === undefined;
|
|
616
|
+
})));
|
|
617
|
+
if (unresolved.size === 0) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
const store = ( new DisposableStore());
|
|
621
|
+
try {
|
|
622
|
+
await raceTimeout(( new Promise(resolve => {
|
|
623
|
+
const complete = id => {
|
|
624
|
+
unresolved.delete(id);
|
|
625
|
+
if (unresolved.size === 0) {
|
|
626
|
+
resolve();
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
store.add(this._onDidResolve.event(resolution => complete(resolution.id)));
|
|
630
|
+
store.add(this._onDidInvalidate.event(complete));
|
|
631
|
+
})), timeoutMs);
|
|
632
|
+
} finally {
|
|
633
|
+
store.dispose();
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
AgentHostEditMarkerService = ( __decorate([( __param(0, IAgentHostConnectionsService)), ( __param(1, IUriIdentityService))], AgentHostEditMarkerService));
|
|
638
|
+
function isValidCoverageGapAcknowledgements(acknowledgements, lastSequence) {
|
|
639
|
+
if (acknowledgements === undefined) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (!Array.isArray(acknowledgements) || acknowledgements.length === 0 || ( new Set(( acknowledgements.map(acknowledgement => acknowledgement.id)))).size !== acknowledgements.length) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
return acknowledgements.every(
|
|
646
|
+
acknowledgement => typeof acknowledgement.id === "string" && acknowledgement.id.length > 0 && Array.isArray(acknowledgement.sequences) && acknowledgement.sequences.length > 0 && acknowledgement.sequences.every(
|
|
647
|
+
sequence => Number.isSafeInteger(sequence) && sequence >= 0 && (lastSequence === undefined || sequence <= lastSequence)
|
|
648
|
+
) && ( new Set(acknowledgement.sequences)).size === acknowledgement.sequences.length && Number.isSafeInteger(acknowledgement.editCount) && acknowledgement.editCount > 0 && Number.isSafeInteger(acknowledgement.insertedCount) && acknowledgement.insertedCount >= 0
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
function getLastAcknowledgedSequence(acknowledgements) {
|
|
652
|
+
return Math.max(...acknowledgements.flatMap(acknowledgement => acknowledgement.sequences));
|
|
653
|
+
}
|
|
654
|
+
function coverageGapAcknowledgementKey(resourceKey, acknowledgementId) {
|
|
655
|
+
return `${resourceKey}\0${acknowledgementId}`;
|
|
656
|
+
}
|
|
657
|
+
function removeCompletedCycle(markers, latestEditId) {
|
|
658
|
+
const latestIndex = markers.findIndex(marker => marker.editId === latestEditId);
|
|
659
|
+
if (latestIndex < 0) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const completedDigest = markers[latestIndex].afterDigest;
|
|
663
|
+
const consumed = [latestIndex];
|
|
664
|
+
let beforeDigest = markers[latestIndex].beforeDigest;
|
|
665
|
+
let sequence = markers[latestIndex].sequence;
|
|
666
|
+
while (true) {
|
|
667
|
+
if (beforeDigest === completedDigest && consumed.length > 1) {
|
|
668
|
+
for (const index of consumed.toSorted((a, b) => b - a)) {
|
|
669
|
+
markers.splice(index, 1);
|
|
670
|
+
}
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
let previousIndex = -1;
|
|
674
|
+
for (let index = markers.length - 1; index >= 0; index--) {
|
|
675
|
+
const marker = markers[index];
|
|
676
|
+
if (marker.sequence < sequence && marker.afterDigest === beforeDigest) {
|
|
677
|
+
previousIndex = index;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (previousIndex < 0) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
consumed.push(previousIndex);
|
|
685
|
+
beforeDigest = markers[previousIndex].beforeDigest;
|
|
686
|
+
sequence = markers[previousIndex].sequence;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
export { AgentHostEditAttributionDeferredError, AgentHostEditAttributionUnknownOutcomeError, AgentHostEditMarkerService };
|