code-puppy 0.0.348__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.
Files changed (87) hide show
  1. code_puppy/agents/__init__.py +8 -0
  2. code_puppy/agents/agent_manager.py +272 -1
  3. code_puppy/agents/agent_pack_leader.py +383 -0
  4. code_puppy/agents/agent_qa_kitten.py +12 -7
  5. code_puppy/agents/agent_terminal_qa.py +323 -0
  6. code_puppy/agents/base_agent.py +11 -8
  7. code_puppy/agents/event_stream_handler.py +101 -8
  8. code_puppy/agents/pack/__init__.py +34 -0
  9. code_puppy/agents/pack/bloodhound.py +304 -0
  10. code_puppy/agents/pack/husky.py +321 -0
  11. code_puppy/agents/pack/retriever.py +393 -0
  12. code_puppy/agents/pack/shepherd.py +348 -0
  13. code_puppy/agents/pack/terrier.py +287 -0
  14. code_puppy/agents/pack/watchdog.py +367 -0
  15. code_puppy/agents/subagent_stream_handler.py +276 -0
  16. code_puppy/api/__init__.py +13 -0
  17. code_puppy/api/app.py +169 -0
  18. code_puppy/api/main.py +21 -0
  19. code_puppy/api/pty_manager.py +446 -0
  20. code_puppy/api/routers/__init__.py +12 -0
  21. code_puppy/api/routers/agents.py +36 -0
  22. code_puppy/api/routers/commands.py +217 -0
  23. code_puppy/api/routers/config.py +74 -0
  24. code_puppy/api/routers/sessions.py +232 -0
  25. code_puppy/api/templates/terminal.html +361 -0
  26. code_puppy/api/websocket.py +154 -0
  27. code_puppy/callbacks.py +73 -0
  28. code_puppy/chatgpt_codex_client.py +53 -0
  29. code_puppy/claude_cache_client.py +294 -41
  30. code_puppy/command_line/add_model_menu.py +13 -4
  31. code_puppy/command_line/agent_menu.py +662 -0
  32. code_puppy/command_line/core_commands.py +89 -112
  33. code_puppy/command_line/model_picker_completion.py +3 -20
  34. code_puppy/command_line/model_settings_menu.py +21 -3
  35. code_puppy/config.py +145 -70
  36. code_puppy/gemini_model.py +706 -0
  37. code_puppy/http_utils.py +6 -3
  38. code_puppy/messaging/__init__.py +15 -0
  39. code_puppy/messaging/messages.py +27 -0
  40. code_puppy/messaging/queue_console.py +1 -1
  41. code_puppy/messaging/rich_renderer.py +36 -1
  42. code_puppy/messaging/spinner/__init__.py +20 -2
  43. code_puppy/messaging/subagent_console.py +461 -0
  44. code_puppy/model_factory.py +50 -16
  45. code_puppy/model_switching.py +63 -0
  46. code_puppy/model_utils.py +27 -24
  47. code_puppy/models.json +12 -12
  48. code_puppy/plugins/antigravity_oauth/antigravity_model.py +206 -172
  49. code_puppy/plugins/antigravity_oauth/register_callbacks.py +15 -8
  50. code_puppy/plugins/antigravity_oauth/transport.py +236 -45
  51. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +2 -2
  52. code_puppy/plugins/claude_code_oauth/register_callbacks.py +2 -30
  53. code_puppy/plugins/claude_code_oauth/utils.py +4 -1
  54. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  55. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  56. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  57. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  58. code_puppy/pydantic_patches.py +52 -0
  59. code_puppy/status_display.py +6 -2
  60. code_puppy/tools/__init__.py +37 -1
  61. code_puppy/tools/agent_tools.py +83 -33
  62. code_puppy/tools/browser/__init__.py +37 -0
  63. code_puppy/tools/browser/browser_control.py +6 -6
  64. code_puppy/tools/browser/browser_interactions.py +21 -20
  65. code_puppy/tools/browser/browser_locators.py +9 -9
  66. code_puppy/tools/browser/browser_manager.py +316 -0
  67. code_puppy/tools/browser/browser_navigation.py +7 -7
  68. code_puppy/tools/browser/browser_screenshot.py +78 -140
  69. code_puppy/tools/browser/browser_scripts.py +15 -13
  70. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  71. code_puppy/tools/browser/terminal_command_tools.py +521 -0
  72. code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
  73. code_puppy/tools/browser/terminal_tools.py +525 -0
  74. code_puppy/tools/command_runner.py +292 -101
  75. code_puppy/tools/common.py +176 -1
  76. code_puppy/tools/display.py +84 -0
  77. code_puppy/tools/subagent_context.py +158 -0
  78. {code_puppy-0.0.348.data → code_puppy-0.0.372.data}/data/code_puppy/models.json +12 -12
  79. {code_puppy-0.0.348.dist-info → code_puppy-0.0.372.dist-info}/METADATA +17 -16
  80. {code_puppy-0.0.348.dist-info → code_puppy-0.0.372.dist-info}/RECORD +84 -51
  81. code_puppy/prompts/codex_system_prompt.md +0 -310
  82. code_puppy/tools/browser/camoufox_manager.py +0 -235
  83. code_puppy/tools/browser/vqa_agent.py +0 -90
  84. {code_puppy-0.0.348.data → code_puppy-0.0.372.data}/data/code_puppy/models_dev_api.json +0 -0
  85. {code_puppy-0.0.348.dist-info → code_puppy-0.0.372.dist-info}/WHEEL +0 -0
  86. {code_puppy-0.0.348.dist-info → code_puppy-0.0.372.dist-info}/entry_points.txt +0 -0
  87. {code_puppy-0.0.348.dist-info → code_puppy-0.0.372.dist-info}/licenses/LICENSE +0 -0
