@@ -494,15 +494,55 @@ describe("VsCodeLmHandler", () => {
494494
495495 it ( "does not recover an invoke block quoted inside a fenced code block" , async ( ) => {
496496 const block = '<invoke name="calculator"><parameter name="operation">add</parameter></invoke>'
497- const chunks = await collect ( [ "Do NOT do this:\n```\n" + block + "\n```\n" ] )
497+ // Open the wrapper outside the fence so only the quoting guard prevents recovery.
498+ const chunks = await collect ( [ "<function_calls>\n```\n" + block + "\n```\n</function_calls>" ] )
498499
499500 expect ( chunks . some ( ( chunk ) => chunk . type === "tool_call" ) ) . toBe ( false )
500501 } )
501502
503+ it . each ( [
504+ { name : "write_to_file" , parameter : "content" } ,
505+ { name : "update_todo_list" , parameter : "todos" } ,
506+ ] ) (
507+ "recovers a large $name call split across chunks without leaking markup" ,
508+ async ( { name, parameter } ) => {
509+ const payload = "x" . repeat ( 5000 )
510+ streamTextParts ( [
511+ `<function_calls><invoke name="${ name } "><parameter name="${ parameter } ">` ,
512+ payload ,
513+ "</parameter></invoke></function_calls>" ,
514+ ] )
515+ const chunks = [ ]
516+ for await ( const chunk of handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
517+ taskId : "test-task" ,
518+ tools : [
519+ {
520+ type : "function" ,
521+ function : {
522+ name,
523+ parameters : { type : "object" , properties : { [ parameter ] : { type : "string" } } } ,
524+ } ,
525+ } ,
526+ ] ,
527+ } ) ) {
528+ chunks . push ( chunk )
529+ }
530+
531+ expect ( chunks . filter ( ( chunk ) => chunk . type !== "usage" ) ) . toEqual ( [
532+ {
533+ type : "tool_call" ,
534+ id : expect . stringContaining ( "vscodelm-salvaged-" ) ,
535+ name,
536+ arguments : JSON . stringify ( { [ parameter ] : payload } ) ,
537+ } ,
538+ ] )
539+ } ,
540+ )
541+
502542 it ( "flushes an over-long never-closing invoke as plain text before the stream ends" , async ( ) => {
503543 // End-of-stream flushing produces identical text, so track production timing to
504544 // prove the buffer releases text before the stream ends.
505- const filler = "x" . repeat ( 5000 )
545+ const filler = "x" . repeat ( 256 * 1024 )
506546 const parts = [ '<invoke name="calculator">' , filler , filler , filler , filler ]
507547 let partsProduced = 0
508548
0 commit comments