diff --git a/go.mod b/go.mod
index 283f8d6..0ca27e7 100644
--- a/go.mod
+++ b/go.mod
@@ -3,18 +3,19 @@ module github.com/gptscript-ai/go-gptscript
 go 1.23.0
 
 require (
-	github.com/getkin/kin-openapi v0.124.0
-	github.com/stretchr/testify v1.8.4
+	github.com/getkin/kin-openapi v0.129.0
+	github.com/stretchr/testify v1.10.0
 )
 
 require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/go-openapi/jsonpointer v0.20.2 // indirect
-	github.com/go-openapi/swag v0.22.8 // indirect
-	github.com/invopop/yaml v0.2.0 // indirect
+	github.com/go-openapi/jsonpointer v0.21.0 // indirect
+	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
-	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/mailru/easyjson v0.9.0 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
+	github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
+	github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
 	github.com/perimeterx/marshmallow v1.1.5 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index 9d94c4d..1c7e6b3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,38 +1,39 @@
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
-github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
-github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
-github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
-github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
-github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
+github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30=
+github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
 github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
 github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
-github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
-github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU=
+github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8=
+github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc=
+github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
 github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
 github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/workspace.go b/workspace.go
index 8bd5a6f..f384a85 100644
--- a/workspace.go
+++ b/workspace.go
@@ -147,9 +147,9 @@ func (g *GPTScript) RemoveAll(ctx context.Context, opts ...RemoveAllOptions) err
 }
 
 type WriteFileInWorkspaceOptions struct {
-	WorkspaceID    string
-	CreateRevision *bool
-	LatestRevision string
+	WorkspaceID      string
+	CreateRevision   *bool
+	LatestRevisionID string
 }
 
 func (g *GPTScript) WriteFileInWorkspace(ctx context.Context, filePath string, contents []byte, opts ...WriteFileInWorkspaceOptions) error {
@@ -161,8 +161,8 @@ func (g *GPTScript) WriteFileInWorkspace(ctx context.Context, filePath string, c
 		if o.CreateRevision != nil {
 			opt.CreateRevision = o.CreateRevision
 		}
-		if o.LatestRevision != "" {
-			opt.LatestRevision = o.LatestRevision
+		if o.LatestRevisionID != "" {
+			opt.LatestRevisionID = o.LatestRevisionID
 		}
 	}
 
@@ -171,13 +171,13 @@ func (g *GPTScript) WriteFileInWorkspace(ctx context.Context, filePath string, c
 	}
 
 	_, err := g.runBasicCommand(ctx, "workspaces/write-file", map[string]any{
-		"id":             opt.WorkspaceID,
-		"contents":       base64.StdEncoding.EncodeToString(contents),
-		"filePath":       filePath,
-		"createRevision": opt.CreateRevision,
-		"latestRevision": opt.LatestRevision,
-		"workspaceTool":  g.globalOpts.WorkspaceTool,
-		"env":            g.globalOpts.Env,
+		"id":               opt.WorkspaceID,
+		"contents":         base64.StdEncoding.EncodeToString(contents),
+		"filePath":         filePath,
+		"createRevision":   opt.CreateRevision,
+		"latestRevisionID": opt.LatestRevisionID,
+		"workspaceTool":    g.globalOpts.WorkspaceTool,
+		"env":              g.globalOpts.Env,
 	})
 
 	return parsePossibleConflictInWorkspaceError(err)
@@ -245,16 +245,57 @@ func (g *GPTScript) ReadFileInWorkspace(ctx context.Context, filePath string, op
 	return base64.StdEncoding.DecodeString(out)
 }
 
+type ReadFileWithRevisionInWorkspaceResponse struct {
+	Content    []byte `json:"content"`
+	RevisionID string `json:"revisionID"`
+}
+
+func (g *GPTScript) ReadFileWithRevisionInWorkspace(ctx context.Context, filePath string, opts ...ReadFileInWorkspaceOptions) (*ReadFileWithRevisionInWorkspaceResponse, error) {
+	var opt ReadFileInWorkspaceOptions
+	for _, o := range opts {
+		if o.WorkspaceID != "" {
+			opt.WorkspaceID = o.WorkspaceID
+		}
+	}
+
+	if opt.WorkspaceID == "" {
+		opt.WorkspaceID = os.Getenv("GPTSCRIPT_WORKSPACE_ID")
+	}
+
+	out, err := g.runBasicCommand(ctx, "workspaces/read-file-with-revision", map[string]any{
+		"id":            opt.WorkspaceID,
+		"filePath":      filePath,
+		"workspaceTool": g.globalOpts.WorkspaceTool,
+		"env":           g.globalOpts.Env,
+	})
+	if err != nil {
+		if strings.HasSuffix(err.Error(), fmt.Sprintf("not found: %s/%s", opt.WorkspaceID, filePath)) {
+			return nil, newNotFoundInWorkspaceError(opt.WorkspaceID, filePath)
+		}
+		return nil, err
+	}
+
+	var resp ReadFileWithRevisionInWorkspaceResponse
+	err = json.Unmarshal([]byte(out), &resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return &resp, nil
+}
+
 type FileInfo struct {
 	WorkspaceID string
 	Name        string
 	Size        int64
 	ModTime     time.Time
 	MimeType    string
+	RevisionID  string
 }
 
 type StatFileInWorkspaceOptions struct {
-	WorkspaceID string
+	WorkspaceID          string
+	WithLatestRevisionID bool
 }
 
 func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, opts ...StatFileInWorkspaceOptions) (FileInfo, error) {
@@ -263,6 +304,7 @@ func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, op
 		if o.WorkspaceID != "" {
 			opt.WorkspaceID = o.WorkspaceID
 		}
+		opt.WithLatestRevisionID = opt.WithLatestRevisionID || o.WithLatestRevisionID
 	}
 
 	if opt.WorkspaceID == "" {
@@ -270,10 +312,11 @@ func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, op
 	}
 
 	out, err := g.runBasicCommand(ctx, "workspaces/stat-file", map[string]any{
-		"id":            opt.WorkspaceID,
-		"filePath":      filePath,
-		"workspaceTool": g.globalOpts.WorkspaceTool,
-		"env":           g.globalOpts.Env,
+		"id":                   opt.WorkspaceID,
+		"filePath":             filePath,
+		"withLatestRevisionID": opt.WithLatestRevisionID,
+		"workspaceTool":        g.globalOpts.WorkspaceTool,
+		"env":                  g.globalOpts.Env,
 	})
 	if err != nil {
 		if strings.HasSuffix(err.Error(), fmt.Sprintf("not found: %s/%s", opt.WorkspaceID, filePath)) {
@@ -291,16 +334,11 @@ func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, op
 	return info, nil
 }
 
-type RevisionInfo struct {
-	FileInfo
-	RevisionID string
-}
-
 type ListRevisionsForFileInWorkspaceOptions struct {
 	WorkspaceID string
 }
 
-func (g *GPTScript) ListRevisionsForFileInWorkspace(ctx context.Context, filePath string, opts ...ListRevisionsForFileInWorkspaceOptions) ([]RevisionInfo, error) {
+func (g *GPTScript) ListRevisionsForFileInWorkspace(ctx context.Context, filePath string, opts ...ListRevisionsForFileInWorkspaceOptions) ([]FileInfo, error) {
 	var opt ListRevisionsForFileInWorkspaceOptions
 	for _, o := range opts {
 		if o.WorkspaceID != "" {
@@ -325,7 +363,7 @@ func (g *GPTScript) ListRevisionsForFileInWorkspace(ctx context.Context, filePat
 		return nil, err
 	}
 
-	var info []RevisionInfo
+	var info []FileInfo
 	err = json.Unmarshal([]byte(out), &info)
 	if err != nil {
 		return nil, err
diff --git a/workspace_test.go b/workspace_test.go
index 669f2b0..eb895d3 100644
--- a/workspace_test.go
+++ b/workspace_test.go
@@ -33,6 +33,13 @@ func TestCreateAndDeleteWorkspaceFromWorkspace(t *testing.T) {
 		t.Fatalf("Error creating workspace: %v", err)
 	}
 
+	t.Cleanup(func() {
+		err = g.DeleteWorkspace(context.Background(), id)
+		if err != nil {
+			t.Errorf("Error deleting workspace: %v", err)
+		}
+	})
+
 	err = g.WriteFileInWorkspace(context.Background(), "file.txt", []byte("hello world"), WriteFileInWorkspaceOptions{
 		WorkspaceID: id,
 	})
@@ -45,26 +52,21 @@ func TestCreateAndDeleteWorkspaceFromWorkspace(t *testing.T) {
 		t.Errorf("Error creating workspace from workspace: %v", err)
 	}
 
-	data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
+	content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
 		WorkspaceID: newID,
 	})
 	if err != nil {
-		t.Errorf("Error reading file: %v", err)
+		t.Fatalf("Error reading file: %v", err)
 	}
 
-	if !bytes.Equal(data, []byte("hello world")) {
-		t.Errorf("Unexpected content: %s", data)
+	if !bytes.Equal(content, []byte("hello world")) {
+		t.Errorf("Unexpected content: %s", content)
 	}
 
 	err = g.DeleteWorkspace(context.Background(), id)
 	if err != nil {
 		t.Errorf("Error deleting workspace: %v", err)
 	}
-
-	err = g.DeleteWorkspace(context.Background(), newID)
-	if err != nil {
-		t.Errorf("Error deleting new workspace: %v", err)
-	}
 }
 
 func TestWriteReadAndDeleteFileFromWorkspace(t *testing.T) {
@@ -94,6 +96,20 @@ func TestWriteReadAndDeleteFileFromWorkspace(t *testing.T) {
 		t.Errorf("Unexpected content: %s", content)
 	}
 
+	// Read the file and request the revision ID
+	contentWithRevision, err := g.ReadFileWithRevisionInWorkspace(context.Background(), "test.txt", ReadFileInWorkspaceOptions{WorkspaceID: id})
+	if err != nil {
+		t.Errorf("Error reading file: %v", err)
+	}
+
+	if !bytes.Equal(contentWithRevision.Content, []byte("test")) {
+		t.Errorf("Unexpected content: %s", contentWithRevision.Content)
+	}
+
+	if contentWithRevision.RevisionID == "" {
+		t.Errorf("Expected file revision ID when requesting it: %s", contentWithRevision.RevisionID)
+	}
+
 	// Stat the file to ensure it exists
 	fileInfo, err := g.StatFileInWorkspace(context.Background(), "test.txt", StatFileInWorkspaceOptions{WorkspaceID: id})
 	if err != nil {
@@ -120,6 +136,24 @@ func TestWriteReadAndDeleteFileFromWorkspace(t *testing.T) {
 		t.Errorf("Unexpected file mime type: %s", fileInfo.MimeType)
 	}
 
+	if fileInfo.RevisionID != "" {
+		t.Errorf("Unexpected file revision ID when not requesting it: %s", fileInfo.RevisionID)
+	}
+
+	// Stat file and request the revision ID
+	fileInfo, err = g.StatFileInWorkspace(context.Background(), "test.txt", StatFileInWorkspaceOptions{WorkspaceID: id, WithLatestRevisionID: true})
+	if err != nil {
+		t.Errorf("Error statting file: %v", err)
+	}
+
+	if fileInfo.WorkspaceID != id {
+		t.Errorf("Unexpected file workspace ID: %v", fileInfo.WorkspaceID)
+	}
+
+	if fileInfo.RevisionID == "" {
+		t.Errorf("Expected file revision ID when requesting it: %s", fileInfo.RevisionID)
+	}
+
 	// Ensure we get the error we expect when trying to read a non-existent file
 	_, err = g.ReadFileInWorkspace(context.Background(), "test1.txt", ReadFileInWorkspaceOptions{WorkspaceID: id})
 	if nf := (*NotFoundInWorkspaceError)(nil); !errors.As(err, &nf) {
@@ -322,7 +356,7 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
 
 	ce := (*ConflictInWorkspaceError)(nil)
 	// Writing a new file with a non-zero latest revision should fail
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: "1"})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: "1"})
 	if err == nil || !errors.As(err, &ce) {
 		t.Errorf("Expected error writing file with non-zero latest revision: %v", err)
 	}
@@ -347,7 +381,7 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
 	}
 
 	// Writing to the file with the latest revision should succeed
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
 	if err != nil {
 		t.Fatalf("Error creating file: %v", err)
 	}
@@ -362,12 +396,13 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
 	}
 
 	// Writing to the file with the same revision should fail
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
 	if err == nil || !errors.As(err, &ce) {
 		t.Errorf("Expected error writing file with same revision: %v", err)
 	}
 
-	err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", revisions[1].RevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
+	latestRevisionID := revisions[1].RevisionID
+	err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", latestRevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
 	if err != nil {
 		t.Errorf("Error deleting revision for file: %v", err)
 	}
@@ -381,10 +416,16 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
 		t.Errorf("Unexpected number of revisions: %d", len(revisions))
 	}
 
+	// Ensure we cannot write a new file with the zero-th revision ID
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
+	if err == nil || !errors.As(err, &ce) {
+		t.Errorf("Unexpected error writing to file: %v", err)
+	}
+
 	// Ensure we can write a new file after deleting the latest revision
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: latestRevisionID})
 	if err != nil {
-		t.Fatalf("Error creating file: %v", err)
+		t.Errorf("Error writing file: %v", err)
 	}
 
 	err = g.DeleteFileInWorkspace(context.Background(), "test.txt", DeleteFileInWorkspaceOptions{WorkspaceID: id})
