tunacode-cli 0.0.75__py3-none-any.whl → 0.0.76.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tunacode-cli might be problematic. Click here for more details.

@@ -11,12 +11,16 @@ from tunacode.configuration.defaults import DEFAULT_USER_CONFIG
11
11
  from tunacode.configuration.models import ModelRegistry
12
12
  from tunacode.constants import APP_NAME, CONFIG_FILE_NAME, UI_COLORS
13
13
  from tunacode.core.setup.base import BaseSetup
14
+ from tunacode.core.setup.config_wizard import ConfigWizard
14
15
  from tunacode.core.state import StateManager
15
16
  from tunacode.exceptions import ConfigurationError
16
17
  from tunacode.types import ConfigFile, ConfigPath, UserConfig
17
18
  from tunacode.ui import console as ui
18
19
  from tunacode.utils import system, user_configuration
19
- from tunacode.utils.api_key_validation import validate_api_key_for_model
20
+ from tunacode.utils.api_key_validation import (
21
+ get_required_api_key_for_model,
22
+ validate_api_key_for_model,
23
+ )
20
24
  from tunacode.utils.text_utils import key_to_title
21
25
 
22
26
 
@@ -105,14 +109,17 @@ class ConfigSetup(BaseSetup):
105
109
  raise
106
110
 
107
111
  if wizard_mode:
108
- await self._wizard_onboarding()
112
+ wizard = ConfigWizard(self.state_manager, self.model_registry, self.config_file)
113
+ await wizard.run_onboarding()
109
114
  else:
110
115
  await self._onboarding()
111
116
  else:
112
- # No config found - show CLI usage instead of onboarding
117
+ # No config found - show CLI usage and continue with safe defaults (no crash)
113
118
  from tunacode.ui.console import console
114
119
 
115
- console.print("\n[bold red]No configuration found![/bold red]")
120
+ console.print(
121
+ "\n[bold yellow]No configuration found — using safe defaults.[/bold yellow]"
122
+ )
116
123
  console.print("\n[bold]Quick Setup:[/bold]")
117
124
  console.print("Configure TunaCode using CLI flags:")
118
125
  console.print("\n[blue]Examples:[/blue]")
@@ -127,16 +134,19 @@ class ConfigSetup(BaseSetup):
127
134
  console.print("\n[yellow]Run 'tunacode --help' for more options[/yellow]\n")
128
135
  console.print("\n[cyan]Or use --wizard for guided setup[/cyan]\n")
129
136
 
130
- raise ConfigurationError(
131
- "No configuration found. Please use CLI flags to configure or --wizard for guided setup."
132
- )
137
+ # Initialize in-memory defaults so we don't crash
138
+ self.state_manager.session.user_config = DEFAULT_USER_CONFIG.copy()
139
+ # Mark config as not fully validated for the fast path
140
+ setattr(self.state_manager, "_config_valid", False)
133
141
 
134
142
  if not self.state_manager.session.user_config.get("default_model"):
135
- raise ConfigurationError(
136
- (
137
- f"No default model found in config at [bold]{self.config_file}[/bold]\n\n"
138
- "Run [code]sidekick --setup[/code] to rerun the setup process."
139
- )
143
+ # Gracefully apply default model instead of crashing
144
+ self.state_manager.session.user_config["default_model"] = DEFAULT_USER_CONFIG[
145
+ "default_model"
146
+ ]
147
+ await ui.warning(
148
+ "No default model set in config; applying safe default "
149
+ f"'{self.state_manager.session.user_config['default_model']}'."
140
150
  )
141
151
 
142
152
  # Validate API key exists for the selected model
@@ -145,10 +155,42 @@ class ConfigSetup(BaseSetup):
145
155
  model, self.state_manager.session.user_config
146
156
  )
147
157
  if not is_valid:
148
- raise ConfigurationError(error_msg)
158
+ # Try to pick a fallback model based on whichever provider has a key configured
159
+ fallback = self._pick_fallback_model(self.state_manager.session.user_config)
160
+ if fallback and fallback != model:
161
+ await ui.warning(
162
+ "API key missing for selected model; switching to configured provider: "
163
+ f"'{fallback}'."
164
+ )
165
+ self.state_manager.session.user_config["default_model"] = fallback
166
+ model = fallback
167
+ else:
168
+ # No suitable fallback; continue without crashing but mark invalid
169
+ await ui.warning(
170
+ (error_msg or "API key missing for model")
171
+ + "\nContinuing without provider initialization; run 'tunacode --setup' later."
172
+ )
173
+ setattr(self.state_manager, "_config_valid", False)
149
174
 
