From 1f4b1a75568d096f1844924da00e767a0edc3b2c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 18 Mar 2026 12:36:38 +0100 Subject: [PATCH] Fixes ChatResponseResource.parseUri bug --- .../contrib/chat/common/model/chatModel.ts | 2 +- .../chat/test/common/model/chatModel.test.ts | 51 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/model/chatModel.ts b/src/vs/workbench/contrib/chat/common/model/chatModel.ts index 6bff6a86544..4124516612e 100644 --- a/src/vs/workbench/contrib/chat/common/model/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/model/chatModel.ts @@ -2809,7 +2809,7 @@ export namespace ChatResponseResource { } const parts = uri.path.split('/'); - if (parts.length < 5) { + if (parts.length < 4) { return undefined; } diff --git a/src/vs/workbench/contrib/chat/test/common/model/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/model/chatModel.test.ts index e89b504b224..d30ca039c5a 100644 --- a/src/vs/workbench/contrib/chat/test/common/model/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/model/chatModel.test.ts @@ -25,7 +25,7 @@ import { TestExtensionService, TestStorageService } from '../../../../../test/co import { CellUri } from '../../../../notebook/common/notebookCommon.js'; import { IChatRequestImplicitVariableEntry, IChatRequestStringVariableEntry, IChatRequestFileEntry, StringChatContextValue } from '../../../common/attachments/chatVariableEntries.js'; import { ChatAgentService, IChatAgentService } from '../../../common/participants/chatAgents.js'; -import { ChatModel, ChatRequestModel, IChatRequestModeInfo, IExportableChatData, ISerializableChatData1, ISerializableChatData2, ISerializableChatData3, isExportableSessionData, isSerializableSessionData, normalizeSerializableChatData, Response } from '../../../common/model/chatModel.js'; +import { ChatModel, ChatRequestModel, ChatResponseResource, IChatRequestModeInfo, IExportableChatData, ISerializableChatData1, ISerializableChatData2, ISerializableChatData3, isExportableSessionData, isSerializableSessionData, normalizeSerializableChatData, Response } from '../../../common/model/chatModel.js'; import { ChatRequestTextPart } from '../../../common/requestParser/chatParserTypes.js'; import { ChatRequestQueueKind, IChatService, IChatToolInvocation } from '../../../common/chatService/chatService.js'; import { ChatAgentLocation, ChatModeKind } from '../../../common/constants.js'; @@ -1048,3 +1048,52 @@ suite('ChatModel - Pending Requests', () => { assert.strictEqual(pending.sendOptions.attempt, 3); }); }); + +suite('ChatResponseResource', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('createUri roundtrips through parseUri without basename', () => { + const sessionResource = URI.parse('vscode-chat-session://local/session1'); + const uri = ChatResponseResource.createUri(sessionResource, 'call-123', 2); + const parsed = ChatResponseResource.parseUri(uri); + + assert.ok(parsed); + assert.strictEqual(parsed.sessionResource.toString(), sessionResource.toString()); + assert.strictEqual(parsed.toolCallId, 'call-123'); + assert.strictEqual(parsed.index, 2); + }); + + test('createUri roundtrips through parseUri with basename', () => { + const sessionResource = URI.parse('vscode-chat-session://local/session1'); + const uri = ChatResponseResource.createUri(sessionResource, 'call-456', 0, 'file.txt'); + const parsed = ChatResponseResource.parseUri(uri); + + assert.ok(parsed); + assert.strictEqual(parsed.sessionResource.toString(), sessionResource.toString()); + assert.strictEqual(parsed.toolCallId, 'call-456'); + assert.strictEqual(parsed.index, 0); + }); + + test('parseUri rejects paths with fewer than 4 segments', () => { + // path "/tool/callId/0" splits into ['', 'tool', 'callId', '0'] = 4 parts => valid + // path "/tool/callId" splits into ['', 'tool', 'callId'] = 3 parts => invalid + const base = URI.from({ scheme: ChatResponseResource.scheme, authority: 'abc', path: '/tool/callId' }); + assert.strictEqual(ChatResponseResource.parseUri(base), undefined); + + const tooShort = URI.from({ scheme: ChatResponseResource.scheme, authority: 'abc', path: '/tool' }); + assert.strictEqual(ChatResponseResource.parseUri(tooShort), undefined); + + const empty = URI.from({ scheme: ChatResponseResource.scheme, authority: 'abc', path: '/' }); + assert.strictEqual(ChatResponseResource.parseUri(empty), undefined); + }); + + test('parseUri rejects wrong scheme', () => { + const uri = URI.from({ scheme: 'file', path: '/tool/callId/0' }); + assert.strictEqual(ChatResponseResource.parseUri(uri), undefined); + }); + + test('parseUri rejects wrong kind', () => { + const uri = URI.from({ scheme: ChatResponseResource.scheme, authority: 'abc', path: '/notTool/callId/0' }); + assert.strictEqual(ChatResponseResource.parseUri(uri), undefined); + }); +});