code-puppy 0.0.361__py3-none-any.whl → 0.0.372__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.
- code_puppy/agents/__init__.py +6 -0
- code_puppy/agents/agent_manager.py +223 -1
- code_puppy/agents/base_agent.py +2 -12
- code_puppy/chatgpt_codex_client.py +53 -0
- code_puppy/claude_cache_client.py +45 -7
- code_puppy/command_line/add_model_menu.py +13 -4
- code_puppy/command_line/agent_menu.py +662 -0
- code_puppy/command_line/core_commands.py +4 -112
- code_puppy/command_line/model_picker_completion.py +3 -20
- code_puppy/command_line/model_settings_menu.py +21 -3
- code_puppy/config.py +79 -8
- code_puppy/gemini_model.py +706 -0
- code_puppy/http_utils.py +6 -3
- code_puppy/model_factory.py +50 -16
- code_puppy/model_switching.py +63 -0
- code_puppy/model_utils.py +1 -52
- code_puppy/models.json +12 -12
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +128 -165
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +15 -8
- code_puppy/plugins/antigravity_oauth/transport.py +235 -45
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +2 -2
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +2 -30
- code_puppy/plugins/claude_code_oauth/utils.py +4 -1
- code_puppy/pydantic_patches.py +52 -0
- code_puppy/tools/agent_tools.py +3 -3
- code_puppy/tools/browser/__init__.py +1 -1
- code_puppy/tools/browser/browser_control.py +1 -1
- code_puppy/tools/browser/browser_interactions.py +1 -1
- code_puppy/tools/browser/browser_locators.py +1 -1
- code_puppy/tools/browser/{camoufox_manager.py → browser_manager.py} +29 -110
- code_puppy/tools/browser/browser_navigation.py +1 -1
- code_puppy/tools/browser/browser_screenshot.py +1 -1
- code_puppy/tools/browser/browser_scripts.py +1 -1
- {code_puppy-0.0.361.data → code_puppy-0.0.372.data}/data/code_puppy/models.json +12 -12
- {code_puppy-0.0.361.dist-info → code_puppy-0.0.372.dist-info}/METADATA +5 -6
- {code_puppy-0.0.361.dist-info → code_puppy-0.0.372.dist-info}/RECORD +40 -38
- code_puppy/prompts/codex_system_prompt.md +0 -310
- {code_puppy-0.0.361.data → code_puppy-0.0.372.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.361.dist-info → code_puppy-0.0.372.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.361.dist-info → code_puppy-0.0.372.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.361.dist-info → code_puppy-0.0.372.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,6 +6,7 @@ discovered by the command registry system.
|
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
8
|
|
|
9
|
+
from code_puppy.command_line.agent_menu import interactive_agent_picker
|
|
9
10
|
from code_puppy.command_line.command_registry import register_command
|
|
10
11
|
from code_puppy.command_line.model_picker_completion import update_model_in_input
|
|
11
12
|
from code_puppy.command_line.motd import print_motd
|
|
@@ -168,7 +169,7 @@ def handle_tutorial_command(command: str) -> bool:
|
|
|
168
169
|
reset_onboarding,
|
|
169
170
|
run_onboarding_wizard,
|
|
170
171
|
)
|
|
171
|
-
from code_puppy.
|
|
172
|
+
from code_puppy.model_switching import set_model_and_reload_agent
|
|
172
173
|
|
|
173
174
|
# Always reset so user can re-run the tutorial anytime
|
|
174
175
|
reset_onboarding()
|
|
@@ -183,7 +184,7 @@ def handle_tutorial_command(command: str) -> bool:
|
|
|
183
184
|
from code_puppy.plugins.chatgpt_oauth.oauth_flow import run_oauth_flow
|
|
184
185
|
|
|
185
186
|
run_oauth_flow()
|
|
186
|
-
|
|
187
|
+
set_model_and_reload_agent("chatgpt-gpt-5.2-codex")
|
|
187
188
|
elif result == "claude":
|
|
188
189
|
emit_info("🔐 Starting Claude Code OAuth flow...")
|
|
189
190
|
from code_puppy.plugins.claude_code_oauth.register_callbacks import (
|
|
@@ -191,7 +192,7 @@ def handle_tutorial_command(command: str) -> bool:
|
|
|
191
192
|
)
|
|
192
193
|
|
|
193
194
|
_perform_authentication()
|
|
194
|
-
|
|
195
|
+
set_model_and_reload_agent("claude-code-claude-opus-4-5-20251101")
|
|
195
196
|
elif result == "completed":
|
|
196
197
|
emit_info("🎉 Tutorial complete! Happy coding!")
|
|
197
198
|
elif result == "skipped":
|
|
@@ -398,115 +399,6 @@ def handle_agent_command(command: str) -> bool:
|
|
|
398
399
|
return True
|
|
399
400
|
|
|
400
401
|
|
|
401
|
-
async def interactive_agent_picker() -> str | None:
|
|
402
|
-
"""Show an interactive arrow-key selector to pick an agent (async version).
|
|
403
|
-
|
|
404
|
-
Returns:
|
|
405
|
-
The selected agent name, or None if cancelled
|
|
406
|
-
"""
|
|
407
|
-
import sys
|
|
408
|
-
import time
|
|
409
|
-
|
|
410
|
-
from rich.console import Console
|
|
411
|
-
from rich.panel import Panel
|
|
412
|
-
from rich.text import Text
|
|
413
|
-
|
|
414
|
-
from code_puppy.agents import (
|
|
415
|
-
get_agent_descriptions,
|
|
416
|
-
get_available_agents,
|
|
417
|
-
get_current_agent,
|
|
418
|
-
)
|
|
419
|
-
from code_puppy.tools.command_runner import set_awaiting_user_input
|
|
420
|
-
from code_puppy.tools.common import arrow_select_async
|
|
421
|
-
|
|
422
|
-
# Load available agents
|
|
423
|
-
available_agents = get_available_agents()
|
|
424
|
-
descriptions = get_agent_descriptions()
|
|
425
|
-
current_agent = get_current_agent()
|
|
426
|
-
|
|
427
|
-
# Build choices with current agent indicator and keep track of agent names
|
|
428
|
-
choices = []
|
|
429
|
-
agent_names = list(available_agents.keys())
|
|
430
|
-
for agent_name in agent_names:
|
|
431
|
-
display_name = available_agents[agent_name]
|
|
432
|
-
if agent_name == current_agent.name:
|
|
433
|
-
choices.append(f"✓ {agent_name} - {display_name} (current)")
|
|
434
|
-
else:
|
|
435
|
-
choices.append(f" {agent_name} - {display_name}")
|
|
436
|
-
|
|
437
|
-
# Create preview callback to show agent description
|
|
438
|
-
def get_preview(index: int) -> str:
|
|
439
|
-
"""Get the description for the agent at the given index."""
|
|
440
|
-
agent_name = agent_names[index]
|
|
441
|
-
description = descriptions.get(agent_name, "No description available")
|
|
442
|
-
return description
|
|
443
|
-
|
|
444
|
-
# Create panel content
|
|
445
|
-
panel_content = Text()
|
|
446
|
-
panel_content.append("🐶 Select an agent to use\n", style="bold cyan")
|
|
447
|
-
panel_content.append("Current agent: ", style="dim")
|
|
448
|
-
panel_content.append(f"{current_agent.name}", style="bold green")
|
|
449
|
-
panel_content.append(" - ", style="dim")
|
|
450
|
-
panel_content.append(current_agent.display_name, style="bold green")
|
|
451
|
-
panel_content.append("\n", style="dim")
|
|
452
|
-
panel_content.append(current_agent.description, style="dim italic")
|
|
453
|
-
|
|
454
|
-
# Display panel
|
|
455
|
-
panel = Panel(
|
|
456
|
-
panel_content,
|
|
457
|
-
title="[bold white]Agent Selection[/bold white]",
|
|
458
|
-
border_style="cyan",
|
|
459
|
-
padding=(1, 2),
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
# Pause spinners BEFORE showing panel
|
|
463
|
-
set_awaiting_user_input(True)
|
|
464
|
-
time.sleep(0.3) # Let spinners fully stop
|
|
465
|
-
|
|
466
|
-
local_console = Console()
|
|
467
|
-
emit_info("")
|
|
468
|
-
local_console.print(panel)
|
|
469
|
-
emit_info("")
|
|
470
|
-
|
|
471
|
-
# Flush output before prompt_toolkit takes control
|
|
472
|
-
sys.stdout.flush()
|
|
473
|
-
sys.stderr.flush()
|
|
474
|
-
time.sleep(0.1)
|
|
475
|
-
|
|
476
|
-
selected_agent = None
|
|
477
|
-
|
|
478
|
-
try:
|
|
479
|
-
# Final flush
|
|
480
|
-
sys.stdout.flush()
|
|
481
|
-
|
|
482
|
-
# Show arrow-key selector with preview (async version)
|
|
483
|
-
choice = await arrow_select_async(
|
|
484
|
-
"💭 Which agent would you like to use?",
|
|
485
|
-
choices,
|
|
486
|
-
preview_callback=get_preview,
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
# Extract agent name from choice (remove prefix and suffix)
|
|
490
|
-
if choice:
|
|
491
|
-
# Remove the "✓ " or " " prefix and extract agent name (before " - ")
|
|
492
|
-
choice_stripped = choice.strip().lstrip("✓").strip()
|
|
493
|
-
# Split on " - " and take the first part (agent name)
|
|
494
|
-
agent_name = choice_stripped.split(" - ")[0].strip()
|
|
495
|
-
# Remove " (current)" suffix if present
|
|
496
|
-
if agent_name.endswith(" (current)"):
|
|
497
|
-
agent_name = agent_name[:-10].strip()
|
|
498
|
-
selected_agent = agent_name
|
|
499
|
-
|
|
500
|
-
except (KeyboardInterrupt, EOFError):
|
|
501
|
-
emit_error("Cancelled by user")
|
|
502
|
-
selected_agent = None
|
|
503
|
-
|
|
504
|
-
finally:
|
|
505
|
-
set_awaiting_user_input(False)
|
|
506
|
-
|
|
507
|
-
return selected_agent
|
|
508
|
-
|
|
509
|
-
|
|
510
402
|
async def interactive_model_picker() -> str | None:
|
|
511
403
|
"""Show an interactive arrow-key selector to pick a model (async version).
|
|
512
404
|
|
|
@@ -6,8 +6,9 @@ from prompt_toolkit.completion import Completer, Completion
|
|
|
6
6
|
from prompt_toolkit.document import Document
|
|
7
7
|
from prompt_toolkit.history import FileHistory
|
|
8
8
|
|
|
9
|
-
from code_puppy.config import get_global_model_name
|
|
9
|
+
from code_puppy.config import get_global_model_name
|
|
10
10
|
from code_puppy.model_factory import ModelFactory
|
|
11
|
+
from code_puppy.model_switching import set_model_and_reload_agent
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def load_model_names():
|
|
@@ -28,25 +29,7 @@ def set_active_model(model_name: str):
|
|
|
28
29
|
"""
|
|
29
30
|
Sets the active model name by updating the config (for persistence).
|
|
30
31
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
set_model_name(model_name)
|
|
34
|
-
# Reload the currently active agent so the new model takes effect immediately
|
|
35
|
-
try:
|
|
36
|
-
from code_puppy.agents import get_current_agent
|
|
37
|
-
|
|
38
|
-
current_agent = get_current_agent()
|
|
39
|
-
# JSON agents may need to refresh their config before reload
|
|
40
|
-
if hasattr(current_agent, "refresh_config"):
|
|
41
|
-
try:
|
|
42
|
-
current_agent.refresh_config()
|
|
43
|
-
except Exception:
|
|
44
|
-
# Non-fatal, continue to reload
|
|
45
|
-
...
|
|
46
|
-
current_agent.reload_code_generation_agent()
|
|
47
|
-
emit_info("Active agent reloaded")
|
|
48
|
-
except Exception as e:
|
|
49
|
-
emit_warning(f"Model changed but agent reload failed: {e}")
|
|
32
|
+
set_model_and_reload_agent(model_name)
|
|
50
33
|
|
|
51
34
|
|
|
52
35
|
class ModelNameCompleter(Completer):
|
|
@@ -39,10 +39,10 @@ SETTING_DEFINITIONS: Dict[str, Dict] = {
|
|
|
39
39
|
"description": "Controls randomness. Lower = more deterministic, higher = more creative.",
|
|
40
40
|
"type": "numeric",
|
|
41
41
|
"min": 0.0,
|
|
42
|
-
"max": 1.0,
|
|
43
|
-
"step": 0.
|
|
42
|
+
"max": 1.0,
|
|
43
|
+
"step": 0.05,
|
|
44
44
|
"default": None, # None means use model default
|
|
45
|
-
"format": "{:.
|
|
45
|
+
"format": "{:.2f}",
|
|
46
46
|
},
|
|
47
47
|
"seed": {
|
|
48
48
|
"name": "Seed",
|
|
@@ -54,6 +54,16 @@ SETTING_DEFINITIONS: Dict[str, Dict] = {
|
|
|
54
54
|
"default": None,
|
|
55
55
|
"format": "{:.0f}",
|
|
56
56
|
},
|
|
57
|
+
"top_p": {
|
|
58
|
+
"name": "Top-P (Nucleus Sampling)",
|
|
59
|
+
"description": "Controls token diversity. 0.0 = least random (only most likely tokens), 1.0 = most random (sample from all tokens).",
|
|
60
|
+
"type": "numeric",
|
|
61
|
+
"min": 0.0,
|
|
62
|
+
"max": 1.0,
|
|
63
|
+
"step": 0.05,
|
|
64
|
+
"default": None,
|
|
65
|
+
"format": "{:.2f}",
|
|
66
|
+
},
|
|
57
67
|
"reasoning_effort": {
|
|
58
68
|
"name": "Reasoning Effort",
|
|
59
69
|
"description": "Controls how much effort GPT-5 models spend on reasoning. Higher = more thorough but slower.",
|
|
@@ -90,6 +100,12 @@ SETTING_DEFINITIONS: Dict[str, Dict] = {
|
|
|
90
100
|
"type": "boolean",
|
|
91
101
|
"default": False,
|
|
92
102
|
},
|
|
103
|
+
"clear_thinking": {
|
|
104
|
+
"name": "Clear Thinking",
|
|
105
|
+
"description": "False = Preserved Thinking (keep <think> blocks visible). True = strip thinking from responses.",
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"default": False,
|
|
108
|
+
},
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
|
|
@@ -569,6 +585,8 @@ class ModelSettingsMenu:
|
|
|
569
585
|
# Default to a sensible starting point for numeric
|
|
570
586
|
if setting_key == "temperature":
|
|
571
587
|
self.edit_value = 0.7
|
|
588
|
+
elif setting_key == "top_p":
|
|
589
|
+
self.edit_value = 0.9 # Common default for top_p
|
|
572
590
|
elif setting_key == "seed":
|
|
573
591
|
self.edit_value = 42
|
|
574
592
|
elif setting_key == "budget_tokens":
|
code_puppy/config.py
CHANGED
|
@@ -88,12 +88,44 @@ def get_subagent_verbose() -> bool:
|
|
|
88
88
|
return str(cfg_val).strip().lower() in {"1", "true", "yes", "on"}
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
# Pack agents - the specialized sub-agents coordinated by Pack Leader
|
|
92
|
+
PACK_AGENT_NAMES = frozenset(
|
|
93
|
+
[
|
|
94
|
+
"pack-leader",
|
|
95
|
+
"bloodhound",
|
|
96
|
+
"husky",
|
|
97
|
+
"shepherd",
|
|
98
|
+
"terrier",
|
|
99
|
+
"watchdog",
|
|
100
|
+
"retriever",
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_pack_agents_enabled() -> bool:
|
|
106
|
+
"""Return True if pack agents are enabled (default False).
|
|
107
|
+
|
|
108
|
+
When False (default), pack agents (pack-leader, bloodhound, husky, shepherd,
|
|
109
|
+
terrier, watchdog, retriever) are hidden from `list_agents` tool and `/agents`
|
|
110
|
+
command. They cannot be invoked by other agents or selected by users.
|
|
111
|
+
|
|
112
|
+
When True, pack agents are available for use.
|
|
113
|
+
"""
|
|
114
|
+
cfg_val = get_value("enable_pack_agents")
|
|
115
|
+
if cfg_val is None:
|
|
116
|
+
return False
|
|
117
|
+
return str(cfg_val).strip().lower() in {"1", "true", "yes", "on"}
|
|
118
|
+
|
|
119
|
+
|
|
91
120
|
DEFAULT_SECTION = "puppy"
|
|
92
121
|
REQUIRED_KEYS = ["puppy_name", "owner_name"]
|
|
93
122
|
|
|
94
123
|
# Runtime-only autosave session ID (per-process)
|
|
95
124
|
_CURRENT_AUTOSAVE_ID: Optional[str] = None
|
|
96
125
|
|
|
126
|
+
# Session-local model name (initialized from file on first access, then cached)
|
|
127
|
+
_SESSION_MODEL: Optional[str] = None
|
|
128
|
+
|
|
97
129
|
# Cache containers for model validation and defaults
|
|
98
130
|
_model_validation_cache = {}
|
|
99
131
|
_default_model_cache = None
|
|
@@ -226,6 +258,8 @@ def get_config_keys():
|
|
|
226
258
|
]
|
|
227
259
|
# Add DBOS control key
|
|
228
260
|
default_keys.append("enable_dbos")
|
|
261
|
+
# Add pack agents control key
|
|
262
|
+
default_keys.append("enable_pack_agents")
|
|
229
263
|
# Add cancel agent key configuration
|
|
230
264
|
default_keys.append("cancel_agent_key")
|
|
231
265
|
# Add banner color keys
|
|
@@ -388,6 +422,16 @@ def clear_model_cache():
|
|
|
388
422
|
_default_vision_model_cache = None
|
|
389
423
|
|
|
390
424
|
|
|
425
|
+
def reset_session_model():
|
|
426
|
+
"""Reset the session-local model cache.
|
|
427
|
+
|
|
428
|
+
This is primarily for testing purposes. In normal operation, the session
|
|
429
|
+
model is set once at startup and only changes via set_model_name().
|
|
430
|
+
"""
|
|
431
|
+
global _SESSION_MODEL
|
|
432
|
+
_SESSION_MODEL = None
|
|
433
|
+
|
|
434
|
+
|
|
391
435
|
def model_supports_setting(model_name: str, setting: str) -> bool:
|
|
392
436
|
"""Check if a model supports a particular setting (e.g., 'temperature', 'seed').
|
|
393
437
|
|
|
@@ -399,6 +443,10 @@ def model_supports_setting(model_name: str, setting: str) -> bool:
|
|
|
399
443
|
True if the model supports the setting, False otherwise.
|
|
400
444
|
Defaults to True for backwards compatibility if model config doesn't specify.
|
|
401
445
|
"""
|
|
446
|
+
# GLM-4.7 models always support clear_thinking setting
|
|
447
|
+
if setting == "clear_thinking" and "glm-4.7" in model_name.lower():
|
|
448
|
+
return True
|
|
449
|
+
|
|
402
450
|
try:
|
|
403
451
|
from code_puppy.model_factory import ModelFactory
|
|
404
452
|
|
|
@@ -424,26 +472,49 @@ def model_supports_setting(model_name: str, setting: str) -> bool:
|
|
|
424
472
|
def get_global_model_name():
|
|
425
473
|
"""Return a valid model name for Code Puppy to use.
|
|
426
474
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
475
|
+
Uses session-local caching so that model changes in other terminals
|
|
476
|
+
don't affect this running instance. The file is only read once at startup.
|
|
477
|
+
|
|
478
|
+
1. If _SESSION_MODEL is set, return it (session cache)
|
|
479
|
+
2. Otherwise, look at ``model`` in *puppy.cfg*
|
|
480
|
+
3. If that value exists **and** is present in *models.json*, use it
|
|
481
|
+
4. Otherwise return the first model listed in *models.json*
|
|
482
|
+
5. As a last resort fall back to ``claude-4-0-sonnet``
|
|
483
|
+
|
|
484
|
+
The result is cached in _SESSION_MODEL for subsequent calls.
|
|
432
485
|
"""
|
|
486
|
+
global _SESSION_MODEL
|
|
487
|
+
|
|
488
|
+
# Return cached session model if already initialized
|
|
489
|
+
if _SESSION_MODEL is not None:
|
|
490
|
+
return _SESSION_MODEL
|
|
433
491
|
|
|
492
|
+
# First access - initialize from file
|
|
434
493
|
stored_model = get_value("model")
|
|
435
494
|
|
|
436
495
|
if stored_model:
|
|
437
496
|
# Use cached validation to avoid hitting ModelFactory every time
|
|
438
497
|
if _validate_model_exists(stored_model):
|
|
439
|
-
|
|
498
|
+
_SESSION_MODEL = stored_model
|
|
499
|
+
return _SESSION_MODEL
|
|
440
500
|
|
|
441
501
|
# Either no stored model or it's not valid – choose default from models.json
|
|
442
|
-
|
|
502
|
+
_SESSION_MODEL = _default_model_from_models_json()
|
|
503
|
+
return _SESSION_MODEL
|
|
443
504
|
|
|
444
505
|
|
|
445
506
|
def set_model_name(model: str):
|
|
446
|
-
"""Sets the model name in the persistent config file.
|
|
507
|
+
"""Sets the model name in both the session cache and persistent config file.
|
|
508
|
+
|
|
509
|
+
Updates _SESSION_MODEL immediately for this process, and writes to the
|
|
510
|
+
config file so new terminals will pick up this model as their default.
|
|
511
|
+
"""
|
|
512
|
+
global _SESSION_MODEL
|
|
513
|
+
|
|
514
|
+
# Update session cache immediately
|
|
515
|
+
_SESSION_MODEL = model
|
|
516
|
+
|
|
517
|
+
# Also persist to file for new terminal sessions
|
|
447
518
|
config = configparser.ConfigParser()
|
|
448
519
|
config.read(CONFIG_FILE)
|
|
449
520
|
if DEFAULT_SECTION not in config:
|