返回 DeepSeek-Reasonix
goal_app.go
根目录 / desktop / goal_app.go
1 package main
2
3 import (
4 "strings"
5
6 "reasonix/internal/control"
7 )
8
9 func (a *App) SetGoal(goal string) error { return a.SetGoalForTab("", goal) }
10
11 // SetGoalForTab activates or clears a Goal only after the session accepts it.
12 func (a *App) SetGoalForTab(tabID, goal string) error {
13 tab := a.tabByID(tabID)
14 if tab == nil {
15 return a.workspaceNotReadyErr(nil)
16 }
17 tab.turnStartMu.Lock()
18 defer tab.turnStartMu.Unlock()
19 goal = strings.TrimSpace(goal)
20 approvalMode := a.tabRuntimeSnapshot(tab).currentToolApprovalMode()
21 a.mu.Lock()
22 if a.tabs[tab.ID] != tab {
23 a.mu.Unlock()
24 return a.workspaceNotReadyErr(nil)
25 }
26 ctrl := tab.Ctrl
27 plan := tabModeHasPlan(tab.mode) && goal == ""
28 a.mu.Unlock()
29 if ctrl != nil {
30 if err := syncTabGoalToController(ctrl, goal); err != nil {
31 return err
32 }
33 ctrl.SetPlanMode(plan)
34 }
35 a.mu.Lock()
36 if a.tabs[tab.ID] == tab {
37 tab.goal = goal
38 if goal != "" {
39 tab.mode = tabModeFromAxes(false, approvalMode == control.ToolApprovalYolo)
40 }
41 a.saveTabsLocked()
42 }
43 a.mu.Unlock()
44 return nil
45 }
46
47 // Composer re-sync is idempotent for an already running Goal, while re-entering
48 // the same text after a terminal state starts a new lifecycle.
49 func syncTabGoalToController(ctrl control.SessionAPI, goal string) error {
50 if ctrl == nil {
51 return nil
52 }
53 goal = strings.TrimSpace(goal)
54 if goal != "" && strings.TrimSpace(ctrl.Goal()) == goal && ctrl.GoalStatus() == control.GoalStatusRunning {
55 return nil
56 }
57 return ctrl.SetGoalDurable(goal)
58 }
59
60 func (a *App) ClearGoalForTab(tabID string) error { return a.SetGoalForTab(tabID, "") }
61
62 // EditGoalForTab preserves lifecycle identity and admitted rounds. A nil limit
63 // means unlimited rounds.
64 func (a *App) EditGoalForTab(tabID, objective string, maxGoalRounds *uint64) error {
65 tab := a.tabByID(tabID)
66 if tab == nil {
67 return a.workspaceNotReadyErr(nil)
68 }
69 tab.turnStartMu.Lock()
70 defer tab.turnStartMu.Unlock()
71 objective = strings.TrimSpace(objective)
72 a.mu.Lock()
73 if a.tabs[tab.ID] != tab {
74 a.mu.Unlock()
75 return a.workspaceNotReadyErr(nil)
76 }
77 ctrl := tab.Ctrl
78 a.mu.Unlock()
79 if ctrl == nil {
80 return a.workspaceNotReadyErr(nil)
81 }
82 if err := ctrl.EditGoalDurable(objective, maxGoalRounds); err != nil {
83 return err
84 }
85 a.mu.Lock()
86 if a.tabs[tab.ID] == tab {
87 tab.goal = objective
88 a.saveTabsLocked()
89 }
90 a.mu.Unlock()
91 return nil
92 }
93
94 func (a *App) ResumeGoalForTab(tabID string) bool {
95 tab := a.tabByID(tabID)
96 if tab == nil {
97 return false
98 }
99 tab.turnStartMu.Lock()
100 defer tab.turnStartMu.Unlock()
101 ctrl := a.controllerForTab(tab)
102 if ctrl == nil || !ctrl.ResumeGoal() {
103 return false
104 }
105 a.mu.Lock()
106 if a.tabs[tab.ID] == tab {
107 tab.goal = strings.TrimSpace(ctrl.Goal())
108 a.saveTabsLocked()
109 }
110 a.mu.Unlock()
111 return true
112 }
113
114 func (a *App) PauseGoalForTab(tabID string) bool {
115 tab := a.tabByID(tabID)
116 if tab == nil {
117 return false
118 }
119 tab.turnStartMu.Lock()
120 defer tab.turnStartMu.Unlock()
121 ctrl := a.controllerForTab(tab)
122 return ctrl != nil && ctrl.PauseGoal()
123 }
124
124 lines GO