code-puppy 0.0.341__py3-none-any.whl → 0.0.361__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 +2 -0
- code_puppy/agents/agent_manager.py +49 -0
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_qa_kitten.py +12 -7
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/base_agent.py +34 -252
- code_puppy/agents/event_stream_handler.py +350 -0
- 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/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 +73 -0
- code_puppy/claude_cache_client.py +249 -34
- code_puppy/cli_runner.py +4 -3
- code_puppy/command_line/add_model_menu.py +8 -9
- code_puppy/command_line/core_commands.py +85 -0
- code_puppy/command_line/mcp/catalog_server_installer.py +5 -6
- code_puppy/command_line/mcp/custom_server_form.py +54 -19
- code_puppy/command_line/mcp/custom_server_installer.py +8 -9
- code_puppy/command_line/mcp/handler.py +0 -2
- code_puppy/command_line/mcp/help_command.py +1 -5
- code_puppy/command_line/mcp/start_command.py +36 -18
- code_puppy/command_line/onboarding_slides.py +0 -1
- code_puppy/command_line/prompt_toolkit_completion.py +16 -10
- code_puppy/command_line/utils.py +54 -0
- code_puppy/config.py +66 -62
- code_puppy/mcp_/async_lifecycle.py +35 -4
- code_puppy/mcp_/managed_server.py +49 -20
- code_puppy/mcp_/manager.py +81 -52
- code_puppy/messaging/__init__.py +15 -0
- code_puppy/messaging/message_queue.py +11 -23
- code_puppy/messaging/messages.py +27 -0
- code_puppy/messaging/queue_console.py +1 -1
- code_puppy/messaging/rich_renderer.py +36 -1
- code_puppy/messaging/spinner/__init__.py +20 -2
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_utils.py +54 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +90 -19
- code_puppy/plugins/antigravity_oauth/transport.py +1 -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/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/status_display.py +6 -2
- code_puppy/tools/__init__.py +37 -1
- code_puppy/tools/agent_tools.py +139 -36
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +6 -6
- code_puppy/tools/browser/browser_interactions.py +21 -20
- code_puppy/tools/browser/browser_locators.py +9 -9
- code_puppy/tools/browser/browser_navigation.py +7 -7
- code_puppy/tools/browser/browser_screenshot.py +78 -140
- code_puppy/tools/browser/browser_scripts.py +15 -13
- code_puppy/tools/browser/camoufox_manager.py +226 -64
- 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 +292 -101
- code_puppy/tools/common.py +176 -1
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/subagent_context.py +158 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/METADATA +13 -11
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/RECORD +84 -53
- code_puppy/command_line/mcp/add_command.py +0 -170
- code_puppy/tools/browser/vqa_agent.py +0 -90
- {code_puppy-0.0.341.data → code_puppy-0.0.361.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.341.data → code_puppy-0.0.361.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.341.dist-info → code_puppy-0.0.361.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Frontend emitter plugin for Code Puppy.
|
|
2
|
+
|
|
3
|
+
This plugin provides event emission capabilities for frontend integration,
|
|
4
|
+
allowing WebSocket handlers to subscribe to real-time events from the
|
|
5
|
+
agent system including tool calls, streaming events, and agent invocations.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from code_puppy.plugins.frontend_emitter.emitter import (
|
|
9
|
+
emit_event,
|
|
10
|
+
subscribe,
|
|
11
|
+
unsubscribe,
|
|
12
|
+
get_recent_events,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Subscribe to events
|
|
16
|
+
queue = subscribe()
|
|
17
|
+
|
|
18
|
+
# Process events in your WebSocket handler
|
|
19
|
+
while True:
|
|
20
|
+
event = await queue.get()
|
|
21
|
+
await websocket.send_json(event)
|
|
22
|
+
|
|
23
|
+
# Clean up
|
|
24
|
+
unsubscribe(queue)
|
|
25
|
+
"""
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Event emitter for frontend integration.
|
|
2
|
+
|
|
3
|
+
Provides a global event queue that WebSocket handlers can subscribe to.
|
|
4
|
+
Events are JSON-serializable dicts with type, timestamp, and data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from typing import Any, Dict, List, Set
|
|
11
|
+
from uuid import uuid4
|
|
12
|
+
|
|
13
|
+
from code_puppy.config import (
|
|
14
|
+
get_frontend_emitter_enabled,
|
|
15
|
+
get_frontend_emitter_max_recent_events,
|
|
16
|
+
get_frontend_emitter_queue_size,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Global state for event distribution
|
|
22
|
+
_subscribers: Set[asyncio.Queue[Dict[str, Any]]] = set()
|
|
23
|
+
_recent_events: List[Dict[str, Any]] = [] # Keep last N events for new subscribers
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def emit_event(event_type: str, data: Any = None) -> None:
|
|
27
|
+
"""Emit an event to all subscribers.
|
|
28
|
+
|
|
29
|
+
Creates a structured event dict with unique ID, type, timestamp, and data,
|
|
30
|
+
then broadcasts it to all active subscriber queues.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
event_type: Type of event (e.g., "tool_call_start", "stream_token")
|
|
34
|
+
data: Event data payload - should be JSON-serializable
|
|
35
|
+
"""
|
|
36
|
+
# Early return if emitter is disabled
|
|
37
|
+
if not get_frontend_emitter_enabled():
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
event: Dict[str, Any] = {
|
|
41
|
+
"id": str(uuid4()),
|
|
42
|
+
"type": event_type,
|
|
43
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
44
|
+
"data": data or {},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Store in recent events for replay to new subscribers
|
|
48
|
+
max_recent = get_frontend_emitter_max_recent_events()
|
|
49
|
+
_recent_events.append(event)
|
|
50
|
+
if len(_recent_events) > max_recent:
|
|
51
|
+
_recent_events.pop(0)
|
|
52
|
+
|
|
53
|
+
# Broadcast to all active subscribers
|
|
54
|
+
for subscriber_queue in _subscribers.copy():
|
|
55
|
+
try:
|
|
56
|
+
subscriber_queue.put_nowait(event)
|
|
57
|
+
except asyncio.QueueFull:
|
|
58
|
+
logger.warning(f"Subscriber queue full, dropping event: {event_type}")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.error(f"Failed to emit event to subscriber: {e}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def subscribe() -> asyncio.Queue[Dict[str, Any]]:
|
|
64
|
+
"""Subscribe to events.
|
|
65
|
+
|
|
66
|
+
Creates and returns a new async queue that will receive all future events.
|
|
67
|
+
The queue has a configurable max size (via frontend_emitter_queue_size)
|
|
68
|
+
to prevent unbounded memory growth if the subscriber is slow to process events.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
An asyncio.Queue that will receive event dictionaries.
|
|
72
|
+
"""
|
|
73
|
+
queue_size = get_frontend_emitter_queue_size()
|
|
74
|
+
queue: asyncio.Queue[Dict[str, Any]] = asyncio.Queue(maxsize=queue_size)
|
|
75
|
+
_subscribers.add(queue)
|
|
76
|
+
logger.debug(f"New subscriber added, total subscribers: {len(_subscribers)}")
|
|
77
|
+
return queue
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def unsubscribe(queue: asyncio.Queue[Dict[str, Any]]) -> None:
|
|
81
|
+
"""Unsubscribe from events.
|
|
82
|
+
|
|
83
|
+
Removes the queue from the subscriber set. Safe to call even if the queue
|
|
84
|
+
was never subscribed or already unsubscribed.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
queue: The queue returned from subscribe()
|
|
88
|
+
"""
|
|
89
|
+
_subscribers.discard(queue)
|
|
90
|
+
logger.debug(f"Subscriber removed, remaining subscribers: {len(_subscribers)}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_recent_events() -> List[Dict[str, Any]]:
|
|
94
|
+
"""Get recent events for new subscribers.
|
|
95
|
+
|
|
96
|
+
Returns a copy of the most recent events (up to frontend_emitter_max_recent_events).
|
|
97
|
+
Useful for allowing new WebSocket connections to "catch up" on
|
|
98
|
+
recent activity.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
A list of recent event dictionaries.
|
|
102
|
+
"""
|
|
103
|
+
return _recent_events.copy()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_subscriber_count() -> int:
|
|
107
|
+
"""Get the current number of active subscribers.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Number of active subscriber queues.
|
|
111
|
+
"""
|
|
112
|
+
return len(_subscribers)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def clear_recent_events() -> None:
|
|
116
|
+
"""Clear the recent events buffer.
|
|
117
|
+
|
|
118
|
+
Useful for testing or resetting state.
|
|
119
|
+
"""
|
|
120
|
+
_recent_events.clear()
|
|
121
|
+
logger.debug("Recent events cleared")
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Callback registration for frontend event emission.
|
|
2
|
+
|
|
3
|
+
This module registers callbacks for various agent events and emits them
|
|
4
|
+
to subscribed WebSocket handlers via the emitter module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import time
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
from code_puppy.callbacks import register_callback
|
|
12
|
+
from code_puppy.plugins.frontend_emitter.emitter import emit_event
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def on_pre_tool_call(
|
|
18
|
+
tool_name: str, tool_args: Dict[str, Any], context: Any = None
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Emit an event when a tool call starts.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
tool_name: Name of the tool being called
|
|
24
|
+
tool_args: Arguments being passed to the tool
|
|
25
|
+
context: Optional context data for the tool call
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
emit_event(
|
|
29
|
+
"tool_call_start",
|
|
30
|
+
{
|
|
31
|
+
"tool_name": tool_name,
|
|
32
|
+
"tool_args": _sanitize_args(tool_args),
|
|
33
|
+
"start_time": time.time(),
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
logger.debug(f"Emitted tool_call_start for {tool_name}")
|
|
37
|
+
except Exception as e:
|
|
38
|
+
logger.error(f"Failed to emit pre_tool_call event: {e}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def on_post_tool_call(
|
|
42
|
+
tool_name: str,
|
|
43
|
+
tool_args: Dict[str, Any],
|
|
44
|
+
result: Any,
|
|
45
|
+
duration_ms: float,
|
|
46
|
+
context: Any = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Emit an event when a tool call completes.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
tool_name: Name of the tool that was called
|
|
52
|
+
tool_args: Arguments that were passed to the tool
|
|
53
|
+
result: The result returned by the tool
|
|
54
|
+
duration_ms: Execution time in milliseconds
|
|
55
|
+
context: Optional context data for the tool call
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
emit_event(
|
|
59
|
+
"tool_call_complete",
|
|
60
|
+
{
|
|
61
|
+
"tool_name": tool_name,
|
|
62
|
+
"tool_args": _sanitize_args(tool_args),
|
|
63
|
+
"duration_ms": duration_ms,
|
|
64
|
+
"success": _is_successful_result(result),
|
|
65
|
+
"result_summary": _summarize_result(result),
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
logger.debug(
|
|
69
|
+
f"Emitted tool_call_complete for {tool_name} ({duration_ms:.2f}ms)"
|
|
70
|
+
)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"Failed to emit post_tool_call event: {e}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def on_stream_event(
|
|
76
|
+
event_type: str, event_data: Any, agent_session_id: Optional[str] = None
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Emit streaming events from the agent.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
event_type: Type of the streaming event
|
|
82
|
+
event_data: Data associated with the event
|
|
83
|
+
agent_session_id: Optional session ID of the agent emitting the event
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
emit_event(
|
|
87
|
+
"stream_event",
|
|
88
|
+
{
|
|
89
|
+
"event_type": event_type,
|
|
90
|
+
"event_data": _sanitize_event_data(event_data),
|
|
91
|
+
"agent_session_id": agent_session_id,
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
logger.debug(f"Emitted stream_event: {event_type}")
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Failed to emit stream_event: {e}")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def on_invoke_agent(*args: Any, **kwargs: Any) -> None:
|
|
100
|
+
"""Emit an event when an agent is invoked.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
*args: Positional arguments from the invoke_agent callback
|
|
104
|
+
**kwargs: Keyword arguments from the invoke_agent callback
|
|
105
|
+
"""
|
|
106
|
+
try:
|
|
107
|
+
# Extract relevant info from args/kwargs
|
|
108
|
+
agent_info = {
|
|
109
|
+
"agent_name": kwargs.get("agent_name") or (args[0] if args else None),
|
|
110
|
+
"session_id": kwargs.get("session_id"),
|
|
111
|
+
"prompt_preview": _truncate_string(
|
|
112
|
+
kwargs.get("prompt") or (args[1] if len(args) > 1 else None),
|
|
113
|
+
max_length=200,
|
|
114
|
+
),
|
|
115
|
+
}
|
|
116
|
+
emit_event("agent_invoked", agent_info)
|
|
117
|
+
logger.debug(f"Emitted agent_invoked: {agent_info.get('agent_name')}")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Failed to emit invoke_agent event: {e}")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _sanitize_args(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
123
|
+
"""Sanitize tool arguments for safe emission.
|
|
124
|
+
|
|
125
|
+
Truncates large values and removes potentially sensitive data.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
args: The raw tool arguments
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Sanitized arguments safe for emission
|
|
132
|
+
"""
|
|
133
|
+
if not isinstance(args, dict):
|
|
134
|
+
return {}
|
|
135
|
+
|
|
136
|
+
sanitized: Dict[str, Any] = {}
|
|
137
|
+
for key, value in args.items():
|
|
138
|
+
if isinstance(value, str):
|
|
139
|
+
sanitized[key] = _truncate_string(value, max_length=500)
|
|
140
|
+
elif isinstance(value, (int, float, bool, type(None))):
|
|
141
|
+
sanitized[key] = value
|
|
142
|
+
elif isinstance(value, (list, dict)):
|
|
143
|
+
# Just indicate the type and length for complex types
|
|
144
|
+
sanitized[key] = f"<{type(value).__name__}[{len(value)}]>"
|
|
145
|
+
else:
|
|
146
|
+
sanitized[key] = f"<{type(value).__name__}>"
|
|
147
|
+
|
|
148
|
+
return sanitized
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _sanitize_event_data(data: Any) -> Any:
|
|
152
|
+
"""Sanitize event data for safe emission.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
data: The raw event data
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Sanitized data safe for emission
|
|
159
|
+
"""
|
|
160
|
+
if data is None:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
if isinstance(data, str):
|
|
164
|
+
return _truncate_string(data, max_length=1000)
|
|
165
|
+
|
|
166
|
+
if isinstance(data, (int, float, bool)):
|
|
167
|
+
return data
|
|
168
|
+
|
|
169
|
+
if isinstance(data, dict):
|
|
170
|
+
return {k: _sanitize_event_data(v) for k, v in list(data.items())[:20]}
|
|
171
|
+
|
|
172
|
+
if isinstance(data, (list, tuple)):
|
|
173
|
+
return [_sanitize_event_data(item) for item in data[:20]]
|
|
174
|
+
|
|
175
|
+
return f"<{type(data).__name__}>"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _is_successful_result(result: Any) -> bool:
|
|
179
|
+
"""Determine if a tool result indicates success.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
result: The tool result
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
True if the result appears successful
|
|
186
|
+
"""
|
|
187
|
+
if result is None:
|
|
188
|
+
return True # No result often means success
|
|
189
|
+
|
|
190
|
+
if isinstance(result, dict):
|
|
191
|
+
# Check for error indicators
|
|
192
|
+
if result.get("error"):
|
|
193
|
+
return False
|
|
194
|
+
if result.get("success") is False:
|
|
195
|
+
return False
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
if isinstance(result, bool):
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
return True # Default to success
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _summarize_result(result: Any) -> str:
|
|
205
|
+
"""Create a brief summary of a tool result.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
result: The tool result
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
A string summary of the result
|
|
212
|
+
"""
|
|
213
|
+
if result is None:
|
|
214
|
+
return "<no result>"
|
|
215
|
+
|
|
216
|
+
if isinstance(result, str):
|
|
217
|
+
return _truncate_string(result, max_length=200)
|
|
218
|
+
|
|
219
|
+
if isinstance(result, dict):
|
|
220
|
+
if "error" in result:
|
|
221
|
+
return f"Error: {_truncate_string(str(result['error']), max_length=100)}"
|
|
222
|
+
if "message" in result:
|
|
223
|
+
return _truncate_string(str(result["message"]), max_length=100)
|
|
224
|
+
return f"<dict with {len(result)} keys>"
|
|
225
|
+
|
|
226
|
+
if isinstance(result, (list, tuple)):
|
|
227
|
+
return f"<{type(result).__name__}[{len(result)}]>"
|
|
228
|
+
|
|
229
|
+
return _truncate_string(str(result), max_length=200)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _truncate_string(value: Any, max_length: int = 100) -> Optional[str]:
|
|
233
|
+
"""Truncate a string value if it exceeds max_length.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
value: The value to truncate (will be converted to str)
|
|
237
|
+
max_length: Maximum length before truncation
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Truncated string or None if value is None
|
|
241
|
+
"""
|
|
242
|
+
if value is None:
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
s = str(value)
|
|
246
|
+
if len(s) > max_length:
|
|
247
|
+
return s[: max_length - 3] + "..."
|
|
248
|
+
return s
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def register() -> None:
|
|
252
|
+
"""Register all frontend emitter callbacks."""
|
|
253
|
+
register_callback("pre_tool_call", on_pre_tool_call)
|
|
254
|
+
register_callback("post_tool_call", on_post_tool_call)
|
|
255
|
+
register_callback("stream_event", on_stream_event)
|
|
256
|
+
register_callback("invoke_agent", on_invoke_agent)
|
|
257
|
+
logger.debug("Frontend emitter callbacks registered")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# Auto-register callbacks when this module is imported
|
|
261
|
+
register()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<identity>\nYou are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.\nYou are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.\nThe USER will send you requests, which you must always prioritize addressing. Along with each USER request, we will attach additional metadata about their current state, such as what files they have open and where their cursor is.\nThis information may or may not be relevant to the coding task, it is up for you to decide.\n</identity>\n\n<tool_calling>\nCall tools as you normally would. The following list provides additional guidance to help you avoid errors:\n - **Absolute paths only**. When using tools that accept file path arguments, ALWAYS use the absolute file path.\n</tool_calling>\n\n<web_application_development>\n## Technology Stack,\nYour web applications should be built using the following technologies:,\n1. **Core**: Use HTML for structure and Javascript for logic.\n2. **Styling (CSS)**: Use Vanilla CSS for maximum flexibility and control. Avoid using TailwindCSS unless the USER explicitly requests it; in this case, first confirm which TailwindCSS version to use.\n3. **Web App**: If the USER specifies that they want a more complex web app, use a framework like Next.js or Vite. Only do this if the USER explicitly requests a web app.\n4. **New Project Creation**: If you need to use a framework for a new app, use `npx` with the appropriate script, but there are some rules to follow:,\n - Use `npx -y` to automatically install the script and its dependencies\n - You MUST run the command with `--help` flag to see all available options first, \n - Initialize the app in the current directory with `./` (example: `npx -y create-vite-app@latest ./`),\n - You should run in non-interactive mode so that the user doesn't need to input anything,\n5. **Running Locally**: When running locally, use `npm run dev` or equivalent dev server. Only build the production bundle if the USER explicitly requests it or you are validating the code for correctness.\n\n# Design Aesthetics,\n1. **Use Rich Aesthetics**: The USER should be wowed at first glance by the design. Use best practices in modern web design (e.g. vibrant colors, dark modes, glassmorphism, and dynamic animations) to create a stunning first impression. Failure to do this is UNACCEPTABLE.\n2. **Prioritize Visual Excellence**: Implement designs that will WOW the user and feel extremely premium:\n\t\t- Avoid generic colors (plain red, blue, green). Use curated, harmonious color palettes (e.g., HSL tailored colors, sleek dark modes).\n - Using modern typography (e.g., from Google Fonts like Inter, Roboto, or Outfit) instead of browser defaults.\n\t\t- Use smooth gradients,\n\t\t- Add subtle micro-animations for enhanced user experience,\n3. **Use a Dynamic Design**: An interface that feels responsive and alive encourages interaction. Achieve this with hover effects and interactive elements. Micro-animations, in particular, are highly effective for improving user engagement.\n4. **Premium Designs**. Make a design that feels premium and state of the art. Avoid creating simple minimum viable products.\n4. **Don't use placeholders**. If you need an image, use your generate_image tool to create a working demonstration.,\n\n## Implementation Workflow,\nFollow this systematic approach when building web applications:,\n1. **Plan and Understand**:,\n\t\t- Fully understand the user's requirements,\n\t\t- Draw inspiration from modern, beautiful, and dynamic web designs,\n\t\t- Outline the features needed for the initial version,\n2. **Build the Foundation**:,\n\t\t- Start by creating/modifying `index.css`,\n\t\t- Implement the core design system with all tokens and utilities,\n3. **Create Components**:,\n\t\t- Build necessary components using your design system,\n\t\t- Ensure all components use predefined styles, not ad-hoc utilities,\n\t\t- Keep components focused and reusable,\n4. **Assemble Pages**:,\n\t\t- Update the main application to incorporate your design and components,\n\t\t- Ensure proper routing and navigation,\n\t\t- Implement responsive layouts,\n5. **Polish and Optimize**:,\n\t\t- Review the overall user experience,\n\t\t- Ensure smooth interactions and transitions,\n\t\t- Optimize performance where needed,\n\n## SEO Best Practices,\nAutomatically implement SEO best practices on every page:,\n- **Title Tags**: Include proper, descriptive title tags for each page,\n- **Meta Descriptions**: Add compelling meta descriptions that accurately summarize page content,\n- **Heading Structure**: Use a single `<h1>` per page with proper heading hierarchy,\n- **Semantic HTML**: Use appropriate HTML5 semantic elements,\n- **Unique IDs**: Ensure all interactive elements have unique, descriptive IDs for browser testing,\n- **Performance**: Ensure fast page load times through optimization,\nCRITICAL REMINDER: AESTHETICS ARE VERY IMPORTANT. If your web app looks simple and basic then you have FAILED!\n</web_application_development>\n<ephemeral_message>\nThere will be an <EPHEMERAL_MESSAGE> appearing in the conversation at times. This is not coming from the user, but instead injected by the system as important information to pay attention to. \nDo not respond to nor acknowledge those messages, but do follow them strictly.\n</ephemeral_message>\n\n\n<communication_style>\n- **Formatting**. Format your responses in github-style markdown to make your responses easier for the USER to parse. For example, use headers to organize your responses and bolded or italicized text to highlight important keywords. Use backticks to format file, directory, function, and class names. If providing a URL to the user, format this in markdown as well, for example `[label](example.com)`.\n- **Proactiveness**. As an agent, you are allowed to be proactive, but only in the course of completing the user's task. For example, if the user asks you to add a new component, you can edit the code, verify build and test statuses, and take any other obvious follow-up actions, such as performing additional research. However, avoid surprising the user. For example, if the user asks HOW to approach something, you should answer their question and instead of jumping into editing a file.\n- **Helpfulness**. Respond like a helpful software engineer who is explaining your work to a friendly collaborator on the project. Acknowledge mistakes or any backtracking you do as a result of new information.\n- **Ask for clarification**. If you are unsure about the USER's intent, always ask for clarification rather than making assumptions.\n</communication_style>
|
code_puppy/status_display.py
CHANGED
|
@@ -7,8 +7,6 @@ from rich.panel import Panel
|
|
|
7
7
|
from rich.spinner import Spinner
|
|
8
8
|
from rich.text import Text
|
|
9
9
|
|
|
10
|
-
from code_puppy.messaging import emit_info
|
|
11
|
-
|
|
12
10
|
# Global variable to track current token per second rate
|
|
13
11
|
CURRENT_TOKEN_RATE = 0.0
|
|
14
12
|
|
|
@@ -186,6 +184,9 @@ class StatusDisplay:
|
|
|
186
184
|
|
|
187
185
|
async def _update_display(self) -> None:
|
|
188
186
|
"""Update the display continuously while active using Rich Live display"""
|
|
187
|
+
# Lazy import to avoid circular dependency during module initialization
|
|
188
|
+
from code_puppy.messaging import emit_info
|
|
189
|
+
|
|
189
190
|
# Add a newline to ensure we're below the blue bar
|
|
190
191
|
emit_info("")
|
|
191
192
|
|
|
@@ -214,6 +215,9 @@ class StatusDisplay:
|
|
|
214
215
|
|
|
215
216
|
def stop(self) -> None:
|
|
216
217
|
"""Stop the status display"""
|
|
218
|
+
# Lazy import to avoid circular dependency during module initialization
|
|
219
|
+
from code_puppy.messaging import emit_info
|
|
220
|
+
|
|
217
221
|
if self.is_active:
|
|
218
222
|
self.is_active = False
|
|
219
223
|
if self.task:
|
code_puppy/tools/__init__.py
CHANGED
|
@@ -55,10 +55,32 @@ from code_puppy.tools.browser.browser_workflows import (
|
|
|
55
55
|
register_read_workflow,
|
|
56
56
|
register_save_workflow,
|
|
57
57
|
)
|
|
58
|
+
from code_puppy.tools.browser.terminal_command_tools import (
|
|
59
|
+
register_run_terminal_command,
|
|
60
|
+
register_send_terminal_keys,
|
|
61
|
+
register_wait_terminal_output,
|
|
62
|
+
)
|
|
63
|
+
from code_puppy.tools.browser.terminal_screenshot_tools import (
|
|
64
|
+
register_load_image,
|
|
65
|
+
register_terminal_compare_mockup,
|
|
66
|
+
register_terminal_read_output,
|
|
67
|
+
register_terminal_screenshot,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Terminal automation tools
|
|
71
|
+
from code_puppy.tools.browser.terminal_tools import (
|
|
72
|
+
register_check_terminal_server,
|
|
73
|
+
register_close_terminal,
|
|
74
|
+
register_open_terminal,
|
|
75
|
+
register_start_api_server,
|
|
76
|
+
)
|
|
58
77
|
from code_puppy.tools.command_runner import (
|
|
59
78
|
register_agent_run_shell_command,
|
|
60
79
|
register_agent_share_your_reasoning,
|
|
61
80
|
)
|
|
81
|
+
from code_puppy.tools.display import (
|
|
82
|
+
display_non_streamed_result as display_non_streamed_result,
|
|
83
|
+
)
|
|
62
84
|
from code_puppy.tools.file_modifications import register_delete_file, register_edit_file
|
|
63
85
|
from code_puppy.tools.file_operations import (
|
|
64
86
|
register_grep,
|
|
@@ -121,12 +143,26 @@ TOOL_REGISTRY = {
|
|
|
121
143
|
"browser_wait_for_element": register_wait_for_element,
|
|
122
144
|
"browser_highlight_element": register_browser_highlight_element,
|
|
123
145
|
"browser_clear_highlights": register_browser_clear_highlights,
|
|
124
|
-
# Browser Screenshots
|
|
146
|
+
# Browser Screenshots
|
|
125
147
|
"browser_screenshot_analyze": register_take_screenshot_and_analyze,
|
|
126
148
|
# Browser Workflows
|
|
127
149
|
"browser_save_workflow": register_save_workflow,
|
|
128
150
|
"browser_list_workflows": register_list_workflows,
|
|
129
151
|
"browser_read_workflow": register_read_workflow,
|
|
152
|
+
# Terminal Connection Tools
|
|
153
|
+
"terminal_check_server": register_check_terminal_server,
|
|
154
|
+
"terminal_open": register_open_terminal,
|
|
155
|
+
"terminal_close": register_close_terminal,
|
|
156
|
+
"start_api_server": register_start_api_server,
|
|
157
|
+
# Terminal Command Execution Tools
|
|
158
|
+
"terminal_run_command": register_run_terminal_command,
|
|
159
|
+
"terminal_send_keys": register_send_terminal_keys,
|
|
160
|
+
"terminal_wait_output": register_wait_terminal_output,
|
|
161
|
+
# Terminal Screenshot Tools
|
|
162
|
+
"terminal_screenshot_analyze": register_terminal_screenshot,
|
|
163
|
+
"terminal_read_output": register_terminal_read_output,
|
|
164
|
+
"terminal_compare_mockup": register_terminal_compare_mockup,
|
|
165
|
+
"load_image_for_analysis": register_load_image,
|
|
130
166
|
}
|
|
131
167
|
|
|
132
168
|
|