Skip to content

agentPlugins: allow targeting a specific plugin install in deep link#304552

Merged
connor4312 merged 3 commits intomainfrom
connor4312/plugin-targeting
Mar 24, 2026
Merged

agentPlugins: allow targeting a specific plugin install in deep link#304552
connor4312 merged 3 commits intomainfrom
connor4312/plugin-targeting

Conversation

@connor4312
Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings March 24, 2026 19:14
@connor4312 connor4312 enabled auto-merge (squash) March 24, 2026 19:14
@connor4312 connor4312 self-assigned this Mar 24, 2026
@vs-code-engineering vs-code-engineering bot added this to the 1.114.0 milestone Mar 24, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for targeting a specific agent plugin install from a deep link by introducing a plugin query parameter on the vscode://chat-plugin/install route, and wiring that through the plugin install service so the UI can open plugin details directly.

Changes:

  • Extend IPluginInstallService.installPluginFromValidatedSource to accept options and (optionally) return a matched marketplace plugin.
  • Update PluginUrlHandler to detect a plugin param on /install and open an AgentPluginEditorInput for the matched plugin.
  • Add tests covering the targeted-plugin install URL path and editor opening behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/vs/workbench/contrib/chat/test/browser/plugins/pluginUrlHandler.test.ts Adds targeted-plugin deep link tests and editor-opening assertions.
src/vs/workbench/contrib/chat/common/plugins/pluginInstallService.ts Introduces options/result types for targeted plugin lookup and updates service signatures/docs.
src/vs/workbench/contrib/chat/browser/pluginUrlHandler.ts Adds plugin param handling for /install and opens plugin details editor on match.
src/vs/workbench/contrib/chat/browser/pluginInstallService.ts Implements targeted-plugin behavior in the install service when options.plugin is provided.
Comments suppressed due to low confidence (5)

src/vs/workbench/contrib/chat/common/plugins/pluginInstallService.ts:90

  • installPluginFromValidatedSource is documented to return matchedPlugin “without auto-installing”, but the current implementation path for options.plugin installs the plugin (and the URL handler comment suggests it should only open details and let the user decide). Please reconcile these semantics.
	/**
	 * Installs a plugin from an already-validated source string.
	 * Handles trust, cloning, scanning, and registration. Returns a result
	 * with an optional error message (e.g. no plugins found).
	 *
	 * When {@link IInstallPluginFromSourceOptions.plugin} is set, targets
	 * a specific plugin and returns it in {@link IInstallPluginFromSourceResult.matchedPlugin}
	 * without auto-installing.
	 */
	installPluginFromValidatedSource(source: string, options?: IInstallPluginFromSourceOptions): Promise<IInstallPluginFromSourceResult>;

src/vs/workbench/contrib/chat/common/plugins/pluginInstallService.ts:73

  • IInstallPluginFromSourceOptions.plugin doc says the matched plugin is returned “without being auto-installed”, but installPluginFromSource returns Promise<void> so there is no way to return the matched plugin from that method. Either adjust the docs to only describe the behavior of installPluginFromValidatedSource, or change the API shape so the targeted-plugin flow can actually return the match.
	/**
	 * Installs a plugin directly from a source location string. Accepts
	 * GitHub shorthand (`owner/repo`) or a full git clone URL. Clones the
	 * repository, reads marketplace metadata to discover plugins, and
	 * registers the selected plugin.
	 *
	 * When {@link IInstallPluginFromSourceOptions.plugin} is set, targets
	 * a specific plugin and returns it without auto-installing.
	 */
	installPluginFromSource(source: string, options?: IInstallPluginFromSourceOptions): Promise<void>;

src/vs/workbench/contrib/chat/test/browser/plugins/pluginUrlHandler.test.ts:279

  • This base64 plugin test only asserts that an editor opens, but it doesn’t verify that the handler actually decodes the plugin query parameter before calling installPluginFromValidatedSource. Since the stub ignores the passed options, the test would still pass even if decoding is broken. Consider capturing the _options argument in MockState and asserting options.plugin equals the expected decoded name.
	test('install with base64-encoded plugin param opens editor', async () => {
		const plugin = makeMarketplacePlugin('my-plugin', 'acme/plugins');
		const { handler, state } = createHandler({
			installFromValidatedSourceResult: { success: true, matchedPlugin: plugin },
		});
		const encodedPlugin = toBase64('my-plugin');
		const result = await handler.handleURL(uri('/install', `source=acme/plugins&plugin=${encodedPlugin}`));
		assert.strictEqual(result, true);
		assert.strictEqual(state.openedEditorInputs.length, 1);
		assert.strictEqual(state.openedEditorInputs[0].item.name, 'my-plugin');

src/vs/workbench/contrib/chat/browser/pluginUrlHandler.ts:96

  • plugin query param is read via _decodeStringParam, but that helper currently just returns the raw query value and never attempts base64 decoding. This means deep links like ...&plugin=<base64> will pass the encoded string to installPluginFromValidatedSource, and the targeted-plugin lookup will fail unless the value happens to already be plain text.
		const pluginName = this._decodeStringParam(uri, 'plugin');
		if (pluginName) {
			return this._handleInstallTargetedPlugin(source, ref.displayLabel, pluginName);
		}

src/vs/workbench/contrib/chat/browser/pluginUrlHandler.ts:226

  • The _decodeStringParam docstring says it “decodes” and tries base64 first, but the implementation only returns URLSearchParams.get() without decoding or fallback behavior. Either implement the base64-then-raw behavior (similar to _decodeQueryParam) or adjust the doc/comment and call sites accordingly.
	/**
	 * Reads a query parameter and decodes it. Tries base64-decoding first,
	 * then falls back to the raw value.
	 */
	private _decodeStringParam(uri: URI, key: string): string | undefined {
		const params = new URLSearchParams(uri.query);
		return params.get(key) ?? undefined;
	}

Comment thread src/vs/workbench/contrib/chat/browser/pluginInstallService.ts
Comment thread src/vs/workbench/contrib/chat/browser/pluginUrlHandler.ts Outdated
@connor4312 connor4312 merged commit 2572ad4 into main Mar 24, 2026
19 checks passed
@connor4312 connor4312 deleted the connor4312/plugin-targeting branch March 24, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants