Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 12 additions & 24 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// For native protocol, tool calls come as tool_call chunks, not XML.
// experiments is always provided via TaskOptions (defaults to experimentDefault in provider)
const toolProtocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
this.assistantMessageParser = toolProtocol === "xml" ? new AssistantMessageParser() : undefined
this.assistantMessageParser = toolProtocol !== "native" ? new AssistantMessageParser() : undefined

this.messageQueueService = new MessageQueueService()

Expand Down Expand Up @@ -2137,6 +2137,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {

await this.diffViewProvider.reset()

// Determine protocol once per API request to avoid repeated calls in the streaming loop
const streamProtocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
const shouldUseXmlParser = streamProtocol === "xml"

// Yields only if the first chunk is successful, otherwise will
// allow the user to retry the request (most likely due to rate
// limit error, which gets thrown on the first chunk).
Expand Down Expand Up @@ -2219,7 +2223,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
case "text": {
assistantMessage += chunk.text

if (this.assistantMessageParser) {
// Use the protocol determined at the start of streaming
// Don't rely solely on parser existence - parser might exist from previous state
if (shouldUseXmlParser && this.assistantMessageParser) {
// XML protocol: Parse raw assistant message chunk into content blocks
const prevLength = this.assistantMessageContent.length
this.assistantMessageContent = this.assistantMessageParser.processChunk(chunk.text)
Expand Down Expand Up @@ -2545,30 +2551,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// this.assistantMessageContent.forEach((e) => (e.partial = false))

// Now that the stream is complete, finalize any remaining partial content blocks (XML protocol only)
if (this.assistantMessageParser) {
// Use the protocol determined at the start of streaming
if (shouldUseXmlParser && this.assistantMessageParser) {
this.assistantMessageParser.finalizeContentBlocks()

const parsedBlocks = this.assistantMessageParser.getContentBlocks()

// Check if we're using native protocol
const state = await this.providerRef.deref()?.getState()
const isNative = isNativeProtocol(
resolveToolProtocol(this.apiConfiguration, this.api.getModel().info),
)

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
}
// For XML protocol: Use only parsed blocks (includes both text and tool_use parsed from XML)
this.assistantMessageContent = parsedBlocks
}

if (partialBlocks.length > 0) {
Expand Down
Loading