@@ -501,15 +542,15 @@ func TestCreateAndDeleteWorkspaceFromWorkspaceS3(t *testing.T) {
 		t.Errorf("Error creating workspace from workspace: %v", err)
 	}
 
-	data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
+	content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
 		WorkspaceID: newID,
 	})
 	if err != nil {
 		t.Errorf("Error reading file: %v", err)
 	}
 
-	if !bytes.Equal(data, []byte("hello world")) {
-		t.Errorf("Unexpected content: %s", data)
+	if !bytes.Equal(content, []byte("hello world")) {
+		t.Errorf("Unexpected content: %s", content)
 	}
 
 	err = g.DeleteWorkspace(context.Background(), id)
@@ -545,15 +586,15 @@ func TestCreateAndDeleteDirectoryWorkspaceFromWorkspaceS3(t *testing.T) {
 		t.Errorf("Error creating workspace from workspace: %v", err)
 	}
 
-	data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
+	content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
 		WorkspaceID: newID,
 	})
 	if err != nil {
 		t.Errorf("Error reading file: %v", err)
 	}
 
-	if !bytes.Equal(data, []byte("hello world")) {
-		t.Errorf("Unexpected content: %s", data)
+	if !bytes.Equal(content, []byte("hello world")) {
+		t.Errorf("Unexpected content: %s", content)
 	}
 
 	err = g.DeleteWorkspace(context.Background(), id)
@@ -577,6 +618,13 @@ func TestCreateAndDeleteS3WorkspaceFromWorkspaceDirectory(t *testing.T) {
 		t.Fatalf("Error creating workspace: %v", err)
 	}
 
+	t.Cleanup(func() {
+		err = g.DeleteWorkspace(context.Background(), id)
+		if err != nil {
+			t.Errorf("Error deleting workspace: %v", err)
+		}
+	})
+
 	err = g.WriteFileInWorkspace(context.Background(), "file.txt", []byte("hello world"), WriteFileInWorkspaceOptions{
 		WorkspaceID: id,
 	})
@@ -589,26 +637,21 @@ func TestCreateAndDeleteS3WorkspaceFromWorkspaceDirectory(t *testing.T) {
 		t.Errorf("Error creating workspace from workspace: %v", err)
 	}
 
-	data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
+	content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
 		WorkspaceID: newID,
 	})
 	if err != nil {
-		t.Errorf("Error reading file: %v", err)
+		t.Fatalf("Error reading file: %v", err)
 	}
 
