code-puppy 0.0.169__py3-none-any.whl → 0.0.366__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/__init__.py +7 -1
- code_puppy/agents/__init__.py +8 -8
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +9 -2
- code_puppy/agents/agent_code_reviewer.py +90 -0
- code_puppy/agents/agent_cpp_reviewer.py +132 -0
- code_puppy/agents/agent_creator_agent.py +48 -9
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +146 -199
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_planning.py +163 -0
- code_puppy/agents/agent_python_programmer.py +165 -0
- code_puppy/agents/agent_python_reviewer.py +90 -0
- code_puppy/agents/agent_qa_expert.py +163 -0
- code_puppy/agents/agent_qa_kitten.py +208 -0
- code_puppy/agents/agent_security_auditor.py +181 -0
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +166 -0
- code_puppy/agents/base_agent.py +1713 -1
- code_puppy/agents/event_stream_handler.py +350 -0
- code_puppy/agents/json_agent.py +12 -1
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +174 -4
- code_puppy/chatgpt_codex_client.py +283 -0
- code_puppy/claude_cache_client.py +586 -0
- code_puppy/cli_runner.py +916 -0
- code_puppy/command_line/add_model_menu.py +1079 -0
- code_puppy/command_line/agent_menu.py +395 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +605 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +520 -0
- code_puppy/command_line/command_handler.py +233 -627
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +715 -0
- code_puppy/command_line/core_commands.py +792 -0
- code_puppy/command_line/diff_menu.py +863 -0
- code_puppy/command_line/load_context_completion.py +15 -22
- code_puppy/command_line/mcp/base.py +1 -4
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +9 -4
- code_puppy/command_line/mcp/help_command.py +6 -5
- code_puppy/command_line/mcp/install_command.py +16 -27
- code_puppy/command_line/mcp/install_menu.py +685 -0
- code_puppy/command_line/mcp/list_command.py +3 -3
- code_puppy/command_line/mcp/logs_command.py +174 -65
- code_puppy/command_line/mcp/remove_command.py +2 -2
- code_puppy/command_line/mcp/restart_command.py +12 -4
- code_puppy/command_line/mcp/search_command.py +17 -11
- code_puppy/command_line/mcp/start_all_command.py +22 -13
- code_puppy/command_line/mcp/start_command.py +50 -31
- code_puppy/command_line/mcp/status_command.py +6 -7
- code_puppy/command_line/mcp/stop_all_command.py +11 -8
- code_puppy/command_line/mcp/stop_command.py +11 -10
- code_puppy/command_line/mcp/test_command.py +2 -2
- code_puppy/command_line/mcp/utils.py +1 -1
- code_puppy/command_line/mcp/wizard_utils.py +22 -18
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +89 -30
- code_puppy/command_line/model_settings_menu.py +884 -0
- code_puppy/command_line/motd.py +14 -8
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +340 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +626 -75
- code_puppy/command_line/session_commands.py +296 -0
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +1181 -51
- code_puppy/error_logging.py +118 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +602 -0
- code_puppy/http_utils.py +220 -104
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +5 -594
- code_puppy/{mcp → mcp_}/__init__.py +17 -0
- code_puppy/{mcp → mcp_}/async_lifecycle.py +35 -4
- code_puppy/{mcp → mcp_}/blocking_startup.py +70 -43
- code_puppy/{mcp → mcp_}/captured_stdio_server.py +2 -2
- code_puppy/{mcp → mcp_}/config_wizard.py +5 -5
- code_puppy/{mcp → mcp_}/dashboard.py +15 -6
- code_puppy/{mcp → mcp_}/examples/retry_example.py +4 -1
- code_puppy/{mcp → mcp_}/managed_server.py +66 -39
- code_puppy/{mcp → mcp_}/manager.py +146 -52
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/{mcp → mcp_}/registry.py +6 -6
- code_puppy/{mcp → mcp_}/server_registry_catalog.py +25 -8
- code_puppy/messaging/__init__.py +199 -2
- code_puppy/messaging/bus.py +610 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +17 -48
- code_puppy/messaging/messages.py +500 -0
- code_puppy/messaging/queue_console.py +1 -24
- code_puppy/messaging/renderers.py +43 -146
- code_puppy/messaging/rich_renderer.py +1027 -0
- code_puppy/messaging/spinner/__init__.py +33 -5
- code_puppy/messaging/spinner/console_spinner.py +92 -52
- code_puppy/messaging/spinner/spinner_base.py +29 -0
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_factory.py +686 -80
- code_puppy/model_utils.py +167 -0
- code_puppy/models.json +86 -104
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +164 -10
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +704 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +136 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +406 -0
- code_puppy/plugins/antigravity_oauth/storage.py +271 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +767 -0
- code_puppy/plugins/antigravity_oauth/utils.py +169 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +94 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +293 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +489 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +6 -0
- code_puppy/plugins/claude_code_oauth/config.py +50 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +308 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/utils.py +518 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +169 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +523 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/prompts/codex_system_prompt.md +310 -0
- code_puppy/pydantic_patches.py +131 -0
- code_puppy/reopenable_async_client.py +8 -8
- code_puppy/round_robin_model.py +10 -15
- code_puppy/session_storage.py +294 -0
- code_puppy/status_display.py +21 -4
- code_puppy/summarization_agent.py +52 -14
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +139 -6
- code_puppy/tools/agent_tools.py +548 -49
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +316 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +941 -153
- code_puppy/tools/common.py +1146 -6
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +288 -89
- code_puppy/tools/file_operations.py +352 -266
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +30 -11
- code_puppy-0.0.366.data/data/code_puppy/models.json +110 -0
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json +1 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/METADATA +184 -67
- code_puppy-0.0.366.dist-info/RECORD +217 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/WHEEL +1 -1
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/entry_points.txt +1 -0
- code_puppy/agent.py +0 -231
- code_puppy/agents/agent_orchestrator.json +0 -26
- code_puppy/agents/runtime_manager.py +0 -272
- code_puppy/command_line/mcp/add_command.py +0 -183
- code_puppy/command_line/meta_command_handler.py +0 -153
- code_puppy/message_history_processor.py +0 -490
- code_puppy/messaging/spinner/textual_spinner.py +0 -101
- code_puppy/state_management.py +0 -200
- code_puppy/tui/__init__.py +0 -10
- code_puppy/tui/app.py +0 -986
- code_puppy/tui/components/__init__.py +0 -21
- code_puppy/tui/components/chat_view.py +0 -550
- code_puppy/tui/components/command_history_modal.py +0 -218
- code_puppy/tui/components/copy_button.py +0 -139
- code_puppy/tui/components/custom_widgets.py +0 -63
- code_puppy/tui/components/human_input_modal.py +0 -175
- code_puppy/tui/components/input_area.py +0 -167
- code_puppy/tui/components/sidebar.py +0 -309
- code_puppy/tui/components/status_bar.py +0 -182
- code_puppy/tui/messages.py +0 -27
- code_puppy/tui/models/__init__.py +0 -8
- code_puppy/tui/models/chat_message.py +0 -25
- code_puppy/tui/models/command_history.py +0 -89
- code_puppy/tui/models/enums.py +0 -24
- code_puppy/tui/screens/__init__.py +0 -15
- code_puppy/tui/screens/help.py +0 -130
- code_puppy/tui/screens/mcp_install_wizard.py +0 -803
- code_puppy/tui/screens/settings.py +0 -290
- code_puppy/tui/screens/tools.py +0 -74
- code_puppy-0.0.169.data/data/code_puppy/models.json +0 -128
- code_puppy-0.0.169.dist-info/RECORD +0 -112
- /code_puppy/{mcp → mcp_}/circuit_breaker.py +0 -0
- /code_puppy/{mcp → mcp_}/error_isolation.py +0 -0
- /code_puppy/{mcp → mcp_}/health_monitor.py +0 -0
- /code_puppy/{mcp → mcp_}/retry_manager.py +0 -0
- /code_puppy/{mcp → mcp_}/status_tracker.py +0 -0
- /code_puppy/{mcp → mcp_}/system_tools.py +0 -0
- {code_puppy-0.0.169.dist-info → code_puppy-0.0.366.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,16 +6,19 @@ import os
|
|
|
6
6
|
import pkgutil
|
|
7
7
|
import uuid
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Dict, Optional, Type, Union
|
|
9
|
+
from typing import Dict, List, Optional, Type, Union
|
|
10
10
|
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
from .base_agent import BaseAgent
|
|
14
|
-
from .json_agent import JSONAgent, discover_json_agents
|
|
11
|
+
from pydantic_ai.messages import ModelMessage
|
|
12
|
+
|
|
13
|
+
from code_puppy.agents.base_agent import BaseAgent
|
|
14
|
+
from code_puppy.agents.json_agent import JSONAgent, discover_json_agents
|
|
15
|
+
from code_puppy.callbacks import on_agent_reload
|
|
16
|
+
from code_puppy.messaging import emit_warning
|
|
15
17
|
|
|
16
18
|
# Registry of available agents (Python classes and JSON file paths)
|
|
17
19
|
_AGENT_REGISTRY: Dict[str, Union[Type[BaseAgent], str]] = {}
|
|
18
|
-
|
|
20
|
+
_AGENT_HISTORIES: Dict[str, List[ModelMessage]] = {}
|
|
21
|
+
_CURRENT_AGENT: Optional[BaseAgent] = None
|
|
19
22
|
|
|
20
23
|
# Terminal session-based agent selection
|
|
21
24
|
_SESSION_AGENTS_CACHE: dict[str, str] = {}
|
|
@@ -25,9 +28,9 @@ _SESSION_FILE_LOADED: bool = False
|
|
|
25
28
|
# Session persistence file path
|
|
26
29
|
def _get_session_file_path() -> Path:
|
|
27
30
|
"""Get the path to the terminal sessions file."""
|
|
28
|
-
from ..config import
|
|
31
|
+
from ..config import STATE_DIR
|
|
29
32
|
|
|
30
|
-
return Path(
|
|
33
|
+
return Path(STATE_DIR) / "terminal_sessions.json"
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
def get_terminal_session_id() -> str:
|
|
@@ -48,21 +51,56 @@ def get_terminal_session_id() -> str:
|
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
def _is_process_alive(pid: int) -> bool:
|
|
51
|
-
"""Check if a process with the given PID is still alive.
|
|
54
|
+
"""Check if a process with the given PID is still alive, cross-platform.
|
|
52
55
|
|
|
53
56
|
Args:
|
|
54
57
|
pid: Process ID to check
|
|
55
58
|
|
|
56
59
|
Returns:
|
|
57
|
-
bool: True if process exists, False otherwise
|
|
60
|
+
bool: True if process likely exists, False otherwise
|
|
58
61
|
"""
|
|
59
62
|
try:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
if os.name == "nt":
|
|
64
|
+
# Windows: use OpenProcess to probe liveness safely
|
|
65
|
+
import ctypes
|
|
66
|
+
from ctypes import wintypes
|
|
67
|
+
|
|
68
|
+
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
|
|
69
|
+
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
|
|
70
|
+
kernel32.OpenProcess.argtypes = [
|
|
71
|
+
wintypes.DWORD,
|
|
72
|
+
wintypes.BOOL,
|
|
73
|
+
wintypes.DWORD,
|
|
74
|
+
]
|
|
75
|
+
kernel32.OpenProcess.restype = wintypes.HANDLE
|
|
76
|
+
handle = kernel32.OpenProcess(
|
|
77
|
+
PROCESS_QUERY_LIMITED_INFORMATION, False, int(pid)
|
|
78
|
+
)
|
|
79
|
+
if handle:
|
|
80
|
+
kernel32.CloseHandle(handle)
|
|
81
|
+
return True
|
|
82
|
+
# If access denied, process likely exists but we can't query it
|
|
83
|
+
last_error = kernel32.GetLastError()
|
|
84
|
+
# ERROR_ACCESS_DENIED = 5
|
|
85
|
+
if last_error == 5:
|
|
86
|
+
return True
|
|
87
|
+
return False
|
|
88
|
+
else:
|
|
89
|
+
# Unix-like: signal 0 does not deliver a signal but checks existence
|
|
90
|
+
os.kill(int(pid), 0)
|
|
91
|
+
return True
|
|
92
|
+
except PermissionError:
|
|
93
|
+
# No permission to signal -> process exists
|
|
63
94
|
return True
|
|
64
95
|
except (OSError, ProcessLookupError):
|
|
96
|
+
# Process does not exist
|
|
65
97
|
return False
|
|
98
|
+
except ValueError:
|
|
99
|
+
# Invalid signal or pid format
|
|
100
|
+
return False
|
|
101
|
+
except Exception:
|
|
102
|
+
# Be conservative – don't crash session cleanup due to platform quirks
|
|
103
|
+
return True
|
|
66
104
|
|
|
67
105
|
|
|
68
106
|
def _cleanup_dead_sessions(sessions: dict[str, str]) -> dict[str, str]:
|
|
@@ -146,41 +184,6 @@ def _ensure_session_cache_loaded() -> None:
|
|
|
146
184
|
_SESSION_FILE_LOADED = True
|
|
147
185
|
|
|
148
186
|
|
|
149
|
-
# Persistent storage for agent message histories
|
|
150
|
-
_AGENT_HISTORIES: Dict[str, Dict[str, any]] = {}
|
|
151
|
-
# Structure: {agent_name: {"message_history": [...], "compacted_hashes": set(...)}}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def _save_agent_history(agent_name: str, agent: BaseAgent) -> None:
|
|
155
|
-
"""Save an agent's message history to persistent storage.
|
|
156
|
-
|
|
157
|
-
Args:
|
|
158
|
-
agent_name: The name of the agent
|
|
159
|
-
agent: The agent instance to save history from
|
|
160
|
-
"""
|
|
161
|
-
global _AGENT_HISTORIES
|
|
162
|
-
_AGENT_HISTORIES[agent_name] = {
|
|
163
|
-
"message_history": agent.get_message_history().copy(),
|
|
164
|
-
"compacted_hashes": agent.get_compacted_message_hashes().copy(),
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def _restore_agent_history(agent_name: str, agent: BaseAgent) -> None:
|
|
169
|
-
"""Restore an agent's message history from persistent storage.
|
|
170
|
-
|
|
171
|
-
Args:
|
|
172
|
-
agent_name: The name of the agent
|
|
173
|
-
agent: The agent instance to restore history to
|
|
174
|
-
"""
|
|
175
|
-
global _AGENT_HISTORIES
|
|
176
|
-
if agent_name in _AGENT_HISTORIES:
|
|
177
|
-
stored_data = _AGENT_HISTORIES[agent_name]
|
|
178
|
-
agent.set_message_history(stored_data["message_history"])
|
|
179
|
-
# Restore compacted hashes
|
|
180
|
-
for hash_val in stored_data["compacted_hashes"]:
|
|
181
|
-
agent.add_compacted_message_hash(hash_val)
|
|
182
|
-
|
|
183
|
-
|
|
184
187
|
def _discover_agents(message_group_id: Optional[str] = None):
|
|
185
188
|
"""Dynamically discover all agent classes and JSON agents."""
|
|
186
189
|
# Always clear the registry to force refresh
|
|
@@ -222,6 +225,55 @@ def _discover_agents(message_group_id: Optional[str] = None):
|
|
|
222
225
|
)
|
|
223
226
|
continue
|
|
224
227
|
|
|
228
|
+
# 1b. Discover agents in sub-packages (like 'pack')
|
|
229
|
+
for _, subpkg_name, ispkg in pkgutil.iter_modules(agents_package.__path__):
|
|
230
|
+
if not ispkg or subpkg_name.startswith("_"):
|
|
231
|
+
continue
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
# Import the sub-package
|
|
235
|
+
subpkg = importlib.import_module(f"code_puppy.agents.{subpkg_name}")
|
|
236
|
+
|
|
237
|
+
# Iterate through modules in the sub-package
|
|
238
|
+
if not hasattr(subpkg, "__path__"):
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
for _, modname, _ in pkgutil.iter_modules(subpkg.__path__):
|
|
242
|
+
if modname.startswith("_"):
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
# Import the submodule
|
|
247
|
+
module = importlib.import_module(
|
|
248
|
+
f"code_puppy.agents.{subpkg_name}.{modname}"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Look for BaseAgent subclasses
|
|
252
|
+
for attr_name in dir(module):
|
|
253
|
+
attr = getattr(module, attr_name)
|
|
254
|
+
if (
|
|
255
|
+
isinstance(attr, type)
|
|
256
|
+
and issubclass(attr, BaseAgent)
|
|
257
|
+
and attr not in [BaseAgent, JSONAgent]
|
|
258
|
+
):
|
|
259
|
+
# Create an instance to get the name
|
|
260
|
+
agent_instance = attr()
|
|
261
|
+
_AGENT_REGISTRY[agent_instance.name] = attr
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
emit_warning(
|
|
265
|
+
f"Warning: Could not load agent {subpkg_name}.{modname}: {e}",
|
|
266
|
+
message_group=message_group_id,
|
|
267
|
+
)
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
emit_warning(
|
|
272
|
+
f"Warning: Could not load agent sub-package {subpkg_name}: {e}",
|
|
273
|
+
message_group=message_group_id,
|
|
274
|
+
)
|
|
275
|
+
continue
|
|
276
|
+
|
|
225
277
|
# 2. Discover JSON agents in user directory
|
|
226
278
|
try:
|
|
227
279
|
json_agents = discover_json_agents()
|
|
@@ -243,12 +295,21 @@ def get_available_agents() -> Dict[str, str]:
|
|
|
243
295
|
Returns:
|
|
244
296
|
Dict mapping agent names to display names.
|
|
245
297
|
"""
|
|
298
|
+
from ..config import PACK_AGENT_NAMES, get_pack_agents_enabled
|
|
299
|
+
|
|
246
300
|
# Generate a message group ID for this operation
|
|
247
301
|
message_group_id = str(uuid.uuid4())
|
|
248
302
|
_discover_agents(message_group_id=message_group_id)
|
|
249
303
|
|
|
304
|
+
# Check if pack agents are enabled
|
|
305
|
+
pack_agents_enabled = get_pack_agents_enabled()
|
|
306
|
+
|
|
250
307
|
agents = {}
|
|
251
308
|
for name, agent_ref in _AGENT_REGISTRY.items():
|
|
309
|
+
# Filter out pack agents if disabled
|
|
310
|
+
if not pack_agents_enabled and name in PACK_AGENT_NAMES:
|
|
311
|
+
continue
|
|
312
|
+
|
|
252
313
|
try:
|
|
253
314
|
if isinstance(agent_ref, str): # JSON agent (file path)
|
|
254
315
|
agent_instance = JSONAgent(agent_ref)
|
|
@@ -265,11 +326,21 @@ def get_current_agent_name() -> str:
|
|
|
265
326
|
"""Get the name of the currently active agent for this terminal session.
|
|
266
327
|
|
|
267
328
|
Returns:
|
|
268
|
-
The name of the current agent for this session
|
|
329
|
+
The name of the current agent for this session.
|
|
330
|
+
Priority: session agent > config default > 'code-puppy'.
|
|
269
331
|
"""
|
|
270
332
|
_ensure_session_cache_loaded()
|
|
271
333
|
session_id = get_terminal_session_id()
|
|
272
|
-
|
|
334
|
+
|
|
335
|
+
# First check for session-specific agent
|
|
336
|
+
session_agent = _SESSION_AGENTS_CACHE.get(session_id)
|
|
337
|
+
if session_agent:
|
|
338
|
+
return session_agent
|
|
339
|
+
|
|
340
|
+
# Fall back to config default
|
|
341
|
+
from ..config import get_default_agent
|
|
342
|
+
|
|
343
|
+
return get_default_agent()
|
|
273
344
|
|
|
274
345
|
|
|
275
346
|
def set_current_agent(agent_name: str) -> bool:
|
|
@@ -281,50 +352,49 @@ def set_current_agent(agent_name: str) -> bool:
|
|
|
281
352
|
Returns:
|
|
282
353
|
True if the agent was set successfully, False if agent not found.
|
|
283
354
|
"""
|
|
355
|
+
global _CURRENT_AGENT
|
|
356
|
+
curr_agent = get_current_agent()
|
|
357
|
+
if curr_agent is not None:
|
|
358
|
+
# Store a shallow copy so future mutations don't affect saved history
|
|
359
|
+
_AGENT_HISTORIES[curr_agent.name] = list(curr_agent.get_message_history())
|
|
284
360
|
# Generate a message group ID for agent switching
|
|
285
361
|
message_group_id = str(uuid.uuid4())
|
|
286
362
|
_discover_agents(message_group_id=message_group_id)
|
|
287
363
|
|
|
288
364
|
# Save current agent's history before switching
|
|
289
|
-
global _CURRENT_AGENT_CONFIG, _CURRENT_AGENT_NAME
|
|
290
|
-
if _CURRENT_AGENT_CONFIG is not None:
|
|
291
|
-
_save_agent_history(_CURRENT_AGENT_CONFIG.name, _CURRENT_AGENT_CONFIG)
|
|
292
365
|
|
|
293
366
|
# Clear the cached config when switching agents
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
# Restore the agent's history if it exists
|
|
298
|
-
_restore_agent_history(agent_name, agent_obj)
|
|
367
|
+
agent_obj = load_agent(agent_name)
|
|
368
|
+
_CURRENT_AGENT = agent_obj
|
|
299
369
|
|
|
300
370
|
# Update session-based agent selection and persist to disk
|
|
301
371
|
_ensure_session_cache_loaded()
|
|
302
372
|
session_id = get_terminal_session_id()
|
|
303
373
|
_SESSION_AGENTS_CACHE[session_id] = agent_name
|
|
304
374
|
_save_session_data(_SESSION_AGENTS_CACHE)
|
|
305
|
-
|
|
375
|
+
if agent_obj.name in _AGENT_HISTORIES:
|
|
376
|
+
# Restore a copy to avoid sharing the same list instance
|
|
377
|
+
agent_obj.set_message_history(list(_AGENT_HISTORIES[agent_obj.name]))
|
|
306
378
|
on_agent_reload(agent_obj.id, agent_name)
|
|
307
379
|
return True
|
|
308
380
|
|
|
309
381
|
|
|
310
|
-
def
|
|
382
|
+
def get_current_agent() -> BaseAgent:
|
|
311
383
|
"""Get the current agent configuration.
|
|
312
384
|
|
|
313
385
|
Returns:
|
|
314
386
|
The current agent configuration instance.
|
|
315
387
|
"""
|
|
316
|
-
global
|
|
388
|
+
global _CURRENT_AGENT
|
|
317
389
|
|
|
318
|
-
if
|
|
390
|
+
if _CURRENT_AGENT is None:
|
|
319
391
|
agent_name = get_current_agent_name()
|
|
320
|
-
|
|
321
|
-
# Restore the agent's history if it exists
|
|
322
|
-
_restore_agent_history(agent_name, _CURRENT_AGENT_CONFIG)
|
|
392
|
+
_CURRENT_AGENT = load_agent(agent_name)
|
|
323
393
|
|
|
324
|
-
return
|
|
394
|
+
return _CURRENT_AGENT
|
|
325
395
|
|
|
326
396
|
|
|
327
|
-
def
|
|
397
|
+
def load_agent(agent_name: str) -> BaseAgent:
|
|
328
398
|
"""Load an agent configuration by name.
|
|
329
399
|
|
|
330
400
|
Args:
|
|
@@ -362,12 +432,21 @@ def get_agent_descriptions() -> Dict[str, str]:
|
|
|
362
432
|
Returns:
|
|
363
433
|
Dict mapping agent names to their descriptions.
|
|
364
434
|
"""
|
|
435
|
+
from ..config import PACK_AGENT_NAMES, get_pack_agents_enabled
|
|
436
|
+
|
|
365
437
|
# Generate a message group ID for this operation
|
|
366
438
|
message_group_id = str(uuid.uuid4())
|
|
367
439
|
_discover_agents(message_group_id=message_group_id)
|
|
368
440
|
|
|
441
|
+
# Check if pack agents are enabled
|
|
442
|
+
pack_agents_enabled = get_pack_agents_enabled()
|
|
443
|
+
|
|
369
444
|
descriptions = {}
|
|
370
445
|
for name, agent_ref in _AGENT_REGISTRY.items():
|
|
446
|
+
# Filter out pack agents if disabled
|
|
447
|
+
if not pack_agents_enabled and name in PACK_AGENT_NAMES:
|
|
448
|
+
continue
|
|
449
|
+
|
|
371
450
|
try:
|
|
372
451
|
if isinstance(agent_ref, str): # JSON agent (file path)
|
|
373
452
|
agent_instance = JSONAgent(agent_ref)
|
|
@@ -380,26 +459,6 @@ def get_agent_descriptions() -> Dict[str, str]:
|
|
|
380
459
|
return descriptions
|
|
381
460
|
|
|
382
461
|
|
|
383
|
-
def clear_agent_cache():
|
|
384
|
-
"""Clear the cached agent configuration to force reload."""
|
|
385
|
-
global _CURRENT_AGENT_CONFIG
|
|
386
|
-
_CURRENT_AGENT_CONFIG = None
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
def reset_to_default_agent():
|
|
390
|
-
"""Reset the current agent to the default (code-puppy) for this terminal session.
|
|
391
|
-
|
|
392
|
-
This is useful for testing or when you want to start fresh.
|
|
393
|
-
"""
|
|
394
|
-
global _CURRENT_AGENT_CONFIG
|
|
395
|
-
_ensure_session_cache_loaded()
|
|
396
|
-
session_id = get_terminal_session_id()
|
|
397
|
-
if session_id in _SESSION_AGENTS_CACHE:
|
|
398
|
-
del _SESSION_AGENTS_CACHE[session_id]
|
|
399
|
-
_save_session_data(_SESSION_AGENTS_CACHE)
|
|
400
|
-
_CURRENT_AGENT_CONFIG = None
|
|
401
|
-
|
|
402
|
-
|
|
403
462
|
def refresh_agents():
|
|
404
463
|
"""Refresh the agent discovery to pick up newly created agents.
|
|
405
464
|
|
|
@@ -408,115 +467,3 @@ def refresh_agents():
|
|
|
408
467
|
# Generate a message group ID for agent refreshing
|
|
409
468
|
message_group_id = str(uuid.uuid4())
|
|
410
469
|
_discover_agents(message_group_id=message_group_id)
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
def clear_all_agent_histories():
|
|
414
|
-
"""Clear all agent message histories from persistent storage.
|
|
415
|
-
|
|
416
|
-
This is useful for debugging or when you want a fresh start.
|
|
417
|
-
"""
|
|
418
|
-
global _AGENT_HISTORIES
|
|
419
|
-
_AGENT_HISTORIES.clear()
|
|
420
|
-
# Also clear the current agent's history
|
|
421
|
-
if _CURRENT_AGENT_CONFIG is not None:
|
|
422
|
-
_CURRENT_AGENT_CONFIG.messages = []
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
def cleanup_dead_terminal_sessions() -> int:
|
|
426
|
-
"""Clean up terminal sessions for processes that no longer exist.
|
|
427
|
-
|
|
428
|
-
Returns:
|
|
429
|
-
int: Number of dead sessions removed
|
|
430
|
-
"""
|
|
431
|
-
_ensure_session_cache_loaded()
|
|
432
|
-
original_count = len(_SESSION_AGENTS_CACHE)
|
|
433
|
-
cleaned_cache = _cleanup_dead_sessions(_SESSION_AGENTS_CACHE)
|
|
434
|
-
|
|
435
|
-
if len(cleaned_cache) != original_count:
|
|
436
|
-
_SESSION_AGENTS_CACHE.clear()
|
|
437
|
-
_SESSION_AGENTS_CACHE.update(cleaned_cache)
|
|
438
|
-
_save_session_data(_SESSION_AGENTS_CACHE)
|
|
439
|
-
|
|
440
|
-
return original_count - len(cleaned_cache)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
# Agent-aware message history functions
|
|
444
|
-
def get_current_agent_message_history():
|
|
445
|
-
"""Get the message history for the currently active agent.
|
|
446
|
-
|
|
447
|
-
Returns:
|
|
448
|
-
List of messages from the current agent's conversation history.
|
|
449
|
-
"""
|
|
450
|
-
current_agent = get_current_agent_config()
|
|
451
|
-
return current_agent.get_message_history()
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
def set_current_agent_message_history(history):
|
|
455
|
-
"""Set the message history for the currently active agent.
|
|
456
|
-
|
|
457
|
-
Args:
|
|
458
|
-
history: List of messages to set as the current agent's conversation history.
|
|
459
|
-
"""
|
|
460
|
-
current_agent = get_current_agent_config()
|
|
461
|
-
current_agent.set_message_history(history)
|
|
462
|
-
# Also update persistent storage
|
|
463
|
-
_save_agent_history(current_agent.name, current_agent)
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
def clear_current_agent_message_history():
|
|
467
|
-
"""Clear the message history for the currently active agent."""
|
|
468
|
-
current_agent = get_current_agent_config()
|
|
469
|
-
current_agent.clear_message_history()
|
|
470
|
-
# Also clear from persistent storage
|
|
471
|
-
global _AGENT_HISTORIES
|
|
472
|
-
if current_agent.name in _AGENT_HISTORIES:
|
|
473
|
-
_AGENT_HISTORIES[current_agent.name] = {
|
|
474
|
-
"message_history": [],
|
|
475
|
-
"compacted_hashes": set(),
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
def append_to_current_agent_message_history(message):
|
|
480
|
-
"""Append a message to the currently active agent's history.
|
|
481
|
-
|
|
482
|
-
Args:
|
|
483
|
-
message: Message to append to the current agent's conversation history.
|
|
484
|
-
"""
|
|
485
|
-
current_agent = get_current_agent_config()
|
|
486
|
-
current_agent.append_to_message_history(message)
|
|
487
|
-
# Also update persistent storage
|
|
488
|
-
_save_agent_history(current_agent.name, current_agent)
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
def extend_current_agent_message_history(history):
|
|
492
|
-
"""Extend the currently active agent's message history with multiple messages.
|
|
493
|
-
|
|
494
|
-
Args:
|
|
495
|
-
history: List of messages to append to the current agent's conversation history.
|
|
496
|
-
"""
|
|
497
|
-
current_agent = get_current_agent_config()
|
|
498
|
-
current_agent.extend_message_history(history)
|
|
499
|
-
# Also update persistent storage
|
|
500
|
-
_save_agent_history(current_agent.name, current_agent)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
def get_current_agent_compacted_message_hashes():
|
|
504
|
-
"""Get the set of compacted message hashes for the currently active agent.
|
|
505
|
-
|
|
506
|
-
Returns:
|
|
507
|
-
Set of hashes for messages that have been compacted/summarized.
|
|
508
|
-
"""
|
|
509
|
-
current_agent = get_current_agent_config()
|
|
510
|
-
return current_agent.get_compacted_message_hashes()
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
def add_current_agent_compacted_message_hash(message_hash: str):
|
|
514
|
-
"""Add a message hash to the current agent's set of compacted message hashes.
|
|
515
|
-
|
|
516
|
-
Args:
|
|
517
|
-
message_hash: Hash of a message that has been compacted/summarized.
|
|
518
|
-
"""
|
|
519
|
-
current_agent = get_current_agent_config()
|
|
520
|
-
current_agent.add_compacted_message_hash(message_hash)
|
|
521
|
-
# Also update persistent storage
|
|
522
|
-
_save_agent_history(current_agent.name, current_agent)
|