code-puppy 0.0.201__py3-none-any.whl → 0.0.203__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.
@@ -252,7 +252,8 @@ def set_current_agent(agent_name: str) -> bool:
252
252
  global _CURRENT_AGENT
253
253
  curr_agent = get_current_agent()
254
254
  if curr_agent is not None:
255
- _AGENT_HISTORIES[curr_agent.name] = curr_agent.get_message_history()
255
+ # Store a shallow copy so future mutations don't affect saved history
256
+ _AGENT_HISTORIES[curr_agent.name] = list(curr_agent.get_message_history())
256
257
  # Generate a message group ID for agent switching
257
258
  message_group_id = str(uuid.uuid4())
258
259
  _discover_agents(message_group_id=message_group_id)
@@ -269,7 +270,8 @@ def set_current_agent(agent_name: str) -> bool:
269
270
  _SESSION_AGENTS_CACHE[session_id] = agent_name
270
271
  _save_session_data(_SESSION_AGENTS_CACHE)
271
272
  if agent_obj.name in _AGENT_HISTORIES:
272
- agent_obj.set_message_history(_AGENT_HISTORIES[agent_obj.name])
273
+ # Restore a copy to avoid sharing the same list instance
274
+ agent_obj.set_message_history(list(_AGENT_HISTORIES[agent_obj.name]))
273
275
  on_agent_reload(agent_obj.id, agent_name)
274
276
  return True
275
277
 
@@ -13,7 +13,7 @@ import pydantic
13
13
  import pydantic_ai.models
14
14
  from pydantic_ai import Agent as PydanticAgent
15
15
  from pydantic_ai import BinaryContent, DocumentUrl, ImageUrl
