返回 DeepSeek-Reasonix
session_source_resolution.go
根目录 / desktop / session_source_resolution.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "reasonix/internal/agent"
7 "reasonix/internal/session"
8 "strings"
9 )
10
11 func (a *App) resolveSourceSessionTarget(selector SessionSelector, allowArchived bool) (SessionTarget, error) {
12 source := selector.Source
13 if source.HostID != "" && source.HostID != localDesktopHostID {
14 return SessionTarget{}, newSessionOperationError("unsupported", "This source belongs to another host.")
15 }
16 if strings.TrimSpace(source.Path) == "" {
17 return SessionTarget{}, newSessionOperationError("target_not_found", "The source no longer exists.")
18 }
19 key := desktopSourceKey(source.Path, source.HeadID)
20 if source.SourceKey != "" && source.SourceKey != key {
21 return SessionTarget{}, newSessionOperationError("target_changed", "The source identity changed.")
22 }
23 state, err := a.workspaceRegistry().Load(a.bootContext())
24 if err != nil {
25 return SessionTarget{}, err
26 }
27 if mapping, ok := state.SourceMappings[key]; ok {
28 return a.resolveCanonicalSessionTargetState(session.SessionRef{HostID: localDesktopHostID, SessionID: mapping.SessionID}, selector.TopicID, allowArchived)
29 }
30 if source.HeadID == "" {
31 canonical, _ := a.desktopHistoricalRoots()
32 for _, root := range canonical {
33 if !sameDesktopPath(filepath.Dir(source.Path), root.root) {
34 continue
35 }
36 info, statErr := os.Lstat(source.Path)
37 pending := pendingHistoricalOperation(state, key)
38 if statErr != nil && !(os.IsNotExist(statErr) && pending != nil) {
39 return SessionTarget{}, newSessionOperationError("target_not_found", "The historical source is unavailable.")
40 }
41 if info != nil && (!info.IsDir() || info.Mode()&os.ModeSymlink != 0) {
42 return SessionTarget{}, newSessionOperationError("target_not_found", "The historical source is not a session directory.")
43 }
44 copy := *source
45 copy.HostID, copy.SourceKey = localDesktopHostID, key
46 return SessionTarget{Source: &copy, SessionPath: source.Path, TopicID: selector.TopicID,
47 Scope: root.scope, WorkspaceRoot: root.workspaceRoot}, nil
48 }
49 }
50 if source.HeadID != "" {
51 dir, validated, err := a.sessionDirForPath(source.Path)
52 if err != nil {
53 return SessionTarget{}, err
54 }
55 if _, _, err = validateSessionPath(dir, validated); err != nil {
56 return SessionTarget{}, err
57 }
58 heads, err := agent.ListSessionHeads(validated)
59 if err != nil {
60 return SessionTarget{}, err
61 }
62 found := false
63 for _, head := range heads {
64 if head.ID == source.HeadID && !head.Retired {
65 found = true
66 }
67 }
68 if !found {
69 return SessionTarget{}, newSessionOperationError("target_not_found", "This historical head is no longer available.")
70 }
71 copy := *source
72 copy.Path, copy.HostID, copy.SourceKey = validated, localDesktopHostID, key
73 target := SessionTarget{Source: &copy, SessionPath: validated, Scope: "global"}
74 for _, project := range loadProjectsFile().Projects {
75 if canonicalRuntimeRoot(dir) == canonicalRuntimeRoot(desktopSessionDir(project.Root)) {
76 target.Scope, target.WorkspaceRoot = "project", project.Root
77 break
78 }
79 }
80 if meta, ok, err := agent.LoadBranchMeta(validated); err == nil && ok {
81 target.TopicID = meta.TopicID
82 if meta.Scope != "" {
83 target.Scope, target.WorkspaceRoot = meta.Scope, meta.WorkspaceRoot
84 }
85 }
86 return target, nil
87 }
88 return a.resolveLegacySessionTarget(source.Path, selector.TopicID, allowArchived)
89 }
90
90 lines GO