code_puppy/http_utils.py CHANGED
@@ -9,11 +9,12 @@ import os
9
9
  import socket
10
10
  import time
11
11
  from dataclasses import dataclass
12
- from typing import Any, Dict, Optional, Union
12
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Union
13
13
 
14
14
  import httpx
15
- import requests
16
15
 
16
+ if TYPE_CHECKING:
17
+ import requests
17
18
  from code_puppy.config import get_http2
18
19
 
19
20
 
@@ -246,7 +247,9 @@ def create_requests_session(
246
247
  timeout: float = 5.0,
247
248
  verify: Union[bool, str] = None,
248
249
  headers: Optional[Dict[str, str]] = None,
249
- ) -> requests.Session:
250
+ ) -> "requests.Session":
251
+ import requests
252
+
250
253
  session = requests.Session()
251
254
 
252
255
  if verify is None:
@@ -113,6 +113,7 @@ from .messages import ( # Enums, Base, Text, File ops, Diff, Shell, Agent, etc.
113
113
  StatusPanelMessage,
114
114
  SubAgentInvocationMessage,
115
115
  SubAgentResponseMessage,
116
+ SubAgentStatusMessage,
116
117
  TextMessage,
117
118
  UserInputRequest,
118
119
  VersionCheckMessage,
@@ -120,6 +121,14 @@ from .messages import ( # Enums, Base, Text, File ops, Diff, Shell, Agent, etc.
120
121
  from .queue_console import QueueConsole, get_queue_console
121
122
  from .renderers import InteractiveRenderer, SynchronousInteractiveRenderer
122
123
 
124
+ # Sub-agent console manager
125
+ from .subagent_console import (
126
+ AgentState,
127
+ SubAgentConsoleManager,
128
+ get_subagent_console_manager,
129
+ STATUS_STYLES as SUBAGENT_STATUS_STYLES,
130
+ )
131
+
123
132
  # Renderer
124
133
  from .rich_renderer import (
125
134
  DEFAULT_STYLES,
@@ -193,6 +202,7 @@ __all__ = [
193
202
  "AgentResponseMessage",
194
203
  "SubAgentInvocationMessage",
195
204
  "SubAgentResponseMessage",
205
+ "SubAgentStatusMessage",
196
206
  "UserInputRequest",
197
207
  "ConfirmationRequest",
198
208
  "SelectionRequest",
@@ -229,4 +239,9 @@ __all__ = [
229
239
  "DIFF_STYLES",
230
240
  # Markdown patches
231
241
  "patch_markdown_headings",
242
+ # Sub-agent console manager
243
+ "AgentState",
244
+ "SubAgentConsoleManager",
245
+ "get_subagent_console_manager",
246
+ "SUBAGENT_STATUS_STYLES",
232
247
  ]
@@ -292,6 +292,31 @@ class SubAgentResponseMessage(BaseMessage):
292
292
  )
293
293
 
294
294
 
295
+ class SubAgentStatusMessage(BaseMessage):
296
+ """Real-time status update for a running sub-agent."""
297
+
298
+ category: MessageCategory = MessageCategory.AGENT
299
+ session_id: str = Field(description="Unique session ID of the sub-agent")
300
+ agent_name: str = Field(description="Name of the agent (e.g., 'code-puppy')")
301
+ model_name: str = Field(description="Model being used by this agent")
302
+ status: Literal[
303
+ "starting", "running", "thinking", "tool_calling", "completed", "error"
304
+ ] = Field(description="Current status of the agent")
305
+ tool_call_count: int = Field(
306
+ default=0, ge=0, description="Number of tools called so far"
307
+ )
308
+ token_count: int = Field(default=0, ge=0, description="Estimated tokens in context")
309
+ current_tool: Optional[str] = Field(
310
+ default=None, description="Name of tool currently being called"
311
+ )
312
+ elapsed_seconds: float = Field(
313
+ default=0.0, ge=0, description="Time since agent started"
314
+ )
315
+ error_message: Optional[str] = Field(
316
+ default=None, description="Error message if status is 'error'"
317
+ )
318
+
319
+
295
320
  # =============================================================================
296
321
  # User Interaction Messages (Agent → User)
297
322
  # =============================================================================
@@ -417,6 +442,7 @@ AnyMessage = Union[
417
442
  AgentResponseMessage,
418
443
  SubAgentInvocationMessage,
419
444
  SubAgentResponseMessage,
445
+ SubAgentStatusMessage,
420
446
  UserInputRequest,
421
447
  ConfirmationRequest,
422
448
  SelectionRequest,
@@ -458,6 +484,7 @@ __all__ = [
458
484
  "AgentResponseMessage",
459
485
  "SubAgentInvocationMessage",
460
486
  "SubAgentResponseMessage",
487
+ "SubAgentStatusMessage",
461
488
  # User interaction
462
489
  "UserInputRequest",
463
490
  "ConfirmationRequest",
@@ -239,7 +239,7 @@ class QueueConsole:
239
239
  # Show the user's response in the chat as well
240
240
  if user_response:
241
241
  self.queue.emit_simple(
242
- MessageType.USER, f"User response: {user_response}"
242
+ MessageType.INFO, f"User response: {user_response}"
243
243
  )
244
244
 
245
245
  return user_response
@@ -18,7 +18,9 @@ from rich.rule import Rule
18
18
  # Note: Syntax import removed - file content not displayed, only header
19
19
  from rich.table import Table
20
20
 
21
+ from code_puppy.config import get_subagent_verbose
21
22
  from code_puppy.tools.common import format_diff_with_colors
23
+ from code_puppy.tools.subagent_context import is_subagent
22
24
 
23
25
  from .bus import MessageBus
24
26
  from .commands import (
@@ -159,6 +161,14 @@ class RichConsoleRenderer:
159
161
  color = self._get_banner_color(banner_name)
160
162
  return f"[bold white on {color}] {text} [/bold white on {color}]"
161
163
 
164
+ def _should_suppress_subagent_output(self) -> bool:
165
+ """Check if sub-agent output should be suppressed.
166
+
167
+ Returns:
168
+ True if we're in a sub-agent context and verbose mode is disabled
169
+ """
170
+ return is_subagent() and not get_subagent_verbose()
171
+
162
172
  # =========================================================================
163
173
  # Lifecycle (Synchronous - for compatibility with main.py)
164
174
  # =========================================================================
@@ -275,7 +285,8 @@ class RichConsoleRenderer:
275
285
  elif isinstance(message, SubAgentInvocationMessage):
276
286
  self._render_subagent_invocation(message)
277
287
  elif isinstance(message, SubAgentResponseMessage):
278
- self._render_subagent_response(message)
288
+ # Skip rendering - we now display sub-agent responses via display_non_streamed_result
289
+ pass
279
290
  elif isinstance(message, UserInputRequest):
280
291
  # Can't handle async user input in sync context - skip
281
292
  self._console.print("[dim]User input requested (requires async)[/dim]")
@@ -356,6 +367,10 @@ class RichConsoleRenderer:
356
367
  - Total size
357
368
  - Number of subdirectories
358
369
  """
370
+ # Skip for sub-agents unless verbose mode
371
+ if self._should_suppress_subagent_output():
372
+ return
373
+
359
374
  import os
360
375
  from collections import defaultdict
361
376
 
@@ -478,6 +493,10 @@ class RichConsoleRenderer:
478
493
 
479
494
  The file content is for the LLM only, not for display in the UI.
480
495
  """
496
+ # Skip for sub-agents unless verbose mode
497
+ if self._should_suppress_subagent_output():
498
+ return
499
+
481
500
  # Build line info
482
501
  line_info = ""
483
502
  if msg.start_line is not None and msg.num_lines is not None:
@@ -492,6 +511,10 @@ class RichConsoleRenderer:
492
511
 
493
512
  def _render_grep_result(self, msg: GrepResultMessage) -> None:
494
513
  """Render grep results grouped by file matching old format."""
514
+ # Skip for sub-agents unless verbose mode
515
+ if self._should_suppress_subagent_output():
516
+ return
517
+
495
518
  import re
496
519
 
497
520
  # Header
@@ -572,6 +595,10 @@ class RichConsoleRenderer:
572
595
 
573
596
  def _render_diff(self, msg: DiffMessage) -> None:
574
597
  """Render a diff with beautiful syntax highlighting."""
598
+ # Skip for sub-agents unless verbose mode
599
+ if self._should_suppress_subagent_output():
600
+ return
601
+
575
602
  # Operation-specific styling
576
603
  op_icons = {"create": "✨", "modify": "✏️", "delete": "🗑️"}
577
604
  op_colors = {"create": "green", "modify": "yellow", "delete": "red"}
@@ -616,6 +643,10 @@ class RichConsoleRenderer:
616
643
 
617
644
  def _render_shell_start(self, msg: ShellStartMessage) -> None:
618
645
  """Render shell command start notification."""
646
+ # Skip for sub-agents unless verbose mode
647
+ if self._should_suppress_subagent_output():
648
+ return
649
+
619
650
  # Escape command to prevent Rich markup injection
620
651
  safe_command = escape_rich_markup(msg.command)
621
652
  # Header showing command is starting
@@ -700,6 +731,10 @@ class RichConsoleRenderer:
700
731
 
701
732
  def _render_subagent_invocation(self, msg: SubAgentInvocationMessage) -> None:
702
733
  """Render sub-agent invocation header with nice formatting."""
734
+ # Skip for sub-agents unless verbose mode (avoid nested invocation banners)
735
+ if self._should_suppress_subagent_output():
736
+ return
737
+
703
738
  # Header with agent name and session
704
739
  session_type = (
705
740
  "New session"
@@ -24,7 +24,16 @@ def unregister_spinner(spinner):
24
24
 
25
25
 
26
26
  def pause_all_spinners():
27
- """Pause all active spinners."""
27
+ """Pause all active spinners.
28
+
29
+ No-op when called from a sub-agent context to prevent
30
+ parallel sub-agents from interfering with the main spinner.
31
+ """
32
+ # Lazy import to avoid circular dependency
33
+ from code_puppy.tools.subagent_context import is_subagent
34
+
35
+ if is_subagent():
36
+ return # Sub-agents don't control the main spinner
28
37
  for spinner in _active_spinners:
29
38
  try:
30
39
  spinner.pause()
@@ -34,7 +43,16 @@ def pause_all_spinners():
34
43
 
35
44
 
36
45
  def resume_all_spinners():
37
- """Resume all active spinners."""
46
+ """Resume all active spinners.
47
+
48
+ No-op when called from a sub-agent context to prevent
49
+ parallel sub-agents from interfering with the main spinner.
50
+ """
51
+ # Lazy import to avoid circular dependency
52
+ from code_puppy.tools.subagent_context import is_subagent
53
+
54
+ if is_subagent():
55
+ return # Sub-agents don't control the main spinner
38
56
  for spinner in _active_spinners:
39
57
  try:
40
58
  spinner.resume()