16
- from pydantic_ai import RunContext, UsageLimitExceeded
16
+ from pydantic_ai import RunContext, UsageLimitExceeded, UsageLimits
17
17
  from pydantic_ai.messages import (
18
18
  ModelMessage,
19
19
  ModelRequest,
@@ -950,7 +950,7 @@ class BaseAgent(ABC):
950
950
  self.set_message_history(
951
951
  self.prune_interrupted_tool_calls(self.get_message_history())
952
952
  )
953
- usage_limits = pydantic_ai.agent._usage.UsageLimits(request_limit=get_message_limit())
953
+ usage_limits = UsageLimits(request_limit=get_message_limit())
954
954
  result_ = await pydantic_agent.run(
955
955
  prompt_payload,
956
956
  message_history=self.get_message_history(),
@@ -151,20 +151,8 @@ def _is_supported_extension(path: Path) -> bool:
151
151
 
152
152
 
153
153
  def _parse_link(token: str) -> PromptLinkAttachment | None:
154
- if "://" not in token:
155
- return None
156
- scheme = token.split(":", 1)[0].lower()
157
- if scheme not in SUPPORTED_INLINE_SCHEMES:
158
- return None
159
- if token.lower().endswith(".pdf"):
160
- return PromptLinkAttachment(
161
- placeholder=token,
162
- url_part=DocumentUrl(url=token),
163
- )
164
- return PromptLinkAttachment(
165
- placeholder=token,
166
- url_part=ImageUrl(url=token),
167
- )
154
+ """URL parsing disabled: no URLs are treated as attachments."""
155
+ return None
168
156
 
169
157
 
170
158
  @dataclass
@@ -1,6 +1,7 @@
1
1
  """Camoufox browser manager - privacy-focused Firefox automation."""
2
2
 
3
- from typing import Optional
3
+ from pathlib import Path
4
+ from typing import Optional, TypeAlias
4
5
 
5
6
  import camoufox
6
7
  from camoufox.addons import DefaultAddons
@@ -9,6 +10,8 @@ from camoufox.locale import ALLOW_GEOIP, download_mmdb
9
10
  from camoufox.pkgman import CamoufoxFetcher, camoufox_path
10
11
  from playwright.async_api import Browser, BrowserContext, Page
11
12
 
13
+ _MIN_VIEWPORT_DIMENSION = 640
14
+
12
15
  from code_puppy.messaging import emit_info
13
16
 
14
17
 
@@ -38,6 +41,9 @@ class CamoufoxManager:
38
41
  self.block_webrtc = True # Block WebRTC for privacy
39
42
  self.humanize = True # Add human-like behavior
40
43
 
44
+ # Persistent profile directory for consistent browser state across runs
45
+ self.profile_dir = self._get_profile_directory()
46
+
41
47
  @classmethod
42
48
  def get_instance(cls) -> "CamoufoxManager":
43
49
  """Get the singleton instance."""
@@ -45,6 +51,16 @@ class CamoufoxManager:
45
51
  cls._instance = cls()
46
52
  return cls._instance
47
53
 
54
+ def _get_profile_directory(self) -> Path:
55
+ """Get or create the persistent profile directory.
56
+
57
+ Returns a Path object pointing to ~/.code_puppy/camoufox_profile
58
+ where browser data (cookies, history, bookmarks, etc.) will be stored.
59
+ """
60
+ profile_path = Path.home() / ".code_puppy" / "camoufox_profile"
61
+ profile_path.mkdir(parents=True, exist_ok=True)
62
+ return profile_path
63
+
48
64
  async def async_initialize(self) -> None:
49
65
  """Initialize Camoufox browser."""
50
66
  if self._initialized:
@@ -68,30 +84,40 @@ class CamoufoxManager:
68
84
 
69
85
  async def _initialize_camoufox(self) -> None:
70
86
  """Try to start Camoufox with the configured privacy settings."""
87
+ emit_info(f"[cyan]📁 Using persistent profile: {self.profile_dir}[/cyan]")
88
+
71
89
  camoufox_instance = camoufox.AsyncCamoufox(
72
90
  headless=self.headless,
73
91
  block_webrtc=self.block_webrtc,
74
92
  humanize=self.humanize,
75
93
  exclude_addons=list(DefaultAddons),
94
+ persistent_context=True,
95
+ user_data_dir=str(self.profile_dir),
76
96
  addons=[],
77
97
  )
78
- self._browser = await camoufox_instance.start()
79
- self._context = await self._browser.new_context(
80
- viewport={"width": 1920, "height": 1080},
81
- ignore_https_errors=True,
82
- )
83
- page = await self._context.new_page()
84
- await page.goto(self.homepage)
98
+
99
+ self._browser = camoufox_instance.browser
100
+ # Use persistent storage directory for browser context
101
+ # This ensures cookies, localStorage, history, etc. persist across runs
102
+ if not self._initialized:
103
+ self._context = await camoufox_instance.start()
104
+ self._initialized = True
105
+ # Do not auto-open a page here to avoid duplicate windows/tabs.
85
106
 
86
107
  async def get_current_page(self) -> Optional[Page]:
87
- """Get the currently active page."""
108
+ """Get the currently active page. Lazily creates one if none exist."""
88
109
  if not self._initialized or not self._context:
89
110
  await self.async_initialize()
90
111
 
91
- if self._context:
92
- pages = self._context.pages
93
- return pages[0] if pages else None
94
- return None
112
+ if not self._context:
113
+ return None
114
+
115
+ pages = self._context.pages
116
+ if pages:
117
+ return pages[0]
118
+
119
+ # Lazily create a new blank page without navigation
120
+ return await self._context.new_page()
95
121
 
96
122
  async def new_page(self, url: Optional[str] = None) -> Page:
97
123
  """Create a new page and optionally navigate to URL."""
@@ -140,9 +166,21 @@ class CamoufoxManager:
140
166
  return self._context.pages
141
167
 
142
168
  async def _cleanup(self) -> None:
143
- """Clean up browser resources."""
169
+ """Clean up browser resources and save persistent state."""
144
170
  try:
171
+ # Save browser state before closing (cookies, localStorage, etc.)
145
172
  if self._context:
173
+ try:
174
+ storage_state_path = self.profile_dir / "storage_state.json"
175
+ await self._context.storage_state(path=str(storage_state_path))
176
+ emit_info(
177
+ f"[green]💾 Browser state saved to {storage_state_path}[/green]"
178
+ )
179
+ except Exception as e:
180
+ emit_info(
181
+ f"[yellow]Warning: Could not save storage state: {e}[/yellow]"
182
+ )
183
+
146
184
  await self._context.close()
147
185
  self._context = None
148
186
  if self._browser:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.201
3
+ Version: 0.0.203
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -26,7 +26,7 @@ Requires-Dist: openai>=1.99.1
26
26
  Requires-Dist: pathspec>=0.11.0
27
27
  Requires-Dist: playwright>=1.40.0
28
28
  Requires-Dist: prompt-toolkit>=3.0.52
29
- Requires-Dist: pydantic-ai==1.0.6
29
+ Requires-Dist: pydantic-ai==1.0.5
30
30
  Requires-Dist: pydantic>=2.4.0
31
31
  Requires-Dist: pyjwt>=2.8.0
32
32
  Requires-Dist: pytest-cov>=6.1.1
@@ -21,16 +21,16 @@ code_puppy/agents/agent_cpp_reviewer.py,sha256=H4INgJo2OJ84QT7bfTkw4s1Ml7luwokhA
21
21
  code_puppy/agents/agent_creator_agent.py,sha256=IiwVirB6uoIeGOmtetut9eDv6o055ykND3V-fvyA8Lw,23042
22
22
  code_puppy/agents/agent_golang_reviewer.py,sha256=-OMuT8hkapVf2Oox46Ck9SRHlsfd8ab8uefbVfdW72M,3348
23
23
  code_puppy/agents/agent_javascript_reviewer.py,sha256=5YC4kRSvorcNgObjHjD2Rrgnvf8jlKhPqWdjOMjU9A0,3636
24
- code_puppy/agents/agent_manager.py,sha256=USzTbsGGZ9CH17LFeb6P6Im8RPY8qZUGKoH9EZZyU3k,11413
24
+ code_puppy/agents/agent_manager.py,sha256=D5l72Xk3XVeb07FZHKxIMNfhOjxAAzC-8min-gv11mY,11568
25
25
  code_puppy/agents/agent_python_reviewer.py,sha256=D0M3VA12QKdsyg2zIBI2FECxz0IP2fSIfg24xGzDhw0,3837
26
26
  code_puppy/agents/agent_qa_expert.py,sha256=wCGXzuAVElT5c-QigQVb8JX9Gw0JmViCUQQnADMSbVc,3796
27
27
  code_puppy/agents/agent_qa_kitten.py,sha256=5PeFFSwCFlTUvP6h5bGntx0xv5NmRwBiw0HnMqY8nLI,9107
28
28
  code_puppy/agents/agent_security_auditor.py,sha256=ADafi2x4gqXw6m-Nch5vjiKjO0Urcbj0x4zxHti3gDw,3712
29
29
  code_puppy/agents/agent_typescript_reviewer.py,sha256=EDY1mFkVpuJ1BPXsJFu2wQ2pfAV-90ipc_8w9ymrKPg,4054
30
- code_puppy/agents/base_agent.py,sha256=dsnoFEHXVhXgK-WIOskCU1Ccx6uz5onfiycWAMxBhaw,41421
30
+ code_puppy/agents/base_agent.py,sha256=4hwqOCXQwL2F6luQsYn_IAGjboz1MTU2YIfHG6IYKnU,41409
31
31
  code_puppy/agents/json_agent.py,sha256=lhopDJDoiSGHvD8A6t50hi9ZBoNRKgUywfxd0Po_Dzc,4886
32
32
  code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
33
- code_puppy/command_line/attachments.py,sha256=GSK-clGmJ1nmZD1XDqnWW7f9YiYLwjgu3D2iwvG6OMc,12362
33
+ code_puppy/command_line/attachments.py,sha256=rqBUR52wJj4cP-iTkN4KdoHZ9Oovc6tBp6ayifi9UF0,12022
34
34
  code_puppy/command_line/command_handler.py,sha256=alxMe5v_4jq8Sm6HETsgfF-VoDtgExj9dVzxP77fwmY,31614
35
35
  code_puppy/command_line/file_path_completion.py,sha256=gw8NpIxa6GOpczUJRyh7VNZwoXKKn-yvCqit7h2y6Gg,2931
36
36
  code_puppy/command_line/load_context_completion.py,sha256=6eZxV6Bs-EFwZjN93V8ZDZUC-6RaWxvtZk-04Wtikyw,2240
@@ -99,7 +99,7 @@ code_puppy/tools/browser/browser_navigation.py,sha256=Tj_fNcM3KGpkM2UTKcGQX8BpI3
99
99
  code_puppy/tools/browser/browser_screenshot.py,sha256=YU4olUqxhqyK3_pBC0BtU6A7_EEtiRlh6saj93nkKAg,8258
100
100
  code_puppy/tools/browser/browser_scripts.py,sha256=MMO5KRjdrhuLOoJGoKGG1jm6UAqhFhUznz02aWqhMAE,15065
101
101
  code_puppy/tools/browser/browser_workflows.py,sha256=jplJ1T60W3G4-dhVJX-CXkm9sskUH_Qzp0Dj-oubvrE,6142
102
- code_puppy/tools/browser/camoufox_manager.py,sha256=RYvLcs0iAoVNtpLjrrA1uu6a5k9tAdBbmhWFGSWjX_A,6106
102
+ code_puppy/tools/browser/camoufox_manager.py,sha256=bzeerqgQa3CBKVe0UGPpbVR7pKpOVcHlvun7ASRD-KA,7770
103
103
  code_puppy/tools/browser/vqa_agent.py,sha256=0GMDgJAK728rIuSQxAVytFSNagjo0LCjCUxBTm3w9Po,1952
104
104
  code_puppy/tui/__init__.py,sha256=XesAxIn32zLPOmvpR2wIDxDAnnJr81a5pBJB4cZp1Xs,321
105
105
  code_puppy/tui/app.py,sha256=D-8qHzxYbe-bVgrkBLl2lLBw7HRbUoVqDTRKy1gaE-E,44279
@@ -123,9 +123,9 @@ code_puppy/tui/screens/help.py,sha256=eJuPaOOCp7ZSUlecearqsuX6caxWv7NQszUh0tZJjB
123
123
  code_puppy/tui/screens/mcp_install_wizard.py,sha256=vObpQwLbXjQsxmSg-WCasoev1usEi0pollKnL0SHu9U,27693
124
124
  code_puppy/tui/screens/settings.py,sha256=EoMxiguyeF0srwV1bj4_MG9rrxkNthh6TdTNsxnXLfE,11460
125
125
  code_puppy/tui/screens/tools.py,sha256=3pr2Xkpa9Js6Yhf1A3_wQVRzFOui-KDB82LwrsdBtyk,1715
126
- code_puppy-0.0.201.data/data/code_puppy/models.json,sha256=dClUciCo2RlVDs0ZAQCIur8MOavZUEAXHEecn0uPa-4,1629
127
- code_puppy-0.0.201.dist-info/METADATA,sha256=aych9u0aHR3SOsgBysmVF12b5JukUSqymvX0gOXdQ3w,20759
128
- code_puppy-0.0.201.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
129
- code_puppy-0.0.201.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
130
- code_puppy-0.0.201.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
131
- code_puppy-0.0.201.dist-info/RECORD,,
126
+ code_puppy-0.0.203.data/data/code_puppy/models.json,sha256=dClUciCo2RlVDs0ZAQCIur8MOavZUEAXHEecn0uPa-4,1629
127
+ code_puppy-0.0.203.dist-info/METADATA,sha256=vEcAaiXB9z01PmgZTX_mCX_u-9KrT5M19oawPnWEAAM,20759
128
+ code_puppy-0.0.203.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
129
+ code_puppy-0.0.203.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
130
+ code_puppy-0.0.203.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
131
+ code_puppy-0.0.203.dist-info/RECORD,,