Skip to content
Open
Show file tree
Hide file tree
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
16 changes: 14 additions & 2 deletions src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,25 @@ function* yieldMissingToolResultBlocks(
assistantMessages: AssistantMessage[],
errorMessage: string,
) {
for (const assistantMessage of assistantMessages) {
// Filter out duplicate tool use blocks within the same message to prevent duplicate tool results
const uniqueAssistantMessages = assistantMessages.map(assistantMessage => {
const content = assistantMessage.message.content
const uniqueContent = content.filter((item, index, self) => {
if (item.type !== 'tool_use') return true
return index === self.findIndex(other =>
other.type === 'tool_use' && other.id === item.id
)
})
return { ...assistantMessage, message: { ...assistantMessage.message, content: uniqueContent } }
})

for (const assistantMessage of uniqueAssistantMessages) {
// Extract all tool use blocks from this assistant message
const toolUseBlocks = assistantMessage.message.content.filter(
content => content.type === 'tool_use',
) as ToolUseBlock[]
Comment on lines +139 to 143

// Emit an interruption message for each tool use
// Emit an interruption message for each unique tool use
for (const toolUse of toolUseBlocks) {
yield createUserMessage({
content: [
Expand Down
6 changes: 6 additions & 0 deletions src/services/tools/StreamingToolExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ export class StreamingToolExecutor {
* Add a tool to the execution queue. Will start executing immediately if conditions allow.
*/
addTool(block: ToolUseBlock, assistantMessage: AssistantMessage): void {
// Prevent duplicate tool additions - if a tool with this ID already exists, skip it.
// This can happen when provider disconnects/reconnects and resends the same tool_use block.
if (this.tools.some(t => t.id === block.id)) {
return
}

const toolDefinition = findToolByName(this.toolDefinitions, block.name)
if (!toolDefinition) {
this.tools.push({
Expand Down