code-puppy 0.0.366__py3-none-any.whl → 0.0.368__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.
@@ -119,24 +119,62 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
119
119
  return None
120
120
 
121
121
  def _should_refresh_token(self, request: httpx.Request) -> bool:
122
- """Check if the token in the request is older than 1 hour."""
122
+ """Check if the token should be refreshed (within 1 hour of expiry).
123
+
124
+ Uses two strategies:
125
+ 1. Decode JWT to check token age (if possible)
126
+ 2. Fall back to stored expires_at from token file
127
+
128
+ Returns True if token expires within TOKEN_MAX_AGE_SECONDS (1 hour).
129
+ """
123
130
  token = self._extract_bearer_token(request)
124
131
  if not token:
125
132
  return False
126
133
 
134
+ # Strategy 1: Try to decode JWT age
127
135
  age = self._get_jwt_age_seconds(token)
128
- if age is None:
129
- return False
130
-
131
- should_refresh = age >= TOKEN_MAX_AGE_SECONDS
136
+ if age is not None:
137
+ should_refresh = age >= TOKEN_MAX_AGE_SECONDS
138
+ if should_refresh:
139
+ logger.info(
140
+ "JWT token is %.1f seconds old (>= %d), will refresh proactively",
141
+ age,
142
+ TOKEN_MAX_AGE_SECONDS,
143
+ )
144
+ return should_refresh
145
+
146
+ # Strategy 2: Fall back to stored expires_at from token file
147
+ should_refresh = self._check_stored_token_expiry()
132
148
  if should_refresh:
133
149
  logger.info(
134
- "JWT token is %.1f seconds old (>= %d), will refresh proactively",
135
- age,
150
+ "Stored token expires within %d seconds, will refresh proactively",
136
151
  TOKEN_MAX_AGE_SECONDS,
137
152
  )
138
153
  return should_refresh
139
154
 
155
+ @staticmethod
156
+ def _check_stored_token_expiry() -> bool:
157
+ """Check if the stored token expires within TOKEN_MAX_AGE_SECONDS.
158
+
159
+ This is a fallback for when JWT decoding fails or isn't available.
160
+ Uses the expires_at timestamp from the stored token file.
161
+ """
162
+ try:
163
+ from code_puppy.plugins.claude_code_oauth.utils import (
164
+ is_token_expired,
165
+ load_stored_tokens,
166
+ )
167
+
168
+ tokens = load_stored_tokens()
169
+ if not tokens:
170
+ return False
171
+
172
+ # is_token_expired already uses TOKEN_REFRESH_BUFFER_SECONDS (1 hour)
173
+ return is_token_expired(tokens)
174
+ except Exception as exc:
175
+ logger.debug("Error checking stored token expiry: %s", exc)
176
+ return False
177
+
140
178
  @staticmethod
141
179
  def _prefix_tool_names(body: bytes) -> bytes | None:
