@@ -85,6 +85,32 @@ export class AssistantMessageParser {
8585 this . currentParamName = undefined
8686 continue
8787 } else {
88+ // Check for malformed closing tags (e.g., missing slash or spaces)
89+ // Some models might generate </path > or < /path> instead of </path>
90+ const malformedClosingPatterns = [
91+ `</ ${ this . currentParamName } >` , // space after slash
92+ `< /${ this . currentParamName } >` , // space before slash
93+ `</${ this . currentParamName } >` , // space before closing bracket
94+ `< / ${ this . currentParamName } >` , // spaces everywhere
95+ ]
96+
97+ for ( const pattern of malformedClosingPatterns ) {
98+ if ( currentParamValue . endsWith ( pattern ) ) {
99+ // Found a malformed closing tag, treat it as valid
100+ const paramValue = currentParamValue . slice ( 0 , - pattern . length )
101+ this . currentToolUse . params [ this . currentParamName ] =
102+ this . currentParamName === "content"
103+ ? paramValue . replace ( / ^ \n / , "" ) . replace ( / \n $ / , "" )
104+ : paramValue . trim ( )
105+ this . currentParamName = undefined
106+ break
107+ }
108+ }
109+
110+ if ( this . currentParamName === undefined ) {
111+ continue
112+ }
113+
88114 // Partial param value is accumulating.
89115 // Write the currently accumulated param content in real time
90116 this . currentToolUse . params [ this . currentParamName ] = currentParamValue
@@ -104,11 +130,22 @@ export class AssistantMessageParser {
104130 this . currentToolUse = undefined
105131 continue
106132 } else {
133+ // Check for parameter opening tags with various formats
107134 const possibleParamOpeningTags = toolParamNames . map ( ( name ) => `<${ name } >` )
108- for ( const paramOpeningTag of possibleParamOpeningTags ) {
135+ // Also check for malformed opening tags
136+ const malformedOpeningTags = toolParamNames . flatMap ( ( name ) => [
137+ `< ${ name } >` , // space after opening bracket
138+ `<${ name } >` , // space before closing bracket
139+ `< ${ name } >` , // spaces on both sides
140+ ] )
141+
142+ const allPossibleTags = [ ...possibleParamOpeningTags , ...malformedOpeningTags ]
143+
144+ for ( const paramOpeningTag of allPossibleTags ) {
109145 if ( this . accumulator . endsWith ( paramOpeningTag ) ) {
110146 // Start of a new parameter.
111- const paramName = paramOpeningTag . slice ( 1 , - 1 )
147+ // Extract the parameter name, handling spaces
148+ const paramName = paramOpeningTag . replace ( / [ < > \s ] / g, '' )
112149 if ( ! toolParamNames . includes ( paramName as ToolParamName ) ) {
113150 // Handle invalid parameter name gracefully
114151 continue
@@ -156,11 +193,19 @@ export class AssistantMessageParser {
156193
157194 let didStartToolUse = false
158195 const possibleToolUseOpeningTags = toolNames . map ( ( name ) => `<${ name } >` )
196+ // Also check for malformed tool opening tags
197+ const malformedToolOpeningTags = toolNames . flatMap ( ( name ) => [
198+ `< ${ name } >` , // space after opening bracket
199+ `<${ name } >` , // space before closing bracket
200+ `< ${ name } >` , // spaces on both sides
201+ ] )
202+
203+ const allPossibleToolTags = [ ...possibleToolUseOpeningTags , ...malformedToolOpeningTags ]
159204
160- for ( const toolUseOpeningTag of possibleToolUseOpeningTags ) {
205+ for ( const toolUseOpeningTag of allPossibleToolTags ) {
161206 if ( this . accumulator . endsWith ( toolUseOpeningTag ) ) {
162- // Extract and validate the tool name
163- const extractedToolName = toolUseOpeningTag . slice ( 1 , - 1 )
207+ // Extract and validate the tool name, handling spaces
208+ const extractedToolName = toolUseOpeningTag . replace ( / [ < > \s ] / g , '' )
164209
165210 // Check if the extracted tool name is valid
166211 if ( ! toolNames . includes ( extractedToolName as ToolName ) ) {
0 commit comments