Skip to content

Track external tool calls#290886

Merged
roblourens merged 11 commits intomainfrom
roblou/invisible-limpet
Feb 7, 2026
Merged

Track external tool calls#290886
roblourens merged 11 commits intomainfrom
roblou/invisible-limpet

Conversation

@roblourens
Copy link
Member

No description provided.

Copilot AI review requested due to automatic review settings January 27, 2026 16:46
@roblourens roblourens self-assigned this Jan 27, 2026
Copy link
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

This PR adds support for tracking external tool invocations from extensions in the chat system. It enables extensions to create live, in-progress tool invocations that can be updated and completed asynchronously, providing better visibility into tool execution status.

Changes:

  • Introduced createExternal factory method and update/complete methods for external tool invocations
  • Added new DTO type IChatExternalToolInvocationUpdateDto for streaming external tool invocation updates
  • Implemented handling logic in mainThreadChatAgents2 to track and update external tool invocations

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/common/model/chatProgressTypes/chatToolInvocation.ts Adds factory method, interface, and update/complete methods for external tool invocations
src/vs/workbench/api/common/extHostTypeConverters.ts Converts ChatToolInvocationPart to either legacy serialized format or new update DTO based on isComplete field
src/vs/workbench/api/common/extHost.protocol.ts Defines IChatExternalToolInvocationUpdateDto for external tool invocation updates
src/vs/workbench/api/browser/mainThreadChatAgents2.ts Implements tracking map and handler for external tool invocation lifecycle
Comments suppressed due to low confidence (3)

src/vs/workbench/api/browser/mainThreadChatAgents2.ts:409

  • When creating an external tool invocation that is already completed (isComplete: true on first push), it's added to the pending map and then immediately removed. This is inefficient - the invocation should only be added to _pendingExternalToolInvocations if it's not complete (isComplete: false).
		if (progress.isComplete) {
			// Already completed on first push
			invocation.completeExternalInvocation({
				pastTenseMessage: progress.pastTenseMessage,
				isError: progress.isError,
			});
		} else {
			// Track for future updates
			this._pendingExternalToolInvocations.set(progress.toolCallId, invocation);
		}

src/vs/workbench/api/browser/mainThreadChatAgents2.ts:388

  • The check for existingInvocation and the early return on line 388 means that if an extension sends an update with isComplete: false after the invocation already exists, nothing is returned and no progress is added. However, if an invocation is created as complete on first push (line 400-405), it won't be in the pending map for any subsequent updates. This creates an inconsistent state where updating an already-completed invocation is silently ignored.
	private _handleExternalToolInvocationUpdate(progress: IChatExternalToolInvocationUpdateDto): ChatToolInvocation | undefined {
		const existingInvocation = this._pendingExternalToolInvocations.get(progress.toolCallId);

		if (existingInvocation) {
			if (progress.isComplete) {
				existingInvocation.completeExternalInvocation({
					pastTenseMessage: progress.pastTenseMessage,
					isError: progress.isError,
				});
				this._pendingExternalToolInvocations.delete(progress.toolCallId);
			} else {
				existingInvocation.updateExternalInvocation({
					invocationMessage: progress.invocationMessage,
					pastTenseMessage: progress.pastTenseMessage,
				});
			}
			return undefined;

src/vs/workbench/api/browser/mainThreadChatAgents2.ts:412

  • The new external tool invocation functionality lacks test coverage. Consider adding tests for the following scenarios: (1) creating and completing external tool invocations, (2) updating in-progress invocations, (3) handling duplicate toolCallIds, (4) memory cleanup when requests complete, and (5) error state propagation. The existing test suite at src/vs/workbench/contrib/chat/test/browser/tools/languageModelToolsService.test.ts provides a good pattern to follow.
	/**
	 * Handle external tool invocation updates from extensions.
	 * Returns the invocation if it's new and should be added to progress, otherwise undefined.
	 */
	private _handleExternalToolInvocationUpdate(progress: IChatExternalToolInvocationUpdateDto): ChatToolInvocation | undefined {
		const existingInvocation = this._pendingExternalToolInvocations.get(progress.toolCallId);

		if (existingInvocation) {
			if (progress.isComplete) {
				existingInvocation.completeExternalInvocation({
					pastTenseMessage: progress.pastTenseMessage,
					isError: progress.isError,
				});
				this._pendingExternalToolInvocations.delete(progress.toolCallId);
			} else {
				existingInvocation.updateExternalInvocation({
					invocationMessage: progress.invocationMessage,
					pastTenseMessage: progress.pastTenseMessage,
				});
			}
			return undefined;
		}

		// Create a new external tool invocation
		const invocation = ChatToolInvocation.createExternal({
			toolCallId: progress.toolCallId,
			toolName: progress.toolName,
			invocationMessage: progress.invocationMessage,
			pastTenseMessage: progress.pastTenseMessage,
			toolSpecificData: progress.toolSpecificData as IPreparedToolInvocation['toolSpecificData'],
		});

		if (progress.isComplete) {
			// Already completed on first push
			invocation.completeExternalInvocation({
				pastTenseMessage: progress.pastTenseMessage,
				isError: progress.isError,
			});
		} else {
			// Track for future updates
			this._pendingExternalToolInvocations.set(progress.toolCallId, invocation);
		}

		return invocation;
	}

@roblourens roblourens marked this pull request as ready for review February 7, 2026 20:18
@vs-code-engineering vs-code-engineering bot added this to the February 2026 milestone Feb 7, 2026
@roblourens roblourens enabled auto-merge (squash) February 7, 2026 20:56
@roblourens roblourens merged commit 7b1aae9 into main Feb 7, 2026
28 of 29 checks passed
@roblourens roblourens deleted the roblou/invisible-limpet branch February 7, 2026 21:06
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.

2 participants