142
180
  """Prefix all tool names in the request body with TOOL_PREFIX.
@@ -46,6 +46,58 @@ def generate_tool_call_id() -> str:
46
46
  return str(uuid.uuid4())
47
47
 
48
48
 
49
+ def _flatten_union_to_object_gemini(union_items: list, defs: dict, resolve_fn) -> dict:
50
+ """Flatten a union of object types into a single object with all properties.
51
+
52
+ For discriminated unions like EditFilePayload, we merge all object types
53
+ into one with all properties (Gemini doesn't support anyOf/oneOf).
54
+ """
55
+ import copy as copy_module
56
+
57
+ merged_properties = {}
58
+ has_string_type = False
59
+
60
+ for item in union_items:
61
+ if not isinstance(item, dict):
62
+ continue
63
+
64
+ # Resolve $ref first
65
+ if "$ref" in item:
66
+ ref_path = item["$ref"]
67
+ ref_name = None
68
+ if ref_path.startswith("#/$defs/"):
69
+ ref_name = ref_path[8:]
70
+ elif ref_path.startswith("#/definitions/"):
71
+ ref_name = ref_path[14:]
72
+ if ref_name and ref_name in defs:
73
+ item = copy_module.deepcopy(defs[ref_name])
74
+ else:
75
+ continue
76
+
77
+ if item.get("type") == "string":
78
+ has_string_type = True
79
+ continue
80
+
81
+ if item.get("type") == "null":
82
+ continue
83
+
84
+ if item.get("type") == "object" or "properties" in item:
85
+ props = item.get("properties", {})
86
+ for prop_name, prop_schema in props.items():
87
+ if prop_name not in merged_properties:
88
+ merged_properties[prop_name] = resolve_fn(
89
+ copy_module.deepcopy(prop_schema)
90
+ )
91
+
92
+ if not merged_properties:
93
+ return {"type": "string"} if has_string_type else {"type": "object"}
94
+
95
+ return {
96
+ "type": "object",
97
+ "properties": merged_properties,
98
+ }
99
+
100
+
49
101
  def _sanitize_schema_for_gemini(schema: dict) -> dict:
50
102
  """Sanitize JSON schema for Gemini API compatibility.
51
103
 
@@ -53,7 +105,9 @@ def _sanitize_schema_for_gemini(schema: dict) -> dict:
53
105
  - $defs, definitions, $schema, $id
54
106
  - additionalProperties
55
107
  - $ref (inlined)
56
- - anyOf/oneOf/allOf (converted to any_of/one_of/all_of)
108
+ - anyOf/oneOf/allOf (flattened - Gemini doesn't support unions!)
109
+ - For unions of objects: merges into single object with all properties
110
+ - For simple unions (string | null): picks first non-null type
57
111
  """
58
112
  import copy
59
113
 
@@ -69,6 +123,62 @@ def _sanitize_schema_for_gemini(schema: dict) -> dict:
69
123
  def resolve_refs(obj):
70
124
  """Recursively resolve $ref references and clean schema."""
71
125
  if isinstance(obj, dict):
126
+ # Handle anyOf/oneOf unions
127
+ for union_key in ["anyOf", "oneOf"]:
128
+ if union_key in obj:
129
+ union = obj[union_key]
130
+ if isinstance(union, list):
131
+ # Check if this is a complex union of objects
132
+ object_count = 0
133
+ has_refs = False
134
+ for item in union:
135
+ if isinstance(item, dict):
136
+ if "$ref" in item:
137
+ has_refs = True
138
+ object_count += 1
139
+ elif (
140
+ item.get("type") == "object" or "properties" in item
141
+ ):
142
+ object_count += 1
143
+
144
+ # If multiple objects or has refs, flatten to single object
145
+ if object_count > 1 or has_refs:
146
+ flattened = _flatten_union_to_object_gemini(
147
+ union, defs, resolve_refs
148
+ )
149
+ if "description" in obj:
150
+ flattened["description"] = obj["description"]
151
+ return flattened
152
+
153
+ # Simple union - pick first non-null type
154
+ for item in union:
155
+ if isinstance(item, dict) and item.get("type") != "null":
156
+ result = dict(item)
157
+ if "description" in obj:
158
+ result["description"] = obj["description"]
159
+ return resolve_refs(result)
160
+
161
+ # Handle allOf by merging all schemas
162
+ if "allOf" in obj:
163
+ all_of = obj["allOf"]
164
+ if isinstance(all_of, list):
165
+ merged = {}
166
+ merged_properties = {}
167
+ for item in all_of:
168
+ if isinstance(item, dict):
169
+ resolved_item = resolve_refs(item)
170
+ if "properties" in resolved_item:
171
+ merged_properties.update(
172
+ resolved_item.pop("properties")
173
+ )
174
+ merged.update(resolved_item)
175
+ if merged_properties:
176
+ merged["properties"] = merged_properties
177
+ for k, v in obj.items():
178
+ if k != "allOf":
179
+ merged[k] = v
180
+ return resolve_refs(merged)
181
+
72
182
  # Check for $ref
73
183
  if "$ref" in obj:
74
184
  ref_path = obj["$ref"]
@@ -102,19 +212,13 @@ def _sanitize_schema_for_gemini(schema: dict) -> dict:
102
212
  "default",
103
213
  "examples",
104
214
  "const",
215
+ "anyOf", # Skip any remaining union types
216
+ "oneOf",
217
+ "allOf",
105
218
  ):
106
219
  continue
107
220
 
108
- # Transform union types for Gemini
109
- new_key = key
110
- if key == "anyOf":
111
- new_key = "any_of"
112
- elif key == "oneOf":
113
- new_key = "one_of"
114
- elif key == "allOf":
115
- new_key = "all_of"
116
-
117
- result[new_key] = resolve_refs(value)
221
+ result[key] = resolve_refs(value)
118
222
  return result
119
223
  elif isinstance(obj, list):
120
224
  return [resolve_refs(item) for item in obj]
@@ -99,12 +99,11 @@ class AntigravityModel(GeminiModel):
99
99
  def _build_tools(self, tools: list) -> list[dict]:
100
100
  """Build tool definitions with model-appropriate schema handling.
101
101
 
102
- Claude and Gemini have different JSON Schema requirements:
103
- - Gemini: needs anyOf->any_of conversion, etc.
104
- - Claude: needs standard JSON Schema, simplified unions
102
+ Both Gemini and Claude require simplified union types in function schemas:
103
+ - Neither supports anyOf/oneOf/allOf in function parameter schemas
104
+ - We simplify by picking the first non-null type from unions
105
105
  """
106
106
 
107
- is_claude = self._is_claude_model()
108
107
  function_declarations = []
109
108
 
110
109
  for tool in tools:
@@ -113,11 +112,10 @@ class AntigravityModel(GeminiModel):
113
112
  "description": tool.description or "",
114
113
  }
115
114
  if tool.parameters_json_schema:
116
- # Use _inline_refs with appropriate flags for the model type
115
+ # Simplify union types for all models (Gemini and Claude both need this)
117
116
  func_decl["parameters"] = _inline_refs(
118
117
  tool.parameters_json_schema,
119
- convert_unions=not is_claude, # Gemini needs any_of conversion
120
- simplify_for_claude=is_claude, # Claude needs simplified unions
118
+ simplify_unions=True, # Both Gemini and Claude need simplified unions
121
119
  )
122
120
  function_declarations.append(func_decl)
123
121
 
@@ -23,20 +23,78 @@ from .constants import (
23
23
  logger = logging.getLogger(__name__)
24
24
 
25
25
 
26
- def _inline_refs(
27
- schema: dict, convert_unions: bool = False, simplify_for_claude: bool = False
28
- ) -> dict:
26
+ def _flatten_union_to_object(union_items: list, defs: dict, resolve_fn) -> dict:
27
+ """Flatten a union of object types into a single object with all properties.
28
+
29
+ For discriminated unions like EditFilePayload (ContentPayload | ReplacementsPayload | DeleteSnippetPayload),
30
+ we merge all object types into one with all properties marked as optional.
31
+ """
32
+ merged_properties = {}
33
+ has_string_type = False
34
+
35
+ for item in union_items:
36
+ if not isinstance(item, dict):
37
+ continue
38
+
39
+ # Resolve $ref first
40
+ if "$ref" in item:
41
+ ref_path = item["$ref"]
42
+ ref_name = None
43
+ if ref_path.startswith("#/$defs/"):
44
+ ref_name = ref_path[8:]
45
+ elif ref_path.startswith("#/definitions/"):
46
+ ref_name = ref_path[14:]
47
+ if ref_name and ref_name in defs:
48
+ item = copy.deepcopy(defs[ref_name])
49
+ else:
50
+ continue
51
+
52
+ # Check for string type (common fallback)
53
+ if item.get("type") == "string":
54
+ has_string_type = True
55
+ continue
56
+
57
+ # Skip null types
58
+ if item.get("type") == "null":
59
+ continue
60
+
61
+ # Merge properties from object types
62
+ if item.get("type") == "object" or "properties" in item:
63
+ props = item.get("properties", {})
64
+ for prop_name, prop_schema in props.items():
65
+ if prop_name not in merged_properties:
66
+ # Resolve the property schema
67
+ merged_properties[prop_name] = resolve_fn(
68
+ copy.deepcopy(prop_schema)
69
+ )
70
+
71
+ if not merged_properties:
72
+ # No object properties found, return string type as fallback
73
+ return {"type": "string"} if has_string_type else {"type": "object"}
74
+
75
+ # Build merged object - no required fields since any subset is valid
76
+ result = {
77
+ "type": "object",
78
+ "properties": merged_properties,
79
+ }
80
+
81
+ return result
82
+
83
+
84
+ def _inline_refs(schema: dict, simplify_unions: bool = False) -> dict:
29
85
  """Inline $ref references and transform schema for Antigravity compatibility.
30
86
 
31
87
  - Inlines $ref references
32
88
  - Removes $defs, definitions, $schema, $id
33
- - Optionally converts anyOf/oneOf/allOf to any_of/one_of/all_of (only for Gemini)
34
89
  - Removes unsupported fields like 'default', 'examples', 'const'
35
- - For Claude: simplifies anyOf unions to single types
90
+ - When simplify_unions=True: flattens anyOf/oneOf unions:
91
+ - For unions of objects: merges into single object with all properties
92
+ - For simple unions (string | null): picks first non-null type
93
+ (required for both Gemini AND Claude - neither supports union types in function schemas!)
36
94
 
37
95
  Args:
38
- convert_unions: If True, convert anyOf->any_of etc. (for Gemini).
39
- simplify_for_claude: If True, simplify anyOf to single types.
96
+ simplify_unions: If True, simplify anyOf/oneOf unions.
97
+ Required for Gemini and Claude models.
40
98
  """
41
99
  if not isinstance(schema, dict):
42
100
  return schema
@@ -47,31 +105,73 @@ def _inline_refs(
47
105
  # Extract $defs for reference resolution
48
106
  defs = schema.pop("$defs", schema.pop("definitions", {}))
49
107
 
50
- def resolve_refs(
51
- obj, convert_unions=convert_unions, simplify_for_claude=simplify_for_claude
52
- ):
108
+ def resolve_refs(obj, simplify_unions=simplify_unions):
53
109
  """Recursively resolve $ref references and transform schema."""
54
110
  if isinstance(obj, dict):
55
- # For Claude: simplify anyOf/oneOf unions to first non-null type
56
- if simplify_for_claude:
111
+ # Handle anyOf/oneOf unions
112
+ if simplify_unions:
57
113
  for union_key in ["anyOf", "oneOf"]:
58
114
  if union_key in obj:
59
115
  union = obj[union_key]
60
116
  if isinstance(union, list):
61
- # Find first non-null type
117
+ # Check if this is a complex union of objects (discriminated union)
118
+ # vs a simple nullable type (string | null)
119
+ object_count = 0
120
+ has_refs = False
121
+ for item in union:
122
+ if isinstance(item, dict):
123
+ if "$ref" in item:
124
+ has_refs = True
125
+ object_count += 1
126
+ elif (
127
+ item.get("type") == "object"
128
+ or "properties" in item
129
+ ):
130
+ object_count += 1
131
+
132
+ # If multiple objects or has refs, flatten to single object
133
+ if object_count > 1 or has_refs:
134
+ flattened = _flatten_union_to_object(
135
+ union, defs, resolve_refs
136
+ )
137
+ # Keep description if present
138
+ if "description" in obj:
139
+ flattened["description"] = obj["description"]
140
+ return flattened
141
+
142
+ # Simple union - pick first non-null type
62
143
  for item in union:
63
144
  if (
64
145
  isinstance(item, dict)
65
146
  and item.get("type") != "null"
66
147
  ):
67
- # Replace the whole object with this type
68
148
  result = dict(item)
69
- # Keep description if present
70
149
  if "description" in obj:
71
150
  result["description"] = obj["description"]
72
- return resolve_refs(
73
- result, convert_unions, simplify_for_claude
74
- )
151
+ return resolve_refs(result, simplify_unions)
152
+
153
+ # Also handle allOf by merging all schemas
154
+ if simplify_unions and "allOf" in obj:
155
+ all_of = obj["allOf"]
156
+ if isinstance(all_of, list):
157
+ merged = {}
158
+ merged_properties = {}
159
+ for item in all_of:
160
+ if isinstance(item, dict):
161
+ resolved_item = resolve_refs(item, simplify_unions)
162
+ # Deep merge properties dicts
163
+ if "properties" in resolved_item:
164
+ merged_properties.update(
165
+ resolved_item.pop("properties")
166
+ )
167
+ merged.update(resolved_item)
168
+ if merged_properties:
169
+ merged["properties"] = merged_properties
170
+ # Keep other fields from original object (except allOf)
171
+ for k, v in obj.items():
172
+ if k != "allOf":
173
+ merged[k] = v
174
+ return resolve_refs(merged, simplify_unions)
75
175
 
76
176
  # Check for $ref
77
177
  if "$ref" in obj:
@@ -111,34 +211,23 @@ def _inline_refs(
111
211
  ):
112
212
  continue
113
213
 
114
- # For Claude: skip additionalProperties
115
- if simplify_for_claude and key == "additionalProperties":
214
+ # Skip additionalProperties (not supported by Gemini or Claude)
215
+ if simplify_unions and key == "additionalProperties":
116
216
  continue
117
217
 
118
- # Optionally transform union types for Gemini
119
- new_key = key
120
- if convert_unions:
121
- if key == "anyOf":
122
- new_key = "any_of"
123
- elif key == "oneOf":
124
- new_key = "one_of"
125
- elif key == "allOf":
126
- new_key = "all_of"
127
- elif key == "additionalProperties":
128
- new_key = "additional_properties"
129
-
130
- result[new_key] = resolve_refs(
131
- value, convert_unions, simplify_for_claude
132
- )
218
+ # Skip any remaining union type keys that weren't simplified above
219
+ # (This shouldn't happen normally, but just in case)
220
+ if simplify_unions and key in ("anyOf", "oneOf", "allOf"):
221
+ continue
222
+
223
+ result[key] = resolve_refs(value, simplify_unions)
133
224
  return result
134
225
  elif isinstance(obj, list):
135
- return [
136
- resolve_refs(item, convert_unions, simplify_for_claude) for item in obj
137
- ]
226
+ return [resolve_refs(item, simplify_unions) for item in obj]
138
227
  else:
139
228
  return obj
140
229
 
141
- return resolve_refs(schema, convert_unions, simplify_for_claude)
230
+ return resolve_refs(schema, simplify_unions)
142
231
 
143
232
 
144
233
  class UnwrappedResponse(httpx.Response):
@@ -405,15 +494,14 @@ class AntigravityClient(httpx.AsyncClient):
405
494
  )
406
495
 
407
496
  # Inline $refs and remove $defs from parameters
408
- # Convert unions (anyOf->any_of) only for Gemini
409
- # Simplify schemas for Claude (no anyOf, no additionalProperties)
497
+ # Simplify union types (anyOf/oneOf/allOf) for BOTH Gemini and Claude
498
+ # Neither API supports union types in function schemas!
410
499
  if "parameters" in func_decl:
411
500
  is_gemini = "gemini" in model.lower()
412
501
  is_claude = "claude" in model.lower()
413
502
  func_decl["parameters"] = _inline_refs(
414
503
  func_decl["parameters"],
415
- convert_unions=is_gemini,
416
- simplify_for_claude=is_claude,
504
+ simplify_unions=(is_gemini or is_claude),
417
505
  )
418
506
 
419
507
  # Fix generationConfig for Antigravity compatibility
@@ -21,7 +21,10 @@ from .config import (
21
21
  get_token_storage_path,
22
22
  )
23
23
 
24
- TOKEN_REFRESH_BUFFER_SECONDS = 60
24
+ # Proactive refresh buffer: refresh tokens 1 hour before expiration
25
+ # This ensures smooth operation by refreshing during model requests,
26
+ # well before the token actually expires
27
+ TOKEN_REFRESH_BUFFER_SECONDS = 3600 # 1 hour
25
28
 
26
29
  logger = logging.getLogger(__name__)
27
30
 
@@ -121,6 +121,67 @@ def patch_process_message_history() -> None:
121
121
  pass
122
122
 
123
123
 
124
+ def patch_tool_call_json_repair() -> None:
125
+ """Patch pydantic-ai's _call_tool to auto-repair malformed JSON arguments.
126
+
127
+ LLMs sometimes produce slightly broken JSON in tool calls (trailing commas,
128
+ missing quotes, etc.). This patch intercepts tool calls and runs json_repair
129
+ on the arguments before validation, preventing unnecessary retries.
130
+ """
131
+ try:
132
+ import json_repair
133
+ from pydantic_ai._tool_manager import ToolManager
134
+
135
+ # Store the original method
136
+ _original_call_tool = ToolManager._call_tool
137
+
138
+ async def _patched_call_tool(
139
+ self,
140
+ call,
141
+ *,
142
+ allow_partial: bool,
143
+ wrap_validation_errors: bool,
144
+ approved: bool,
145
+ ):
146
+ """Patched _call_tool that repairs malformed JSON before validation."""
147
+ # Only attempt repair if args is a string (JSON)
148
+ if isinstance(call.args, str) and call.args:
149
+ try:
150
+ repaired = json_repair.repair_json(call.args)
151
+ if repaired != call.args:
152
+ # JSON was repaired! Log and update
153
+ try:
154
+ from rich.console import Console
155
+ console = Console(stderr=True)
156
+ console.print(
157
+ "[bold yellow]🐕 WOOF! Repaired malformed tool call JSON! AWOOOOOOOOOO![/]"
158
+ )
159
+ except ImportError:
160
+ pass # No rich console available
161
+
162
+ # Update the call args with repaired JSON
163
+ call.args = repaired
164
+ except Exception:
165
+ pass # If repair fails, let original validation handle it
166
+
167
+ # Call the original method
168
+ return await _original_call_tool(
169
+ self,
170
+ call,
171
+ allow_partial=allow_partial,
172
+ wrap_validation_errors=wrap_validation_errors,
173
+ approved=approved,
174
+ )
175
+
176
+ # Apply the patch
177
+ ToolManager._call_tool = _patched_call_tool
178
+
179
+ except ImportError:
180
+ pass # json_repair or pydantic_ai not available
181
+ except Exception:
182
+ pass # Don't crash on patch failure
183
+
184
+
124
185
  def apply_all_patches() -> None:
125
186
  """Apply all pydantic-ai monkey patches.
126
187
 
@@ -129,3 +190,4 @@ def apply_all_patches() -> None:
129
190
  patch_user_agent()
130
191
  patch_message_history_cleaning()
131
192
  patch_process_message_history()
193
+ patch_tool_call_json_repair()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.366
3
+ Version: 0.0.368
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
@@ -24,7 +24,7 @@ Requires-Dist: openai>=1.99.1
24
24
  Requires-Dist: pillow>=10.0.0
25
25
  Requires-Dist: playwright>=1.40.0
26
26
  Requires-Dist: prompt-toolkit>=3.0.52
27
- Requires-Dist: pydantic-ai-slim[anthropic,openai]==1.25.0
27
+ Requires-Dist: pydantic-ai-slim[anthropic,openai]==1.26.0
28
28
  Requires-Dist: pydantic>=2.4.0
29
29
  Requires-Dist: pyfiglet>=0.8.post1
30
30
  Requires-Dist: python-dotenv>=1.0.0
@@ -2,12 +2,12 @@ code_puppy/__init__.py,sha256=xMPewo9RNHb3yfFNIk5WCbv2cvSPtJOCgK2-GqLbNnU,373
2
2
  code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
3
3
  code_puppy/callbacks.py,sha256=Pp0VyeXJBEtk-N_RSWr5pbveelovsdLUiJ4f11dzwGw,10775
4
4
  code_puppy/chatgpt_codex_client.py,sha256=Om0ANB_kpHubhCwNzF9ENf8RvKBqs0IYzBLl_SNw0Vk,9833
5
- code_puppy/claude_cache_client.py,sha256=Gl6um5ZaKpcnxOvoFSM8Lwm_Vu4-VyWz8Nli8DnRLa4,22508
5
+ code_puppy/claude_cache_client.py,sha256=1-rIDtZBJ_aiAZSprdsq0ty2urftu6F0uzJDn_OE41Q,23966
6
6
  code_puppy/cli_runner.py,sha256=w5CLKgQYYaT7My3Cga2StXYol-u6DBxNzzUuhhsfhsA,34952
7
7
  code_puppy/config.py,sha256=blowBU3bBOdQSuLYKBUrb7f7CxHH_e25a_A4lQGsjgk,53494
8
8
  code_puppy/error_logging.py,sha256=a80OILCUtJhexI6a9GM-r5LqIdjvSRzggfgPp2jv1X0,3297
9
9
  code_puppy/gemini_code_assist.py,sha256=KGS7sO5OLc83nDF3xxS-QiU6vxW9vcm6hmzilu79Ef8,13867
10
- code_puppy/gemini_model.py,sha256=-PFyuJDboaD7M44MkNAlHqHplhS-oepyXR2F-GN9GFk,21617
10
+ code_puppy/gemini_model.py,sha256=i8XXmx9s1eWEXpJ8U288w0yayTt6Nq8V-hxpUHhti4s,25984
11
11
  code_puppy/http_utils.py,sha256=SAH6EOdbR6Cbfmi-4EtHDqRDBUV5bWtGc-5nr44F0Is,10418
12
12
  code_puppy/keymap.py,sha256=IvMkTlB_bIqOWpbTpmftkdyjhtD5todXuEIw1zCZ4u0,3584
13
13
  code_puppy/main.py,sha256=82r3vZy_XcyEsenLn82BnUusaoyL3Bpm_Th_jKgqecE,273
@@ -16,7 +16,7 @@ code_puppy/model_utils.py,sha256=55TKNnGTXQlHJNqejL2PfQqQmChXfzOjJg-hlarfR7w,555
16
16
  code_puppy/models.json,sha256=FMQdE_yvP_8y0xxt3K918UkFL9cZMYAqW1SfXcQkU_k,3105
17
17
  code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
18
18
  code_puppy/models_dev_parser.py,sha256=8ndmWrsSyKbXXpRZPXc0w6TfWMuCcgaHiMifmlaBaPc,20611
19
- code_puppy/pydantic_patches.py,sha256=YecAEeCOjSIwIBu2O5vEw72atMSL37cXGrbEuukI07o,4582
19
+ code_puppy/pydantic_patches.py,sha256=Y8v2VIlhOArODIaQJYJWNR3rVtte0qox3Am0X_d70N8,6943
20
20
  code_puppy/reopenable_async_client.py,sha256=pD34chyBFcC7_OVPJ8fp6aRI5jYdN-7VDycObMZPwG8,8292
21
21
  code_puppy/round_robin_model.py,sha256=kSawwPUiPgg0yg8r4AAVgvjzsWkptxpSORd75-HP7W4,5335
22
22
  code_puppy/session_storage.py,sha256=T4hOsAl9z0yz2JZCptjJBOnN8fCmkLZx5eLy1hTdv6Q,9631
@@ -148,7 +148,7 @@ code_puppy/plugins/__init__.py,sha256=gWgrXWoFpl-3Mxz2DAvxKW6SkCWrOnw-hKsY9O7nHc
148
148
  code_puppy/plugins/oauth_puppy_html.py,sha256=Wpa-V_NlRiBAvo_OXHuR7wvOH_jSt8L9HSFGiab6xI0,13058
149
149
  code_puppy/plugins/antigravity_oauth/__init__.py,sha256=1miHihSqRNXO20Vh_Gn9M3Aa2szh0gtdSCaKKj9nq0Q,362
150
150
  code_puppy/plugins/antigravity_oauth/accounts.py,sha256=GQit2-K24bsopmTZyscFUq3M0cAEO5WutHWnipVdgz8,14304
151
- code_puppy/plugins/antigravity_oauth/antigravity_model.py,sha256=WgivqX8fmRXxNI4d5t1uXmMo_KkD-Ww8gysJBNkW3V8,26350
151
+ code_puppy/plugins/antigravity_oauth/antigravity_model.py,sha256=ZFarvPYgYixQxEmEylIIbsSl7fmL8bYD8RXL_9wzDRA,26281
152
152
  code_puppy/plugins/antigravity_oauth/config.py,sha256=BoQgqf5I2XoHWnBBo9vhCIc_XwPj9Mbp0Z95ygWwt78,1362
153
153
  code_puppy/plugins/antigravity_oauth/constants.py,sha256=qsrA10JJvzNuY0OobvvwCQcoGpILBninllcUUMKkUrQ,4644
154
154
  code_puppy/plugins/antigravity_oauth/oauth.py,sha256=ZHXJtZP63l6brOpX1WdLfuUClIleA79-4y36YUJc6Wo,15137
@@ -156,7 +156,7 @@ code_puppy/plugins/antigravity_oauth/register_callbacks.py,sha256=uKIvfzH-dXj1g_
156
156
  code_puppy/plugins/antigravity_oauth/storage.py,sha256=LW1DkY6Z-GRbBDrIitT6glKemZptp3NzldIrLRqTAK0,8971
157
157
  code_puppy/plugins/antigravity_oauth/test_plugin.py,sha256=n0kjFG8Vt2n1j0GgTRSdSyhF0t9xxE8Ht60SH5CSwzw,11027
158
158
  code_puppy/plugins/antigravity_oauth/token.py,sha256=WbiFCkrZvChpGXvwIYsJMgqU9xdJ81KwR062lFlnL3U,5038
159
- code_puppy/plugins/antigravity_oauth/transport.py,sha256=fCOg_BsSVtuvwPhLJdDLiuPt-Pps8REjxnxCXgg2qzw,31807
159
+ code_puppy/plugins/antigravity_oauth/transport.py,sha256=XgeG2Ys_Ui0_9cnrYWN7nENehFv76Rr2_QPAcIbPROI,35723
160
160
  code_puppy/plugins/antigravity_oauth/utils.py,sha256=mXHRv0l07r27VjtSsIy9rlpkUheP88RaM4x4M0O1mMY,5401
161
161
  code_puppy/plugins/chatgpt_oauth/__init__.py,sha256=Kjc6Hsz1sWvMD2OdAlWZvJRiKJSj4fx22boa-aVFKjA,189
162
162
  code_puppy/plugins/chatgpt_oauth/config.py,sha256=H_wAH9Duyn8WH2Kq8oe72uda-_4qu1uXLPun_SDdtsk,2023
@@ -170,7 +170,7 @@ code_puppy/plugins/claude_code_oauth/__init__.py,sha256=mCcOU-wM7LNCDjr-w-WLPzom
170
170
  code_puppy/plugins/claude_code_oauth/config.py,sha256=DjGySCkvjSGZds6DYErLMAi3TItt8iSLGvyJN98nSEM,2013
171
171
  code_puppy/plugins/claude_code_oauth/register_callbacks.py,sha256=g8sl-i7jIOF6OFALeaLqTF3mS4tD8GR_FCzvPjVw2js,10165
172
172
  code_puppy/plugins/claude_code_oauth/test_plugin.py,sha256=yQy4EeZl4bjrcog1d8BjknoDTRK75mRXXvkSQJYSSEM,9286
173
- code_puppy/plugins/claude_code_oauth/utils.py,sha256=TVgz5aFd2GFPHSiG9NnOYiw-y6KRkWwt_SZxmMpwMIY,17243
173
+ code_puppy/plugins/claude_code_oauth/utils.py,sha256=Ltk_m2efGNQSSvmb1lxgcPp_vcEig8VMydTZffQP-2c,17433
174
174
  code_puppy/plugins/customizable_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
175
  code_puppy/plugins/customizable_commands/register_callbacks.py,sha256=zVMfIzr--hVn0IOXxIicbmgj2s-HZUgtrOc0NCDOnDw,5183
176
176
  code_puppy/plugins/example_custom_command/README.md,sha256=5c5Zkm7CW6BDSfe3WoLU7GW6t5mjjYAbu9-_pu-b3p4,8244
@@ -208,10 +208,10 @@ code_puppy/tools/browser/chromium_terminal_manager.py,sha256=w1thQ_ACb6oV45L93TS
208
208
  code_puppy/tools/browser/terminal_command_tools.py,sha256=9byOZku-dwvTtCl532xt7Lumed_jTn0sLvUe_X75XCQ,19068
209
209
  code_puppy/tools/browser/terminal_screenshot_tools.py,sha256=J_21YO_495NvYgNFu9KQP6VYg2K_f8CtSdZuF94Yhnw,18448
210
210
  code_puppy/tools/browser/terminal_tools.py,sha256=F5LjVH3udSCFHmqC3O1UJLoLozZFZsEdX42jOmkqkW0,17853
211
- code_puppy-0.0.366.data/data/code_puppy/models.json,sha256=FMQdE_yvP_8y0xxt3K918UkFL9cZMYAqW1SfXcQkU_k,3105
212
- code_puppy-0.0.366.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
213
- code_puppy-0.0.366.dist-info/METADATA,sha256=ZVqCp8f2qRwykFJwxr-lid_mwcMQVCGm7y-kHjyA4jQ,27600
214
- code_puppy-0.0.366.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
215
- code_puppy-0.0.366.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
216
- code_puppy-0.0.366.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
217
- code_puppy-0.0.366.dist-info/RECORD,,
211
+ code_puppy-0.0.368.data/data/code_puppy/models.json,sha256=FMQdE_yvP_8y0xxt3K918UkFL9cZMYAqW1SfXcQkU_k,3105
212
+ code_puppy-0.0.368.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
213
+ code_puppy-0.0.368.dist-info/METADATA,sha256=kymVWcqiOTKCZz-LgbRVLSz49pa4S42pdp3ZAi5kNhI,27600
214
+ code_puppy-0.0.368.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
215
+ code_puppy-0.0.368.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
216
+ code_puppy-0.0.368.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
217
+ code_puppy-0.0.368.dist-info/RECORD,,