150
175
  self.state_manager.session.current_model = model
151
176
 
177
+ def _pick_fallback_model(self, user_config: UserConfig) -> str | None:
178
+ """Select a reasonable fallback model based on configured API keys."""
179
+ env = (user_config or {}).get("env", {})
180
+
181
+ # Preference order: OpenAI → Anthropic → Google → OpenRouter
182
+ if env.get("OPENAI_API_KEY", "").strip():
183
+ return "openai:gpt-4o"
184
+ if env.get("ANTHROPIC_API_KEY", "").strip():
185
+ return "anthropic:claude-sonnet-4"
186
+ if env.get("GEMINI_API_KEY", "").strip():
187
+ return "google:gemini-2.5-flash"
188
+ if env.get("OPENROUTER_API_KEY", "").strip():
189
+ # Use the project default when OpenRouter is configured
190
+ return DEFAULT_USER_CONFIG.get("default_model", "openrouter:openai/gpt-4.1")
191
+
192
+ return None
193
+
152
194
  async def validate(self) -> bool:
153
195
  """Validate that configuration is properly set up."""
154
196
  # Check that we have a user config
@@ -168,6 +210,18 @@ class ConfigSetup(BaseSetup):
168
210
  # Store error message for later use
169
211
  setattr(self.state_manager, "_config_error", error_msg)
170
212
 
213
+ # Provide actionable guidance for manual setup
214
+ required_key, provider_name = get_required_api_key_for_model(model)
215
+ setup_hint = (
216
+ f"Missing API key for {provider_name}.\n"
217
+ f"Either run 'tunacode --wizard' (recommended) or add it manually to: {self.config_file}\n\n"
218
+ "Example snippet (add under 'env'):\n"
219
+ ' "env": {\n'
220
+ f' "{required_key or "PROVIDER_API_KEY"}": "your-key-here"\n'
221
+ " }\n"
222
+ )
223
+ await ui.error(setup_hint)
224
+
171
225
  # Cache result for fastpath
172
226
  if valid:
173
227
  setattr(self.state_manager, "_config_valid", True)
@@ -373,214 +427,3 @@ class ConfigSetup(BaseSetup):
373
427
  await ui.success(f"Configuration saved to: {self.config_file}")
374
428
  except ConfigurationError as e:
375
429
  await ui.error(str(e))
