@vscode/chat-lib 0.43.2026040705 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/ghostText/completionsFromNetwork.d.ts +1 -5
  2. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/ghostText/completionsFromNetwork.d.ts.map +1 -1
  3. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/ghostText/completionsFromNetwork.js +3 -11
  4. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/ghostText/completionsFromNetwork.js.map +1 -1
  5. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/networking.d.ts +0 -30
  6. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/networking.d.ts.map +1 -1
  7. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/networking.js +4 -82
  8. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/networking.js.map +1 -1
  9. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/fetch.d.ts +2 -10
  10. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/fetch.d.ts.map +1 -1
  11. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/fetch.js +6 -160
  12. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/fetch.js.map +1 -1
  13. package/dist/src/_internal/extension/inlineEdits/node/nextEditCache.d.ts +11 -0
  14. package/dist/src/_internal/extension/inlineEdits/node/nextEditCache.d.ts.map +1 -1
  15. package/dist/src/_internal/extension/inlineEdits/node/nextEditCache.js +21 -3
  16. package/dist/src/_internal/extension/inlineEdits/node/nextEditCache.js.map +1 -1
  17. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.d.ts +2 -2
  18. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.d.ts.map +1 -1
  19. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.js +34 -12
  20. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.js.map +1 -1
  21. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.d.ts +1 -1
  22. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.d.ts.map +1 -1
  23. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.js +22 -15
  24. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.js.map +1 -1
  25. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.js +2 -2
  26. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.js.map +1 -1
  27. package/dist/src/_internal/extension/xtab/node/cursorLineDivergence.d.ts +41 -0
  28. package/dist/src/_internal/extension/xtab/node/cursorLineDivergence.d.ts.map +1 -0
  29. package/dist/src/_internal/extension/xtab/node/cursorLineDivergence.js +181 -0
  30. package/dist/src/_internal/extension/xtab/node/cursorLineDivergence.js.map +1 -0
  31. package/dist/src/_internal/extension/xtab/node/editIntent.d.ts +31 -0
  32. package/dist/src/_internal/extension/xtab/node/editIntent.d.ts.map +1 -0
  33. package/dist/src/_internal/extension/xtab/node/editIntent.js +193 -0
  34. package/dist/src/_internal/extension/xtab/node/editIntent.js.map +1 -0
  35. package/dist/src/_internal/extension/xtab/node/responseFormatHandlers.d.ts +52 -0
  36. package/dist/src/_internal/extension/xtab/node/responseFormatHandlers.d.ts.map +1 -0
  37. package/dist/src/_internal/extension/xtab/node/responseFormatHandlers.js +159 -0
  38. package/dist/src/_internal/extension/xtab/node/responseFormatHandlers.js.map +1 -0
  39. package/dist/src/_internal/extension/xtab/node/xtabProvider.d.ts +10 -37
  40. package/dist/src/_internal/extension/xtab/node/xtabProvider.d.ts.map +1 -1
  41. package/dist/src/_internal/extension/xtab/node/xtabProvider.js +277 -349
  42. package/dist/src/_internal/extension/xtab/node/xtabProvider.js.map +1 -1
  43. package/dist/src/_internal/extension/xtab/node/xtabUtils.d.ts +10 -0
  44. package/dist/src/_internal/extension/xtab/node/xtabUtils.d.ts.map +1 -1
  45. package/dist/src/_internal/extension/xtab/node/xtabUtils.js +24 -0
  46. package/dist/src/_internal/extension/xtab/node/xtabUtils.js.map +1 -1
  47. package/dist/src/_internal/platform/authentication/common/copilotToken.d.ts +1 -0
  48. package/dist/src/_internal/platform/authentication/common/copilotToken.d.ts.map +1 -1
  49. package/dist/src/_internal/platform/authentication/common/copilotToken.js +3 -0
  50. package/dist/src/_internal/platform/authentication/common/copilotToken.js.map +1 -1
  51. package/dist/src/_internal/platform/chat/common/commonTypes.d.ts.map +1 -1
  52. package/dist/src/_internal/platform/chat/common/commonTypes.js +34 -4
  53. package/dist/src/_internal/platform/chat/common/commonTypes.js.map +1 -1
  54. package/dist/src/_internal/platform/configuration/common/configurationService.d.ts +17 -4
  55. package/dist/src/_internal/platform/configuration/common/configurationService.d.ts.map +1 -1
  56. package/dist/src/_internal/platform/configuration/common/configurationService.js +22 -9
  57. package/dist/src/_internal/platform/configuration/common/configurationService.js.map +1 -1
  58. package/dist/src/_internal/platform/configuration/common/defaultsOnlyConfigurationService.d.ts +1 -1
  59. package/dist/src/_internal/platform/configuration/common/defaultsOnlyConfigurationService.d.ts.map +1 -1
  60. package/dist/src/_internal/platform/configuration/common/defaultsOnlyConfigurationService.js +1 -1
  61. package/dist/src/_internal/platform/configuration/common/defaultsOnlyConfigurationService.js.map +1 -1
  62. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.d.ts +20 -0
  63. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.d.ts.map +1 -1
  64. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.js +45 -1
  65. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.js.map +1 -1
  66. package/dist/src/_internal/platform/endpoint/common/endpointProvider.d.ts +2 -0
  67. package/dist/src/_internal/platform/endpoint/common/endpointProvider.d.ts.map +1 -1
  68. package/dist/src/_internal/platform/endpoint/common/endpointProvider.js.map +1 -1
  69. package/dist/src/_internal/platform/endpoint/common/statefulMarkerContainer.d.ts +5 -0
  70. package/dist/src/_internal/platform/endpoint/common/statefulMarkerContainer.d.ts.map +1 -1
  71. package/dist/src/_internal/platform/endpoint/common/statefulMarkerContainer.js +13 -0
  72. package/dist/src/_internal/platform/endpoint/common/statefulMarkerContainer.js.map +1 -1
  73. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.d.ts +3 -1
  74. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.d.ts.map +1 -1
  75. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.js +14 -14
  76. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.js.map +1 -1
  77. package/dist/src/_internal/platform/endpoint/node/messagesApi.d.ts.map +1 -1
  78. package/dist/src/_internal/platform/endpoint/node/messagesApi.js +28 -12
  79. package/dist/src/_internal/platform/endpoint/node/messagesApi.js.map +1 -1
  80. package/dist/src/_internal/platform/endpoint/node/responsesApi.d.ts +12 -2
  81. package/dist/src/_internal/platform/endpoint/node/responsesApi.d.ts.map +1 -1
  82. package/dist/src/_internal/platform/endpoint/node/responsesApi.js +201 -23
  83. package/dist/src/_internal/platform/endpoint/node/responsesApi.js.map +1 -1
  84. package/dist/src/_internal/platform/git/common/gitService.d.ts +7 -3
  85. package/dist/src/_internal/platform/git/common/gitService.d.ts.map +1 -1
  86. package/dist/src/_internal/platform/git/common/gitService.js.map +1 -1
  87. package/dist/src/_internal/platform/github/common/githubAPI.d.ts +1 -0
  88. package/dist/src/_internal/platform/github/common/githubAPI.d.ts.map +1 -1
  89. package/dist/src/_internal/platform/github/common/githubAPI.js +2 -0
  90. package/dist/src/_internal/platform/github/common/githubAPI.js.map +1 -1
  91. package/dist/src/_internal/platform/github/common/githubService.d.ts +3 -2
  92. package/dist/src/_internal/platform/github/common/githubService.d.ts.map +1 -1
  93. package/dist/src/_internal/platform/github/common/githubService.js.map +1 -1
  94. package/dist/src/_internal/platform/inlineEdits/common/inlineEditLogContext.d.ts +12 -3
  95. package/dist/src/_internal/platform/inlineEdits/common/inlineEditLogContext.d.ts.map +1 -1
  96. package/dist/src/_internal/platform/inlineEdits/common/inlineEditLogContext.js +72 -46
  97. package/dist/src/_internal/platform/inlineEdits/common/inlineEditLogContext.js.map +1 -1
  98. package/dist/src/_internal/platform/inlineEdits/common/utils/utils.d.ts +3 -0
  99. package/dist/src/_internal/platform/inlineEdits/common/utils/utils.d.ts.map +1 -1
  100. package/dist/src/_internal/platform/inlineEdits/common/utils/utils.js +12 -0
  101. package/dist/src/_internal/platform/inlineEdits/common/utils/utils.js.map +1 -1
  102. package/dist/src/_internal/platform/log/common/logService.d.ts +5 -0
  103. package/dist/src/_internal/platform/log/common/logService.d.ts.map +1 -1
  104. package/dist/src/_internal/platform/log/common/logService.js +22 -0
  105. package/dist/src/_internal/platform/log/common/logService.js.map +1 -1
  106. package/dist/src/_internal/platform/nesFetch/node/completionsFetchServiceImpl.d.ts +1 -1
  107. package/dist/src/_internal/platform/nesFetch/node/completionsFetchServiceImpl.d.ts.map +1 -1
  108. package/dist/src/_internal/platform/nesFetch/node/completionsFetchServiceImpl.js +1 -1
  109. package/dist/src/_internal/platform/nesFetch/node/completionsFetchServiceImpl.js.map +1 -1
  110. package/dist/src/_internal/platform/networking/common/anthropic.d.ts +0 -16
  111. package/dist/src/_internal/platform/networking/common/anthropic.d.ts.map +1 -1
  112. package/dist/src/_internal/platform/networking/common/anthropic.js +10 -42
  113. package/dist/src/_internal/platform/networking/common/anthropic.js.map +1 -1
  114. package/dist/src/_internal/platform/networking/common/fetcherService.d.ts +1 -0
  115. package/dist/src/_internal/platform/networking/common/fetcherService.d.ts.map +1 -1
  116. package/dist/src/_internal/platform/networking/common/fetcherService.js.map +1 -1
  117. package/dist/src/_internal/platform/networking/common/networking.d.ts +22 -4
  118. package/dist/src/_internal/platform/networking/common/networking.d.ts.map +1 -1
  119. package/dist/src/_internal/platform/networking/common/networking.js.map +1 -1
  120. package/dist/src/_internal/platform/networking/node/chatStream.d.ts +10 -0
  121. package/dist/src/_internal/platform/networking/node/chatStream.d.ts.map +1 -1
  122. package/dist/src/_internal/platform/networking/node/chatStream.js +28 -0
  123. package/dist/src/_internal/platform/networking/node/chatStream.js.map +1 -1
  124. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.d.ts +23 -5
  125. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.d.ts.map +1 -1
  126. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.js +67 -20
  127. package/dist/src/_internal/platform/networking/node/chatWebSocketManager.js.map +1 -1
  128. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.d.ts +15 -1
  129. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.d.ts.map +1 -1
  130. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.js +51 -7
  131. package/dist/src/_internal/platform/networking/node/chatWebSocketTelemetry.js.map +1 -1
  132. package/dist/src/_internal/platform/requestLogger/common/requestLogger.d.ts +171 -0
  133. package/dist/src/_internal/platform/requestLogger/common/requestLogger.d.ts.map +1 -0
  134. package/dist/src/_internal/platform/requestLogger/common/requestLogger.js +145 -0
  135. package/dist/src/_internal/platform/requestLogger/common/requestLogger.js.map +1 -0
  136. package/dist/src/_internal/platform/requestLogger/node/nullRequestLogger.d.ts +2 -1
  137. package/dist/src/_internal/platform/requestLogger/node/nullRequestLogger.d.ts.map +1 -1
  138. package/dist/src/_internal/platform/requestLogger/node/nullRequestLogger.js.map +1 -1
  139. package/dist/src/_internal/platform/requestLogger/node/requestLogger.d.ts +4 -166
  140. package/dist/src/_internal/platform/requestLogger/node/requestLogger.d.ts.map +1 -1
  141. package/dist/src/_internal/platform/requestLogger/node/requestLogger.js +3 -139
  142. package/dist/src/_internal/platform/requestLogger/node/requestLogger.js.map +1 -1
  143. package/dist/src/main.d.ts +2 -0
  144. package/dist/src/main.d.ts.map +1 -1
  145. package/dist/src/main.js +48 -6
  146. package/dist/src/main.js.map +1 -1
  147. package/dist/src/package.json +106 -96
  148. package/package.json +71 -71
  149. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/stream.d.ts +0 -182
  150. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/stream.d.ts.map +0 -1
  151. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/stream.js +0 -558
  152. package/dist/src/_internal/extension/completions-core/vscode-node/lib/src/openai/stream.js.map +0 -1
