返回 DeepSeek-Reasonix
session_recovery_pinned.go
根目录 / desktop / session_recovery_pinned.go
1 package main
2
3 import (
4 "fmt"
5 "strings"
6
7 "reasonix/internal/agent"
8 "reasonix/internal/control"
9 )
10
11 func (a *App) handleTabSessionRecovered(tab *WorkspaceTab) func(control.SessionRecoveryInfo) error {
12 return func(info control.SessionRecoveryInfo) error {
13 if strings.TrimSpace(info.RecoveryPath) == "" {
14 return nil
15 }
16 if err := copyPinnedContextState(info.OriginalPath, info.RecoveryPath); err != nil {
17 return fmt.Errorf("copy pinned context to recovery session: %w", err)
18 }
19 pinnedState, err := loadPinnedContextState(info.RecoveryPath)
20 if err != nil {
21 return fmt.Errorf("load recovery pinned context: %w", err)
22 }
23 if err := a.handoffTabRecoveryLease(tab, info.RecoveryPath); err != nil {
24 return err
25 }
26 meta := info.Meta
27 scope := strings.TrimSpace(meta.Scope)
28 if scope != "project" {
29 scope = "global"
30 }
31 workspaceRoot := strings.TrimSpace(meta.WorkspaceRoot)
32 if scope == "global" {
33 workspaceRoot = ""
34 }
35 invalidateTopicSessionIndexForPath(info.RecoveryPath)
36 a.mu.Lock()
37 if tab != nil && !tab.removed {
38 oldKey := sessionRuntimeKey(info.OriginalPath)
39 newKey := sessionRuntimeKey(info.RecoveryPath)
40 if oldKey != "" && newKey != "" && a.detachedSessions[oldKey] == tab {
41 delete(a.detachedSessions, oldKey)
42 a.ensureDetachedSessionsLocked()
43 a.detachedSessions[newKey] = tab
44 }
45 tab.SessionPath = canonicalTabSessionPath(info.RecoveryPath)
46 if a.tabs[tab.ID] == tab {
47 a.saveTabsLocked()
48 }
49 }
50 a.mu.Unlock()
51 info.OnCommit(func() {
52 if tab != nil {
53 tab.setPinnedFiles(pinnedState.Files)
54 }
55 })
56 // The fork continues the same conversation, so its cost history moves
57 // with it: re-key the in-memory telemetry from the original session to
58 // the recovery path and persist the sidecar right away.
59 if tab != nil && !tab.removed {
60 origKey := sessionRuntimeKey(info.OriginalPath)
61 newKey := sessionRuntimeKey(info.RecoveryPath)
62 carried := false
63 if newKey != "" {
64 tab.telemMu.Lock()
65 if tab.telemetrySessionKey == origKey || tab.telemetrySessionKey == "" {
66 tab.telemetrySessionKey = newKey
67 carried = true
68 }
69 tab.telemMu.Unlock()
70 }
71 if carried {
72 _ = saveTelemetry(info.RecoveryPath+".telemetry.json", tab.telemetrySnapshot())
73 }
74 }
75 a.emitSessionRecoveredAndRefresh(sessionDirectoryForPath(info.RecoveryPath), sessionRecoveryEvent{
76 ConversationID: meta.TopicID,
77 ActiveVersionID: agent.BranchID(info.RecoveryPath),
78 RecoveryVersionID: agent.BranchID(info.RecoveryPath),
79 OriginalPath: info.OriginalPath,
80 RecoveryPath: info.RecoveryPath,
81 Scope: scope,
82 WorkspaceRoot: workspaceRoot,
83 TopicID: meta.TopicID,
84 TopicTitle: meta.TopicTitle,
85 RecoveryReason: meta.RecoveryReason,
86 RecoveryDigest: meta.RecoveryDigest,
87 RecoveryParentID: string(meta.ParentID),
88 Existing: info.Existing,
89 BaseRevision: info.BaseRevision,
90 DiskRevision: info.DiskRevision,
91 CanContinue: true,
92 RequiresChoice: false,
93 })
94 a.invalidatePromptHistoryCache()
95 return nil
96 }
97 }
98
98 lines GO