376
-
377
- async def _wizard_onboarding(self):
378
- """Run enhanced wizard-style onboarding process for new users."""
379
- initial_config = json.dumps(self.state_manager.session.user_config, sort_keys=True)
380
-
381
- # Welcome message with provider guidance
382
- await ui.panel(
383
- "Welcome to TunaCode Setup Wizard!",
384
- "This guided setup will help you configure TunaCode in under 5 minutes.\n"
385
- "We'll help you choose a provider, set up your API keys, and configure your preferred model.",
386
- border_style=UI_COLORS["primary"],
387
- )
388
-
389
- # Step 1: Provider selection with detailed guidance
390
- await self._wizard_step1_provider_selection()
391
-
392
- # Step 2: API key setup with provider-specific guidance
393
- await self._wizard_step2_api_key_setup()
394
-
395
- # Step 3: Model selection with smart recommendations
396
- await self._wizard_step3_model_selection()
397
-
398
- # Step 4: Optional settings configuration
399
- await self._wizard_step4_optional_settings()
400
-
401
- # Save configuration and finish
402
- current_config = json.dumps(self.state_manager.session.user_config, sort_keys=True)
403
- if initial_config != current_config:
404
- try:
405
- user_configuration.save_config(self.state_manager)
406
- await ui.panel(
407
- "Setup Complete!",
408
- f"Configuration saved to: [bold]{self.config_file}[/bold]\n\n"
409
- "You're ready to start using TunaCode!\n"
410
- "Use [green]/quickstart[/green] anytime for a tutorial.",
411
- border_style=UI_COLORS["success"],
412
- )
413
- except ConfigurationError as e:
414
- await ui.error(str(e))
415
-
416
- async def _wizard_step1_provider_selection(self):
417
- """Wizard step 1: Provider selection with detailed explanations."""
418
- provider_info = {
419
- "1": {
420
- "name": "OpenRouter",
421
- "description": "Access to multiple models (GPT-4, Claude, Gemini, etc.)",
422
- "signup": "https://openrouter.ai/",
423
- "key_name": "OPENROUTER_API_KEY",
424
- },
425
- "2": {
426
- "name": "OpenAI",
427
- "description": "GPT-4 models",
428
- "signup": "https://platform.openai.com/signup",
429
- "key_name": "OPENAI_API_KEY",
430
- },
431
- "3": {
432
- "name": "Anthropic",
433
- "description": "Claude-3 models",
434
- "signup": "https://console.anthropic.com/",
435
- "key_name": "ANTHROPIC_API_KEY",
436
- },
437
- "4": {
438
- "name": "Google",
439
- "description": "Gemini models",
440
- "signup": "https://ai.google.dev/",
441
- "key_name": "GEMINI_API_KEY",
442
- },
443
- }
444
-
445
- message = "Choose your AI provider:\n\n"
446
- for key, info in provider_info.items():
447
- message += f" {key} - {info['name']}: {info['description']}\n"
448
-
449
- await ui.panel("Provider Selection", message, border_style=UI_COLORS["primary"])
450
-
451
- while True:
452
- choice = await ui.input(
453
- "wizard_provider",
454
- pretext=" Choose provider (1-4): ",
455
- state_manager=self.state_manager,
456
- )
457
-
458
- if choice.strip() in provider_info:
459
- selected = provider_info[choice.strip()]
460
- self._wizard_selected_provider = selected
461
-
462
- await ui.success(f"Selected: {selected['name']}")
463
- await ui.info(f"Sign up at: {selected['signup']}")
464
- break
465
- else:
466
- await ui.error("Please enter 1, 2, 3, or 4")
467
-
468
- async def _wizard_step2_api_key_setup(self):
469
- """Wizard step 2: API key setup with provider-specific guidance."""
470
- provider = self._wizard_selected_provider
471
-
472
- message = f"Enter your {provider['name']} API key:\n\n"
473
- message += f"Get your key from: {provider['signup']}\n"
474
- message += "Your key will be stored securely in your local config"
475
-
476
- await ui.panel(f"{provider['name']} API Key", message, border_style=UI_COLORS["primary"])
477
-
478
- while True:
479
- api_key = await ui.input(
480
- "wizard_api_key",
481
- pretext=f" {provider['name']} API Key: ",
482
- is_password=True,
483
- state_manager=self.state_manager,
484
- )
485
-
486
- if api_key.strip():
487
- # Ensure env dict exists
488
- if "env" not in self.state_manager.session.user_config:
489
- self.state_manager.session.user_config["env"] = {}
490
-
491
- self.state_manager.session.user_config["env"][provider["key_name"]] = (
492
- api_key.strip()
493
- )
494
- await ui.success("API key saved successfully!")
495
- break
496
- else:
497
- await ui.error("API key cannot be empty")
498
-
499
- async def _wizard_step3_model_selection(self):
500
- """Wizard step 3: Model selection with smart recommendations."""
501
- provider = self._wizard_selected_provider
502
-
503
- # Provide smart recommendations based on provider
504
- recommendations = {
505
- "OpenAI": [
506
- ("openai:gpt-4o", "GPT-4o flagship multimodal model (recommended)"),
507
- ("openai:gpt-4.1", "Latest GPT-4.1 with enhanced coding"),
508
- ("openai:o3", "Advanced reasoning model for complex tasks"),
509
- ],
510
- "Anthropic": [
511
- ("anthropic:claude-sonnet-4", "Claude Sonnet 4 latest generation (recommended)"),
512
- ("anthropic:claude-opus-4.1", "Most capable Claude with extended thinking"),
513
- ("anthropic:claude-3.5-sonnet", "Claude 3.5 Sonnet proven performance"),
514
- ],
515
- "OpenRouter": [
516
- (
517
- "openrouter:anthropic/claude-sonnet-4",
518
- "Claude Sonnet 4 via OpenRouter (recommended)",
519
- ),
520
- ("openrouter:openai/gpt-4.1", "GPT-4.1 via OpenRouter"),
521
- ("openrouter:google/gemini-2.5-flash", "Google Gemini 2.5 Flash latest"),
522
- ],
523
- "Google": [
524
- (
525
- "google:gemini-2.5-pro",
526
- "Gemini 2.5 Pro with thinking capabilities (recommended)",
527
- ),
528
- ("google:gemini-2.5-flash", "Gemini 2.5 Flash best price-performance"),
529
- ("google:gemini-2.0-flash", "Gemini 2.0 Flash with native tool use"),
530
- ],
531
- }
532
-
533
- models = recommendations.get(provider["name"], [])
534
- message = f"Choose your default {provider['name']} model:\n\n"
535
-
536
- for i, (model_id, description) in enumerate(models, 1):
537
- message += f" {i} - {description}\n"
538
-
539
- message += "\nYou can change this later with [green]/model[/green]"
540
-
541
- await ui.panel("Model Selection", message, border_style=UI_COLORS["primary"])
542
-
543
- while True:
544
- choice = await ui.input(
545
- "wizard_model",
546
- pretext=f" Choose model (1-{len(models)}): ",
547
- state_manager=self.state_manager,
548
- )
549
-
550
- try:
551
- index = int(choice.strip()) - 1
552
- if 0 <= index < len(models):
553
- selected_model = models[index][0]
554
- self.state_manager.session.user_config["default_model"] = selected_model
555
- await ui.success(f"Selected: {selected_model}")
556
- break
557
- else:
558
- await ui.error(f"Please enter a number between 1 and {len(models)}")
559
- except ValueError:
560
- await ui.error("Please enter a valid number")
561
-
562
- async def _wizard_step4_optional_settings(self):
563
- """Wizard step 4: Optional settings configuration."""
564
- message = "Configure optional settings:\n\n"
565
- message += "• Tutorial: Enable interactive tutorial for new users\n"
566
- message += "\nSkip this step to use recommended defaults"
567
-
568
- await ui.panel("Optional Settings", message, border_style=UI_COLORS["primary"])
569
-
570
- # Ask about tutorial
571
- tutorial_choice = await ui.input(
572
- "wizard_tutorial",
573
- pretext=" Enable tutorial for new users? [Y/n]: ",
574
- state_manager=self.state_manager,
575
- )
576
-
577
- enable_tutorial = tutorial_choice.strip().lower() not in ["n", "no", "false"]
578
-
579
- if "settings" not in self.state_manager.session.user_config:
580
- self.state_manager.session.user_config["settings"] = {}
581
-
582
- self.state_manager.session.user_config["settings"]["enable_tutorial"] = enable_tutorial
583
-
584
- # Streaming is always enabled - no user choice needed
585
-
586
- await ui.info("Optional settings configured!")
@@ -0,0 +1,229 @@
1
+ """Module: tunacode.core.setup.config_wizard
2
+
3
+ Wizard-style onboarding helpers extracted from ConfigSetup to reduce file size
4
+ and keep responsibilities focused.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Dict, Optional
10
+
11
+ from tunacode.constants import UI_COLORS
12
+ from tunacode.exceptions import ConfigurationError
13
+ from tunacode.ui import console as ui
14
+ from tunacode.utils import user_configuration
15
+
16
+
17
+ class ConfigWizard:
18
+ """Encapsulates the interactive configuration wizard flow."""
19
+
20
+ def __init__(self, state_manager, model_registry, config_file: Path):
21
+ self.state_manager = state_manager
22
+ self.model_registry = model_registry
23
+ self.config_file = config_file
24
+ self._wizard_selected_provider: Optional[Dict[str, str]] = None
25
+
26
+ async def run_onboarding(self) -> None:
27
+ """Run enhanced wizard-style onboarding process for new users."""
28
+ initial_config = json.dumps(self.state_manager.session.user_config, sort_keys=True)
29
+
30
+ # Welcome message with provider guidance
31
+ await ui.panel(
32
+ "Welcome to TunaCode Setup Wizard!",
33
+ "This guided setup will help you configure TunaCode in under 5 minutes.\n"
34
+ "We'll help you choose a provider, set up your API keys, and configure your preferred model.",
35
+ border_style=UI_COLORS["primary"],
36
+ )
37
+
38
+ # Steps
39
+ await self._step1_provider_selection()
40
+ await self._step2_api_key_setup()
41
+ await self._step3_model_selection()
42
+ await self._step4_optional_settings()
43
+
44
+ # Save configuration and finish
45
+ current_config = json.dumps(self.state_manager.session.user_config, sort_keys=True)
46
+ if initial_config != current_config:
47
+ try:
48
+ user_configuration.save_config(self.state_manager)
49
+ await ui.panel(
50
+ "Setup Complete!",
51
+ f"Configuration saved to: [bold]{self.config_file}[/bold]\n\n"
52
+ "You're ready to start using TunaCode!\n"
53
+ "Use [green]/quickstart[/green] anytime for a tutorial.",
54
+ border_style=UI_COLORS["success"],
55
+ )
56
+ except ConfigurationError as e:
57
+ await ui.error(str(e))
58
+
59
+ async def _step1_provider_selection(self) -> None:
60
+ """Wizard step 1: Provider selection with detailed explanations."""
61
+ provider_info = {
62
+ "1": {
63
+ "name": "OpenRouter",
64
+ "description": "Access to multiple models (GPT-4, Claude, Gemini, etc.)",
65
+ "signup": "https://openrouter.ai/",
66
+ "key_name": "OPENROUTER_API_KEY",
67
+ },
68
+ "2": {
69
+ "name": "OpenAI",
70
+ "description": "GPT-4 models",
71
+ "signup": "https://platform.openai.com/signup",
72
+ "key_name": "OPENAI_API_KEY",
73
+ },
74
+ "3": {
75
+ "name": "Anthropic",
76
+ "description": "Claude-3 models",
77
+ "signup": "https://console.anthropic.com/",
78
+ "key_name": "ANTHROPIC_API_KEY",
79
+ },
80
+ "4": {
81
+ "name": "Google",
82
+ "description": "Gemini models",
83
+ "signup": "https://ai.google.dev/",
84
+ "key_name": "GEMINI_API_KEY",
85
+ },
86
+ }
87
+
88
+ message = "Choose your AI provider:\n\n"
89
+ for key, info in provider_info.items():
90
+ message += f" {key} - {info['name']}: {info['description']}\n"
91
+
92
+ await ui.panel("Provider Selection", message, border_style=UI_COLORS["primary"])
93
+
94
+ while True:
95
+ choice = await ui.input(
96
+ "wizard_provider",
97
+ pretext=" Choose provider (1-4): ",
98
+ state_manager=self.state_manager,
99
+ )
100
+
101
+ if choice.strip() in provider_info:
102
+ selected = provider_info[choice.strip()]
103
+ self._wizard_selected_provider = selected
104
+
105
+ await ui.success(f"Selected: {selected['name']}")
106
+ await ui.info(f"Sign up at: {selected['signup']}")
107
+ break
108
+ else:
109
+ await ui.error("Please enter 1, 2, 3, or 4")
110
+
111
+ async def _step2_api_key_setup(self) -> None:
112
+ """Wizard step 2: API key setup with provider-specific guidance."""
113
+ provider = self._wizard_selected_provider
114
+
115
+ message = f"Enter your {provider['name']} API key:\n\n"
116
+ message += f"Get your key from: {provider['signup']}\n"
117
+ message += "Your key will be stored securely in your local config"
118
+
119
+ await ui.panel(f"{provider['name']} API Key", message, border_style=UI_COLORS["primary"])
120
+
121
+ while True:
122
+ api_key = await ui.input(
123
+ "wizard_api_key",
124
+ pretext=f" {provider['name']} API Key: ",
125
+ is_password=True,
126
+ state_manager=self.state_manager,
127
+ )
128
+
129
+ if api_key.strip():
130
+ # Ensure env dict exists
131
+ if "env" not in self.state_manager.session.user_config:
132
+ self.state_manager.session.user_config["env"] = {}
133
+
134
+ self.state_manager.session.user_config["env"][provider["key_name"]] = (
135
+ api_key.strip()
136
+ )
137
+ await ui.success("API key saved successfully!")
138
+ break
139
+ else:
140
+ await ui.error("API key cannot be empty")
141
+
142
+ async def _step3_model_selection(self) -> None:
143
+ """Wizard step 3: Model selection with smart recommendations."""
144
+ provider = self._wizard_selected_provider
145
+
146
+ # Provide smart recommendations based on provider
147
+ recommendations = {
148
+ "OpenAI": [
149
+ ("openai:gpt-4o", "GPT-4o flagship multimodal model (recommended)"),
150
+ ("openai:gpt-4.1", "Latest GPT-4.1 with enhanced coding"),
151
+ ("openai:o3", "Advanced reasoning model for complex tasks"),
152
+ ],
153
+ "Anthropic": [
154
+ ("anthropic:claude-sonnet-4", "Claude Sonnet 4 latest generation (recommended)"),
155
+ ("anthropic:claude-opus-4.1", "Most capable Claude with extended thinking"),
156
+ ("anthropic:claude-3.5-sonnet", "Claude 3.5 Sonnet proven performance"),
157
+ ],
158
+ "OpenRouter": [
159
+ (
160
+ "openrouter:anthropic/claude-sonnet-4",
161
+ "Claude Sonnet 4 via OpenRouter (recommended)",
162
+ ),
163
+ ("openrouter:openai/gpt-4.1", "GPT-4.1 via OpenRouter"),
164
+ ("openrouter:google/gemini-2.5-flash", "Google Gemini 2.5 Flash latest"),
165
+ ],
166
+ "Google": [
167
+ (
168
+ "google:gemini-2.5-pro",
169
+ "Gemini 2.5 Pro with thinking capabilities (recommended)",
170
+ ),
171
+ ("google:gemini-2.5-flash", "Gemini 2.5 Flash best price-performance"),
172
+ ("google:gemini-2.0-flash", "Gemini 2.0 Flash with native tool use"),
173
+ ],
174
+ }
175
+
176
+ models = recommendations.get(provider["name"], [])
177
+ message = f"Choose your default {provider['name']} model:\n\n"
178
+
179
+ for i, (model_id, description) in enumerate(models, 1):
180
+ message += f" {i} - {description}\n"
181
+
182
+ message += "\nYou can change this later with [green]/model[/green]"
183
+
184
+ await ui.panel("Model Selection", message, border_style=UI_COLORS["primary"])
185
+
186
+ while True:
187
+ choice = await ui.input(
188
+ "wizard_model",
189
+ pretext=f" Choose model (1-{len(models)}): ",
190
+ state_manager=self.state_manager,
191
+ )
192
+
193
+ try:
194
+ index = int(choice.strip()) - 1
195
+ if 0 <= index < len(models):
196
+ selected_model = models[index][0]
197
+ self.state_manager.session.user_config["default_model"] = selected_model
198
+ await ui.success(f"Selected: {selected_model}")
199
+ break
200
+ else:
201
+ await ui.error(f"Please enter a number between 1 and {len(models)}")
202
+ except ValueError:
203
+ await ui.error("Please enter a valid number")
204
+
205
+ async def _step4_optional_settings(self) -> None:
206
+ """Wizard step 4: Optional settings configuration."""
207
+ message = "Configure optional settings:\n\n"
208
+ message += "• Tutorial: Enable interactive tutorial for new users\n"
209
+ message += "\nSkip this step to use recommended defaults"
210
+
211
+ await ui.panel("Optional Settings", message, border_style=UI_COLORS["primary"])
212
+
213
+ # Ask about tutorial
214
+ tutorial_choice = await ui.input(
215
+ "wizard_tutorial",
216
+ pretext=" Enable tutorial for new users? [Y/n]: ",
217
+ state_manager=self.state_manager,
218
+ )
219
+
220
+ enable_tutorial = tutorial_choice.strip().lower() not in ["n", "no", "false"]
221
+
222
+ if "settings" not in self.state_manager.session.user_config:
223
+ self.state_manager.session.user_config["settings"] = {}
224
+
225
+ self.state_manager.session.user_config["settings"]["enable_tutorial"] = enable_tutorial
226
+
227
+ # Streaming is always enabled - no user choice needed
228
+
229
+ await ui.info("Optional settings configured!")
tunacode/core/state.py CHANGED
@@ -10,6 +10,7 @@ import uuid
10
10
  from dataclasses import dataclass, field
11
11
  from typing import TYPE_CHECKING, Any, Optional
12
12
 
13
+ from tunacode.configuration.defaults import DEFAULT_USER_CONFIG
13
14
  from tunacode.types import (
14
15
  DeviceId,
15
16
  InputSessions,
@@ -39,7 +40,8 @@ class SessionState:
39
40
  ) # Keep as dict[str, Any] for agent instances
40
41
  messages: MessageHistory = field(default_factory=list)
41
42
  total_cost: float = 0.0
42
- current_model: ModelName = "openai:gpt-4o"
43
+ # Keep session default in sync with configuration default
44
+ current_model: ModelName = DEFAULT_USER_CONFIG["default_model"]
43
45
  spinner: Optional[Any] = None
44
46
  tool_ignore: list[ToolName] = field(default_factory=list)
45
47
  yolo: bool = False