Просмотр исходного кода

feat: delete sessions (#362)

Co-authored-by: adamdottv <[email protected]>
Adam 8 месяцев назад
Родитель
Сommit
6f18475428

+ 27 - 0
packages/opencode/src/server/server.ts

@@ -390,6 +390,33 @@ export namespace Server {
           return c.json(Session.abort(body.sessionID))
         },
       )
+      .post(
+        "/session_delete",
+        describeRoute({
+          description: "Delete a session and all its data",
+          responses: {
+            200: {
+              description: "Successfully deleted session",
+              content: {
+                "application/json": {
+                  schema: resolver(z.boolean()),
+                },
+              },
+            },
+          },
+        }),
+        zValidator(
+          "json",
+          z.object({
+            sessionID: z.string(),
+          }),
+        ),
+        async (c) => {
+          const body = c.req.valid("json")
+          await Session.remove(body.sessionID)
+          return c.json(true)
+        },
+      )
       .post(
         "/session_summarize",
         describeRoute({

+ 39 - 0
packages/opencode/src/session/index.ts

@@ -72,6 +72,12 @@ export namespace Session {
         info: Info,
       }),
     ),
+    Deleted: Bus.event(
+      "session.deleted",
+      z.object({
+        info: Info,
+      }),
+    ),
     Error: Bus.event(
       "session.error",
       z.object({
@@ -206,6 +212,17 @@ export namespace Session {
     }
   }
 
+  export async function children(parentID: string) {
+    const result = [] as Session.Info[]
+    for await (const item of Storage.list("session/info")) {
+      const sessionID = path.basename(item, ".json")
+      const session = await get(sessionID)
+      if (session.parentID !== parentID) continue
+      result.push(session)
+    }
+    return result
+  }
+
   export function abort(sessionID: string) {
     const controller = state().pending.get(sessionID)
     if (!controller) return false
@@ -214,6 +231,28 @@ export namespace Session {
     return true
   }
 
+  export async function remove(sessionID: string, emitEvent = true) {
+    try {
+      abort(sessionID)
+      const session = await get(sessionID)
+      for (const child of await children(sessionID)) {
+        await remove(child.id, false)
+      }
+      await unshare(sessionID).catch(() => {})
+      await Storage.remove(`session/info/${sessionID}`).catch(() => {})
+      await Storage.removeDir(`session/message/${sessionID}/`).catch(() => {})
+      state().sessions.delete(sessionID)
+      state().messages.delete(sessionID)
+      if (emitEvent) {
+        Bus.publish(Event.Deleted, {
+          info: session,
+        })
+      }
+    } catch (e) {
+      log.error(e)
+    }
+  }
+
   async function updateMessage(msg: Message.Info) {
     await Storage.writeJSON(
       "session/message/" + msg.metadata.sessionID + "/" + msg.id,

+ 5 - 0
packages/opencode/src/storage/storage.ts

@@ -29,6 +29,11 @@ export namespace Storage {
     await fs.unlink(target).catch(() => {})
   }
 
+  export async function removeDir(key: string) {
+    const target = path.join(state().dir, key)
+    await fs.rm(target, { recursive: true, force: true }).catch(() => {})
+  }
+
   export async function readJSON<T>(key: string) {
     return Bun.file(path.join(state().dir, key + ".json")).json() as Promise<T>
   }

+ 13 - 0
packages/tui/internal/app/app.go

@@ -396,6 +396,19 @@ func (a *App) ListSessions(ctx context.Context) ([]client.SessionInfo, error) {
 	return sessions, nil
 }
 
+func (a *App) DeleteSession(ctx context.Context, sessionID string) error {
+	resp, err := a.Client.PostSessionDeleteWithResponse(ctx, client.PostSessionDeleteJSONRequestBody{
+		SessionID: sessionID,
+	})
+	if err != nil {
+		return err
+	}
+	if resp.StatusCode() != 200 {
+		return fmt.Errorf("failed to delete session: %d", resp.StatusCode())
+	}
+	return nil
+}
+
 func (a *App) ListMessages(ctx context.Context, sessionId string) ([]client.MessageInfo, error) {
 	resp, err := a.Client.PostSessionMessagesWithResponse(ctx, client.PostSessionMessagesJSONRequestBody{SessionID: sessionId})
 	if err != nil {

+ 148 - 14
packages/tui/internal/components/dialog/session.go

@@ -2,12 +2,20 @@ package dialog
 
 import (
 	"context"
+	"strings"
+
+	"slices"
 
 	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/lipgloss/v2"
+	"github.com/muesli/reflow/truncate"
 	"github.com/sst/opencode/internal/app"
 	"github.com/sst/opencode/internal/components/list"
 	"github.com/sst/opencode/internal/components/modal"
+	"github.com/sst/opencode/internal/components/toast"
 	"github.com/sst/opencode/internal/layout"
+	"github.com/sst/opencode/internal/styles"
+	"github.com/sst/opencode/internal/theme"
 	"github.com/sst/opencode/internal/util"
 	"github.com/sst/opencode/pkg/client"
 )
@@ -17,12 +25,65 @@ type SessionDialog interface {
 	layout.Modal
 }
 
+// sessionItem is a custom list item for sessions that can show delete confirmation
+type sessionItem struct {
+	title              string
+	isDeleteConfirming bool
+}
+
+func (s sessionItem) Render(selected bool, width int) string {
+	t := theme.CurrentTheme()
+	baseStyle := styles.BaseStyle()
+
+	var text string
+	if s.isDeleteConfirming {
+		text = "Press again to confirm delete"
+	} else {
+		text = s.title
+	}
+
+	truncatedStr := truncate.StringWithTail(text, uint(width-1), "...")
+
+	var itemStyle lipgloss.Style
+	if selected {
+		if s.isDeleteConfirming {
+			// Red background for delete confirmation
+			itemStyle = baseStyle.
+				Background(t.Error()).
+				Foreground(t.Background()).
+				Width(width).
+				PaddingLeft(1)
+		} else {
+			// Normal selection
+			itemStyle = baseStyle.
+				Background(t.Primary()).
+				Foreground(t.Background()).
+				Width(width).
+				PaddingLeft(1)
+		}
+	} else {
+		if s.isDeleteConfirming {
+			// Red text for delete confirmation when not selected
+			itemStyle = baseStyle.
+				Foreground(t.Error()).
+				PaddingLeft(1)
+		} else {
+			itemStyle = baseStyle.
+				PaddingLeft(1)
+		}
+	}
+
+	return itemStyle.Render(truncatedStr)
+}
+
 type sessionDialog struct {
-	width    int
-	height   int
-	modal    *modal.Modal
-	sessions []client.SessionInfo
-	list     list.List[list.StringItem]
+	width              int
+	height             int
+	modal              *modal.Modal
+	sessions           []client.SessionInfo
+	list               list.List[sessionItem]
+	app                *app.App
+	deleteConfirmation int // -1 means no confirmation, >= 0 means confirming deletion of session at this index
 }
 
 func (s *sessionDialog) Init() tea.Cmd {
@@ -38,6 +99,11 @@ func (s *sessionDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	case tea.KeyPressMsg:
 		switch msg.String() {
 		case "enter":
+			if s.deleteConfirmation >= 0 {
+				s.deleteConfirmation = -1
+				s.updateListItems()
+				return s, nil
+			}
 			if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) {
 				selectedSession := s.sessions[idx]
 				return s, tea.Sequence(
@@ -45,17 +111,79 @@ func (s *sessionDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 					util.CmdHandler(app.SessionSelectedMsg(&selectedSession)),
 				)
 			}
+		case "x", "delete", "backspace":
+			if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) {
+				if s.deleteConfirmation == idx {
+					// Second press - actually delete the session
+					sessionToDelete := s.sessions[idx]
+					return s, tea.Sequence(
+						func() tea.Msg {
+							s.sessions = slices.Delete(s.sessions, idx, idx+1)
+							s.deleteConfirmation = -1
+							s.updateListItems()
+							return nil
+						},
+						s.deleteSession(sessionToDelete.Id),
+					)
+				} else {
+					// First press - enter delete confirmation mode
+					s.deleteConfirmation = idx
+					s.updateListItems()
+					return s, nil
+				}
+			}
+		case "esc":
+			if s.deleteConfirmation >= 0 {
+				s.deleteConfirmation = -1
+				s.updateListItems()
+				return s, nil
+			}
 		}
 	}
 
 	var cmd tea.Cmd
 	listModel, cmd := s.list.Update(msg)
-	s.list = listModel.(list.List[list.StringItem])
+	s.list = listModel.(list.List[sessionItem])
 	return s, cmd
 }
 
 func (s *sessionDialog) Render(background string) string {
-	return s.modal.Render(s.list.View(), background)
+	listView := s.list.View()
+
+	t := theme.CurrentTheme()
+	helpStyle := styles.BaseStyle().PaddingLeft(1).PaddingTop(1)
+	helpText := styles.BaseStyle().Foreground(t.Text()).Render("x/del")
+	helpText = helpText + styles.BaseStyle().Background(t.BackgroundElement()).Foreground(t.TextMuted()).Render(" delete session")
+	helpText = helpStyle.Render(helpText)
+
+	content := strings.Join([]string{listView, helpText}, "\n")
+
+	return s.modal.Render(content, background)
+}
+
+func (s *sessionDialog) updateListItems() {
+	_, currentIdx := s.list.GetSelectedItem()
+
+	var items []sessionItem
+	for i, sess := range s.sessions {
+		item := sessionItem{
+			title:              sess.Title,
+			isDeleteConfirming: s.deleteConfirmation == i,
+		}
+		items = append(items, item)
+	}
+	s.list.SetItems(items)
+	s.list.SetSelectedIndex(currentIdx)
+}
+
+func (s *sessionDialog) deleteSession(sessionID string) tea.Cmd {
+	return func() tea.Msg {
+		ctx := context.Background()
+		if err := s.app.DeleteSession(ctx, sessionID); err != nil {
+			return toast.NewErrorToast("Failed to delete session: " + err.Error())()
+		}
+		return nil
+	}
 }
 
 func (s *sessionDialog) Close() tea.Cmd {
@@ -67,26 +195,32 @@ func NewSessionDialog(app *app.App) SessionDialog {
 	sessions, _ := app.ListSessions(context.Background())
 
 	var filteredSessions []client.SessionInfo
-	var sessionTitles []string
+	var items []sessionItem
 	for _, sess := range sessions {
 		if sess.ParentID != nil {
 			continue
 		}
 		filteredSessions = append(filteredSessions, sess)
-		sessionTitles = append(sessionTitles, sess.Title)
+		items = append(items, sessionItem{
+			title:              sess.Title,
+			isDeleteConfirming: false,
+		})
 	}
 
-	list := list.NewStringList(
-		sessionTitles,
+	// Create a generic list component
+	listComponent := list.NewListComponent(
+		items,
 		10, // maxVisibleSessions
 		"No sessions available",
 		true, // useAlphaNumericKeys
 	)
-	list.SetMaxWidth(layout.Current.Container.Width - 12)
+	listComponent.SetMaxWidth(layout.Current.Container.Width - 12)
 
 	return &sessionDialog{
-		sessions: filteredSessions,
-		list:     list,
+		sessions:           filteredSessions,
+		list:               listComponent,
+		app:                app,
+		deleteConfirmation: -1,
 		modal: modal.New(
 			modal.WithTitle("Switch Session"),
 			modal.WithMaxWidth(layout.Current.Container.Width-8),

+ 9 - 3
packages/tui/internal/tui/tui.go

@@ -261,6 +261,12 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			"opencode updated to "+msg.Properties.Version+", restart to apply.",
 			toast.WithTitle("New version installed"),
 		)
+	case client.EventSessionDeleted:
+		if a.app.Session != nil && msg.Properties.Info.Id == a.app.Session.Id {
+			a.app.Session = &client.SessionInfo{}
+			a.app.Messages = []client.MessageInfo{}
+		}
+		return a, toast.NewSuccessToast("Session deleted successfully")
 	case client.EventSessionUpdated:
 		if msg.Properties.Info.Id == a.app.Session.Id {
 			a.app.Session = &msg.Properties.Info
@@ -269,7 +275,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if msg.Properties.Info.Metadata.SessionID == a.app.Session.Id {
 			exists := false
 			optimisticReplaced := false
-			
+
 			// First check if this is replacing an optimistic message
 			if msg.Properties.Info.Role == client.User {
 				// Look for optimistic messages to replace
@@ -283,7 +289,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 					}
 				}
 			}
-			
+
 			// If not replacing optimistic, check for existing message with same ID
 			if !optimisticReplaced {
 				for i, m := range a.app.Messages {
@@ -294,7 +300,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 					}
 				}
 			}
-			
+
 			if !exists {
 				a.app.Messages = append(a.app.Messages, msg.Properties.Info)
 			}

+ 64 - 0
packages/tui/pkg/client/gen/openapi.json

@@ -363,6 +363,42 @@
         }
       }
     },
+    "/session_delete": {
+      "post": {
+        "responses": {
+          "200": {
+            "description": "Successfully deleted session",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "boolean"
+                }
+              }
+            }
+          }
+        },
+        "operationId": "postSession_delete",
+        "parameters": [],
+        "description": "Delete a session and all its data",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "sessionID": {
+                    "type": "string"
+                  }
+                },
+                "required": [
+                  "sessionID"
+                ]
+              }
+            }
+          }
+        }
+      }
+    },
     "/session_summarize": {
       "post": {
         "responses": {
@@ -579,6 +615,9 @@
           {
             "$ref": "#/components/schemas/Event.session.updated"
           },
+          {
+            "$ref": "#/components/schemas/Event.session.deleted"
+          },
           {
             "$ref": "#/components/schemas/Event.session.error"
           }
@@ -593,6 +632,7 @@
             "message.updated": "#/components/schemas/Event.message.updated",
             "message.part.updated": "#/components/schemas/Event.message.part.updated",
             "session.updated": "#/components/schemas/Event.session.updated",
+            "session.deleted": "#/components/schemas/Event.session.deleted",
             "session.error": "#/components/schemas/Event.session.error"
           }
         }
@@ -1339,6 +1379,30 @@
           "time"
         ]
       },
