@@ -19,7 +19,10 @@ import (
1919 "github.com/docker/cagent/pkg/api"
2020 "github.com/docker/cagent/pkg/config"
2121 "github.com/docker/cagent/pkg/config/latest"
22+ "github.com/docker/cagent/pkg/content"
23+ "github.com/docker/cagent/pkg/oci"
2224 "github.com/docker/cagent/pkg/session"
25+ "github.com/docker/cagent/pkg/team"
2326 "github.com/docker/cagent/pkg/teamloader"
2427)
2528
@@ -674,9 +677,9 @@ agents:
674677 count := srv .countTeams ()
675678 assert .Equal (t , 1 , count , "should only load from agentsPath" )
676679
677- team , exists := srv .getTeam ("pirate.yaml" )
680+ tm , exists := srv .getTeam ("pirate.yaml" )
678681 require .True (t , exists )
679- agent , err := team .Agent ("root" )
682+ agent , err := tm .Agent ("root" )
680683 require .NoError (t , err )
681684 assert .Contains (t , agent .Instruction (), "MODIFIED" , "should have loaded modified pirate from agentsPath" )
682685
@@ -744,10 +747,10 @@ agents:
744747 assert .Equal (t , 1 , count , "should only have the OCI agent, not files from /tmp" )
745748
746749 // Verify it's the correct agent
747- team , exists := srv .getTeam ("docker.io_myorg_pirate_v1.yaml" )
750+ tm , exists := srv .getTeam ("docker.io_myorg_pirate_v1.yaml" )
748751 require .True (t , exists , "should have the OCI agent" )
749752
750- agent , err := team .Agent ("root" )
753+ agent , err := tm .Agent ("root" )
751754 require .NoError (t , err )
752755 assert .Contains (t , agent .Instruction (), "pirate" , "should be the pirate agent" )
753756
@@ -935,3 +938,180 @@ type mockStore struct {
935938func (s mockStore ) GetSessions (context.Context ) ([]* session.Session , error ) {
936939 return nil , nil
937940}
941+
942+ // TestServer_OCIRef_WithOCIRefCaching verifies that OCI agents store their
943+ // reference so per-session working directories can reload from the content store.
944+ func TestServer_OCIRef_WithOCIRefCaching (t * testing.T ) {
945+ t .Setenv ("OPENAI_API_KEY" , "dummy" )
946+
947+ runConfig := config.RuntimeConfig {}
948+ teams := map [string ]* team.Team {}
949+
950+ ociFilename := "docker.io_myorg_pirate_v1.yaml"
951+ ociRef := "docker.io/myorg/pirate:v1"
952+
953+ var store mockStore
954+ srv , err := New (store , & runConfig , teams , WithOCIRef (ociFilename , ociRef ))
955+ require .NoError (t , err )
956+
957+ // Verify that the OCI ref was cached
958+ cachedRef , exists := srv .getOCIRef (ociFilename )
959+ require .True (t , exists , "OCI ref should be cached" )
960+ assert .Equal (t , ociRef , cachedRef , "cached ref should match original" )
961+ }
962+
963+ // TestServer_LocalAgent_HasAgentsDirSet verifies that local file/directory agents
964+ // have hasAgentsDir() == true, ensuring they reload from disk (not use OCI path).
965+ func TestServer_LocalAgent_HasAgentsDirSet (t * testing.T ) {
966+ t .Setenv ("OPENAI_API_KEY" , "dummy" )
967+
968+ ctx := t .Context ()
969+ runConfig := config.RuntimeConfig {}
970+
971+ // Test 1: Directory of agents
972+ agentsDir := prepareAgentsDir (t , "pirate.yaml" )
973+ teams , err := teamloader .LoadTeams (ctx , agentsDir , & runConfig )
974+ require .NoError (t , err )
975+
976+ var store mockStore
977+ srv , err := New (store , & runConfig , teams , WithAgentsDir (agentsDir ))
978+ require .NoError (t , err )
979+
980+ assert .True (t , srv .hasAgentsDir (), "directory of agents should have agentsDir set" )
981+ assert .Empty (t , srv .ociRef , "local agents should not have OCI ref" )
982+ assert .Empty (t , srv .ociTeamKey , "local agents should not have OCI team key" )
983+
984+ // Test 2: Single file agent
985+ agentFile := filepath .Join (agentsDir , "pirate.yaml" )
986+ teams2 , err := teamloader .LoadTeams (ctx , agentFile , & runConfig )
987+ require .NoError (t , err )
988+
989+ srv2 , err := New (store , & runConfig , teams2 ,
990+ WithAgentsPath (agentFile ),
991+ WithAgentsDir (filepath .Dir (agentFile )))
992+ require .NoError (t , err )
993+
994+ assert .True (t , srv2 .hasAgentsDir (), "single file agent should have agentsDir set" )
995+ assert .Empty (t , srv2 .ociRef , "local agents should not have OCI ref" )
996+ assert .Empty (t , srv2 .ociTeamKey , "local agents should not have OCI team key" )
997+ }
998+
999+ // TestServer_loadTeamForSession_LocalAgent verifies that local agents always reload from disk.
1000+ func TestServer_loadTeamForSession_LocalAgent (t * testing.T ) {
1001+ t .Setenv ("OPENAI_API_KEY" , "dummy" )
1002+
1003+ ctx := t .Context ()
1004+ runConfig := config.RuntimeConfig {}
1005+
1006+ agentsDir := prepareAgentsDir (t , "pirate.yaml" )
1007+ teams , err := teamloader .LoadTeams (ctx , agentsDir , & runConfig )
1008+ require .NoError (t , err )
1009+
1010+ var store mockStore
1011+ srv , err := New (store , & runConfig , teams , WithAgentsDir (agentsDir ))
1012+ require .NoError (t , err )
1013+
1014+ sess := session .New ()
1015+ rc := runConfig .Clone ()
1016+
1017+ // Call the extracted method
1018+ tm , err := srv .loadTeamForSession (ctx , "pirate.yaml" , sess , rc )
1019+ require .NoError (t , err )
1020+ require .NotNil (t , tm )
1021+
1022+ agent , err := tm .Agent ("root" )
1023+ require .NoError (t , err )
1024+ assert .Contains (t , agent .Instruction (), "pirate" )
1025+ }
1026+
1027+ // TestServer_loadTeamForSession_OCIRef_NoWorkingDir verifies OCI agents use cached team
1028+ // when session has no custom working directory.
1029+ func TestServer_loadTeamForSession_OCIRef_NoWorkingDir (t * testing.T ) {
1030+ t .Setenv ("OPENAI_API_KEY" , "dummy" )
1031+
1032+ ctx := t .Context ()
1033+ tmpDir := t .TempDir ()
1034+
1035+ pirateContent , err := os .ReadFile (filepath .Join ("testdata" , "pirate.yaml" ))
1036+ require .NoError (t , err )
1037+
1038+ ociFilename := "docker.io_myorg_pirate_v1.yaml"
1039+ ociFile := filepath .Join (tmpDir , ociFilename )
1040+ err = os .WriteFile (ociFile , pirateContent , 0o600 )
1041+ require .NoError (t , err )
1042+
1043+ ociRef := "docker.io/myorg/pirate:v1"
1044+ runConfig := config.RuntimeConfig {}
1045+ teams , err := teamloader .LoadTeams (ctx , ociFile , & runConfig )
1046+ require .NoError (t , err )
1047+
1048+ var store mockStore
1049+ srv , err := New (store , & runConfig , teams , WithOCIRef (ociFilename , ociRef ))
1050+ require .NoError (t , err )
1051+
1052+ // Session without working dir
1053+ sess := session .New ()
1054+ rc := runConfig .Clone ()
1055+
1056+ // Should use cached team
1057+ tm , err := srv .loadTeamForSession (ctx , ociFilename , sess , rc )
1058+ require .NoError (t , err )
1059+ require .NotNil (t , tm )
1060+
1061+ // Verify it's the same team instance (pointer equality)
1062+ cachedTeam , _ := srv .getTeam (ociFilename )
1063+ assert .Same (t , cachedTeam , tm , "should return cached team when no custom workingDir" )
1064+ }
1065+
1066+ // TestServer_loadTeamForSession_OCIRef_WithWorkingDir verifies OCI agents reload
1067+ // when session has a custom working directory (requires content store).
1068+ func TestServer_loadTeamForSession_OCIRef_WithWorkingDir (t * testing.T ) {
1069+ t .Setenv ("OPENAI_API_KEY" , "dummy" )
1070+
1071+ ctx := t .Context ()
1072+ tmpDir := t .TempDir ()
1073+
1074+ // Create agent with filesystem tool to show working dir matters
1075+ agentWithFS := `version: "2"
1076+ agents:
1077+ root:
1078+ model: openai/gpt-4o
1079+ instruction: Test agent
1080+ toolsets:
1081+ - type: filesystem`
1082+
1083+ ociFilename := "docker.io_test_fs_v1.yaml"
1084+ ociFile := filepath .Join (tmpDir , ociFilename )
1085+ err := os .WriteFile (ociFile , []byte (agentWithFS ), 0o600 )
1086+ require .NoError (t , err )
1087+
1088+ // Push to content store so FromStore can retrieve it
1089+ ociRef := "docker.io/test/fs:v1"
1090+ contentStore , err := content .NewStore ()
1091+ require .NoError (t , err )
1092+ _ , err = oci .PackageFileAsOCIToStore (ctx , ociFile , ociRef , contentStore )
1093+ require .NoError (t , err )
1094+
1095+ runConfig := config.RuntimeConfig {}
1096+ teams , err := teamloader .LoadTeams (ctx , ociFile , & runConfig )
1097+ require .NoError (t , err )
1098+
1099+ var sessionStore mockStore
1100+ srv , err := New (sessionStore , & runConfig , teams , WithOCIRef (ociFilename , ociRef ))
1101+ require .NoError (t , err )
1102+
1103+ // Session WITH working dir
1104+ customWorkingDir := t .TempDir ()
1105+ sess := session .New (session .WithWorkingDir (customWorkingDir ))
1106+ rc := runConfig .Clone ()
1107+ rc .WorkingDir = customWorkingDir
1108+
1109+ // Should reload from content store
1110+ tm , err := srv .loadTeamForSession (ctx , ociFilename , sess , rc )
1111+ require .NoError (t , err )
1112+ require .NotNil (t , tm )
1113+
1114+ // Verify it's NOT the same team instance (was reloaded)
1115+ cachedTeam , _ := srv .getTeam (ociFilename )
1116+ assert .NotSame (t , cachedTeam , tm , "should return new team instance when custom workingDir is set" )
1117+ }
0 commit comments