@@ -47,7 +47,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
47
47
  };
48
48
  var XtabProvider_1;
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
- exports.EditIntentParseMode = exports.XtabProvider = void 0;
50
+ exports.XtabProvider = void 0;
51
51
  exports.filterOutEditsWithSubstrings = filterOutEditsWithSubstrings;
52
52
  exports.computeAreaAroundEditWindowLinesRange = computeAreaAroundEditWindowLinesRange;
53
53
  exports.mapChatFetcherErrorToNoNextEditReason = mapChatFetcherErrorToNoNextEditReason;
@@ -55,8 +55,6 @@ exports.overrideModelConfig = overrideModelConfig;
55
55
  exports.pickSystemPrompt = pickSystemPrompt;
56
56
  exports.determineLanguageContextOptions = determineLanguageContextOptions;
57
57
  exports.getPredictionContents = getPredictionContents;
58
- exports.parseEditIntentFromStream = parseEditIntentFromStream;
59
- exports.findMergeConflictMarkersRange = findMergeConflictMarkersRange;
60
58
  const chatMLFetcher_1 = require("../../../platform/chat/common/chatMLFetcher");
61
59
  const commonTypes_1 = require("../../../platform/chat/common/commonTypes");
62
60
  const configurationService_1 = require("../../../platform/configuration/common/configurationService");
@@ -85,6 +83,7 @@ const errors_1 = require("../../../util/common/errors");
85
83
  const result_1 = require("../../../util/common/result");
86
84
  const assert_1 = require("../../../util/vs/base/common/assert");
87
85
  const async_2 = require("../../../util/vs/base/common/async");
86
+ const cancellation_1 = require("../../../util/vs/base/common/cancellation");
88
87
  const path_1 = require("../../../util/vs/base/common/path");
89
88
  const stopwatch_1 = require("../../../util/vs/base/common/stopwatch");
90
89
  const uri_1 = require("../../../util/vs/base/common/uri");
@@ -109,6 +108,9 @@ const systemMessages_1 = require("../common/systemMessages");
109
108
  const tags_1 = require("../common/tags");
110
109
  const terminalOutput_1 = require("../common/terminalOutput");
111
110
  const xtabCurrentDocument_1 = require("../common/xtabCurrentDocument");
111
+ const cursorLineDivergence_1 = require("./cursorLineDivergence");
112
+ const editIntent_1 = require("./editIntent");
113
+ const responseFormatHandlers_1 = require("./responseFormatHandlers");
112
114
  const xtabCustomDiffPatchResponseHandler_1 = require("./xtabCustomDiffPatchResponseHandler");
113
115
  const xtabEndpoint_1 = require("./xtabEndpoint");
114
116
  const xtabNextCursorPredictor_1 = require("./xtabNextCursorPredictor");
@@ -134,6 +136,28 @@ var RetryState;
134
136
  }
135
137
  RetryState.Retrying = Retrying;
136
138
  })(RetryState || (RetryState = {}));