-	if !bytes.Equal(data, []byte("hello world")) {
-		t.Errorf("Unexpected content: %s", data)
+	if !bytes.Equal(content, []byte("hello world")) {
+		t.Errorf("Unexpected content: %s", content)
 	}
 
 	err = g.DeleteWorkspace(context.Background(), id)
 	if err != nil {
 		t.Errorf("Error deleting workspace: %v", err)
 	}
-
-	err = g.DeleteWorkspace(context.Background(), newID)
-	if err != nil {
-		t.Errorf("Error deleting new workspace: %v", err)
-	}
 }
 
 func TestWriteReadAndDeleteFileFromWorkspaceS3(t *testing.T) {
@@ -642,6 +685,20 @@ func TestWriteReadAndDeleteFileFromWorkspaceS3(t *testing.T) {
 		t.Errorf("Unexpected content: %s", content)
 	}
 
+	// Read the file and request the revision ID
+	contentWithRevision, err := g.ReadFileWithRevisionInWorkspace(context.Background(), "test.txt", ReadFileInWorkspaceOptions{WorkspaceID: id})
+	if err != nil {
+		t.Errorf("Error reading file: %v", err)
+	}
+
+	if !bytes.Equal(contentWithRevision.Content, []byte("test")) {
+		t.Errorf("Unexpected content: %s", contentWithRevision.Content)
+	}
+
+	if contentWithRevision.RevisionID == "" {
+		t.Errorf("Expected file revision ID when requesting it: %s", contentWithRevision.RevisionID)
+	}
+
 	// Stat the file to ensure it exists
 	fileInfo, err := g.StatFileInWorkspace(context.Background(), "test.txt", StatFileInWorkspaceOptions{WorkspaceID: id})
 	if err != nil {
@@ -668,6 +725,24 @@ func TestWriteReadAndDeleteFileFromWorkspaceS3(t *testing.T) {
 		t.Errorf("Unexpected file mime type: %s", fileInfo.MimeType)
 	}
 
+	if fileInfo.RevisionID != "" {
+		t.Errorf("Unexpected file revision ID when not requesting it: %s", fileInfo.RevisionID)
+	}
+
+	// Stat file and request the revision ID
+	fileInfo, err = g.StatFileInWorkspace(context.Background(), "test.txt", StatFileInWorkspaceOptions{WorkspaceID: id, WithLatestRevisionID: true})
+	if err != nil {
+		t.Errorf("Error statting file: %v", err)
+	}
+
+	if fileInfo.WorkspaceID != id {
+		t.Errorf("Unexpected file workspace ID: %v", fileInfo.WorkspaceID)
+	}
+
+	if fileInfo.RevisionID == "" {
+		t.Errorf("Expected file revision ID when requesting it: %s", fileInfo.RevisionID)
+	}
+
 	// Ensure we get the error we expect when trying to read a non-existent file
 	_, err = g.ReadFileInWorkspace(context.Background(), "test1.txt", ReadFileInWorkspaceOptions{WorkspaceID: id})
 	if nf := (*NotFoundInWorkspaceError)(nil); !errors.As(err, &nf) {
@@ -795,7 +870,7 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
 
 	ce := (*ConflictInWorkspaceError)(nil)
 	// Writing a new file with a non-zero latest revision should fail
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: "1"})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: "1"})
 	if err == nil || !errors.As(err, &ce) {
 		t.Errorf("Expected error writing file with non-zero latest revision: %v", err)
 	}
@@ -820,7 +895,7 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
 	}
 
 	// Writing to the file with the latest revision should succeed
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
 	if err != nil {
 		t.Fatalf("Error creating file: %v", err)
 	}
@@ -835,12 +910,13 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
 	}
 
 	// Writing to the file with the same revision should fail
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
 	if err == nil || !errors.As(err, &ce) {
 		t.Errorf("Expected error writing file with same revision: %v", err)
 	}
 
-	err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", revisions[1].RevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
+	latestRevisionID := revisions[1].RevisionID
+	err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", latestRevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
 	if err != nil {
 		t.Errorf("Error deleting revision for file: %v", err)
 	}
@@ -854,8 +930,14 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
 		t.Errorf("Unexpected number of revisions: %d", len(revisions))
 	}
 
+	// Ensure we cannot write a new file with the zero-th revision ID
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
+	if err == nil || !errors.As(err, &ce) {
+		t.Fatalf("Error creating file: %v", err)
+	}
+
 	// Ensure we can write a new file after deleting the latest revision
-	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
+	err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: latestRevisionID})
 	if err != nil {
 		t.Fatalf("Error creating file: %v", err)
 	}