editSurvivalTracker.ts ×8

Frontier kind: Code frontier

unlabeled · c_6bbc55ea46db

1079 tests · 3494 LOC · 19 files · introduces 0 tests · 116 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
8 ranges116 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
488 ranges3494 lines · 19 files · Browse complete extent
All tests (intent)
1079 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 116 introduced LOC across 8 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/shared/editSurvivalTracker.ts 116 introduced LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editSurvivalTracker.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > /*
7 > * Edit-survival math for agent-host file edits.
8 > *
9 > * Sister implementation of the chat extension's `EditSurvivalTracker`
10 > * (`extensions/copilot/src/platform/editSurvivalTracking/common/editSurvivalTracker.ts`).
11 > * The extension version operates on multi-range `StringEdit`s with a
12 > * live `TextModel`; here we only have whole-file snapshots and (when
13 > * the tool input is recognisable) the explicit text the AI wrote. The
14 > * whole-file path is the baseline; the chunked path uses asymmetric
15 > * "fraction of AI 4-grams still present in the file" scoring so an
16 > * edit's score doesn't decay as the file grows around it.
17 > *
18 > * Mostly carried over from the chat extension's version.
19 > */
20 >
21 > /**
22 > * Computes a number between 0 and 1 that reflects how similar the two
23 > * texts are by counting how many 4-grams are shared between them.
24 > */
25 > export function compute4GramTextSimilarity(text1: string, text2: string): number {
26 const n = 4;
27
55 return equalNGramCount / totalNGramCount;
56 }
58 > /**
59 > * Computes the share of `chunk`'s 4-grams that appear anywhere in
60 > * `currentText`. Unlike {@link compute4GramTextSimilarity}, this is
61 > * asymmetric: the denominator is the chunk's n-gram count, not the
62 > * combined corpus. That makes the result stable as `currentText` grows
63 > * around the chunk — appending unrelated content does not drag the
64 > * score down. Returns a number in [0, 1].
65 > *
66 > * Used to ask "is the text the AI wrote still present in the file?"
67 > * when we have an explicit chunk (the `new_string` from `Edit`, each
68 > * entry of `MultiEdit.edits[*].new_string`, or `Write.content`) rather
69 > * than a whole-file before/after pair.
70 > *
71 > * For multi-chunk scoring against the same file, prefer building the
72 > * file n-gram set once via {@link buildNGramSet} and passing it to
73 > * {@link computeFractionPresentInSet} to avoid rebuilding the set per
74 > * chunk — see {@link computeChunkedFourGramSurvival}.
75 > */
76 > export function computeFractionPresentIn(chunk: string, currentText: string): number {
77 const n = 4;
78 if (chunk.length === 0) {
87 return computeFractionPresentInSet(chunk, buildNGramSet(currentText, n), n);
88 }
90 > /** Builds the set of length-`n` substrings of `text`. */
91 function buildNGramSet(text: string, n: number): Set<string> {
92 const set = new Set<string>();
96 return set;
97 }
99 > /**
100 > * {@link computeFractionPresentIn} with a precomputed file n-gram set.
101 > * `chunk.length >= n` is the caller's responsibility; short/empty
102 > * chunks are handled by {@link computeFractionPresentIn}.
103 > */
104 function computeFractionPresentInSet(chunk: string, fileNGrams: ReadonlySet<string>, n: number): number {
105 const total = chunk.length - n + 1;
112 return present / total;
113 }
115 > /**
116 > * Length-weighted average of {@link computeFractionPresentIn} across
117 > * multiple AI-written chunks. The weight is the chunk's n-gram count
118 > * (approx its character length), so a 200-char chunk counts ~10x as much
119 > * as a 20-char chunk. Returns 0 when there are no chunks (callers
120 > * should branch on that and fall back to whole-file scoring).
121 > *
122 > * Builds the file n-gram set exactly once and reuses it for every
123 > * chunk, so cost is O(|currentText| + sum(|chunk|)) rather than
124 > * O(|chunks| × |currentText|).
125 > */
126 > export function computeChunkedFourGramSurvival(aiChunks: readonly string[], currentText: string): number {
127 if (aiChunks.length === 0) {
128 return 0;
154 return weightedSum / totalWeight;
155 }
157 > /**
158 > * Result of {@link computeWholeFileEditSurvival}.
159 > */
160 > export interface IEditSurvivalScore {
161 > /**
162 > * 4-gram similarity between the current file content and the
163 > * text the AI wrote. 1 = current text is identical to AI text,
164 > * 0 = nothing in common.
165 > */
166 > readonly fourGram: number;
167 > /**
168 > * 1 minus the fraction by which the user moved the text back
169 > * toward the original. 1 = no revert (user kept or refined AI
170 > * output), 0 = full revert to original.
171 > */
172 > readonly noRevert: number;
173 > }
174 >
175 > /**
176 > * Computes the whole-file revert score. 1 = file did not move back
177 > * toward the original, 0 = file is back to the original. Used by both
178 > * the whole-file and the chunked code paths, since revert detection is
179 > * intrinsically a whole-file question (we want to know whether the
180 > * user undid the change, not whether each AI-written region is still
181 > * present).
182 > */
183 > export function computeNoRevertScore(beforeText: string, afterText: string, currentText: string): number {
184 const aiSimilarity = compute4GramTextSimilarity(afterText, beforeText);
185 if (aiSimilarity === 1) {
191 return 1 - Math.max(userSimilarity - aiSimilarity, 0) / (1 - aiSimilarity);
192 }
194 > /**
195 > * Computes survival scores for a whole-file edit.
196 > *
197 > * @param beforeText - File content before the AI edit was applied.
198 > * @param afterText - File content the AI wrote.
199 > * @param currentText - File content right now.
200 > */
201 > export function computeWholeFileEditSurvival(
202 beforeText: string,
203 afterText: string,
209 };
210 }
212 > /**
213 > * Computes survival scores for an edit when we know the explicit
214 > * AI-written chunks. `fourGram` uses the chunked, search-within scoring
215 > * so the denominator is bounded by the AI's written text (immune to
216 > * file-growth artifacts); `noRevert` continues to use the whole-file
217 > * comparison so reverts are still detectable.
218 > *
219 > * Falls back to whole-file scoring when `aiChunks` is empty (e.g. tool
220 > * input was unrecognised or malformed) so callers can pass through
221 > * uniformly.
222 > */
223 > export function computeChunkedEditSurvival(
224 beforeText: string,
225 afterText: string,