139
+ var FetchResult;
140
+ (function (FetchResult) {
141
+ class Lines {
142
+ constructor(linesStream, getFetchFailure, getResponseSoFar, fetchRequestStopWatch) {
143
+ this.linesStream = linesStream;
144
+ this.getFetchFailure = getFetchFailure;
145
+ this.getResponseSoFar = getResponseSoFar;
146
+ this.fetchRequestStopWatch = fetchRequestStopWatch;
147
+ }
148
+ }
149
+ FetchResult.Lines = Lines;
150
+ class ModelNotFound {
151
+ static { this.INSTANCE = new ModelNotFound(); }
152
+ }
153
+ FetchResult.ModelNotFound = ModelNotFound;
154
+ class FetchFailure {
155
+ constructor(reason) {
156
+ this.reason = reason;
157
+ }
158
+ }
159
+ FetchResult.FetchFailure = FetchFailure;
160
+ })(FetchResult || (FetchResult = {}));
137
161
  let XtabProvider = class XtabProvider {
138
162
  static { XtabProvider_1 = this; }
139
163
  static { this.ID = configurationService_1.XTabProviderId; }
@@ -174,7 +198,8 @@ let XtabProvider = class XtabProvider {
174
198
  return new statelessNextEditProvider_1.WithStatelessProviderTelemetry(noSuggestionReason, telemetry.build(result_1.Result.error(noSuggestionReason)));
175
199
  }
176
200
  const delaySession = this.userInteractionMonitor.createDelaySession(request.providerRequestStartDateTime);
177
- const iterator = this.doGetNextEdit(request, delaySession, logger, logContext, cancellationToken, telemetry, RetryState.NotRetrying.INSTANCE);
201
+ const tracing = { tracer: logger, logContext, telemetry };
202
+ const iterator = this.doGetNextEdit(request, delaySession, tracing, cancellationToken, RetryState.NotRetrying.INSTANCE);
178
203
  let res = await iterator.next(); // for-async-await loop doesn't work because we need to access the final return value
179
204
  while (!res.done) {
180
205
  yield new statelessNextEditProvider_1.WithStatelessProviderTelemetry(res.value, telemetry.build(result_1.Result.ok(undefined)));
@@ -195,29 +220,30 @@ let XtabProvider = class XtabProvider {
195
220
  logContext.setProviderEndTime();
196
221
  }
197
222
  }
198
- doGetNextEdit(request, delaySession, logger, logContext, cancellationToken, telemetryBuilder, retryState) {
199
- return this.doGetNextEditWithSelection(request, (0, nearbyCursorInlineEditProvider_1.getOrDeduceSelectionFromLastEdit)(request.getActiveDocument()), delaySession, logger, logContext, cancellationToken, telemetryBuilder, retryState);
223
+ doGetNextEdit(request, delaySession, tracing, cancellationToken, retryState) {
224
+ return this.doGetNextEditWithSelection(request, (0, nearbyCursorInlineEditProvider_1.getOrDeduceSelectionFromLastEdit)(request.getActiveDocument()), delaySession, tracing, cancellationToken, retryState);
200
225
  }
201
- async *doGetNextEditWithSelection(request, selection, delaySession, parentTracer, logContext, cancellationToken, telemetryBuilder, retryState,
226
+ async *doGetNextEditWithSelection(request, selection, delaySession, tracing, cancellationToken, retryState,
202
227
  /**
203
228
  * For cursor jump scenarios, this is the edit window around the original cursor position
204
229
  * (before the jump). When provided, yielded edits will include this as `originalWindow`
205
230
  * so the cache can serve the edit when the cursor is in either location.
206
231
  */
207
232
  originalEditWindow) {
208
- const tracer = parentTracer.createSubLogger(['XtabProvider', 'doGetNextEditWithSelection']);
233
+ const tracer = tracing.tracer.createSubLogger(['XtabProvider', 'doGetNextEditWithSelection']);
234
+ const { logContext, telemetry } = tracing;
209
235
  const activeDocument = request.getActiveDocument();
210
236
  if (selection === null) {
211
237
  return new statelessNextEditProvider_1.NoNextEditReason.Uncategorized(new Error('NoSelection'));
212
238
  }
213
239
  const { promptOptions, modelServiceConfig } = this.determineModelConfiguration(activeDocument);
214
- telemetryBuilder.setModelConfig(JSON.stringify(modelServiceConfig));
215
- const endpoint = this.getEndpointWithLogging(promptOptions.modelName, logContext, telemetryBuilder);
240
+ telemetry.setModelConfig(JSON.stringify(modelServiceConfig));
241
+ const endpoint = this.getEndpointWithLogging(promptOptions.modelName, logContext, telemetry);
216
242
  const cursorPosition = new position_1.Position(selection.endLineNumber, selection.endColumn);
217
243
  const currentDocument = new xtabCurrentDocument_1.CurrentDocument(activeDocument.documentAfterEdits, cursorPosition);
218
- this._configureDebounceTimings(request, currentDocument, promptOptions, telemetryBuilder, delaySession, tracer);
244
+ this._configureDebounceTimings(request, currentDocument, promptOptions, telemetry, delaySession, tracer);
219
245
  const areaAroundEditWindowLinesRange = computeAreaAroundEditWindowLinesRange(currentDocument);
220
- const editWindowLinesRange = this.computeEditWindowLinesRange(currentDocument, request, tracer, telemetryBuilder);
246
+ const editWindowLinesRange = this.computeEditWindowLinesRange(currentDocument, request, tracer, telemetry);
221
247
  const cursorOriginalLinesOffset = Math.max(0, currentDocument.cursorLineOffset - editWindowLinesRange.start);
222
248
  const editWindowLastLineLength = currentDocument.transformer.getLineLength(editWindowLinesRange.endExclusive);
223
249
  const editWindow = currentDocument.transformer.getOffsetRange(new range_1.Range(editWindowLinesRange.start + 1, 1, editWindowLinesRange.endExclusive, editWindowLastLineLength + 1));
@@ -242,25 +268,25 @@ let XtabProvider = class XtabProvider {
242
268
  return new statelessNextEditProvider_1.NoNextEditReason.PromptTooLarge('currentFile');
243
269
  }
244
270
  const { clippedTaggedCurrentDoc, areaAroundCodeToEdit } = taggedCurrentFileContentResult.val;
245
- telemetryBuilder.setNLinesOfCurrentFileInPrompt(clippedTaggedCurrentDoc.lines.length);
271
+ telemetry.setNLinesOfCurrentFileInPrompt(clippedTaggedCurrentDoc.lines.length);
246
272
  const { aggressivenessLevel, userHappinessScore } = this.userInteractionMonitor.getAggressivenessLevel();
247
273
  // Log user's raw aggressiveness setting when explicitly changed from default
248
274
  const userAggressivenessSetting = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.Advanced.InlineEditsAggressiveness, this.expService);
249
- telemetryBuilder.setUserAggressivenessSetting(userAggressivenessSetting);
275
+ telemetry.setUserAggressivenessSetting(userAggressivenessSetting);
250
276
  // Log aggressiveness level and user happiness score
251
- telemetryBuilder.setXtabAggressivenessLevel(aggressivenessLevel);
277
+ telemetry.setXtabAggressivenessLevel(aggressivenessLevel);
252
278
  if (userHappinessScore !== undefined) {
253
- telemetryBuilder.setXtabUserHappinessScore(userHappinessScore);
279
+ telemetry.setXtabUserHappinessScore(userHappinessScore);
254
280
  }
255
- const langCtx = await this.getAndProcessLanguageContext(request, delaySession, activeDocument, cursorPosition, promptOptions, tracer, logContext, cancellationToken);
281
+ const langCtx = await this.getAndProcessLanguageContext(request, delaySession, activeDocument, cursorPosition, promptOptions, { tracer, logContext, telemetry }, cancellationToken);
256
282
  if (cancellationToken.isCancellationRequested) {
257
283
  return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterLanguageContextAwait');
258
284
  }
259
285
  const lintErrors = new lintErrors_1.LintErrors(activeDocument.id, currentDocument, this.langDiagService, request.xtabEditHistory);
260
286
  const promptPieces = new promptCrafting_1.PromptPieces(currentDocument, editWindowLinesRange, areaAroundEditWindowLinesRange, activeDocument, request.xtabEditHistory, clippedTaggedCurrentDoc.lines, areaAroundCodeToEdit, langCtx, aggressivenessLevel, lintErrors, XtabProvider_1.computeTokens, promptOptions);
261
287
  const { prompt: userPrompt, nDiffsInPrompt, diffTokensInPrompt } = (0, promptCrafting_1.getUserPrompt)(promptPieces);
262
- telemetryBuilder.setNDiffsInPrompt(nDiffsInPrompt);
263
- telemetryBuilder.setDiffTokensInPrompt(diffTokensInPrompt);
288
+ telemetry.setNDiffsInPrompt(nDiffsInPrompt);
289
+ telemetry.setDiffTokensInPrompt(diffTokensInPrompt);
264
290
  const responseFormat = xtabPromptOptions.ResponseFormat.fromPromptingStrategy(promptOptions.promptingStrategy);
265
291
  const prediction = this.getPredictedOutput(activeDocument, editWindowLines, responseFormat);
266
292
  const messages = (0, xtabUtils_1.constructMessages)({
@@ -268,35 +294,47 @@ let XtabProvider = class XtabProvider {
268
294
  userMsg: userPrompt,
269
295
  });
270
296
  logContext.setPrompt(messages);
271
- telemetryBuilder.setPrompt(messages);
297
+ telemetry.setPrompt(messages);
272
298
  const HARD_CHAR_LIMIT = 30000 * 4; // 30K tokens, assuming 4 chars per token -- we use approximation here because counting tokens exactly is time-consuming
273
299
  const promptCharCount = (0, xtabUtils_1.charCount)(messages);
274
300
  if (promptCharCount > HARD_CHAR_LIMIT) {
275
301
  return new statelessNextEditProvider_1.NoNextEditReason.PromptTooLarge('final');
276
302
  }
277
- await this.debounce(delaySession, retryState, tracer, telemetryBuilder, cancellationToken);
303
+ await this.debounce(delaySession, retryState, tracer, telemetry, cancellationToken);
278
304
  if (cancellationToken.isCancellationRequested) {
279
305
  return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterDebounce');
280
306
  }
281
307
  // Fire-and-forget: collect lint errors and terminal output for telemetry in background to avoid blocking the main path
282
308
  Promise.resolve().then(() => {
283
309
  const lintErrorsData = lintErrors.getData();
284
- telemetryBuilder.setLintErrors(lintErrorsData);
310
+ telemetry.setLintErrors(lintErrorsData);
285
311
  logContext.setDiagnosticsData(lintErrorsData);
286
312
  const terminalOutputData = this.terminalMonitor.getData();
287
- telemetryBuilder.setTerminalOutput(terminalOutputData);
313
+ telemetry.setTerminalOutput(terminalOutputData);
288
314
  logContext.setTerminalData(terminalOutputData);
289
315
  });
290
316
  // Fire-and-forget: compute GhostText-style similar files context for telemetry
291
- telemetryBuilder.setSimilarFilesContext(this.similarFilesContextService.compute(activeDocument.id.uri, activeDocument.languageId, activeDocument.documentAfterEdits.value, currentDocument.cursorOffset));
317
+ telemetry.setSimilarFilesContext(this.similarFilesContextService.compute(activeDocument.id.uri, activeDocument.languageId, activeDocument.documentAfterEdits.value, currentDocument.cursorOffset));
292
318
  request.fetchIssued = true;
293
- return yield* this.streamEditsWithFiltering(request, endpoint, modelServiceConfig, messages, clippedTaggedCurrentDoc, editWindow, editWindowLines, cursorOriginalLinesOffset, editWindowLinesRange, promptPieces, prediction, {
319
+ const editStreamCtx = {
320
+ endpoint,
321
+ modelServiceConfig,
322
+ messages,
323
+ clippedTaggedCurrentDoc,
324
+ editWindowInfo: {
325
+ editWindow,
326
+ editWindowLines,
327
+ cursorOriginalLinesOffset,
328
+ editWindowLineRange: editWindowLinesRange,
329
+ },
330
+ promptPieces,
331
+ prediction,
332
+ originalEditWindow,
333
+ };
334
+ return yield* this.streamEditsWithFiltering(request, editStreamCtx, {
294
335
  shouldRemoveCursorTagFromResponse,
295
336
  responseFormat,
296
- retryState,
297
- aggressivenessLevel,
298
- userHappinessScore,
299
- }, delaySession, tracer, telemetryBuilder, logContext, cancellationToken, originalEditWindow);
337
+ }, { aggressivenessLevel, userHappinessScore }, retryState, delaySession, { tracer, logContext, telemetry }, cancellationToken);
300
338
  }
301
339
  _configureDebounceTimings(request, currentDocument, promptOptions, telemetry, delaySession, tracer) {
302
340
  const isCursorAtEndOfLine = currentDocument.isCursorAtEndOfLine();
@@ -356,17 +394,17 @@ let XtabProvider = class XtabProvider {
356
394
  tracer.trace(`Aggressiveness ${userAggressiveness}: min response time set to ${minResponseTimeMs}ms`);
357
395
  }
358
396
  }
359
- getAndProcessLanguageContext(request, delaySession, activeDocument, cursorPosition, promptOptions, tracer, logContext, cancellationToken) {
397
+ getAndProcessLanguageContext(request, delaySession, activeDocument, cursorPosition, promptOptions, tracing, cancellationToken) {
360
398
  const recordingEnabled = this.configService.getConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsLogContextRecorderEnabled);
361
399
  if (!promptOptions.languageContext.enabled && !recordingEnabled) {
362
400
  return Promise.resolve(undefined);
363
401
  }
364
- const langCtxPromise = this.getLanguageContext(request, delaySession, activeDocument, cursorPosition, tracer, logContext, cancellationToken);
402
+ const langCtxPromise = this.getLanguageContext(request, delaySession, activeDocument, cursorPosition, tracing, cancellationToken);
365
403
  // if recording, add diagnostics for the file to the recording and hook up the language context promise to write to the recording
366
404
  if (recordingEnabled) {
367
405
  langCtxPromise.then(langCtxs => {
368
406
  if (langCtxs) {
369
- logContext.setLanguageContext(langCtxs);
407
+ tracing.logContext.setLanguageContext(langCtxs);
370
408
  }
371
409
  });
372
410
  }
@@ -374,7 +412,7 @@ let XtabProvider = class XtabProvider {
374
412
  ? langCtxPromise
375
413
  : Promise.resolve(undefined);
376
414
  }
377
- async getLanguageContext(request, delaySession, activeDocument, cursorPosition, tracer, logContext, cancellationToken) {
415
+ async getLanguageContext(request, delaySession, activeDocument, cursorPosition, tracing, cancellationToken) {
378
416
  try {
379
417
  const textDoc = this.workspaceService.textDocuments.find(doc => doc.uri.toString() === activeDocument.id.uri);
380
418
  if (textDoc === undefined) {
@@ -434,14 +472,15 @@ let XtabProvider = class XtabProvider {
434
472
  return { start, end, items: langCtxItems };
435
473
  }
436
474
  catch (error) {
437
- logContext.setError(errors_1.ErrorUtils.fromUnknown(error));
438
- tracer.trace(`Failed to fetch language context: ${error}`);
475
+ tracing.logContext.setError(errors_1.ErrorUtils.fromUnknown(error));
476
+ tracing.tracer.trace(`Failed to fetch language context: ${error}`);
439
477
  return undefined;
440
478
  }
441
479
  }
442
- async *streamEditsWithFiltering(request, endpoint, modelServiceConfig, messages, clippedTaggedCurrentDoc, editWindow, editWindowLines, cursorOriginalLinesOffset, editWindowLineRange, promptPieces, prediction, opts, delaySession, parentTracer, telemetryBuilder, logContext, cancellationToken, originalEditWindow) {
443
- const tracer = parentTracer.createSubLogger('streamEditsWithFiltering');
444
- const iterator = this.streamEdits(request, endpoint, modelServiceConfig, messages, clippedTaggedCurrentDoc, editWindow, editWindowLines, cursorOriginalLinesOffset, editWindowLineRange, promptPieces, prediction, opts, delaySession, tracer, telemetryBuilder, logContext, cancellationToken, originalEditWindow);
480
+ async *streamEditsWithFiltering(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, tracing, cancellationToken) {
481
+ const tracer = tracing.tracer.createSubLogger('streamEditsWithFiltering');
482
+ const subTracing = { ...tracing, tracer };
483
+ const iterator = this.streamEdits(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, subTracing, cancellationToken);
445
484
  let nEdits = 0;
446
485
  let r = await iterator.next();
447
486
  while (!r.done) {
@@ -461,13 +500,35 @@ let XtabProvider = class XtabProvider {
461
500
  if (nEdits === 0 &&
462
501
  r.value instanceof statelessNextEditProvider_1.NoNextEditReason.NoSuggestions // only retry if there was no error, cancellation, etc.
463
502
  ) {
464
- return yield* this.doGetNextEditsWithCursorJump(request, modelServiceConfig, editWindow, promptPieces, delaySession, parentTracer, logContext, cancellationToken, telemetryBuilder, opts.retryState);
503
+ return yield* this.doGetNextEditsWithCursorJump(request, editStreamCtx, delaySession, tracing, cancellationToken, retryState);
465
504
  }
466
505
  return r.value;
467
506
  }
468
- async *streamEdits(request, endpoint, modelServiceConfig, messages, clippedTaggedCurrentDoc, editWindow, editWindowLines, cursorOriginalLinesOffset, editWindowLineRange, promptPieces, prediction, opts, delaySession, parentTracer, telemetryBuilder, logContext, cancellationToken, originalEditWindow) {
469
- const tracer = parentTracer.createSubLogger('streamEdits');
470
- const targetDocument = request.getActiveDocument().id;
507
+ async *streamEdits(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, tracing, cancellationToken) {
508
+ const tracer = tracing.tracer.createSubLogger('streamEdits');
509
+ // Create a local cancellation source linked to the caller's token.
510
+ // This lets us cancel the fetch immediately on cursor-line divergence
511
+ // without reaching into the request's CancellationTokenSource (which
512
+ // is owned by nextEditProvider.ts).
513
+ const fetchCts = new cancellation_1.CancellationTokenSource(cancellationToken);
514
+ const fetchCancellationToken = fetchCts.token;
515
+ try {
516
+ return yield* this._streamEditsImpl(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, { ...tracing, tracer }, cancellationToken, fetchCts, fetchCancellationToken);
517
+ }
518
+ finally {
519
+ fetchCts.dispose();
520
+ }
521
+ }
522
+ /**
523
+ * Initiates the HTTP fetch, sets up the streaming pipeline, and returns either
524
+ * a clean line stream (with cursor-tag removal and latency logging applied)
525
+ * or an error / retry signal.
526
+ *
527
+ * This method encapsulates all fetch infrastructure so that downstream response
528
+ * format handlers only need an `AsyncIterable<string>` line stream.
529
+ */
530
+ async _performFetch(endpoint, messages, prediction, requestId, fetchMetadata, shouldRemoveCursorTagFromResponse, editWindow, documentBeforeEdits, fetchCancellationToken, tracing) {
531
+ const { tracer, logContext, telemetry } = tracing;
471
532
  const useFetcher = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.NextEditSuggestionsFetcher, this.expService) || undefined;
472
533
  const fetchStreamSource = new chatMLFetcher_1.FetchStreamSource();
473
534
  const fetchRequestStopWatch = new stopwatch_1.StopWatch();
@@ -475,8 +536,8 @@ let XtabProvider = class XtabProvider {
475
536
  let chatResponseFailure;
476
537
  let ttft;
477
538
  const firstTokenReceived = new async_2.DeferredPromise();
478
- logContext.setHeaderRequestId(request.headerRequestId);
479
- telemetryBuilder.setFetchStartedAt();
539
+ logContext.setHeaderRequestId(requestId);
540
+ telemetry.setFetchStartedAt();
480
541
  logContext.setFetchStartTime();
481
542
  // we must not await this promise because we want to stream edits as they come in
482
543
  const fetchResultPromise = endpoint.makeChatRequest2({
@@ -504,30 +565,16 @@ let XtabProvider = class XtabProvider {
504
565
  },
505
566
  userInitiatedRequest: undefined,
506
567
  telemetryProperties: {
507
- requestId: request.headerRequestId,
568
+ requestId,
508
569
  },
509
570
  useFetcher,
510
571
  customMetadata: {
511
- aggressivenessLevel: opts.aggressivenessLevel,
512
- userHappinessScore: opts.userHappinessScore,
572
+ aggressivenessLevel: fetchMetadata.aggressivenessLevel,
573
+ userHappinessScore: fetchMetadata.userHappinessScore,
513
574
  },
514
- }, cancellationToken);
515
- telemetryBuilder.setResponse(fetchResultPromise.then((response) => ({ response, ttft })));
575
+ }, fetchCancellationToken);
576
+ telemetry.setResponse(fetchResultPromise.then((response) => ({ response, ttft })));
516
577
  logContext.setFullResponse(fetchResultPromise.then((response) => response.type === commonTypes_1.ChatFetchResponseType.Success ? response.value : undefined));
517
- const fetchRes = await Promise.race([firstTokenReceived.p, fetchResultPromise]);
518
- if (fetchRes && fetchRes.type !== commonTypes_1.ChatFetchResponseType.Success) {
519
- if (fetchRes.type === commonTypes_1.ChatFetchResponseType.NotFound &&
520
- !this.forceUseDefaultModel // if we haven't already forced using the default model; otherwise, this could cause an infinite loop
521
- ) {
522
- this.forceUseDefaultModel = true;
523
- return yield* this.doGetNextEdit(request, delaySession, tracer, logContext, cancellationToken, telemetryBuilder, opts.retryState); // use the same retry state
524
- }
525
- // diff-patch based model returns no choices if it has no edits to suggest
526
- if (fetchRes.type === commonTypes_1.ChatFetchResponseType.Unknown && fetchRes.reason === commonTypes_1.RESPONSE_CONTAINED_NO_CHOICES) {
527
- return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
528
- }
529
- return mapChatFetcherErrorToNoNextEditReason(fetchRes);
530
- }
531
578
  fetchResultPromise
532
579
  .then((response) => {
533
580
  // this's a way to signal the edit-pushing code to know if the request failed and
@@ -546,6 +593,21 @@ let XtabProvider = class XtabProvider {
546
593
  fetchStreamSource.resolve();
547
594
  logContext.setResponse(responseSoFar);
548
595
  });
596
+ const fetchRes = await Promise.race([firstTokenReceived.p, fetchResultPromise]);
597
+ if (fetchRes && fetchRes.type !== commonTypes_1.ChatFetchResponseType.Success) {
598
+ if (fetchRes.type === commonTypes_1.ChatFetchResponseType.NotFound &&
599
+ !this.forceUseDefaultModel // if we haven't already forced using the default model; otherwise, this could cause an infinite loop
600
+ ) {
601
+ this.forceUseDefaultModel = true;
602
+ return FetchResult.ModelNotFound.INSTANCE;
603
+ }
604
+ // diff-patch based model returns no choices if it has no edits to suggest
605
+ if (fetchRes.type === commonTypes_1.ChatFetchResponseType.Unknown && fetchRes.reason === commonTypes_1.RESPONSE_CONTAINED_NO_CHOICES) {
606
+ return new FetchResult.FetchFailure(new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(documentBeforeEdits, editWindow));
607
+ }
608
+ return new FetchResult.FetchFailure(mapChatFetcherErrorToNoNextEditReason(fetchRes));
609
+ }
610
+ const getFetchFailure = () => chatResponseFailure ? mapChatFetcherErrorToNoNextEditReason(chatResponseFailure) : undefined;
549
611
  const llmLinesStream = asyncIterableUtils_1.AsyncIterUtilsExt.splitLines(asyncIterableUtils_1.AsyncIterUtils.map(fetchStreamSource.stream, (chunk) => chunk.delta.text));
550
612
  // logging of times
551
613
  // removal of cursor tag if option is set
@@ -554,119 +616,150 @@ let XtabProvider = class XtabProvider {
554
616
  for await (const v of llmLinesStream) {
555
617
  const trace = `Line ${i++} emitted with latency ${fetchRequestStopWatch.elapsed()} ms`;
556
618
  tracer.trace(trace);
557
- yield opts.shouldRemoveCursorTagFromResponse
619
+ yield shouldRemoveCursorTagFromResponse
558
620
  ? v.replaceAll(tags_1.PromptTags.CURSOR, '')
559
621
  : v;
560
622
  }
561
623
  })();
562
- const isFromCursorJump = opts.retryState instanceof RetryState.Retrying && opts.retryState.reason === 'cursorJump';
563
- let cleanedLinesStream;
564
- if (opts.responseFormat === xtabPromptOptions.ResponseFormat.EditWindowOnly) {
565
- cleanedLinesStream = linesStream;
566
- }
567
- else if (opts.responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntent ||
568
- opts.responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort) {
569
- // Determine parse mode based on response format
570
- const parseMode = opts.responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort
571
- ? EditIntentParseMode.ShortName
572
- : EditIntentParseMode.Tags;
573
- // Parse the edit_intent from the response
574
- const { editIntent, remainingLinesStream, parseError } = await parseEditIntentFromStream(linesStream, tracer, parseMode);
575
- // Log the edit intent for telemetry
576
- telemetryBuilder.setEditIntent(editIntent);
577
- // Log parse errors for telemetry - this helps detect malformed model output during flights
578
- if (parseError) {
579
- telemetryBuilder.setEditIntentParseError(parseError);
580
- }
581
- // Check if we should show this edit based on intent and aggressiveness
582
- if (!xtabPromptOptions.EditIntent.shouldShowEdit(editIntent, promptPieces.aggressivenessLevel)) {
583
- tracer.trace(`Filtered out edit due to edit intent "${editIntent}" with aggressiveness "${promptPieces.aggressivenessLevel}"`);
584
- return new statelessNextEditProvider_1.NoNextEditReason.FilteredOut(`editIntent:${editIntent} aggressivenessLevel:${promptPieces.aggressivenessLevel}`);
624
+ return new FetchResult.Lines(linesStream, getFetchFailure, () => responseSoFar, fetchRequestStopWatch);
625
+ }
626
+ async *_streamEditsImpl(request, editStreamCtx, responseOpts, fetchMetadata, retryState, delaySession, tracing, cancellationToken, fetchCts, fetchCancellationToken) {
627
+ const { tracer, logContext, telemetry } = tracing;
628
+ const { endpoint, messages, clippedTaggedCurrentDoc, editWindowInfo, promptPieces, prediction, originalEditWindow } = editStreamCtx;
629
+ const { editWindow, editWindowLines, cursorOriginalLinesOffset, editWindowLineRange } = editWindowInfo;
630
+ const targetDocument = request.getActiveDocument().id;
631
+ // Phase 1: Fetch lifecycle initiate HTTP request and produce a clean line stream
632
+ const fetchResult = await this._performFetch(endpoint, messages, prediction, request.headerRequestId, fetchMetadata, responseOpts.shouldRemoveCursorTagFromResponse, editWindow, request.documentBeforeEdits, fetchCancellationToken, tracing);
633
+ if (fetchResult instanceof FetchResult.ModelNotFound) {
634
+ return yield* this.doGetNextEdit(request, delaySession, tracing, cancellationToken, retryState);
635
+ }
636
+ if (fetchResult instanceof FetchResult.FetchFailure) {
637
+ return fetchResult.reason;
638
+ }
639
+ const { linesStream, getFetchFailure, getResponseSoFar, fetchRequestStopWatch } = fetchResult;
640
+ // Phase 2: Dispatch to the appropriate response format handler
641
+ const isFromCursorJump = retryState instanceof RetryState.Retrying && retryState.reason === 'cursorJump';
642
+ let parseResult;
643
+ switch (responseOpts.responseFormat) {
644
+ case xtabPromptOptions.ResponseFormat.EditWindowOnly: {
645
+ parseResult = (0, responseFormatHandlers_1.handleEditWindowOnly)(linesStream);
646
+ break;
585
647
  }
586
- cleanedLinesStream = remainingLinesStream;
587
- }
588
- else if (opts.responseFormat === xtabPromptOptions.ResponseFormat.CustomDiffPatch) {
589
- const activeDoc = request.getActiveDocument();
590
- const currentDocument = promptPieces.currentDocument;
591
- const lastLine = currentDocument.lines[clippedTaggedCurrentDoc.keptRange.endExclusive - 1];
592
- const lastLineLength = lastLine.length;
593
- const pseudoEditWindow = currentDocument.transformer.getOffsetRange(new range_1.Range(clippedTaggedCurrentDoc.keptRange.start + 1, 1, clippedTaggedCurrentDoc.keptRange.endExclusive, lastLineLength + 1));
594
- return yield* xtabCustomDiffPatchResponseHandler_1.XtabCustomDiffPatchResponseHandler.handleResponse(linesStream, currentDocument, activeDoc.id, activeDoc.workspaceRoot, pseudoEditWindow, tracer, () => chatResponseFailure ? mapChatFetcherErrorToNoNextEditReason(chatResponseFailure) : undefined);
595
- }
596
- else if (opts.responseFormat === xtabPromptOptions.ResponseFormat.UnifiedWithXml) {
597
- const linesIter = linesStream[Symbol.asyncIterator]();
598
- const firstLine = await linesIter.next();
599
- if (chatResponseFailure !== undefined) { // handle fetch failure
600
- return new statelessNextEditProvider_1.NoNextEditReason.Unexpected(errors_1.ErrorUtils.fromUnknown(chatResponseFailure));
648
+ case xtabPromptOptions.ResponseFormat.CodeBlock: {
649
+ parseResult = (0, responseFormatHandlers_1.handleCodeBlock)(linesStream);
650
+ break;
601
651
  }
602
- if (firstLine.done) { // no lines in response -- unexpected case but take as no suggestions
603
- return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
652
+ case xtabPromptOptions.ResponseFormat.EditWindowWithEditIntent:
653
+ case xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort: {
654
+ const parseMode = responseOpts.responseFormat === xtabPromptOptions.ResponseFormat.EditWindowWithEditIntentShort
655
+ ? editIntent_1.EditIntentParseMode.ShortName
656
+ : editIntent_1.EditIntentParseMode.Tags;
657
+ parseResult = await (0, responseFormatHandlers_1.handleEditWindowWithEditIntent)(linesStream, tracer, parseMode, getFetchFailure);
658
+ break;
604
659
  }
605
- const trimmedLines = firstLine.value.trim();
606
- if (trimmedLines === tags_1.ResponseTags.NO_CHANGE.start) {
607
- return yield* this.doGetNextEditsWithCursorJump(request, modelServiceConfig, editWindow, promptPieces, delaySession, tracer, logContext, cancellationToken, telemetryBuilder, opts.retryState);
660
+ case xtabPromptOptions.ResponseFormat.CustomDiffPatch: {
661
+ const activeDoc = request.getActiveDocument();
662
+ const currentDocument = promptPieces.currentDocument;
663
+ const lastLine = currentDocument.lines[clippedTaggedCurrentDoc.keptRange.endExclusive - 1];
664
+ const lastLineLength = lastLine.length;
665
+ const pseudoEditWindow = currentDocument.transformer.getOffsetRange(new range_1.Range(clippedTaggedCurrentDoc.keptRange.start + 1, 1, clippedTaggedCurrentDoc.keptRange.endExclusive, lastLineLength + 1));
666
+ parseResult = new responseFormatHandlers_1.ResponseParseResult.DirectEdits(xtabCustomDiffPatchResponseHandler_1.XtabCustomDiffPatchResponseHandler.handleResponse(linesStream, currentDocument, activeDoc.id, activeDoc.workspaceRoot, pseudoEditWindow, tracer, getFetchFailure));
667
+ break;
608
668
  }
609
- if (trimmedLines === tags_1.ResponseTags.INSERT.start) {
610
- const lineWithCursorContinued = await linesIter.next();
611
- if (lineWithCursorContinued.done || lineWithCursorContinued.value.includes(tags_1.ResponseTags.INSERT.end)) {
612
- return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
613
- }
614
- const cursorColumnOffsetZeroBased = promptPieces.currentDocument.cursorPosition.column - 1;
615
- const edit = new lineEdit_1.LineReplacement(new lineRange_1.LineRange(editWindowLineRange.start + cursorOriginalLinesOffset + 1 /* 0-based to 1-based */, editWindowLineRange.start + cursorOriginalLinesOffset + 2), [editWindowLines[cursorOriginalLinesOffset].slice(0, cursorColumnOffsetZeroBased) + lineWithCursorContinued.value + editWindowLines[cursorOriginalLinesOffset].slice(cursorColumnOffsetZeroBased)]);
616
- yield { edit, isFromCursorJump, window: editWindow, originalWindow: originalEditWindow, targetDocument };
617
- const lines = [];
618
- let v = await linesIter.next();
619
- while (!v.done) {
620
- if (v.value.includes(tags_1.ResponseTags.INSERT.end)) {
621
- break;
622
- }
623
- else {
624
- lines.push(v.value);
625
- }
626
- v = await linesIter.next();
627
- }
628
- const line = editWindowLineRange.start + cursorOriginalLinesOffset + 2;
629
- yield {
630
- edit: new lineEdit_1.LineReplacement(new lineRange_1.LineRange(line, line), lines),
631
- isFromCursorJump,
632
- window: editWindow,
633
- originalWindow: originalEditWindow,
669
+ case xtabPromptOptions.ResponseFormat.UnifiedWithXml: {
670
+ parseResult = await (0, responseFormatHandlers_1.handleUnifiedWithXml)(linesStream, {
671
+ editWindowLines,
672
+ editWindowLineRange,
673
+ cursorOriginalLinesOffset,
674
+ cursorColumnZeroBased: promptPieces.currentDocument.cursorPosition.column - 1,
675
+ editWindow,
676
+ originalEditWindow,
634
677
  targetDocument,
635
- };
636
- return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
637
- }
638
- if (trimmedLines === tags_1.ResponseTags.EDIT.start) {
639
- cleanedLinesStream = (async function* () {
640
- let v = await linesIter.next();
641
- while (!v.done) {
642
- if (v.value.includes(tags_1.ResponseTags.EDIT.end)) {
643
- return;
644
- }
645
- yield v.value;
646
- v = await linesIter.next();
647
- }
648
- })();
649
- }
650
- else {
651
- return new statelessNextEditProvider_1.NoNextEditReason.Unexpected(new Error(`unexpected tag ${trimmedLines}`));
678
+ isFromCursorJump,
679
+ }, request.documentBeforeEdits, tracer, getFetchFailure);
680
+ break;
652
681
  }
682
+ default:
683
+ (0, assert_1.assertNever)(responseOpts.responseFormat);
653
684
  }
654
- else if (opts.responseFormat === xtabPromptOptions.ResponseFormat.CodeBlock) {
655
- cleanedLinesStream = (0, xtabUtils_1.linesWithBackticksRemoved)(linesStream);
685
+ // Handle result uniformly
686
+ if (parseResult instanceof responseFormatHandlers_1.ResponseParseResult.Done) {
687
+ return parseResult.reason;
656
688
  }
657
- else {
658
- (0, assert_1.assertNever)(opts.responseFormat);
689
+ if (parseResult instanceof responseFormatHandlers_1.ResponseParseResult.DirectEdits) {
690
+ return yield* parseResult.stream;
659
691
  }
692
+ // parseResult is EditWindowLines — log edit-intent telemetry and apply aggressiveness filter
693
+ if (parseResult.editIntentMetadata) {
694
+ const { intent, parseError } = parseResult.editIntentMetadata;
695
+ telemetry.setEditIntent(intent);
696
+ if (parseError) {
697
+ telemetry.setEditIntentParseError(parseError);
698
+ }
699
+ if (!xtabPromptOptions.EditIntent.shouldShowEdit(intent, promptPieces.aggressivenessLevel)) {
700
+ tracer.trace(`Filtered out edit due to edit intent "${intent}" with aggressiveness "${promptPieces.aggressivenessLevel}"`);
701
+ return new statelessNextEditProvider_1.NoNextEditReason.FilteredOut(`editIntent:${intent} aggressivenessLevel:${promptPieces.aggressivenessLevel}`);
702
+ }
703
+ }
704
+ const cleanedLinesStream = parseResult.lines;
660
705
  const diffOptions = {
661
706
  emitFastCursorLineChange: responseProcessor_1.ResponseProcessor.mapEmitFastCursorLineChange(this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabProviderEmitFastCursorLineChange, this.expService)),
662
707
  nLinesToConverge: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabNNonSignificantLinesToConverge, this.expService),
663
708
  nSignificantLinesToConverge: this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabNSignificantLinesToConverge, this.expService),
664
709
  };
665
710
  tracer.trace(`starting to diff stream against edit window lines with latency ${fetchRequestStopWatch.elapsed()} ms`);
711
+ // Wrap the line stream to detect early cursor-line divergence.
712
+ // If the user has typed at the cursor since the request started and the cursor line
713
+ // in the model's response doesn't match what the user currently has, the response
714
+ // is stale and we can cancel early instead of waiting for the full response.
715
+ //
716
+ // We check compatibility using `isModelCursorLineCompatible`: the user's
717
+ // cursor-line change must be contained within the model's cursor-line change range
718
+ // and match via the helper's `startsWith` / auto-close subsequence rules.
719
+ const earlyCursorLineDivergenceCancellation = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabEarlyCursorLineDivergenceCancellation, this.expService);
720
+ let cursorLineDiverged = false;
721
+ const divergenceCheckedStream = earlyCursorLineDivergenceCancellation
722
+ ? (async function* () {
723
+ let lineIdx = 0;
724
+ for await (const line of cleanedLinesStream) {
725
+ if (lineIdx === cursorOriginalLinesOffset) {
726
+ const intermediateEdit = request.intermediateUserEdit;
727
+ if (intermediateEdit && !intermediateEdit.isEmpty()) {
728
+ const cursorDocLineIdx = editWindowLineRange.start + cursorOriginalLinesOffset;
729
+ const currentCursorLine = (0, cursorLineDivergence_1.getCurrentCursorLine)(request.documentBeforeEdits.getTransformer(), cursorDocLineIdx, intermediateEdit);
730
+ if (currentCursorLine !== undefined) {
731
+ const originalCursorLine = editWindowLines[cursorOriginalLinesOffset];
732
+ if (currentCursorLine !== originalCursorLine // user changed the cursor line
733
+ && !(0, cursorLineDivergence_1.isModelCursorLineCompatible)(originalCursorLine, currentCursorLine, line) // model's cursor line isn't compatible with user's typing
734
+ ) {
735
+ cursorLineDiverged = true;
736
+ tracer.trace(`Cursor line DIVERGED: model="${line}" current="${currentCursorLine}"`);
737
+ // Cancel our local fetch token so the HTTP request is
738
+ // aborted immediately. We own this token, so this is safe.
739
+ fetchCts.cancel();
740
+ return;
741
+ }
742
+ }
743
+ }
744
+ }
745
+ yield line;
746
+ lineIdx++;
747
+ }
748
+ })()
749
+ : cleanedLinesStream;
666
750
  let i = 0;
667
751
  let hasBeenDelayed = false;
668
752
  try {
669
- for await (const edit of responseProcessor_1.ResponseProcessor.diff(editWindowLines, cleanedLinesStream, cursorOriginalLinesOffset, diffOptions)) {
753
+ for await (const edit of responseProcessor_1.ResponseProcessor.diff(editWindowLines, divergenceCheckedStream, cursorOriginalLinesOffset, diffOptions)) {
754
+ if (cursorLineDiverged) {
755
+ break;
756
+ }
757
+ {
758
+ const fetchFailure = getFetchFailure();
759
+ if (fetchFailure) {
760
+ return fetchFailure;
761
+ }
762
+ }
670
763
  tracer.trace(`ResponseProcessor streamed edit #${i} with latency ${fetchRequestStopWatch.elapsed()} ms`);
671
764
  const singleLineEdits = [];
672
765
  if (edit.lineRange.startLineNumber === edit.lineRange.endLineNumberExclusive || // we don't want to run diff on insertion
@@ -690,15 +783,12 @@ let XtabProvider = class XtabProvider {
690
783
  singleLineEdits.push(singleLineEdit);
691
784
  }
692
785
  }
693
- if (chatResponseFailure) { // do not emit edits if chat response failed
694
- break;
695
- }
696
- logContext.setResponse(responseSoFar);
786
+ logContext.setResponse(getResponseSoFar());
697
787
  for (const singleLineEdit of singleLineEdits) {
698
788
  tracer.trace(`extracting edit #${i}: ${singleLineEdit.toString()}`);
699
789
  if (!hasBeenDelayed) { // delay only the first one
700
790
  hasBeenDelayed = true;
701
- const artificialDelay = this.determineArtificialDelayMs(delaySession, tracer, telemetryBuilder);
791
+ const artificialDelay = this.determineArtificialDelayMs(delaySession, tracer, telemetry);
702
792
  if (artificialDelay) {
703
793
  await (0, async_2.timeout)(artificialDelay);
704
794
  tracer.trace(`Artificial delay of ${artificialDelay} ms completed`);
@@ -711,8 +801,14 @@ let XtabProvider = class XtabProvider {
711
801
  i++;
712
802
  }
713
803
  }
714
- if (chatResponseFailure) {
715
- return mapChatFetcherErrorToNoNextEditReason(chatResponseFailure);
804
+ if (cursorLineDiverged) {
805
+ return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('cursorLineDiverged');
806
+ }
807
+ {
808
+ const fetchFailure = getFetchFailure();
809
+ if (fetchFailure) {
810
+ return fetchFailure;
811
+ }
716
812
  }
717
813
  return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
718
814
  }
@@ -722,9 +818,11 @@ let XtabProvider = class XtabProvider {
722
818
  return new statelessNextEditProvider_1.NoNextEditReason.Unexpected(errors_1.ErrorUtils.fromUnknown(err));
723
819
  }
724
820
  }
725
- async *doGetNextEditsWithCursorJump(request, modelConfig, editWindow, promptPieces, delaySession, tracer, logContext, cancellationToken, telemetryBuilder, retryState) {
821
+ async *doGetNextEditsWithCursorJump(request, editStreamCtx, delaySession, tracing, cancellationToken, retryState) {
822
+ const { tracer, telemetry } = tracing;
823
+ const { editWindowInfo: { editWindow }, modelServiceConfig, promptPieces } = editStreamCtx;
726
824
  const noSuggestions = new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
727
- const nextCursorLinePrediction = this.nextCursorPredictor.determineEnablement(modelConfig.supportsNextCursorLinePrediction);
825
+ const nextCursorLinePrediction = this.nextCursorPredictor.determineEnablement(modelServiceConfig.supportsNextCursorLinePrediction);
728
826
  if (nextCursorLinePrediction === undefined || retryState instanceof RetryState.Retrying) {
729
827
  return noSuggestions;
730
828
  }
@@ -732,7 +830,7 @@ let XtabProvider = class XtabProvider {
732
830
  tracer.trace('Skipping cursor prediction: user typed during request');
733
831
  return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('beforeNextCursorPredictionFetchUserTyped');
734
832
  }
735
- const nextCursorLineR = await this.nextCursorPredictor.predictNextCursorPosition(promptPieces, tracer, telemetryBuilder, cancellationToken);
833
+ const nextCursorLineR = await this.nextCursorPredictor.predictNextCursorPosition(promptPieces, tracer, telemetry, cancellationToken);
736
834
  if (cancellationToken.isCancellationRequested) {
737
835
  return new statelessNextEditProvider_1.NoNextEditReason.GotCancelled('afterNextCursorPredictionFetch');
738
836
  }
@@ -742,26 +840,26 @@ let XtabProvider = class XtabProvider {
742
840
  }
743
841
  if (nextCursorLineR.isError()) {
744
842
  tracer.trace(`Predicted next cursor line error: ${nextCursorLineR.err.message}`);
745
- telemetryBuilder.setNextCursorLineError(nextCursorLineR.err.message);
843
+ telemetry.setNextCursorLineError(nextCursorLineR.err.message);
746
844
  return noSuggestions;
747
845
  }
748
846
  const prediction = nextCursorLineR.val;
749
847
  if (prediction.kind === 'differentFile') {
750
- return yield* this.handleCrossFilePrediction(prediction, nextCursorLinePrediction, request, editWindow, promptPieces, delaySession, tracer, logContext, cancellationToken, telemetryBuilder);
848
+ return yield* this.handleCrossFilePrediction(prediction, nextCursorLinePrediction, request, editStreamCtx, delaySession, tracing, cancellationToken);
751
849
  }
752
850
  const nextCursorLineZeroBased = prediction.lineNumber;
753
851
  const lineDistanceFromCursorLine = nextCursorLineZeroBased - promptPieces.currentDocument.cursorLineOffset;
754
- telemetryBuilder.setNextCursorLineDistance(lineDistanceFromCursorLine);
755
- telemetryBuilder.setNextCursorIsCrossFile(false);
852
+ telemetry.setNextCursorLineDistance(lineDistanceFromCursorLine);
853
+ telemetry.setNextCursorIsCrossFile(false);
756
854
  tracer.trace(`Predicted next cursor line: ${nextCursorLineZeroBased}`);
757
855
  if (nextCursorLineZeroBased >= promptPieces.currentDocument.lines.length) { // >= because the line index is zero-based
758
856
  tracer.trace(`Predicted next cursor line error: exceedsDocumentLines`);
759
- telemetryBuilder.setNextCursorLineError('exceedsDocumentLines');
857
+ telemetry.setNextCursorLineError('exceedsDocumentLines');
760
858
  return noSuggestions;
761
859
  }
762
860
  if (promptPieces.editWindowLinesRange.contains(nextCursorLineZeroBased)) {
763
861
  tracer.trace(`Predicted next cursor line error: withinEditWindow`);
764
- telemetryBuilder.setNextCursorLineError('withinEditWindow');
862
+ telemetry.setNextCursorLineError('withinEditWindow');
765
863
  return noSuggestions;
766
864
  }
767
865
  const nextCursorLineOneBased = nextCursorLineZeroBased + 1;
@@ -774,7 +872,7 @@ let XtabProvider = class XtabProvider {
774
872
  return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow, nextCursorPosition);
775
873
  }
776
874
  case nextCursorLinePrediction_1.NextCursorLinePrediction.OnlyWithEdit: {
777
- const v = this.doGetNextEditWithSelection(request, new range_1.Range(nextCursorLineOneBased, nextCursorColumn, nextCursorLineOneBased, nextCursorColumn), delaySession, tracer, logContext, cancellationToken, telemetryBuilder, new RetryState.Retrying('cursorJump'), editWindow);
875
+ const v = this.doGetNextEditWithSelection(request, new range_1.Range(nextCursorLineOneBased, nextCursorColumn, nextCursorLineOneBased, nextCursorColumn), delaySession, tracing, cancellationToken, new RetryState.Retrying('cursorJump'), editWindow);
778
876
  return yield* v;
779
877
  }
780
878
  default: {
@@ -782,11 +880,13 @@ let XtabProvider = class XtabProvider {
782
880
  }
783
881
  }
784
882
  }
785
- async *handleCrossFilePrediction(prediction, nextCursorLinePrediction, request, editWindow, promptPieces, delaySession, tracer, logContext, cancellationToken, telemetryBuilder) {
883
+ async *handleCrossFilePrediction(prediction, nextCursorLinePrediction, request, editStreamCtx, delaySession, tracing, cancellationToken) {
884
+ const { tracer, telemetry } = tracing;
885
+ const { editWindowInfo: { editWindow }, promptPieces } = editStreamCtx;
786
886
  const workspaceRoot = promptPieces.activeDoc.workspaceRoot;
787
887
  if (!workspaceRoot && !(0, path_1.isAbsolute)(prediction.filePath)) {
788
888
  tracer.trace('Predicted cross-file cursor jump error: noWorkspaceRoot');
789
- telemetryBuilder.setNextCursorLineError('crossFile:noWorkspaceRoot');
889
+ telemetry.setNextCursorLineError('crossFile:noWorkspaceRoot');
790
890
  return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow);
791
891
  }
792
892
  const targetUri = (0, path_1.isAbsolute)(prediction.filePath)
@@ -795,7 +895,7 @@ let XtabProvider = class XtabProvider {
795
895
  const targetDocumentId = documentId_1.DocumentId.create(targetUri.toString());
796
896
  const nextCursorLineOneBased = prediction.lineNumber + 1;
797
897
  const nextCursorPosition = new position_1.Position(nextCursorLineOneBased, 1);
798
- telemetryBuilder.setNextCursorIsCrossFile(true);
898
+ telemetry.setNextCursorIsCrossFile(true);
799
899
  tracer.trace(`Predicted cross-file cursor jump: ${prediction.filePath}:${prediction.lineNumber}`);
800
900
  switch (nextCursorLinePrediction) {
801
901
  case nextCursorLinePrediction_1.NextCursorLinePrediction.Jump: {
@@ -808,7 +908,7 @@ let XtabProvider = class XtabProvider {
808
908
  }
809
909
  catch (err) {
810
910
  tracer.trace(`Failed to open target file for cross-file edit: ${errors_1.ErrorUtils.fromUnknown(err).message}`);
811
- telemetryBuilder.setNextCursorLineError('crossFile:failedToOpenFile');
911
+ telemetry.setNextCursorLineError('crossFile:failedToOpenFile');
812
912
  return new statelessNextEditProvider_1.NoNextEditReason.NoSuggestions(request.documentBeforeEdits, editWindow, nextCursorPosition, targetDocumentId);
813
913
  }
814
914
  if (cancellationToken.isCancellationRequested) {
@@ -821,7 +921,7 @@ let XtabProvider = class XtabProvider {
821
921
  const targetContent = new abstractText_1.StringText(targetTextDoc.getText());
822
922
  const syntheticDoc = new statelessNextEditProvider_1.StatelessNextEditDocument(targetDocumentId, promptPieces.activeDoc.workspaceRoot, languageId_1.LanguageId.create(targetTextDoc.languageId), targetContent.getLines(), lineEdit_1.LineEdit.empty, targetContent, new edit_1.Edits(stringEdit_1.StringEdit, []));
823
923
  const syntheticRequest = new statelessNextEditProvider_1.StatelessNextEditRequest(request.headerRequestId, request.opportunityId, targetContent, [syntheticDoc], 0, request.xtabEditHistory, new async_2.DeferredPromise(), request.expandedEditWindowNLines, request.isSpeculative, request.logContext, request.recordingBookmark, request.recording, request.providerRequestStartDateTime);
824
- return yield* this.doGetNextEditWithSelection(syntheticRequest, new range_1.Range(nextCursorLineOneBased, 1, nextCursorLineOneBased, 1), delaySession, tracer, logContext, cancellationToken, telemetryBuilder, new RetryState.Retrying('cursorJump'), editWindow);
924
+ return yield* this.doGetNextEditWithSelection(syntheticRequest, new range_1.Range(nextCursorLineOneBased, 1, nextCursorLineOneBased, 1), delaySession, tracing, cancellationToken, new RetryState.Retrying('cursorJump'), editWindow);
825
925
  }
826
926
  default: {
827
927
  (0, assert_1.assertNever)(nextCursorLinePrediction);
@@ -873,7 +973,7 @@ let XtabProvider = class XtabProvider {
873
973
  const maxMergeConflictLines = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabMaxMergeConflictLines, this.expService);
874
974
  if (maxMergeConflictLines) {
875
975
  const tentativeEditWindow = new offsetRange_1.OffsetRange(codeToEditStart, codeToEditEndExcl);
876
- const mergeConflictRange = findMergeConflictMarkersRange(currentDocLines, tentativeEditWindow, maxMergeConflictLines);
976
+ const mergeConflictRange = (0, xtabUtils_1.findMergeConflictMarkersRange)(currentDocLines, tentativeEditWindow, maxMergeConflictLines);
877
977
  if (mergeConflictRange) {
878
978
  const onlyMergeConflictLines = this.configService.getExperimentBasedConfig(configurationService_1.ConfigKey.TeamInternal.InlineEditsXtabOnlyMergeConflictLines, this.expService);
879
979
  telemetry.setMergeConflictExpanded(onlyMergeConflictLines ? 'only' : 'normal');
@@ -1191,176 +1291,4 @@ function getPredictionContents(doc, editWindowLines, responseFormat) {
1191
1291
  (0, assert_1.assertNever)(responseFormat);
1192
1292
  }
1193
1293
  }
1194
- /**
1195
- * Mode for parsing edit intent from the model response.
1196
- */
1197
- var EditIntentParseMode;
1198
- (function (EditIntentParseMode) {
1199
- /** Parse using XML-style tags: <|edit_intent|>value<|/edit_intent|> */
1200
- EditIntentParseMode["Tags"] = "tags";
1201
- /** Parse using short names on the first line: N|L|M|H */
1202
- EditIntentParseMode["ShortName"] = "shortName";
1203
- })(EditIntentParseMode || (exports.EditIntentParseMode = EditIntentParseMode = {}));
1204
- /**
1205
- * Parses the edit_intent from the first line of the response stream.
1206
- * The edit_intent MUST be on the first line, otherwise it's treated as not provided.
1207
- * Returns the parsed EditIntent and a new stream with the remaining content.
1208
- *
1209
- * Supports two modes:
1210
- * - Tags (default): <|edit_intent|>low|medium|high|no_edit<|/edit_intent|>
1211
- * - ShortName: N|L|M|H on the first line
1212
- *
1213
- * @param linesStream The stream of lines from the model response
1214
- * @param tracer Logger for tracing
1215
- * @param mode The parse mode (Tags or ShortName), defaults to Tags
1216
- */
1217
- async function parseEditIntentFromStream(linesStream, tracer, mode = EditIntentParseMode.Tags) {
1218
- if (mode === EditIntentParseMode.ShortName) {
1219
- return parseEditIntentFromStreamShortName(linesStream, tracer);
1220
- }
1221
- return parseEditIntentFromStreamTags(linesStream, tracer);
1222
- }
1223
- /**
1224
- * Parses the edit_intent using short name format (N|L|M|H on first line).
1225
- */
1226
- async function parseEditIntentFromStreamShortName(linesStream, tracer) {
1227
- let editIntent = xtabPromptOptions.EditIntent.High; // Default to high (always show) if no short name found
1228
- let parseError;
1229
- const linesIter = linesStream[Symbol.asyncIterator]();
1230
- const firstLineResult = await linesIter.next();
1231
- if (firstLineResult.done) {
1232
- // Empty stream
1233
- parseError = 'emptyResponse';
1234
- tracer.warn(`Empty response stream, no edit_intent short name found`);
1235
- const remainingLinesStream = (async function* () { })();
1236
- return { editIntent, remainingLinesStream, parseError };
1237
- }
1238
- const firstLine = firstLineResult.value.trim();
1239
- // Check if the first line is a single character short name
1240
- const parsedIntent = xtabPromptOptions.EditIntent.fromShortName(firstLine);
1241
- if (parsedIntent !== undefined) {
1242
- editIntent = parsedIntent;
1243
- tracer.trace(`Parsed edit_intent short name from first line: "${firstLine}" -> ${editIntent}`);
1244
- // Create a new stream with the remaining lines (excluding the short name line)
1245
- const remainingLinesStream = (async function* () {
1246
- let next = await linesIter.next();
1247
- while (!next.done) {
1248
- yield next.value;
1249
- next = await linesIter.next();
1250
- }
1251
- })();
1252
- return { editIntent, remainingLinesStream, parseError };
1253
- }
1254
- // Short name not found or invalid
1255
- parseError = `unknownIntentValue:${firstLine}`;
1256
- tracer.warn(`Edit intent parse error: ${parseError} (using Xtab275EditIntentShort prompting strategy). ` +
1257
- `Defaulting to High (always show). First line was: "${firstLine.substring(0, 100)}..."`);
1258
- // Return the first line plus the rest of the stream
1259
- const remainingLinesStream = (async function* () {
1260
- yield firstLineResult.value; // Use original value, not trimmed
1261
- let next = await linesIter.next();
1262
- while (!next.done) {
1263
- yield next.value;
1264
- next = await linesIter.next();
1265
- }
1266
- })();
1267
- return { editIntent, remainingLinesStream, parseError };
1268
- }
1269
- /**
1270
- * Parses the edit_intent tag from the first line of the response stream (original tag-based format).
1271
- */
1272
- async function parseEditIntentFromStreamTags(linesStream, tracer) {
1273
- const EDIT_INTENT_START_TAG = '<|edit_intent|>';
1274
- const EDIT_INTENT_END_TAG = '<|/edit_intent|>';
1275
- let editIntent = xtabPromptOptions.EditIntent.High; // Default to high (always show) if no tag found
1276
- let parseError;
1277
- const linesIter = linesStream[Symbol.asyncIterator]();
1278
- const firstLineResult = await linesIter.next();
1279
- if (firstLineResult.done) {
1280
- // Empty stream
1281
- parseError = 'emptyResponse';
1282
- tracer.warn(`Empty response stream, no edit_intent tag found`);
1283
- const remainingLinesStream = (async function* () { })();
1284
- return { editIntent, remainingLinesStream, parseError };
1285
- }
1286
- const firstLine = firstLineResult.value;
1287
- // Check if the first line contains the complete edit_intent tag
1288
- const startIdx = firstLine.indexOf(EDIT_INTENT_START_TAG);
1289
- const endIdx = firstLine.indexOf(EDIT_INTENT_END_TAG);
1290
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
1291
- // Found complete tag on first line
1292
- const intentValue = firstLine.substring(startIdx + EDIT_INTENT_START_TAG.length, endIdx).trim().toLowerCase();
1293
- // Check if it's a known intent value
1294
- const knownIntentValues = ['no_edit', 'low', 'medium', 'high'];
1295
- if (!knownIntentValues.includes(intentValue)) {
1296
- parseError = `unknownIntentValue:${intentValue}`;
1297
- tracer.warn(`Unknown edit_intent value: "${intentValue}", defaulting to High`);
1298
- }
1299
- editIntent = xtabPromptOptions.EditIntent.fromString(intentValue);
1300
- tracer.trace(`Parsed edit_intent from first line: "${intentValue}" -> ${editIntent}`);
1301
- // Calculate remaining content after the end tag on the first line
1302
- const afterEndTag = firstLine.substring(endIdx + EDIT_INTENT_END_TAG.length);
1303
- // Create a new stream that first yields remaining content from first line, then continues
1304
- const remainingLinesStream = (async function* () {
1305
- // Only yield remaining content from first line if non-empty
1306
- if (afterEndTag.trim() !== '') {
1307
- yield afterEndTag;
1308
- }
1309
- // Continue with rest of the stream
1310
- let next = await linesIter.next();
1311
- while (!next.done) {
1312
- yield next.value;
1313
- next = await linesIter.next();
1314
- }
1315
- })();
1316
- return { editIntent, remainingLinesStream, parseError };
1317
- }
1318
- // Determine the parse error type
1319
- if (startIdx !== -1 && endIdx === -1) {
1320
- // Start tag found but no end tag - malformed (possibly split across lines)
1321
- parseError = 'malformedTag:startWithoutEnd';
1322
- }
1323
- else if (startIdx === -1 && endIdx !== -1) {
1324
- // End tag found but no start tag - malformed
1325
- parseError = 'malformedTag:endWithoutStart';
1326
- }
1327
- else {
1328
- // No tag found at all
1329
- parseError = 'noTagFound';
1330
- }
1331
- tracer.warn(`Edit intent parse error: ${parseError} (using Xtab275EditIntent prompting strategy). ` +
1332
- `Defaulting to High (always show). First line was: "${firstLine.substring(0, 100)}..."`);
1333
- // Return the first line plus the rest of the stream
1334
- const remainingLinesStream = (async function* () {
1335
- yield firstLine;
1336
- let next = await linesIter.next();
1337
- while (!next.done) {
1338
- yield next.value;
1339
- next = await linesIter.next();
1340
- }
1341
- })();
1342
- return { editIntent, remainingLinesStream, parseError };
1343
- }
1344
- /**
1345
- * Finds the range of lines containing merge conflict markers within a specified edit window.
1346
- *
1347
- * @param lines - Array of strings representing the lines of text to search through
1348
- * @param editWindowRange - The range within which to search for merge conflict markers
1349
- * @param maxMergeConflictLines - Maximum number of lines to search for conflict markers
1350
- * @returns An OffsetRange object representing the start and end of the conflict markers, or undefined if not found
1351
- */
1352
- function findMergeConflictMarkersRange(lines, editWindowRange, maxMergeConflictLines) {
1353
- for (let i = editWindowRange.start; i < Math.min(lines.length, editWindowRange.endExclusive); ++i) {
1354
- if (!lines[i].startsWith('<<<<<<<')) {
1355
- continue;
1356
- }
1357
- // found start of merge conflict markers -- now find the end
1358
- for (let j = i + 1; j < lines.length && (j - i) < maxMergeConflictLines; ++j) {
1359
- if (lines[j].startsWith('>>>>>>>')) {
1360
- return new offsetRange_1.OffsetRange(i, j + 1 /* because endExclusive */);
1361
- }
1362
- }
1363
- }
1364
- return undefined;
1365
- }
1366
1294
  //# sourceMappingURL=xtabProvider.js.map