| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "path/filepath" |
| 6 | "strings" |
| 7 | |
| 8 | "reasonix/internal/config" |
| 9 | "reasonix/internal/session" |
| 10 | ) |
| 11 | |
| 12 | const embeddedBotSessionPrefix = "bot-session:local:" |
| 13 | |
| 14 | // Embedded bot conversations keep their existing project-local store. Resolve |
| 15 | // the sidebar's stable session reference there before creating a surface; a |
| 16 | // forged reference must not open a session belonging to another workspace. |
| 17 | func (a *App) embeddedBotSessionPath(scope, workspaceRoot, route string) (string, error) { |
| 18 | id := strings.TrimPrefix(route, embeddedBotSessionPrefix) |
| 19 | if err := session.ValidateSessionID(id); err != nil { |
| 20 | return "", err |
| 21 | } |
| 22 | root := globalTabWorkspaceRoot() |
| 23 | if scope == "project" { |
| 24 | root = normalizeProjectRoot(workspaceRoot) |
| 25 | if root == "" { |
| 26 | return "", fmt.Errorf("workspaceRoot is required") |
| 27 | } |
| 28 | } |
| 29 | storeRoot := session.RootForLegacyDir(config.ProjectSessionDir(root)) |
| 30 | service, err := a.historicalSessionService(storeRoot) |
| 31 | if err != nil { |
| 32 | return "", err |
| 33 | } |
| 34 | info, err := service.Query().Stat(a.bootContext(), session.SessionRef{HostID: localDesktopHostID, SessionID: id}) |
| 35 | if err != nil { |
| 36 | return "", err |
| 37 | } |
| 38 | same, err := sameDesktopPathStrict(info.CWD, root) |
| 39 | if err != nil { |
| 40 | return "", err |
| 41 | } |
| 42 | if !same { |
| 43 | return "", errSessionWorkspaceConflict |
| 44 | } |
| 45 | path := filepath.Join(storeRoot, id) |
| 46 | if !nativeStoredDirectory(path) { |
| 47 | return "", session.ErrSessionNotFound |
| 48 | } |
| 49 | return path, nil |
| 50 | } |
| 51 |