@@ -12,8 +12,8 @@ function asMessages(messages: unknown[]): AgentMessage[] {
1212
1313function makeDualToolUseAssistantContent ( ) {
1414 return [
15- { type : "toolUse" , id : "tool-1" , name : "test1" , input : { } } ,
16- { type : "toolUse" , id : "tool-2" , name : "test2" , input : { } } ,
15+ { type : "toolUse" , id : "tool-1" , name : "test1" , arguments : { } } ,
16+ { type : "toolUse" , id : "tool-2" , name : "test2" , arguments : { } } ,
1717 { type : "text" , text : "Done" } ,
1818 ] ;
1919}
@@ -368,7 +368,7 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
368368 {
369369 role : "assistant" ,
370370 content : [
371- { type : "toolUse" , id : "tool-1" , name : "test" , input : { } } ,
371+ { type : "toolUse" , id : "tool-1" , name : "test" , arguments : { } } ,
372372 { type : "text" , text : "I'll check that" } ,
373373 ] ,
374374 } ,
@@ -389,7 +389,7 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
389389 {
390390 role : "assistant" ,
391391 content : [
392- { type : "toolUse" , id : "tool-1" , name : "test" , input : { } } ,
392+ { type : "toolUse" , id : "tool-1" , name : "test" , arguments : { } } ,
393393 { type : "text" , text : "Here's result" } ,
394394 ] ,
395395 } ,
@@ -408,7 +408,7 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
408408 // tool_use should be preserved because matching tool_result exists
409409 const assistantContent = ( result [ 1 ] as { content ?: unknown [ ] } ) . content ;
410410 expect ( assistantContent ) . toEqual ( [
411- { type : "toolUse" , id : "tool-1" , name : "test" , input : { } } ,
411+ { type : "toolUse" , id : "tool-1" , name : "test" , arguments : { } } ,
412412 { type : "text" , text : "Here's result" } ,
413413 ] ) ;
414414 } ) ;
@@ -418,7 +418,7 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
418418 { role : "user" , content : [ { type : "text" , text : "Use tool" } ] } ,
419419 {
420420 role : "assistant" ,
421- content : [ { type : "toolUse" , id : "tool-1" , name : "test" , input : { } } ] ,
421+ content : [ { type : "toolUse" , id : "tool-1" , name : "test" , arguments : { } } ] ,
422422 } ,
423423 { role : "user" , content : [ { type : "text" , text : "Hello" } ] } ,
424424 ] ) ;
@@ -431,6 +431,23 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
431431 expect ( assistantContent ) . toEqual ( [ { type : "text" , text : "[tool calls omitted]" } ] ) ;
432432 } ) ;
433433
434+ it ( "leaves aborted tool-only assistant turns empty instead of synthesizing fallback text" , ( ) => {
435+ const msgs = asMessages ( [
436+ { role : "user" , content : [ { type : "text" , text : "Use tool" } ] } ,
437+ {
438+ role : "assistant" ,
439+ stopReason : "aborted" ,
440+ content : [ { type : "toolCall" , id : "tool-1" , name : "test" , arguments : { } } ] ,
441+ } ,
442+ { role : "user" , content : [ { type : "text" , text : "Hello" } ] } ,
443+ ] ) ;
444+
445+ const result = validateAnthropicTurns ( msgs ) ;
446+
447+ expect ( result ) . toHaveLength ( 3 ) ;
448+ expect ( ( result [ 1 ] as { content ?: unknown [ ] } ) . content ) . toEqual ( [ ] ) ;
449+ } ) ;
450+
434451 it ( "should handle multiple dangling tool_use blocks" , ( ) => {
435452 const msgs = makeDualToolAnthropicTurns ( [ { type : "text" , text : "OK" } ] ) ;
436453
@@ -458,28 +475,54 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
458475 // tool-1 should be preserved (has matching tool_result), tool-2 stripped, text preserved
459476 const assistantContent = ( result [ 1 ] as { content ?: unknown [ ] } ) . content ;
460477 expect ( assistantContent ) . toEqual ( [
461- { type : "toolUse" , id : "tool-1" , name : "test1" , input : { } } ,
478+ { type : "toolUse" , id : "tool-1" , name : "test1" , arguments : { } } ,
462479 { type : "text" , text : "Done" } ,
463480 ] ) ;
464481 } ) ;
465482
466- it ( "should not modify messages when next is not user " , ( ) => {
483+ it ( "matches standalone toolResult messages before the next assistant turn " , ( ) => {
467484 const msgs = asMessages ( [
468485 { role : "user" , content : [ { type : "text" , text : "Use tool" } ] } ,
469486 {
470487 role : "assistant" ,
471- content : [ { type : "toolUse " , id : "tool-1" , name : "test" , input : { } } ] ,
488+ content : [ { type : "toolCall " , id : "tool-1" , name : "test" , arguments : { } } ] ,
472489 } ,
473- // Next is assistant, not user - should not strip
474- { role : "assistant " , content : [ { type : "text" , text : "Continue" } ] } ,
490+ { role : "toolResult" , toolCallId : "tool-1" , content : [ { type : "text" , text : "data" } ] } ,
491+ { role : "user " , content : [ { type : "text" , text : "Continue" } ] } ,
475492 ] ) ;
476493
477494 const result = validateAnthropicTurns ( msgs ) ;
478495
479- expect ( result ) . toHaveLength ( 3 ) ;
480- // Original tool_use should be preserved
496+ expect ( result ) . toHaveLength ( 4 ) ;
481497 const assistantContent = ( result [ 1 ] as { content ?: unknown [ ] } ) . content ;
482- expect ( assistantContent ) . toEqual ( [ { type : "toolUse" , id : "tool-1" , name : "test" , input : { } } ] ) ;
498+ expect ( assistantContent ) . toEqual ( [
499+ { type : "toolCall" , id : "tool-1" , name : "test" , arguments : { } } ,
500+ ] ) ;
501+ } ) ;
502+
503+ it ( "matches tool result blocks across intermediate non-assistant messages" , ( ) => {
504+ const msgs = asMessages ( [
505+ { role : "user" , content : [ { type : "text" , text : "Use tool" } ] } ,
506+ {
507+ role : "assistant" ,
508+ content : [
509+ { type : "functionCall" , id : "tool-1" , name : "test" , arguments : { } } ,
510+ { type : "text" , text : "Checking" } ,
511+ ] ,
512+ } ,
513+ { role : "user" , content : [ { type : "text" , text : "still waiting" } ] } ,
514+ { role : "tool" , toolCallId : "tool-1" , content : [ { type : "text" , text : "data" } ] } ,
515+ { role : "user" , content : [ { type : "text" , text : "Continue" } ] } ,
516+ ] ) ;
517+
518+ const result = validateAnthropicTurns ( msgs ) ;
519+
520+ expect ( result ) . toHaveLength ( 5 ) ;
521+ const assistantContent = ( result [ 1 ] as { content ?: unknown [ ] } ) . content ;
522+ expect ( assistantContent ) . toEqual ( [
523+ { type : "functionCall" , id : "tool-1" , name : "test" , arguments : { } } ,
524+ { type : "text" , text : "Checking" } ,
525+ ] ) ;
483526 } ) ;
484527
485528 it ( "is replay-safe across repeated validation passes" , ( ) => {
0 commit comments