Disable XML parser for native tool protocol (#9277)

This commit is contained in:
Daniel 2025-11-14 19:53:22 -05:00 committed by GitHub
parent 44b96f9d10
commit b2f204f123
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -301,7 +301,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
didRejectTool = false
didAlreadyUseTool = false
didCompleteReadingStream = false
assistantMessageParser: AssistantMessageParser
assistantMessageParser?: AssistantMessageParser
// Token Usage Cache
private tokenUsageSnapshot?: TokenUsage
@ -406,8 +406,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
TelemetryService.instance.captureTaskCreated(this.taskId)
}
// Initialize the assistant message parser.
this.assistantMessageParser = new AssistantMessageParser()
// Initialize the assistant message parser only for XML protocol.
// For native protocol, tool calls come as tool_call chunks, not XML.
const toolProtocol = vscode.workspace.getConfiguration(Package.name).get<ToolProtocol>("toolProtocol", "xml")
this.assistantMessageParser = toolProtocol === "xml" ? new AssistantMessageParser() : undefined
this.messageQueueService = new MessageQueueService()
@ -1996,7 +1998,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
this.didAlreadyUseTool = false
this.presentAssistantMessageLocked = false
this.presentAssistantMessageHasPendingUpdates = false
this.assistantMessageParser.reset()
this.assistantMessageParser?.reset()
await this.diffViewProvider.reset()
@ -2082,18 +2084,41 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
case "text": {
assistantMessage += chunk.text
// Parse raw assistant message chunk into content blocks.
const prevLength = this.assistantMessageContent.length
this.assistantMessageContent = this.assistantMessageParser.processChunk(chunk.text)
if (this.assistantMessageParser) {
// XML protocol: Parse raw assistant message chunk into content blocks
const prevLength = this.assistantMessageContent.length
this.assistantMessageContent = this.assistantMessageParser.processChunk(chunk.text)
if (this.assistantMessageContent.length > prevLength) {
// New content we need to present, reset to
// false in case previous content set this to true.
this.userMessageContentReady = false
if (this.assistantMessageContent.length > prevLength) {
// New content we need to present, reset to
// false in case previous content set this to true.
this.userMessageContentReady = false
}
// Present content to user.
presentAssistantMessage(this)
} else {
// Native protocol: Text chunks are plain text, not XML tool calls
// Create or update a text content block directly
const lastBlock =
this.assistantMessageContent[this.assistantMessageContent.length - 1]
if (lastBlock?.type === "text" && lastBlock.partial) {
// Update existing partial text block
lastBlock.content = assistantMessage
} else {
// Create new text block
this.assistantMessageContent.push({
type: "text",
content: assistantMessage,
partial: true,
})
this.userMessageContentReady = false
}
// Present content to user
presentAssistantMessage(this)
}
// Present content to user.
presentAssistantMessage(this)
break
}
}
@ -2384,28 +2409,30 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// Can't just do this b/c a tool could be in the middle of executing.
// this.assistantMessageContent.forEach((e) => (e.partial = false))
// Now that the stream is complete, finalize any remaining partial content blocks
this.assistantMessageParser.finalizeContentBlocks()
// Now that the stream is complete, finalize any remaining partial content blocks (XML protocol only)
if (this.assistantMessageParser) {
this.assistantMessageParser.finalizeContentBlocks()
const parsedBlocks = this.assistantMessageParser.getContentBlocks()
const parsedBlocks = this.assistantMessageParser.getContentBlocks()
// Check if we're using native protocol
const toolProtocol = vscode.workspace
.getConfiguration(Package.name)
.get<ToolProtocol>("toolProtocol", "xml")
const isNative = isNativeProtocol(toolProtocol)
// Check if we're using native protocol
const toolProtocol = vscode.workspace
.getConfiguration(Package.name)
.get<ToolProtocol>("toolProtocol", "xml")
const isNative = isNativeProtocol(toolProtocol)
if (isNative) {
// For native protocol: Preserve tool_use blocks that were added via tool_call chunks
// These are added directly to assistantMessageContent and have an 'id' property
const nativeToolBlocks = this.assistantMessageContent.filter(
(block): block is ToolUse<any> => block.type === "tool_use" && (block as any).id !== undefined,
)
// Merge: parser blocks (text) + native tool blocks (tools with IDs)
this.assistantMessageContent = [...parsedBlocks, ...nativeToolBlocks]
} else {
// For XML protocol: Use only parsed blocks (includes both text and tool_use parsed from XML)
this.assistantMessageContent = parsedBlocks
if (isNative) {
// For native protocol: Preserve tool_use blocks that were added via tool_call chunks
// These are added directly to assistantMessageContent and have an 'id' property
const nativeToolBlocks = this.assistantMessageContent.filter(
(block): block is ToolUse<any> => block.type === "tool_use" && (block as any).id !== undefined,
)
// Merge: parser blocks (text) + native tool blocks (tools with IDs)
this.assistantMessageContent = [...parsedBlocks, ...nativeToolBlocks]
} else {
// For XML protocol: Use only parsed blocks (includes both text and tool_use parsed from XML)
this.assistantMessageContent = parsedBlocks
}
}
if (partialBlocks.length > 0) {
@ -2439,8 +2466,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
await this.saveClineMessages()
await this.providerRef.deref()?.postStateToWebview()
// Reset parser after each complete conversation round
this.assistantMessageParser.reset()
// Reset parser after each complete conversation round (XML protocol only)
this.assistantMessageParser?.reset()
// Now add to apiConversationHistory.
// Need to save assistant responses to file before proceeding to