返回 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/config"
8 "reasonix/internal/session"
9 "slices"
10 "strings"
11 )
12
13 func (a *App) resolveSourceSessionTarget(selector SessionSelector, allowArchived bool) (SessionTarget, error) {
14 source := selector.Source
15 if source.HostID != "" && source.HostID != localDesktopHostID {
16 return SessionTarget{}, newSessionOperationError("unsupported", "This source belongs to another host.")
17 }
18 if strings.TrimSpace(source.Path) == "" {
19 return SessionTarget{}, newSessionOperationError("target_not_found", "The source no longer exists.")
20 }
21 key := desktopSourceKey(source.Path, source.HeadID)
22 state, err := a.workspaceRegistry().Load(a.bootContext())
23 if err != nil {
24 return SessionTarget{}, err
25 }
26 if source.SourceKey != "" && !slices.Contains(state.SourceKeys(source.SourceKey), key) {
27 return SessionTarget{}, newSessionOperationError("target_changed", "The source identity changed.")
28 }
29 mapping, ok, err := state.ResolveSource(key)
30 if err != nil {
31 return SessionTarget{}, err
32 }
33 if ok {
34 return a.resolveCanonicalSessionTargetState(session.SessionRef{HostID: localDesktopHostID, SessionID: mapping.SessionID}, selector.TopicID, allowArchived)
35 }
36 if source.HeadID == "" {
37 canonical, _ := a.desktopHistoricalRoots()
38 for _, root := range canonical {
39 if !sameDesktopPath(filepath.Dir(source.Path), root.root) {
40 continue
41 }
42 info, statErr := os.Lstat(source.Path)
43 pending := pendingHistoricalOperation(state, key)
44 if statErr != nil && !(os.IsNotExist(statErr) && pending != nil) {
45 return SessionTarget{}, newSessionOperationError("target_not_found", "The historical source is unavailable.")
46 }
47 if info != nil && (!info.IsDir() || info.Mode()&os.ModeSymlink != 0) {
48 return SessionTarget{}, newSessionOperationError("target_not_found", "The historical source is not a session directory.")
49 }
50 copy := *source
51 copy.HostID, copy.SourceKey = localDesktopHostID, key
52 return SessionTarget{Source: &copy, SessionPath: source.Path, TopicID: selector.TopicID,
53 Scope: root.scope, WorkspaceRoot: root.workspaceRoot}, nil
54 }
55 }
56 if source.HeadID != "" {
57 target, err := a.resolveHistoricalHeadTarget(*source, key)
58 return recoverHistoricalRuntimeOwner(target), err
59 }
60 target, err := a.resolveLegacySessionTarget(source.Path, selector.TopicID, allowArchived)
61 if err == nil && target.SessionRef.SessionID == "" {
62 copy := *source
63 copy.HostID, copy.SourceKey = localDesktopHostID, key
64 target.Source = &copy
65 target = recoverHistoricalRuntimeOwner(target)
66 }
67 return target, err
68 }
69
70 // Global legacy storage is independent of old project metadata. If that
71 // metadata names a removed project, recover its actual global storage owner.
72 // A source inside a project never gains authority to run in another project.
73 func recoverHistoricalRuntimeOwner(target SessionTarget) SessionTarget {
74 if target.Scope != "project" || target.Source == nil {
75 return target
76 }
77 if info, err := os.Stat(target.WorkspaceRoot); err == nil && info.IsDir() || err != nil && !os.IsNotExist(err) {
78 return target
79 }
80 dir := filepath.Dir(target.Source.Path)
81 if sameDesktopPath(dir, config.SessionDir()) || sameDesktopPath(dir, desktopSessionDir(globalWorkspaceRoot())) {
82 target.Scope, target.WorkspaceRoot = "global", ""
83 }
84 return target
85 }
86
87 func (a *App) resolveHistoricalHeadTarget(source SessionSourceRef, key string) (SessionTarget, error) {
88 dir, validated, err := a.sessionDirForPath(source.Path)
89 if err != nil {
90 return SessionTarget{}, err
91 }
92 if _, _, err = validateSessionPath(dir, validated); err != nil {
93 return SessionTarget{}, err
94 }
95 heads, err := agent.ListSessionHeads(validated)
96 if err != nil {
97 return SessionTarget{}, err
98 }
99 found := false
100 for _, head := range heads {
101 if head.ID == source.HeadID && !head.Retired {
102 found = true
103 }
104 }
105 if !found {
106 return SessionTarget{}, newSessionOperationError("target_not_found", "This historical head is no longer available.")
107 }
108 source.Path, source.HostID, source.SourceKey = validated, localDesktopHostID, key
109 target := SessionTarget{Source: &source, SessionPath: validated, Scope: "global"}
110 for _, project := range loadProjectsFile().Projects {
111 if canonicalRuntimeRoot(dir) == canonicalRuntimeRoot(desktopSessionDir(project.Root)) {
112 target.Scope, target.WorkspaceRoot = "project", project.Root
113 break
114 }
115 }
116 if meta, ok, err := agent.LoadBranchMeta(validated); err == nil && ok {
117 target.TopicID = meta.TopicID
118 if meta.Scope != "" {
119 target.Scope, target.WorkspaceRoot = meta.Scope, meta.WorkspaceRoot
120 }
121 }
122 return target, nil
123 }
124
124 lines GO