+      "Event.session.deleted": {
+        "type": "object",
+        "properties": {
+          "type": {
+            "type": "string",
+            "const": "session.deleted"
+          },
+          "properties": {
+            "type": "object",
+            "properties": {
+              "info": {
+                "$ref": "#/components/schemas/session.info"
+              }
+            },
+            "required": [
+              "info"
+            ]
+          }
+        },
+        "required": [
+          "type",
+          "properties"
+        ]
+      },
       "Event.session.error": {
         "type": "object",
         "properties": {

+ 185 - 0
packages/tui/pkg/client/generated-client.go

@@ -254,6 +254,14 @@ type EventPermissionUpdated struct {
 	Type       string         `json:"type"`
 }
 
+// EventSessionDeleted defines model for Event.session.deleted.
+type EventSessionDeleted struct {
+	Properties struct {
+		Info SessionInfo `json:"info"`
+	} `json:"properties"`
+	Type string `json:"type"`
+}
+
 // EventSessionError defines model for Event.session.error.
 type EventSessionError struct {
 	Properties struct {
@@ -518,6 +526,11 @@ type PostSessionChatJSONBody struct {
 	SessionID  string        `json:"sessionID"`
 }
 
+// PostSessionDeleteJSONBody defines parameters for PostSessionDelete.
+type PostSessionDeleteJSONBody struct {
+	SessionID string `json:"sessionID"`
+}
+
 // PostSessionInitializeJSONBody defines parameters for PostSessionInitialize.
 type PostSessionInitializeJSONBody struct {
 	ModelID    string `json:"modelID"`
@@ -556,6 +569,9 @@ type PostSessionAbortJSONRequestBody PostSessionAbortJSONBody
 // PostSessionChatJSONRequestBody defines body for PostSessionChat for application/json ContentType.
 type PostSessionChatJSONRequestBody PostSessionChatJSONBody
 
+// PostSessionDeleteJSONRequestBody defines body for PostSessionDelete for application/json ContentType.
+type PostSessionDeleteJSONRequestBody PostSessionDeleteJSONBody
+
 // PostSessionInitializeJSONRequestBody defines body for PostSessionInitialize for application/json ContentType.
 type PostSessionInitializeJSONRequestBody PostSessionInitializeJSONBody
 
@@ -935,6 +951,34 @@ func (t *Event) MergeEventSessionUpdated(v EventSessionUpdated) error {
 	return err
 }
 
+// AsEventSessionDeleted returns the union data inside the Event as a EventSessionDeleted
+func (t Event) AsEventSessionDeleted() (EventSessionDeleted, error) {
+	var body EventSessionDeleted
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+// FromEventSessionDeleted overwrites any union data inside the Event as the provided EventSessionDeleted
+func (t *Event) FromEventSessionDeleted(v EventSessionDeleted) error {
+	v.Type = "session.deleted"
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+// MergeEventSessionDeleted performs a merge with any union data inside the Event, using the provided EventSessionDeleted
+func (t *Event) MergeEventSessionDeleted(v EventSessionDeleted) error {
+	v.Type = "session.deleted"
+	b, err := json.Marshal(v)
+	if err != nil {
+		return err
+	}
+
+	merged, err := runtime.JSONMerge(t.union, b)
+	t.union = merged
+	return err
+}
+
 // AsEventSessionError returns the union data inside the Event as a EventSessionError
 func (t Event) AsEventSessionError() (EventSessionError, error) {
 	var body EventSessionError
@@ -987,6 +1031,8 @@ func (t Event) ValueByDiscriminator() (interface{}, error) {
 		return t.AsEventMessageUpdated()
 	case "permission.updated":
 		return t.AsEventPermissionUpdated()
+	case "session.deleted":
+		return t.AsEventSessionDeleted()
 	case "session.error":
 		return t.AsEventSessionError()
 	case "session.updated":
@@ -1626,6 +1672,11 @@ type ClientInterface interface {
 	// PostSessionCreate request
 	PostSessionCreate(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
 
+	// PostSessionDeleteWithBody request with any body
+	PostSessionDeleteWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+	PostSessionDelete(ctx context.Context, body PostSessionDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
+
 	// PostSessionInitializeWithBody request with any body
 	PostSessionInitializeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
 
@@ -1823,6 +1874,30 @@ func (c *Client) PostSessionCreate(ctx context.Context, reqEditors ...RequestEdi
 	return c.Client.Do(req)
 }
 
+func (c *Client) PostSessionDeleteWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewPostSessionDeleteRequestWithBody(c.Server, contentType, body)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
+func (c *Client) PostSessionDelete(ctx context.Context, body PostSessionDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewPostSessionDeleteRequest(c.Server, body)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
 func (c *Client) PostSessionInitializeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
 	req, err := NewPostSessionInitializeRequestWithBody(c.Server, contentType, body)
 	if err != nil {
@@ -2291,6 +2366,46 @@ func NewPostSessionCreateRequest(server string) (*http.Request, error) {
 	return req, nil
 }
 
+// NewPostSessionDeleteRequest calls the generic PostSessionDelete builder with application/json body
+func NewPostSessionDeleteRequest(server string, body PostSessionDeleteJSONRequestBody) (*http.Request, error) {
+	var bodyReader io.Reader
+	buf, err := json.Marshal(body)
+	if err != nil {
+		return nil, err
+	}
+	bodyReader = bytes.NewReader(buf)
+	return NewPostSessionDeleteRequestWithBody(server, "application/json", bodyReader)
+}
+
+// NewPostSessionDeleteRequestWithBody generates requests for PostSessionDelete with any type of body
+func NewPostSessionDeleteRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
+	var err error
+
+	serverURL, err := url.Parse(server)
+	if err != nil {
+		return nil, err
+	}
+
+	operationPath := fmt.Sprintf("/session_delete")
+	if operationPath[0] == '/' {
+		operationPath = "." + operationPath
+	}
+
+	queryURL, err := serverURL.Parse(operationPath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest("POST", queryURL.String(), body)
+	if err != nil {
+		return nil, err
+	}
+
+	req.Header.Add("Content-Type", contentType)
+
+	return req, nil
+}
+
 // NewPostSessionInitializeRequest calls the generic PostSessionInitialize builder with application/json body
 func NewPostSessionInitializeRequest(server string, body PostSessionInitializeJSONRequestBody) (*http.Request, error) {
 	var bodyReader io.Reader
@@ -2600,6 +2715,11 @@ type ClientWithResponsesInterface interface {
 	// PostSessionCreateWithResponse request
 	PostSessionCreateWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostSessionCreateResponse, error)
 
+	// PostSessionDeleteWithBodyWithResponse request with any body
+	PostSessionDeleteWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSessionDeleteResponse, error)
+
+	PostSessionDeleteWithResponse(ctx context.Context, body PostSessionDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSessionDeleteResponse, error)
+
 	// PostSessionInitializeWithBodyWithResponse request with any body
 	PostSessionInitializeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSessionInitializeResponse, error)
 
@@ -2880,6 +3000,28 @@ func (r PostSessionCreateResponse) StatusCode() int {
 	return 0
 }
 
+type PostSessionDeleteResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *bool
+}
+
+// Status returns HTTPResponse.Status
+func (r PostSessionDeleteResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r PostSessionDeleteResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
 type PostSessionInitializeResponse struct {
 	Body         []byte
 	HTTPResponse *http.Response
@@ -3135,6 +3277,23 @@ func (c *ClientWithResponses) PostSessionCreateWithResponse(ctx context.Context,
 	return ParsePostSessionCreateResponse(rsp)
 }
 
+// PostSessionDeleteWithBodyWithResponse request with arbitrary body returning *PostSessionDeleteResponse
+func (c *ClientWithResponses) PostSessionDeleteWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSessionDeleteResponse, error) {
+	rsp, err := c.PostSessionDeleteWithBody(ctx, contentType, body, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParsePostSessionDeleteResponse(rsp)
+}
+
+func (c *ClientWithResponses) PostSessionDeleteWithResponse(ctx context.Context, body PostSessionDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSessionDeleteResponse, error) {
+	rsp, err := c.PostSessionDelete(ctx, body, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParsePostSessionDeleteResponse(rsp)
+}
+
 // PostSessionInitializeWithBodyWithResponse request with arbitrary body returning *PostSessionInitializeResponse
 func (c *ClientWithResponses) PostSessionInitializeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSessionInitializeResponse, error) {
 	rsp, err := c.PostSessionInitializeWithBody(ctx, contentType, body, reqEditors...)
@@ -3530,6 +3689,32 @@ func ParsePostSessionCreateResponse(rsp *http.Response) (*PostSessionCreateRespo
 	return response, nil
 }
 
+// ParsePostSessionDeleteResponse parses an HTTP response from a PostSessionDeleteWithResponse call
+func ParsePostSessionDeleteResponse(rsp *http.Response) (*PostSessionDeleteResponse, error) {
+	bodyBytes, err := io.ReadAll(rsp.Body)
+	defer func() { _ = rsp.Body.Close() }()
+	if err != nil {
+		return nil, err
+	}
+
+	response := &PostSessionDeleteResponse{
+		Body:         bodyBytes,
+		HTTPResponse: rsp,
+	}
+
+	switch {
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+		var dest bool
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON200 = &dest
+
+	}
+
+	return response, nil
+}
+
 // ParsePostSessionInitializeResponse parses an HTTP response from a PostSessionInitializeWithResponse call
 func ParsePostSessionInitializeResponse(rsp *http.Response) (*PostSessionInitializeResponse, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)