gemini-computer-use 0.4.3 → 0.4.4

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.
@@ -1,67 +0,0 @@
1
- import os
2
- import uuid
3
- from typing import Optional, List
4
- from google.antigravity import LocalAgentConfig, types
5
- from core.hooks import get_mesh_hooks
6
- from core.triggers import create_mesh_watchdog_trigger, create_config_trigger
7
- from core.policies import get_mesh_policies, ALLOWED_WORKSPACES
8
- from core.subagents import get_mesh_subagents, get_root_capabilities_config
9
-
10
- # Derived from the coordinator workspace so the harness works wherever that root
11
- # is: the documented /root/agy-gdrive-runner on Linux, %USERPROFILE%\agy-gdrive-runner
12
- # on Windows. A hardcoded POSIX literal was rejected there by the SDK
13
- # ("app_data_dir must be an absolute path") because os.path.isabs('/root/...') is
14
- # False on Windows. os.path.abspath keeps it absolute even if the operator pointed
15
- # MESH_COORDINATOR_WORKSPACE at a relative path.
16
- _COORDINATOR_ROOT = os.path.abspath(ALLOWED_WORKSPACES[0])
17
- DEFAULT_SAVE_DIR = os.path.join(_COORDINATOR_ROOT, ".state", "sessions")
18
- DEFAULT_APP_DATA_DIR = os.path.join(_COORDINATOR_ROOT, ".state", "artifacts")
19
-
20
- ROOT_SYSTEM_INSTRUCTIONS = (
21
- "You are the Root Orchestrator for the Antigravity Remote Execution Mesh. "
22
- "Execute commands strictly through native tools and verified subagents. "
23
- "Adhere to Zero Drive Queue Policy and Hard Error Halt invariants."
24
- )
25
-
26
- def create_agent_config(
27
- conversation_id: Optional[str] = None,
28
- save_dir: Optional[str] = None,
29
- app_data_dir: Optional[str] = None,
30
- interactive: bool = True,
31
- max_model_calls: int = 30,
32
- ) -> LocalAgentConfig:
33
- resolved_save_dir = save_dir or DEFAULT_SAVE_DIR
34
- resolved_app_data_dir = app_data_dir or DEFAULT_APP_DATA_DIR
35
- resolved_conv_id = conversation_id or str(uuid.uuid4())
36
-
37
- os.makedirs(resolved_save_dir, exist_ok=True)
38
- os.makedirs(resolved_app_data_dir, exist_ok=True)
39
-
40
- hooks = get_mesh_hooks()
41
- triggers = [
42
- create_mesh_watchdog_trigger(interval_seconds=60.0),
43
- create_config_trigger(path="server_facts.json"),
44
- ]
45
- policies = get_mesh_policies()
46
- subagents = get_mesh_subagents()
47
- capabilities = get_root_capabilities_config()
48
-
49
- budget = types.BudgetConfig(
50
- max_model_calls=max_model_calls,
51
- max_tool_calls=60,
52
- max_total_tokens=200_000,
53
- )
54
-
55
- return LocalAgentConfig(
56
- system_instructions=ROOT_SYSTEM_INSTRUCTIONS,
57
- conversation_id=resolved_conv_id,
58
- save_dir=resolved_save_dir,
59
- app_data_dir=resolved_app_data_dir,
60
- hooks=hooks,
61
- triggers=triggers,
62
- policies=policies,
63
- subagents=subagents,
64
- capabilities=capabilities,
65
- workspaces=ALLOWED_WORKSPACES,
66
- budget_config=budget,
67
- )
@@ -1,244 +0,0 @@
1
- import ast
2
- import os
3
- import json
4
- from typing import List, Dict, Optional, Any
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- def _index_key(path: str) -> str:
9
- """Canonical, platform-stable key for a file inside the index.
10
-
11
- ``os.path.normpath`` returns backslashes on Windows, so the same repository
12
- produced ``core\\schemas.py`` there and ``core/schemas.py`` on Linux: the
13
- cache written on one platform did not match on the other and symbols were
14
- reported with a separator the model could not paste back into a command.
15
- Forward slashes work for every Windows API too, so they are used everywhere.
16
- """
17
- return os.path.normpath(path).replace("\\", "/")
18
-
19
- class CodeSymbol(BaseModel):
20
- name: str
21
- kind: str # "class", "function", "async_function"
22
- file_path: str
23
- line_start: int
24
- line_end: int
25
- docstring: Optional[str] = None
26
-
27
- class FileEntry(BaseModel):
28
- path: str
29
- mtime: float
30
- size: int
31
- symbols: List[CodeSymbol] = Field(default_factory=list)
32
-
33
- class CodeIndex:
34
- IGNORE_DIRS = {
35
- ".git",
36
- "__pycache__",
37
- ".pytest_cache",
38
- ".state",
39
- "live_logs",
40
- "node_modules",
41
- ".gemini",
42
- ".agents",
43
- }
44
-
45
- def __init__(self, root_dir: str = ".", cache_file: str = ".state/code_index.json"):
46
- self.root_dir = root_dir
47
- self.cache_file = cache_file
48
- self.files: Dict[str, FileEntry] = {}
49
- self.symbol_map: Dict[str, List[CodeSymbol]] = {}
50
- self._content_cache: Dict[str, tuple[float, List[str]]] = {}
51
- self._load_cache()
52
-
53
- def _load_cache(self) -> None:
54
- if os.path.exists(self.cache_file):
55
- try:
56
- with open(self.cache_file, "r", encoding="utf-8") as f:
57
- data = json.load(f)
58
- for path, entry_data in data.get("files", {}).items():
59
- entry = FileEntry.model_validate(entry_data)
60
- self.files[path] = entry
61
- for sym in entry.symbols:
62
- self.symbol_map.setdefault(sym.name, []).append(sym)
63
- except Exception:
64
- self.files = {}
65
- self.symbol_map = {}
66
-
67
- def _save_cache(self) -> None:
68
- try:
69
- os.makedirs(os.path.dirname(os.path.abspath(self.cache_file)), exist_ok=True)
70
- data = {
71
- "files": {k: v.model_dump() for k, v in self.files.items()}
72
- }
73
- tmp_file = self.cache_file + ".tmp"
74
- with open(tmp_file, "w", encoding="utf-8") as f:
75
- json.dump(data, f, indent=2)
76
- os.replace(tmp_file, self.cache_file)
77
- except Exception:
78
- pass
79
-
80
- def _parse_py_file(self, file_path: str) -> List[CodeSymbol]:
81
- symbols: List[CodeSymbol] = []
82
- try:
83
- with open(file_path, "r", encoding="utf-8", errors="replace") as f:
84
- code = f.read()
85
- tree = ast.parse(code, filename=file_path)
86
- for node in ast.iter_child_nodes(tree):
87
- if isinstance(node, ast.ClassDef):
88
- doc = ast.get_docstring(node)
89
- end_lineno = getattr(node, "end_lineno", node.lineno)
90
- symbols.append(CodeSymbol(
91
- name=node.name,
92
- kind="class",
93
- file_path=file_path,
94
- line_start=node.lineno,
95
- line_end=end_lineno,
96
- docstring=doc,
97
- ))
98
- for item in node.body:
99
- if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
100
- m_doc = ast.get_docstring(item)
101
- m_end = getattr(item, "end_lineno", item.lineno)
102
- symbols.append(CodeSymbol(
103
- name=f"{node.name}.{item.name}",
104
- kind="method",
105
- file_path=file_path,
106
- line_start=item.lineno,
107
- line_end=m_end,
108
- docstring=m_doc,
109
- ))
110
- elif isinstance(node, ast.AsyncFunctionDef):
111
- doc = ast.get_docstring(node)
112
- end_lineno = getattr(node, "end_lineno", node.lineno)
113
- symbols.append(CodeSymbol(
114
- name=node.name,
115
- kind="async_function",
116
- file_path=file_path,
117
- line_start=node.lineno,
118
- line_end=end_lineno,
119
- docstring=doc,
120
- ))
121
- elif isinstance(node, ast.FunctionDef):
122
- doc = ast.get_docstring(node)
123
- end_lineno = getattr(node, "end_lineno", node.lineno)
124
- symbols.append(CodeSymbol(
125
- name=node.name,
126
- kind="function",
127
- file_path=file_path,
128
- line_start=node.lineno,
129
- line_end=end_lineno,
130
- docstring=doc,
131
- ))
132
- except Exception:
133
- pass
134
- return symbols
135
-
136
- def build_or_update(self) -> Dict[str, int]:
137
- total_files = 0
138
- parsed_files = 0
139
- cache_hits = 0
140
- current_seen: set[str] = set()
141
-
142
- for root, dirs, files in os.walk(self.root_dir):
143
- dirs[:] = [d for d in dirs if d not in self.IGNORE_DIRS]
144
- for file in files:
145
- if not file.endswith(".py"):
146
- continue
147
- file_path = _index_key(os.path.join(root, file))
148
- current_seen.add(file_path)
149
- total_files += 1
150
-
151
- try:
152
- stat = os.stat(file_path)
153
- except OSError:
154
- continue
155
-
156
- if file_path in self.files and self.files[file_path].mtime == stat.st_mtime:
157
- cache_hits += 1
158
- else:
159
- parsed_files += 1
160
- symbols = self._parse_py_file(file_path)
161
- self.files[file_path] = FileEntry(
162
- path=file_path,
163
- mtime=stat.st_mtime,
164
- size=stat.st_size,
165
- symbols=symbols,
166
- )
167
-
168
- # Remove deleted files
169
- for old_path in list(self.files.keys()):
170
- if old_path not in current_seen:
171
- del self.files[old_path]
172
-
173
- # Rebuild symbol map
174
- self.symbol_map.clear()
175
- for entry in self.files.values():
176
- for sym in entry.symbols:
177
- self.symbol_map.setdefault(sym.name, []).append(sym)
178
-
179
- self._save_cache()
180
- return {
181
- "total_files": total_files,
182
- "parsed_files": parsed_files,
183
- "cache_hits": cache_hits,
184
- }
185
-
186
- def find_symbol(self, name: str, exact: bool = True) -> List[CodeSymbol]:
187
- if exact:
188
- return self.symbol_map.get(name, [])
189
- matches: List[CodeSymbol] = []
190
- name_lower = name.lower()
191
- for sym_name, sym_list in self.symbol_map.items():
192
- if name_lower in sym_name.lower():
193
- matches.extend(sym_list)
194
- return matches
195
-
196
- def get_file_outline(self, file_path: str) -> List[CodeSymbol]:
197
- norm = _index_key(file_path)
198
- for path, entry in self.files.items():
199
- if norm == path or norm.endswith(path) or path.endswith(norm):
200
- return entry.symbols
201
- return []
202
-
203
- def get_cached_chunk(self, file_path: str, start_line: int = 1, end_line: int = 100) -> str:
204
- norm = _index_key(file_path)
205
- try:
206
- stat = os.stat(norm)
207
- mtime = stat.st_mtime
208
- except OSError:
209
- return f"Error: File '{file_path}' not found."
210
-
211
- cached = self._content_cache.get(norm)
212
- if cached is None or cached[0] != mtime:
213
- try:
214
- with open(norm, "r", encoding="utf-8", errors="replace") as f:
215
- lines = f.readlines()
216
- self._content_cache[norm] = (mtime, lines)
217
- except Exception as e:
218
- return f"Error reading file '{file_path}': {e}"
219
- else:
220
- lines = cached[1]
221
-
222
- total = len(lines)
223
- s = max(1, start_line) - 1
224
- e = min(total, end_line)
225
- slice_lines = lines[s:e]
226
- return f"[{norm} | Lines {s+1}-{e} of {total}]\n" + "".join(slice_lines)
227
-
228
- def get_sitemap(self) -> Dict[str, Any]:
229
- sitemap = {}
230
- for path, entry in sorted(self.files.items()):
231
- sitemap[path] = [
232
- {"name": s.name, "kind": s.kind, "lines": f"{s.line_start}-{s.line_end}"}
233
- for s in entry.symbols
234
- ]
235
- return sitemap
236
-
237
- _GLOBAL_INDEX: Optional[CodeIndex] = None
238
-
239
- def get_code_index(root_dir: str = ".") -> CodeIndex:
240
- global _GLOBAL_INDEX
241
- if _GLOBAL_INDEX is None:
242
- _GLOBAL_INDEX = CodeIndex(root_dir=root_dir)
243
- _GLOBAL_INDEX.build_or_update()
244
- return _GLOBAL_INDEX