@@ -29,11 +29,25 @@ export type PlainTextToolCallBlock = {
2929export type PlainTextToolCallParseOptions = {
3030 /** Optional allowlist of tool names that may be repaired. */
3131 allowedToolNames ?: Iterable < string > ;
32- /** Maximum JSON payload size accepted for one repaired call. */
32+ /** Maximum serialized payload size accepted for one repaired call. */
3333 maxPayloadBytes ?: number ;
3434} ;
3535
3636const DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES = 256_000 ;
37+ const utf8Encoder = new TextEncoder ( ) ;
38+
39+ function utf8ByteLengthWithinLimit (
40+ text : string ,
41+ start : number ,
42+ end : number ,
43+ maxBytes : number ,
44+ ) : number | null {
45+ if ( end - start > maxBytes ) {
46+ return null ;
47+ }
48+ const byteLength = utf8Encoder . encode ( text . slice ( start , end ) ) . byteLength ;
49+ return byteLength <= maxBytes ? byteLength : null ;
50+ }
3751
3852type PlainTextToolCallOpening = {
3953 allowsOptionalXmlishClose ?: boolean ;
@@ -140,7 +154,7 @@ function consumeJsonObject(
140154 return null ;
141155 }
142156 const end = findJsonObjectEnd ( text , cursor , maxPayloadBytes ) ;
143- if ( end === null ) {
157+ if ( end === null || utf8ByteLengthWithinLimit ( text , cursor , end , maxPayloadBytes ) === null ) {
144158 return null ;
145159 }
146160 const rawJson = text . slice ( cursor , end ) ;
@@ -247,15 +261,17 @@ function consumeXmlishParameterBlock(
247261 text : string ,
248262 start : number ,
249263 maxPayloadBytes : number ,
250- ) : { end : number ; name : string ; value : string } | null {
264+ ) : { byteLength : number ; end : number ; name : string ; value : string } | null {
251265 const bounds = findXmlishParameterBlock ( text , start ) ;
252266 if ( ! bounds ) {
253267 return null ;
254268 }
255- if ( bounds . end - bounds . start > maxPayloadBytes ) {
269+ const byteLength = utf8ByteLengthWithinLimit ( text , start , bounds . end , maxPayloadBytes ) ;
270+ if ( byteLength === null ) {
256271 return null ;
257272 }
258273 return {
274+ byteLength,
259275 end : bounds . end ,
260276 name : bounds . name ,
261277 value : extractXmlishParameterValue ( text , bounds . payloadStart , bounds . closeStart ) ,
@@ -339,12 +355,14 @@ function parseXmlishPlainTextToolCallBlockAt(
339355 const args : Record < string , unknown > = { } ;
340356 let cursor = opening . end ;
341357 let parameterCount = 0 ;
358+ let payloadBytes = 0 ;
342359 while ( true ) {
343360 const parameter = consumeXmlishParameterBlock ( text , cursor , maxPayloadBytes ) ;
344361 if ( ! parameter ) {
345362 break ;
346363 }
347- if ( parameter . end - opening . end > maxPayloadBytes ) {
364+ payloadBytes += parameter . byteLength ;
365+ if ( payloadBytes > maxPayloadBytes ) {
348366 return null ;
349367 }
350368 args [ parameter . name ] = parameter . value ;
0 commit comments