Add Aspire editor assistance language model tools - #19414
Add Aspire editor assistance language model tools#19414Adam Ratzman (adamint) wants to merge 42 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e3c8fa3a-d5fd-427d-baa5-3d0c1334a513
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e3c8fa3a-d5fd-427d-baa5-3d0c1334a513
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19414Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19414" |
There was a problem hiding this comment.
Pull request overview
Adds five privacy-bounded VS Code language-model tools for inspecting Aspire debug state, explaining launch failures, and opening relevant UI.
Changes:
- Adds editor-state snapshots, safe AppHost resolution, failure journaling, and telemetry.
- Adds confirmation-gated Dashboard and Output handoffs.
- Extends debugger metadata and comprehensive unit/E2E coverage.
Show a summary per file
| File | Description |
|---|---|
extension/telemetry.json |
Defines new telemetry events. |
extension/src/views/AspireAppHostTreeProvider.ts |
Reuses Dashboard URL validation. |
extension/src/utils/telemetryRegistry.ts |
Types new telemetry schemas. |
extension/src/utils/appHostIdentity.ts |
Adds opaque lexical identities. |
extension/src/types/extensionApi.ts |
Extends E2E invocation controls. |
extension/src/testing/e2eStateFileBridge.ts |
Supports testing all LM tools. |
extension/src/testing/e2eStateFileBridge.production.ts |
Updates production bridge signature. |
extension/src/test/telemetryInventory.test.ts |
Adjusts telemetry inventory test. |
extension/src/test/strings.test.ts |
Verifies localized confirmations. |
extension/src/test/rustDebugger.test.ts |
Tests typed Rust build failures. |
extension/src/test/launchFailureTelemetry.test.ts |
Tests sanitized failure telemetry. |
extension/src/test/dotnetDebugger.test.ts |
Tests typed .NET build failures. |
extension/src/test/dcpTypes.test.ts |
Tests safe launch-path extraction. |
extension/src/test/AspireExtensionContext.test.ts |
Tests assistance-state lifecycle. |
extension/src/test/aspireDebugConfigurationProvider.test.ts |
Tests discovery-failure journaling. |
extension/src/test/appHostLifecycleTools.test.ts |
Updates lifecycle-tool coverage. |
extension/src/test/appHostDataRepository.test.ts |
Tests one-shot resource queries. |
extension/src/test/adapterTracker.test.ts |
Tests AppHost termination attribution. |
extension/src/test-e2e/packageSurface.e2e.test.ts |
Verifies packaged tool contributions. |
extension/src/test-e2e/appHostLifecycleTools.e2e.test.ts |
Exercises assistance tools end-to-end. |
extension/src/services/launchFailureJournal.ts |
Implements bounded failure journal. |
extension/src/services/editorAssistanceWindowState.ts |
Resets activation-scoped state. |
extension/src/services/AppHostLaunchService.ts |
Exposes safe session state and journaling. |
extension/src/services/appHostLaunchReservations.ts |
Reports pending external runs. |
extension/src/services/appHostLaunchContracts.ts |
Defines safe session snapshots. |
extension/src/server/interactionService.ts |
Reuses Dashboard-launch helpers. |
extension/src/loc/strings.ts |
Adds localized confirmation strings. |
extension/src/lm/safeAppHostTargetResolver.ts |
Resolves trusted workspace AppHosts. |
extension/src/lm/languageModelToolUi.ts |
Escapes confirmation Markdown. |
extension/src/lm/editorUiHandoffService.ts |
Handles Dashboard and Output UI. |
extension/src/lm/editorStateSnapshotService.ts |
Produces bounded session summaries. |
extension/src/lm/editorAssistanceToolContracts.ts |
Defines tool contracts and validation. |
extension/src/lm/editorAssistanceToolAdapters.ts |
Registers the five tools. |
extension/src/lm/editorAssistanceTelemetry.ts |
Emits bounded result telemetry. |
extension/src/lm/appHostLifecycleToolService.ts |
Shares safe target resolution. |
extension/src/lm/appHostLifecycleTools.ts |
Updates lifecycle exports. |
extension/src/lm/appHostLifecycleToolContracts.ts |
Extends lifecycle service contracts. |
extension/src/lm/appHostLifecycleToolAdapters.ts |
Shares UI escaping and E2E access. |
extension/src/extension.ts |
Wires editor-assistance services. |
extension/src/debugger/languages/rust.ts |
Uses typed build failures. |
extension/src/debugger/languages/dotnet.ts |
Uses typed build failures. |
extension/src/debugger/debuggerExtensions.ts |
Captures structured resource identity. |
extension/src/debugger/AspireDebugConfigurationProviderInternal.ts |
Tracks recorded discovery failures. |
extension/src/debugger/AspireDebugConfigurationProvider.ts |
Journals terminal launch failures. |
extension/src/debugger/appHostBuildFailureError.ts |
Defines build-failure boundary type. |
extension/src/debugger/adapterTracker.ts |
Tracks explicit AppHost termination. |
extension/src/dcp/types.ts |
Extracts safe target/executable paths. |
extension/src/data/AppHostDataRepository.ts |
Exposes cancellable resource snapshots. |
extension/src/AspireExtensionContext.ts |
Exposes safe editor session projections. |
extension/package.nls.json |
Adds localized manifest strings. |
extension/package.json |
Contributes and activates new tools. |
extension/loc/xlf/aspire-vscode.xlf |
Updates localization catalog. |
Review details
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 58/59 changed files
- Comments generated: 2
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
There was a problem hiding this comment.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
extension/src/lm/editorUiHandoffService.ts:121
- This reports
openedeven when the notification was not shown.showDashboardLaunchNotificationcatches a synchronousshowInformationMessagefailure (and asynchronous rejection), logs it, and returnsvoid, so this tool cannot distinguish that failure from a successful presentation. Have the shared helper return/await a success result and return the boundederroroutcome when presentation fails.
if (resolvedBehavior.behavior === 'notification') {
showDashboardLaunchNotification({
baseUrl: dashboardUrl,
source: resolvedBehavior.source,
});
return { outcome: 'opened', presentation: 'notification' };
- Files reviewed: 70/71 changed files
- Comments generated: 1
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
|
Copilot review |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
There was a problem hiding this comment.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
extension/src/data/AppHostDataRepository.ts:632
- This cache lookup is not bound to the AppHost identity that created the describe stream. If a stream was opened through a symlink and that symlink is retargeted, both strings now resolve through the new target, so this returns the old AppHost's cached resources. A resource-scoped status request can then report
notDebuggingfor a resource that does not exist in the newly resolved AppHost instead of failing closed. Capture the opaque target identity when the describe stream is created and require that identity to match here.
private _getAppHostResources(appHostPath: string): ResourceJson[] {
const matchingStreams = Array.from(this._describeStreams.entries())
.filter(([currentAppHostPath]) => compareAppHostIdentity(currentAppHostPath, appHostPath) === 'same');
return matchingStreams.length === 1
? Array.from(matchingStreams[0][1].resources.values())
: [];
extension/src/services/AppHostLaunchService.ts:557
- Pending and active run state is correlated by re-resolving stored paths at query time. If an AppHost was launched through a symlink and the symlink is retargeted before the session snapshot is available, the stored path and the newly selected target both resolve to the new file, so the status tool reports the new AppHost as
startingeven though the launch belongs to the old target. Store the opaque identity with each pending/active launch and compare identities here.
extension/src/services/appHostLaunchReservations.ts:101 - This is not a launch-time identity match:
compareAppHostIdentityresolves the reservation path again. After a symlink-backed external reservation is created, retargeting that symlink makes the reservation appear to belong to the new AppHost, so editor assistance incorrectly reports that new target as starting. Capture the opaque target identity in the reservation and compare it with the current target identity instead.
- Files reviewed: 70/71 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Resolve the AppHost debugger overlap by preserving both typed launch-failure capture and the selected launch-profile environment behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b9501ad-f5ba-4bc8-a7c2-ccdfaf15061c
There was a problem hiding this comment.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
extension/src/lm/editorAssistanceToolAdapters.ts:188
- In invocation-time confirmation mode, a rejected
showWarningMessagepromise escapes from the tool instead of producing the boundederror/canceledresult promised by this surface. This mode is used after overlapping, expired, or overflowed preparations, so an Extension Host/UI failure can makevscode.lm.invokeToolreject and expose an uncontrolled error rather than the tool contract. Catch the modal failure and return a bounded outcome (usingcanceledwhen the token was canceled).
const selected = await vscode.window.showWarningMessage(
editorAssistanceOpenDashboardConfirmationTitle,
{
modal: true,
detail: editorAssistanceOpenDashboardConfirmationMessage(preparedTarget.displayPath),
},
confirmationItem);
- Files reviewed: 70/71 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Description
Copilot can start and stop an Aspire AppHost, but it could not safely answer the editor-owned
questions that usually come next: is this AppHost already debugging, which Aspire sessions are
active, where is the Dashboard, what happened during the last failed launch, and how do I get the
Aspire Output view in front of the user?
This adds five narrow VS Code language-model tools:
aspire_debug_session_statusaspire_list_debug_sessionstruncatedmarker when neededaspire_open_dashboardopenedand one bounded presentation bucketaspire_open_outputopenedaspire_explain_launch_failureThese are editor-assistance tools, not lifecycle or diagnostic tools. They do not start, stop, or
restart AppHosts/resources, scrape terminal output, summarize logs, return Dashboard URLs, or add a
second MCP implementation.
One editor-assistance subsystem
The tools are thin adapters over four shared services:
SafeAppHostTargetResolverresolves only AppHosts already discovered through the Aspireworkspace registry. It supports multi-root workspaces, returns absolute paths only inside the
extension, and exposes workspace-relative display paths to tool results and confirmation copy.
EditorStateSnapshotServiceprojects editor-owned Aspire run/debug sessions into bounded AppHostand resource summaries. Full, active-only, and exact-AppHost queries now share one projection
pipeline.
EditorUiHandoffServiceuses the existing Dashboard launcher/browser policy and Aspire Outputchannel. It does not duplicate browser selection, Dashboard ownership, or CLI resource-state logic.
LaunchFailureJournalstores only normalized launch-failure categories in memory for the lifetimeof the extension window.
Aspire CLI remains the control plane for AppHost/resource discovery and running-state refresh.
Trust, identity, and confirmation
Language-model input is treated as untrusted.
when: "isWorkspaceTrusted".filesystem.
retargeting changes identity, while an atomic save or checkout of the same AppHost does not.
user accepts. A model cannot confirm one AppHost and swap the selector before the UI operation.
stable VS Code API does not expose its internal prepare/invoke correlation identifier, so
overlapping, expired, or overflowed preparation state switches the tool to a conservative
invocation-time confirmation mode for the rest of the extension window. That mode freshly
resolves the target, shows one localized modal confirmation, and re-resolves again before opening.
This preserves the handoff without allowing a delayed invocation to consume another call's
confirmed target.
PID both match the fresh repository row. Ownerless external CLI rows fail closed because they do
not carry a launch-time identity that remains trustworthy after symlink retargeting or file
replacement.
show(true), so the Output view opens withoutstealing focus from the active editor.
The final lifecycle review also tightened three adjacent ownership boundaries:
replacing the original failure or reporting expected cancellation as an independent watch error;
second lexical identity registry.
Bounded session results
aspire_debug_session_statusreturns only:runningstartingstoppingnotDebuggingmultipleSessionsWith
resourceName, lookup is scoped to the exact resolved AppHost and refreshed through CLI-backedresource state. Missing or duplicate exact names fail closed. The result does not include the
resource snapshot or resource properties. The caller-supplied exact resource name is echoed only to
identify the requested scope; it is never included in telemetry.
aspire_list_debug_sessionsreports only editor-owned active AppHost summaries. Each item containsthe safe AppHost display path plus bounded
state,controller, andmodevalues. Results aresorted, capped at 20, and marked
truncatedwhen more active AppHosts exist.Launch-failure journal
Launch failures are captured where they occur during discovery, validation, CLI launch, build, DCP
startup, debug-session startup, and Dashboard opening. The original error is normalized immediately
and discarded.
Each record contains only:
The journal is memory-only, keeps at most five failures per AppHost and 50 globally, and expires
records after approximately 30 minutes. Launch-attempt fallback suppression is correlated to the
exact internal launch token so one launch cannot hide another launch's failure.
aspire_explain_launch_failuremaps the latest unexpired record to bounded action identifiers suchas
fixBuildErrors,installAspireCli,freeRequiredPort, orretryLaunch. It does not manufacturedetailed guidance when the normalized category is
unknown.Privacy boundary
Tool results, telemetry, journal records, and persisted E2E evidence do not contain:
The packaged Extension Host tests recursively inspect the persisted editor-assistance artifact for
these values rather than checking only individual result fields.
Telemetry
Two typed events were added:
aspire/vscode/editorassistance/resultaspire/vscode/launchfailure/recordedThey accept only bounded dimensions:
AppHost paths, resource names, caller-supplied extension IDs, URLs, and raw errors are not accepted.
Tests inspect the exact telemetry payloads and classifications in
telemetry.json.Edge-case coverage
disconnect(false)does not hide startup failureThe scenarios that need a synthetic clock, more than 20 concurrent sessions, duplicate internal
resource snapshots, or symlink retargeting stay in focused unit tests. The user-facing paths run
through a packaged VSIX in a real VS Code Extension Host and call
vscode.lm.invokeTool.Screenshots / recordings
Dashboard confirmation:
Output confirmation:
These are real VS Code confirmation dialogs captured during the packaged Extension Host run. There
is no meaningful "before" screenshot because the change adds language-model tool entry points rather
than changing an existing visible view.
Validation
Final head:
7e5a676d0d6e3a65e4b55c6c536da7d6599e8d07yarn compile-tests,yarn compile, andyarn lintDescribeCommandTests— 39/39 passingThe tree-actions failure on an earlier head exposed a CLI follow-mode bug: the
includeDisabledCommandsvalue was passed positionally as the environment-value flag. The call nowuses the named argument, preserving environment-value behavior and including disabled commands in
the streamed snapshot. The command-level regression test and packaged tree-actions scenario both
cover this path.
The final packaged lifecycle run also caught an E2E-only preparation leak: the bridge inspected
prepareInvocationoutput without canceling that synthetic preparation. The bridge now cancels anddisposes its token in
finally, matching the fact that the inspection never invokes the tool andpreventing it from affecting the next real packaged invocation.
No dependency versions, lockfiles, or MCP contributions changed.
Checklist
<remarks />and<code />elements on your triple slash comments?