-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add NES expected edit capture feature and related tests #2614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
erikportillo
wants to merge
9
commits into
microsoft:main
Choose a base branch
from
erikportillo:erikportillo/nes-expected-edit-capture
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
883b6c8
feat: add NES expected edit capture feature
erikportillo d633565
Revert debug recorder to original settings
erikportillo 2248458
feat: add tests for filtering sensitive files in inline edit logs
erikportillo b423496
refactor: update GitHub session retrieval method and adjust test expe…
erikportillo b249d30
test: update inlineEditDebugComponent tests for consistent string quo…
erikportillo b0f3039
feat: enhance sensitive file filtering with case-insensitive matching…
erikportillo cc267da
Merge branch 'microsoft:main' into erikportillo/nes-expected-edit-cap…
erikportillo eb99a01
feat: add User-Agent header to GitHub API requests in NesFeedbackSubm…
erikportillo 1826347
Move NES Expected Edit Capture documentation
erikportillo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| # NES Expected Edit Capture Feature | ||
|
|
||
| ## Overview | ||
|
|
||
| A feature that allows users to record/capture their "expected suggestion" when a Next Edit Suggestion (NES) was rejected or failed to appear. The captured data is saved in `.recording.w.json` format (compatible with stest infrastructure) for analysis and model improvement. | ||
|
|
||
| ## Getting Started | ||
|
|
||
| ### 1. Enable the Feature | ||
| Add this setting to your VS Code `settings.json`: | ||
|
|
||
| ```json | ||
| { | ||
| // Enable the capture feature | ||
| "github.copilot.chat.advanced.inlineEdits.recordExpectedEdit.enabled": true | ||
| } | ||
| ``` | ||
|
|
||
| That's it! Auto-capture on rejection is enabled by default. To disable it (you can still capture manually via **Cmd+K Cmd+R**): | ||
| ```json | ||
| { | ||
| "github.copilot.chat.advanced.inlineEdits.recordExpectedEdit.onReject": false | ||
| } | ||
| ``` | ||
|
|
||
| ### 2. Capture an Expected Edit | ||
|
|
||
| **When NES shows a wrong suggestion:** | ||
| 1. Reject the suggestion (press `Esc` or continue typing) | ||
| 2. If `onReject` is enabled, capture mode starts automatically | ||
| 3. Type the code you *expected* NES to suggest | ||
| 4. Press **Enter** to save, or **Esc** to cancel | ||
|
|
||
| **When NES didn't appear but should have:** | ||
| 1. Press **Cmd+K Cmd+R** (Mac) or **Ctrl+K Ctrl+R** (Windows/Linux) | ||
| 2. Type the code you expected NES to suggest | ||
| 3. Press **Enter** to save | ||
|
|
||
| > **Tip:** Use **Shift+Enter** to insert newlines during capture (since Enter saves). | ||
|
|
||
| ### 3. Submit Your Feedback | ||
| Once you've captured some edits: | ||
| 1. Open Command Palette (**Cmd+Shift+P** / **Ctrl+Shift+P**) | ||
| 2. Run **"Copilot: Submit NES Captures"** | ||
| 3. Review the files to be included (you can exclude sensitive files) | ||
| 4. Click **Submit Feedback** to create a PR | ||
|
|
||
| ### Quick Reference | ||
|
|
||
| | Action | Keybinding | | ||
| |--------|------------| | ||
| | Start capture manually | **Cmd+K Cmd+R** / **Ctrl+K Ctrl+R** | | ||
| | Save capture | **Enter** | | ||
| | Cancel capture | **Esc** | | ||
| | Insert newline | **Shift+Enter** | | ||
|
|
||
| | Command | Description | | ||
| |---------|-------------| | ||
| | Copilot: Record Expected Edit (NES) | Start a capture session | | ||
| | Copilot: Submit NES Captures | Upload feedback to internal repo | | ||
|
|
||
| ## How It Works | ||
|
|
||
| ### Trigger Points | ||
| - **Automatic**: Capture starts when you reject an NES suggestion (if `onReject` setting is enabled) | ||
| - **Manual**: Use the keyboard shortcut or Command Palette when NES didn't appear but should have | ||
|
|
||
| ### Capture Session | ||
| When capture mode is active: | ||
| 1. A status bar indicator shows: **"NES CAPTURE MODE ACTIVE"** | ||
| 2. Type your expected edit naturally in the editor | ||
| 3. Press **Enter** to save or **Esc** to cancel | ||
|
|
||
| ### Where Captures Are Saved | ||
| Recordings are stored in your workspace under `.copilot/nes-feedback/`: | ||
| - `capture-<timestamp>.recording.w.json` — The edit recording | ||
| - `capture-<timestamp>.metadata.json` — Context about the capture | ||
|
|
||
| --- | ||
|
|
||
| ## Technical Reference | ||
|
|
||
| ### Commands | ||
|
|
||
| | Command ID | Description | | ||
| |------------|-------------| | ||
| | `github.copilot.nes.captureExpected.start` | Start capture manually | | ||
| | `github.copilot.nes.captureExpected.confirm` | Confirm and save | | ||
| | `github.copilot.nes.captureExpected.abort` | Cancel capture | | ||
| | `github.copilot.nes.captureExpected.submit` | Submit to `microsoft/copilot-nes-feedback` | | ||
|
|
||
| ### Architecture | ||
|
|
||
| #### State Management | ||
| The capture controller maintains minimal state: | ||
| ```typescript | ||
| { | ||
| active: boolean; | ||
| startBookmark: DebugRecorderBookmark; | ||
| endBookmark?: DebugRecorderBookmark; | ||
| startDocumentId: DocumentId; | ||
| startTime: number; | ||
| trigger: 'rejection' | 'manual'; | ||
| originalNesMetadata?: { | ||
| requestUuid: string; | ||
| providerInfo?: string; | ||
| modelName?: string; | ||
| endpointUrl?: string; | ||
| suggestionText?: string; | ||
| // [startLine, startCharacter, endLine, endCharacter] | ||
| suggestionRange?: [number, number, number, number]; | ||
| documentPath?: string; | ||
| }; | ||
| } | ||
| ``` | ||
|
|
||
| ### Implementation Flow | ||
|
|
||
| The capture flow leverages **DebugRecorder**, which already tracks all document edits automatically—no custom event listeners or manual diff computation needed. | ||
|
|
||
| 1. **Start Capture**: Create a bookmark in DebugRecorder, store the current document ID, set context key `copilotNesCaptureMode` to enable keybindings, and show status bar indicator. | ||
|
|
||
| 2. **User Edits**: User types their expected edit naturally in the editor. DebugRecorder automatically tracks all changes in the background. | ||
|
|
||
| 3. **Confirm Capture**: Create an end bookmark, extract the log slice between start/end bookmarks, filter for edits on the target document, compose them into a single `nextUserEdit`, and save to disk. | ||
|
|
||
| 4. **Abort/Cleanup**: Clear state, reset context key, and dispose status bar item. | ||
|
|
||
| See `ExpectedEditCaptureController` in [vscode-node/components/expectedEditCaptureController.ts](vscode-node/components/expectedEditCaptureController.ts) for the full implementation. | ||
|
|
||
| ### File Output | ||
|
|
||
| #### Location | ||
| Recordings are stored in the **first workspace folder** under the `.copilot/nes-feedback/` directory: | ||
|
|
||
| - **Full path**: `<workspaceFolder>/.copilot/nes-feedback/capture-<timestamp>.recording.w.json` | ||
| - **Timestamp format**: ISO 8601 with colons/periods replaced by hyphens (e.g., `2025-12-04T14-30-45`) | ||
| - **Example**: `.copilot/nes-feedback/capture-2025-12-04T14-30-45.recording.w.json` | ||
| - The folder is automatically created if it doesn't exist | ||
|
|
||
| Each recording generates two files: | ||
| 1. **Recording file**: `capture-<timestamp>.recording.w.json` - Contains the log and edit data | ||
| 2. **Metadata file**: `capture-<timestamp>.metadata.json` - Contains capture context and timing | ||
|
|
||
| #### Format | ||
| Matches existing `.recording.w.json` structure used by stest infrastructure: | ||
|
|
||
| ```json | ||
| { | ||
| "log": [ | ||
| { | ||
| "kind": "header", | ||
| "repoRootUri": "file:///workspace", | ||
| "time": 1234567890, | ||
| "uuid": "..." | ||
| }, | ||
| { | ||
| "kind": "documentEncountered", | ||
| "id": 0, | ||
| "relativePath": "src/foo.ts", | ||
| "time": 1234567890 | ||
| }, | ||
| { | ||
| "kind": "setContent", | ||
| "id": 0, | ||
| "v": 1, | ||
| "content": "...", | ||
| "time": 1234567890 | ||
| }, | ||
| ... | ||
| ], | ||
| "nextUserEdit": { | ||
| "relativePath": "src/foo.ts", | ||
| "edit": [ | ||
| [876, 996, "replaced text"], | ||
| [1522, 1530, "more text"] | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### Metadata File | ||
| A metadata file is saved alongside each recording with capture context: | ||
| ```jsonc | ||
| { | ||
| "captureTimestamp": "2025-11-19T...", // ISO timestamp when capture started | ||
| "trigger": "rejection", // How capture was initiated: 'rejection' or 'manual' | ||
| "durationMs": 5432, // Time between start and confirm in milliseconds | ||
| "noEditExpected": false, // True if user confirmed without making edits | ||
| "originalNesContext": { // Metadata from the rejected NES suggestion (if any) | ||
| "requestUuid": "...", // Unique ID of the NES request | ||
| "providerInfo": "...", // Source of the suggestion (e.g., 'provider', 'diagnostics') | ||
| "modelName": "...", // AI model that generated the suggestion | ||
| "endpointUrl": "...", // API endpoint used for the request | ||
| "suggestionText": "...", // The actual suggested text that was rejected | ||
| "suggestionRange": [10, 0, 15, 20] // [startLine, startChar, endLine, endChar] of suggestion | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Benefits | ||
|
|
||
| - **Zero-friction**: Type naturally, press Enter — no forms or dialogs | ||
| - **Works for both**: Rejected suggestions and missed opportunities | ||
| - **Privacy-aware**: Sensitive files are automatically filtered before submission | ||
|
|
||
| ## Edge Cases | ||
|
|
||
| | Scenario | Behavior | | ||
| |----------|----------| | ||
| | **Multiple rapid rejections** | Only one capture active at a time; subsequent rejections ignored | | ||
| | **Document closed** | Capture automatically aborted | | ||
| | **No edits made** | Valid feedback! Saved with `noEditExpected: true` (indicates the rejection was correct) | | ||
| | **Large edits** | DebugRecorder handles size limits automatically | | ||
|
|
||
| ## Feedback Submission | ||
|
|
||
| When you run **"Copilot: Submit NES Captures"**: | ||
|
|
||
| 1. All captures from `.copilot/nes-feedback/` are collected | ||
| 2. A preview dialog shows which files will be included | ||
| 3. You can exclude specific files if needed | ||
| 4. A pull request is created in `microsoft/copilot-nes-feedback` | ||
|
|
||
| ### Privacy & Filtering | ||
| Sensitive files are **automatically excluded** from submissions: | ||
| - VS Code settings (`settings.json`, `launch.json`) | ||
| - Credentials (`.npmrc`, `.env`, `.gitconfig`, etc.) | ||
| - Private keys (`.pem`, `.key`, `id_rsa`, etc.) | ||
| - Sensitive directories (`.aws/`, `.ssh/`, `.gnupg/`) | ||
|
|
||
| **Requirements:** GitHub authentication with repo access to `microsoft/copilot-nes-feedback` | ||
|
|
||
| --- | ||
|
|
||
| ## Future Enhancements | ||
|
|
||
| - **Diff Preview**: Show visual comparison before saving | ||
| - **Category Tagging**: Quick-pick to categorize expectation type (import, refactor, etc.) | ||
| - **Auto-Generate stest**: Create `.stest.ts` wrapper file automatically | ||
|
|
||
| ## Related Files | ||
|
|
||
| - [node/debugRecorder.ts](node/debugRecorder.ts) - Core recording infrastructure | ||
| - [vscode-node/components/inlineEditDebugComponent.ts](vscode-node/components/inlineEditDebugComponent.ts) - Existing feedback/debug tooling and sensitive file filtering | ||
| - [vscode-node/components/expectedEditCaptureController.ts](vscode-node/components/expectedEditCaptureController.ts) - Capture session management | ||
| - [vscode-node/components/nesFeedbackSubmitter.ts](vscode-node/components/nesFeedbackSubmitter.ts) - Feedback submission to GitHub | ||
| - [common/observableWorkspaceRecordingReplayer.ts](common/observableWorkspaceRecordingReplayer.ts) - Recording replay logic | ||
| - [../../../test/simulation/inlineEdit/inlineEditTester.ts](../../../test/simulation/inlineEdit/inlineEditTester.ts) - stest infrastructure |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -212,3 +212,4 @@ suite('Debug recorder', () => { | |
| }); | ||
|
|
||
| }); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.