返回 DeepSeek-Reasonix
tab_profile_test.go
根目录 / desktop / tab_profile_test.go
1 package main
2
3 import (
4 "context"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9
10 "reasonix/internal/boot"
11 "reasonix/internal/control"
12 )
13
14 func testTab(id, root string) *WorkspaceTab {
15 return &WorkspaceTab{
16 ID: id,
17 Scope: "project",
18 WorkspaceRoot: root,
19 Ready: true,
20 Ctrl: control.New(control.Options{Label: id}),
21 model: "deepseek-flash/deepseek-v4-flash",
22 mode: "normal",
23 disabledMCP: map[string]ServerView{},
24 }
25 }
26
27 func TestSetEffortForTabIsTabLocal(t *testing.T) {
28 isolateDesktopUserDirs(t)
29
30 rootA := t.TempDir()
31 rootB := t.TempDir()
32 app := NewApp()
33 app.ctx = context.Background()
34 app.readyHook = func() {}
35 tabA := testTab("a", rootA)
36 tabB := testTab("b", rootB)
37 tabA.sink = &tabEventSink{tabID: tabA.ID, app: app}
38 tabB.sink = &tabEventSink{tabID: tabB.ID, app: app}
39 app.tabs = map[string]*WorkspaceTab{tabA.ID: tabA, tabB.ID: tabB}
40 app.tabOrder = []string{tabA.ID, tabB.ID}
41 app.activeTabID = tabA.ID
42 defer func() {
43 for _, tab := range app.tabs {
44 if tab.Ctrl != nil {
45 tab.Ctrl.Close()
46 }
47 }
48 }()
49
50 if err := app.SetEffortForTab(tabA.ID, "max"); err != nil {
51 t.Fatalf("SetEffortForTab: %v", err)
52 }
53 if got := app.EffortForTab(tabA.ID).Current; got != "max" {
54 t.Fatalf("tab A effort = %q, want max", got)
55 }
56 if got := app.EffortForTab(tabB.ID).Current; got != "auto" {
57 t.Fatalf("tab B effort = %q, want auto", got)
58 }
59 if tabB.effort != nil {
60 t.Fatalf("tab B stored effort = %q, want nil", *tabB.effort)
61 }
62 body, err := os.ReadFile(userConfigPathForTest())
63 if err == nil && strings.Contains(string(body), `effort`) {
64 t.Fatalf("tab-local effort should not write provider config:\n%s", body)
65 }
66 }
67
68 func TestEffortForTabResolvesProjectProviderConfig(t *testing.T) {
69 isolateDesktopUserDirs(t)
70
71 projectRoot := t.TempDir()
72 configBody := `default_model = "project-provider/deepseek-v4-flash"
73 [[providers]]
74 name = "project-provider"
75 kind = "openai"
76 base_url = "https://api.deepseek.com"
77 model = "deepseek-v4-flash"
78 api_key_env = "PROJECT_API_KEY"
79 effort = "max"
80 `
81 if err := os.WriteFile(filepath.Join(projectRoot, "reasonix.toml"), []byte(configBody), 0o644); err != nil {
82 t.Fatal(err)
83 }
84
85 app := NewApp()
86 tab := testTab("project", projectRoot)
87 tab.model = "project-provider/deepseek-v4-flash"
88 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
89 app.activeTabID = tab.ID
90 defer tab.Ctrl.Close()
91
92 got := app.EffortForTab(tab.ID)
93 if !got.Supported || got.Current != "max" {
94 t.Fatalf("EffortForTab project config = %+v, want supported max", got)
95 }
96 }
97
98 func TestEffortForTabUsesKnownModelRegistry(t *testing.T) {
99 isolateDesktopUserDirs(t)
100
101 projectRoot := t.TempDir()
102 configBody := `default_model = "project-provider/deepseek-v4-flash"
103 [[providers]]
104 name = "project-provider"
105 kind = "openai"
106 base_url = "https://proxy.example.com/v1"
107 model = "deepseek-v4-flash"
108 api_key_env = "PROJECT_API_KEY"
109 `
110 if err := os.WriteFile(filepath.Join(projectRoot, "reasonix.toml"), []byte(configBody), 0o644); err != nil {
111 t.Fatal(err)
112 }
113
114 app := NewApp()
115 tab := testTab("project", projectRoot)
116 tab.model = "project-provider/deepseek-v4-flash"
117 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
118 app.activeTabID = tab.ID
119 defer tab.Ctrl.Close()
120
121 got := app.EffortForTab(tab.ID)
122 if !got.Supported || got.Current != "auto" || got.Default != "high" {
123 t.Fatalf("EffortForTab model registry = %+v, want supported auto/high", got)
124 }
125 wantLevels := []string{"auto", "disabled", "low", "high", "max"}
126 if len(got.Levels) != len(wantLevels) {
127 t.Fatalf("levels = %v, want %v", got.Levels, wantLevels)
128 }
129 for i, want := range wantLevels {
130 if got.Levels[i] != want {
131 t.Fatalf("levels[%d] = %q, want %q", i, got.Levels[i], want)
132 }
133 }
134 }
135
136 func TestSaveTabsPersistsModelAndEffort(t *testing.T) {
137 isolateDesktopUserDirs(t)
138
139 effort := "max"
140 app := NewApp()
141 tab := testTab("a", t.TempDir())
142 tab.effort = &effort
143 tab.model = "deepseek/deepseek-v4-pro"
144 tab.mode = "plan"
145 tab.Ctrl.SetPlanMode(true)
146 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
147 app.tabOrder = []string{tab.ID}
148 app.activeTabID = tab.ID
149
150 app.mu.Lock()
151 app.saveTabsLocked()
152 app.mu.Unlock()
153
154 got := loadTabsFile()
155 if len(got.Tabs) != 1 {
156 t.Fatalf("tabs len = %d, want 1", len(got.Tabs))
157 }
158 if got.Tabs[0].Model != tab.model {
159 t.Fatalf("saved model = %q, want %q", got.Tabs[0].Model, tab.model)
160 }
161 if got.Tabs[0].Effort == nil || *got.Tabs[0].Effort != effort {
162 t.Fatalf("saved effort = %#v, want %q", got.Tabs[0].Effort, effort)
163 }
164 if got.Tabs[0].Mode != "plan" {
165 t.Fatalf("saved mode = %q, want plan", got.Tabs[0].Mode)
166 }
167 }
168
169 func TestBuildTabControllerMigratesPersistedBareMimoModel(t *testing.T) {
170 isolateDesktopUserDirs(t)
171
172 root := t.TempDir()
173 configBody := `default_model = "deepseek-flash"
174 [[providers]]
175 name = "deepseek-flash"
176 kind = "openai"
177 base_url = "https://example.invalid/v1"
178 model = "deepseek-v4-flash"
179 api_key_env = "REASONIX_TEST_KEY_UNSET"
180 `
181 if err := os.WriteFile(filepath.Join(root, "reasonix.toml"), []byte(configBody), 0o644); err != nil {
182 t.Fatal(err)
183 }
184
185 app := NewApp()
186 app.readyHook = func() {}
187 tab := testTab("mimo", root)
188 tab.model = "mimo-v2.5-pro"
189 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
190 app.tabOrder = []string{tab.ID}
191 app.activeTabID = tab.ID
192
193 app.buildTabController(tab)
194 if tab.StartupErr != "" {
195 t.Fatalf("tab startup error = %q", tab.StartupErr)
196 }
197 defer tab.Ctrl.Close()
198 if tab.model != "mimo-pro/mimo-v2.5-pro" {
199 t.Fatalf("tab model = %q, want migrated MiMo provider ref", tab.model)
200 }
201 }
202
203 func TestSaveTabsPersistsNonBalancedTokenModes(t *testing.T) {
204 // New writes pin the deprecated dual-write compat labels; economy/delivery
205 // must not be persisted as live modes.
206 isolateDesktopUserDirs(t)
207
208 app := NewApp()
209 tab := testTab("a", t.TempDir())
210 tab.qualityFloor = control.QualityFloorStandard
211 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
212 app.tabOrder = []string{tab.ID}
213 app.activeTabID = tab.ID
214
215 for _, inMemory := range []string{"", "delivery", ""} {
216 tab.qualityFloor = inMemory
217 app.mu.Lock()
218 app.saveTabsLocked()
219 app.mu.Unlock()
220
221 got := loadTabsFile()
222 if len(got.Tabs) != 1 {
223 t.Fatalf("tabs len = %d, want 1", len(got.Tabs))
224 }
225 wantToken, wantPreset := boot.TokenModeFull, boot.AgentPresetStandard
226 if got.Tabs[0].TokenMode != wantToken || got.Tabs[0].AgentPreset != wantPreset {
227 t.Fatalf("saved compat after in-memory %q = token:%q preset:%q, want %q/%q",
228 inMemory, got.Tabs[0].TokenMode, got.Tabs[0].AgentPreset, wantToken, wantPreset)
229 }
230 }
231 }
232
233 func TestLoadTabsFileDecodesLegacyTokenModes(t *testing.T) {
234 isolateDesktopUserDirs(t)
235 dir := desktopConfigDir()
236 if err := os.MkdirAll(dir, 0o755); err != nil {
237 t.Fatal(err)
238 }
239 body := `{"tabs":[{"id":"old","scope":"global","tokenMode":"economy","agentPreset":"light"}],"activeTab":"old"}`
240 if err := os.WriteFile(filepath.Join(dir, tabsFileName), []byte(body), 0o644); err != nil {
241 t.Fatal(err)
242 }
243 got := loadTabsFile()
244 if len(got.Tabs) != 1 || got.Tabs[0].TokenMode != "economy" || got.Tabs[0].AgentPreset != "light" {
245 t.Fatalf("legacy tabs decode = %+v", got.Tabs)
246 }
247 }
248
249 func TestSaveTabsPersistsFullAccessSeparatelyFromCollaborationMode(t *testing.T) {
250 isolateDesktopUserDirs(t)
251
252 app := NewApp()
253 tab := testTab("a", t.TempDir())
254 tab.mode = "normal"
255 tab.toolApprovalMode = control.ToolApprovalDangerFullAccess
256 tab.Ctrl.SetToolApprovalMode(control.ToolApprovalDangerFullAccess)
257 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
258 app.tabOrder = []string{tab.ID}
259 app.activeTabID = tab.ID
260
261 app.mu.Lock()
262 app.saveTabsLocked()
263 app.mu.Unlock()
264
265 got := loadTabsFile()
266 if len(got.Tabs) != 1 {
267 t.Fatalf("tabs len = %d, want 1", len(got.Tabs))
268 }
269 if got.Tabs[0].Mode != "" || got.Tabs[0].ToolApprovalMode != control.ToolApprovalDangerFullAccess {
270 t.Fatalf("saved profile = mode:%q permission:%q, want normal/full access", got.Tabs[0].Mode, got.Tabs[0].ToolApprovalMode)
271 }
272 }
273
274 func TestSaveTabsPersistsPlanAndFullAccessAsIndependentAxes(t *testing.T) {
275 isolateDesktopUserDirs(t)
276
277 app := NewApp()
278 tab := testTab("a", t.TempDir())
279 tab.mode = "plan"
280 tab.toolApprovalMode = control.ToolApprovalDangerFullAccess
281 tab.Ctrl.SetPlanMode(true)
282 tab.Ctrl.SetToolApprovalMode(control.ToolApprovalDangerFullAccess)
283 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
284 app.tabOrder = []string{tab.ID}
285 app.activeTabID = tab.ID
286
287 app.mu.Lock()
288 app.saveTabsLocked()
289 app.mu.Unlock()
290
291 got := loadTabsFile()
292 if len(got.Tabs) != 1 {
293 t.Fatalf("tabs len = %d, want 1", len(got.Tabs))
294 }
295 if got.Tabs[0].Mode != "plan" || got.Tabs[0].ToolApprovalMode != control.ToolApprovalDangerFullAccess {
296 t.Fatalf("saved profile = mode:%q permission:%q, want plan/full access", got.Tabs[0].Mode, got.Tabs[0].ToolApprovalMode)
297 }
298 }
299
300 func TestSaveTabsPersistsGoalAndToolApprovalMode(t *testing.T) {
301 isolateDesktopUserDirs(t)
302
303 app := NewApp()
304 tab := testTab("a", t.TempDir())
305 tab.goal = "finish the mode redesign"
306 tab.toolApprovalMode = control.ToolApprovalAuto
307 tab.Ctrl.SetGoal(tab.goal)
308 tab.Ctrl.SetToolApprovalMode(control.ToolApprovalAuto)
309 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
310 app.tabOrder = []string{tab.ID}
311 app.activeTabID = tab.ID
312
313 app.mu.Lock()
314 app.saveTabsLocked()
315 app.mu.Unlock()
316
317 got := loadTabsFile()
318 if len(got.Tabs) != 1 {
319 t.Fatalf("tabs len = %d, want 1", len(got.Tabs))
320 }
321 if got.Tabs[0].Goal != "finish the mode redesign" {
322 t.Fatalf("saved goal = %q", got.Tabs[0].Goal)
323 }
324 if got.Tabs[0].ToolApprovalMode != control.ToolApprovalAuto {
325 t.Fatalf("saved tool approval mode = %q, want auto", got.Tabs[0].ToolApprovalMode)
326 }
327 }
328
329 func TestCollaborationModesPreserveToolApprovalMode(t *testing.T) {
330 isolateDesktopUserDirs(t)
331
332 app := NewApp()
333 tab := testTab("a", t.TempDir())
334 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
335 app.tabOrder = []string{tab.ID}
336 app.activeTabID = tab.ID
337 defer tab.Ctrl.Close()
338
339 app.SetToolApprovalModeForTab(tab.ID, control.ToolApprovalAuto)
340 app.SetGoalForTab(tab.ID, "finish the approval redesign")
341 if got := currentTabCollaborationMode(tab); got != "goal" {
342 t.Fatalf("collaboration mode = %q, want goal", got)
343 }
344 if tab.Ctrl.PlanMode() {
345 t.Fatal("goal mode should leave plan mode off")
346 }
347 if got := tab.Ctrl.ToolApprovalMode(); got != control.ToolApprovalAuto {
348 t.Fatalf("tool approval after goal = %q, want auto", got)
349 }
350
351 app.SetCollaborationModeForTab(tab.ID, "plan")
352 if got := currentTabCollaborationMode(tab); got != "plan" {
353 t.Fatalf("collaboration mode = %q, want plan", got)
354 }
355 if got := tab.Ctrl.Goal(); got != "" {
356 t.Fatalf("plan mode should clear goal, got %q", got)
357 }
358 if got := tab.Ctrl.ToolApprovalMode(); got != control.ToolApprovalAuto {
359 t.Fatalf("tool approval after plan = %q, want auto", got)
360 }
361
362 app.SetCollaborationModeForTab(tab.ID, "normal")
363 if got := currentTabCollaborationMode(tab); got != "normal" {
364 t.Fatalf("collaboration mode = %q, want normal", got)
365 }
366 if tab.Ctrl.PlanMode() {
367 t.Fatal("normal mode should leave plan mode off")
368 }
369 if got := tab.Ctrl.ToolApprovalMode(); got != control.ToolApprovalAuto {
370 t.Fatalf("tool approval after normal = %q, want auto", got)
371 }
372 }
373
374 func TestToolApprovalModesPreserveCollaborationMode(t *testing.T) {
375 isolateDesktopUserDirs(t)
376
377 app := NewApp()
378 tab := testTab("a", t.TempDir())
379 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
380 app.tabOrder = []string{tab.ID}
381 app.activeTabID = tab.ID
382 defer tab.Ctrl.Close()
383
384 app.SetCollaborationModeForTab(tab.ID, "plan")
385 for _, mode := range []string{control.ToolApprovalAsk, control.ToolApprovalAuto, control.ToolApprovalYolo} {
386 app.SetToolApprovalModeForTab(tab.ID, mode)
387 if got := currentTabCollaborationMode(tab); got != "plan" {
388 t.Fatalf("collaboration after tool mode %q = %q, want plan", mode, got)
389 }
390 if got := tab.Ctrl.ToolApprovalMode(); got != mode {
391 t.Fatalf("tool approval = %q, want %q", got, mode)
392 }
393 }
394
395 app.SetGoalForTab(tab.ID, "ship the goal runner")
396 app.SetToolApprovalModeForTab(tab.ID, control.ToolApprovalAsk)
397 if got := currentTabCollaborationMode(tab); got != "goal" {
398 t.Fatalf("collaboration after ask = %q, want goal", got)
399 }
400 app.SetToolApprovalModeForTab(tab.ID, control.ToolApprovalAuto)
401 if got := currentTabCollaborationMode(tab); got != "goal" {
402 t.Fatalf("collaboration after auto = %q, want goal", got)
403 }
404 app.SetToolApprovalModeForTab(tab.ID, control.ToolApprovalYolo)
405 if got := currentTabCollaborationMode(tab); got != "goal" {
406 t.Fatalf("collaboration after yolo = %q, want goal", got)
407 }
408 }
409
410 func TestSetComposerProfileForTabAppliesAllAxes(t *testing.T) {
411 isolateDesktopUserDirs(t)
412
413 app := NewApp()
414 if drained, err := app.SetComposerProfileForTab("missing", "normal", control.ToolApprovalAsk, ""); err == nil || drained == nil {
415 t.Fatal("missing tab returned a nil drained-id slice")
416 }
417 tab := testTab("a", t.TempDir())
418 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
419 app.tabOrder = []string{tab.ID}
420 app.activeTabID = tab.ID
421 defer tab.Ctrl.Close()
422
423 if _, err := app.SetComposerProfileForTab(tab.ID, "plan", control.ToolApprovalYolo, ""); err != nil {
424 t.Fatal(err)
425 }
426 if got := currentTabCollaborationMode(tab); got != "plan" {
427 t.Fatalf("collaboration mode = %q, want plan", got)
428 }
429 if !tab.Ctrl.PlanMode() {
430 t.Fatal("controller plan mode is off")
431 }
432 if got := tab.Ctrl.ToolApprovalMode(); got != control.ToolApprovalYolo {
433 t.Fatalf("tool approval mode = %q, want yolo", got)
434 }
435
436 if _, err := app.SetComposerProfileForTab(tab.ID, "goal", control.ToolApprovalAuto, "finish the profile migration"); err != nil {
437 t.Fatal(err)
438 }
439 if got := currentTabCollaborationMode(tab); got != "goal" {
440 t.Fatalf("collaboration mode = %q, want goal", got)
441 }
442 if tab.Ctrl.PlanMode() {
443 t.Fatal("goal profile kept controller plan mode on")
444 }
445 if got := tab.Ctrl.ToolApprovalMode(); got != control.ToolApprovalAuto {
446 t.Fatalf("tool approval mode = %q, want auto", got)
447 }
448 if got := tab.Ctrl.Goal(); got != "finish the profile migration" {
449 t.Fatalf("controller goal = %q", got)
450 }
451
452 got := loadTabsFile()
453 if len(got.Tabs) != 1 || got.Tabs[0].Goal != "finish the profile migration" || got.Tabs[0].ToolApprovalMode != control.ToolApprovalAuto {
454 t.Fatalf("persisted profile = %+v", got.Tabs)
455 }
456 }
457
458 func TestMetaReportsGoalStatus(t *testing.T) {
459 isolateDesktopUserDirs(t)
460
461 app := NewApp()
462 tab := testTab("a", t.TempDir())
463 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
464 app.tabOrder = []string{tab.ID}
465 app.activeTabID = tab.ID
466 defer tab.Ctrl.Close()
467
468 meta := app.MetaForTab(tab.ID)
469 if meta.GoalStatus != control.GoalStatusStopped {
470 t.Fatalf("initial goal status = %q, want stopped", meta.GoalStatus)
471 }
472 if meta.CollaborationMode != "normal" {
473 t.Fatalf("initial collaboration mode = %q, want normal", meta.CollaborationMode)
474 }
475
476 app.SetGoalForTab(tab.ID, "finish the goal runner")
477 meta = app.MetaForTab(tab.ID)
478 if meta.Goal != "finish the goal runner" || meta.GoalStatus != control.GoalStatusRunning || meta.CollaborationMode != "goal" {
479 t.Fatalf("goal meta = %+v, want running goal", meta)
480 }
481
482 app.ClearGoalForTab(tab.ID)
483 meta = app.MetaForTab(tab.ID)
484 if meta.Goal != "" || meta.GoalStatus != control.GoalStatusStopped || meta.CollaborationMode != "normal" {
485 t.Fatalf("cleared goal meta = %+v, want stopped empty goal", meta)
486 }
487
488 app.SetCollaborationModeForTab(tab.ID, "plan")
489 app.mu.Lock()
490 tab.qualityFloor = ""
491 app.mu.Unlock()
492 meta = app.MetaForTab(tab.ID)
493 if meta.CollaborationMode != "plan" || meta.TokenMode != boot.TokenModeFull || meta.AgentPreset != boot.AgentPresetStandard {
494 t.Fatalf("profile meta = %+v, want plan + full/standard", meta)
495 }
496 }
497
498 func TestMetaReportsStoredCollaborationModeWhileControllerRebuilds(t *testing.T) {
499 isolateDesktopUserDirs(t)
500
501 app := NewApp()
502 tab := testTab("a", t.TempDir())
503 tab.Ctrl.Close()
504 tab.Ctrl = nil
505 tab.mode = "plan-yolo"
506 tab.toolApprovalMode = control.ToolApprovalYolo
507 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
508 app.tabOrder = []string{tab.ID}
509 app.activeTabID = tab.ID
510
511 meta := app.MetaForTab(tab.ID)
512 if meta.CollaborationMode != "plan" || meta.ToolApprovalMode != control.ToolApprovalYolo {
513 t.Fatalf("rebuilding plan-yolo meta = %+v, want plan/yolo", meta)
514 }
515
516 tab.mode = "normal"
517 tab.goal = "finish the goal runner"
518 meta = app.MetaForTab(tab.ID)
519 if meta.CollaborationMode != "goal" || meta.Goal != "finish the goal runner" || meta.GoalStatus != control.GoalStatusRunning {
520 t.Fatalf("rebuilding goal meta = %+v, want running goal", meta)
521 }
522 }
523
524 func TestSetPlanModePreservesFullAccess(t *testing.T) {
525 isolateDesktopUserDirs(t)
526
527 app := NewApp()
528 tab := testTab("a", t.TempDir())
529 tab.Ctrl.SetToolApprovalMode(control.ToolApprovalDangerFullAccess)
530 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
531 app.tabOrder = []string{tab.ID}
532 app.activeTabID = tab.ID
533
534 app.SetPlanMode(true)
535 if !tab.Ctrl.PlanMode() || !tab.Ctrl.AutoApproveTools() {
536 t.Fatalf("after SetPlanMode(true): plan=%v autoApproveTools=%v, want true/true", tab.Ctrl.PlanMode(), tab.Ctrl.AutoApproveTools())
537 }
538 if got := currentTabCollaborationMode(tab); got != "plan" {
539 t.Fatalf("collaboration mode = %q, want plan", got)
540 }
541
542 app.SetPlanMode(false)
543 if tab.Ctrl.PlanMode() || !tab.Ctrl.AutoApproveTools() {
544 t.Fatalf("after SetPlanMode(false): plan=%v autoApproveTools=%v, want false/true", tab.Ctrl.PlanMode(), tab.Ctrl.AutoApproveTools())
545 }
546 }
547
548 func TestLegacyBypassMigratesToWorkspaceWriteAndPreservesPlanMode(t *testing.T) {
549 isolateDesktopUserDirs(t)
550
551 app := NewApp()
552 tab := testTab("a", t.TempDir())
553 tab.Ctrl.SetPlanMode(true)
554 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
555 app.tabOrder = []string{tab.ID}
556 app.activeTabID = tab.ID
557
558 app.SetBypass(true)
559 if !tab.Ctrl.PlanMode() || tab.Ctrl.ToolApprovalMode() != control.ToolApprovalWorkspaceWrite {
560 t.Fatalf("after SetBypass(true): plan=%v permission=%v, want true/workspace-write", tab.Ctrl.PlanMode(), tab.Ctrl.ToolApprovalMode())
561 }
562 if got := currentTabCollaborationMode(tab); got != "plan" {
563 t.Fatalf("collaboration mode = %q, want plan", got)
564 }
565
566 app.SetBypass(false)
567 if !tab.Ctrl.PlanMode() || tab.Ctrl.ToolApprovalMode() != control.ToolApprovalWorkspaceWrite {
568 t.Fatalf("after SetBypass(false): plan=%v permission=%v, want true/workspace-write", tab.Ctrl.PlanMode(), tab.Ctrl.ToolApprovalMode())
569 }
570 }
571
572 func userConfigPathForTest() string {
573 if dir, err := os.UserConfigDir(); err == nil {
574 return dir + "/reasonix/reasonix.toml"
575 }
576 return ""
577 }
578
578 lines GO