@@ -745,6 +745,88 @@ Always include PINEAPPLE_COCONUT_42.
745745 }
746746 } ) ;
747747
748+ test ( "matches shell tool results with shell ID completion markers" , async ( ) => {
749+ const originalShellConfig =
750+ process . platform === "win32" ? ShellConfig . powerShell : ShellConfig . bash ;
751+ const cachePath = path . join ( tempDir , "cache.yaml" ) ;
752+ const cacheContent = yaml . stringify ( {
753+ models : [ "test-model" ] ,
754+ conversations : [
755+ {
756+ messages : [
757+ { role : "system" , content : "${system}" } ,
758+ { role : "user" , content : "Run command" } ,
759+ {
760+ role : "assistant" ,
761+ tool_calls : [
762+ {
763+ id : "toolcall_0" ,
764+ type : "function" ,
765+ function : {
766+ name : "${shell}" ,
767+ arguments : '{"command":"echo ok"}' ,
768+ } ,
769+ } ,
770+ ] ,
771+ } ,
772+ {
773+ role : "tool" ,
774+ tool_call_id : "toolcall_0" ,
775+ content : "ok\n<exited with exit code 0>" ,
776+ } ,
777+ { role : "assistant" , content : "Done" } ,
778+ ] ,
779+ } ,
780+ ] ,
781+ } satisfies NormalizedData ) ;
782+ await writeFile ( cachePath , cacheContent ) ;
783+
784+ const proxy = new ReplayingCapiProxy (
785+ "http://localhost:9999" ,
786+ cachePath ,
787+ workDir ,
788+ ) ;
789+ const proxyUrl = await proxy . start ( ) ;
790+
791+ try {
792+ const response = await makeRequest ( proxyUrl , "/chat/completions" , {
793+ body : {
794+ model : "test-model" ,
795+ messages : [
796+ { role : "system" , content : "System prompt" } ,
797+ { role : "user" , content : "Run command" } ,
798+ {
799+ role : "assistant" ,
800+ tool_calls : [
801+ {
802+ id : "runtime-call-id" ,
803+ type : "function" ,
804+ function : {
805+ name : originalShellConfig . shellToolName ,
806+ arguments : '{"command":"echo ok"}' ,
807+ } ,
808+ } ,
809+ ] ,
810+ } ,
811+ {
812+ role : "tool" ,
813+ tool_call_id : "runtime-call-id" ,
814+ content : "ok\n<shellId: 42 completed with exit code 0>" ,
815+ } ,
816+ ] ,
817+ } ,
818+ } ) ;
819+
820+ expect ( response . status ) . toBe ( 200 ) ;
821+ expect (
822+ ( JSON . parse ( response . body ) as ChatCompletion ) . choices [ 0 ] . message
823+ . content ,
824+ ) . toBe ( "Done" ) ;
825+ } finally {
826+ await proxy . stop ( ) ;
827+ }
828+ } ) ;
829+
748830 test ( "expands workdir placeholder in cached response" , async ( ) => {
749831 const cachePath = path . join ( tempDir , "cache.yaml" ) ;
750832 const cacheContent = yaml . stringify ( {
0 commit comments