gemcode 0.3.50__tar.gz → 0.3.51__tar.gz

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 (121) hide show
  1. {gemcode-0.3.50/src/gemcode.egg-info → gemcode-0.3.51}/PKG-INFO +1 -1
  2. {gemcode-0.3.50 → gemcode-0.3.51}/pyproject.toml +1 -1
  3. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/session_runtime.py +76 -70
  4. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tui/input_handler.py +17 -3
  5. {gemcode-0.3.50 → gemcode-0.3.51/src/gemcode.egg-info}/PKG-INFO +1 -1
  6. {gemcode-0.3.50 → gemcode-0.3.51}/LICENSE +0 -0
  7. {gemcode-0.3.50 → gemcode-0.3.51}/MANIFEST.in +0 -0
  8. {gemcode-0.3.50 → gemcode-0.3.51}/README.md +0 -0
  9. {gemcode-0.3.50 → gemcode-0.3.51}/setup.cfg +0 -0
  10. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/__init__.py +0 -0
  11. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/__main__.py +0 -0
  12. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/agent.py +0 -0
  13. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/audit.py +0 -0
  14. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/autocompact.py +0 -0
  15. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/callbacks.py +0 -0
  16. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/capability_routing.py +0 -0
  17. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/cli.py +0 -0
  18. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/compaction.py +0 -0
  19. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/computer_use/__init__.py +0 -0
  20. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/computer_use/browser_computer.py +0 -0
  21. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/config.py +0 -0
  22. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/context_budget.py +0 -0
  23. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/context_warning.py +0 -0
  24. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/credentials.py +0 -0
  25. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/hitl_session.py +0 -0
  26. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/hooks.py +0 -0
  27. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/intent_classifier.py +0 -0
  28. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/interactions.py +0 -0
  29. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/invoke.py +0 -0
  30. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/kairos_daemon.py +0 -0
  31. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/limits.py +0 -0
  32. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/live_audio_engine.py +0 -0
  33. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/logging_config.py +0 -0
  34. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/mcp_loader.py +0 -0
  35. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/memory/__init__.py +0 -0
  36. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/memory/embedding_memory_service.py +0 -0
  37. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/memory/file_memory_service.py +0 -0
  38. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/modality_tools.py +0 -0
  39. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/model_errors.py +0 -0
  40. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/model_routing.py +0 -0
  41. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/openapi_loader.py +0 -0
  42. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/paths.py +0 -0
  43. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/permissions.py +0 -0
  44. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/plugins/__init__.py +0 -0
  45. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
  46. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
  47. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/pricing.py +0 -0
  48. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/prompt_suggestions.py +0 -0
  49. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/__init__.py +0 -0
  50. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/config.py +0 -0
  51. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/deps.py +0 -0
  52. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/engine.py +0 -0
  53. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/stop_hooks.py +0 -0
  54. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/token_budget.py +0 -0
  55. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/query/transitions.py +0 -0
  56. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/refine.py +0 -0
  57. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/repl_commands.py +0 -0
  58. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/repl_slash.py +0 -0
  59. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/review_agent.py +0 -0
  60. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/session_store.py +0 -0
  61. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/slash_commands.py +0 -0
  62. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/thinking.py +0 -0
  63. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tool_prompt_manifest.py +0 -0
  64. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tool_registry.py +0 -0
  65. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/__init__.py +0 -0
  66. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/bash.py +0 -0
  67. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/browser.py +0 -0
  68. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/edit.py +0 -0
  69. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/filesystem.py +0 -0
  70. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/notes.py +0 -0
  71. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/search.py +0 -0
  72. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/shell.py +0 -0
  73. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/shell_gate.py +0 -0
  74. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/subtask.py +0 -0
  75. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/think.py +0 -0
  76. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/todo.py +0 -0
  77. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools/web.py +0 -0
  78. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tools_inspector.py +0 -0
  79. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/trust.py +0 -0
  80. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tui/scrollback.py +0 -0
  81. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tui/spinner.py +0 -0
  82. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tui/welcome_banner.py +0 -0
  83. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/tui/welcome_rich.py +0 -0
  84. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/version.py +0 -0
  85. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/vertex.py +0 -0
  86. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/web/__init__.py +0 -0
  87. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/web/claude_sse_adapter.py +0 -0
  88. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/web/terminal_repl.py +0 -0
  89. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode/workspace_hints.py +0 -0
  90. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode.egg-info/SOURCES.txt +0 -0
  91. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode.egg-info/dependency_links.txt +0 -0
  92. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode.egg-info/entry_points.txt +0 -0
  93. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode.egg-info/requires.txt +0 -0
  94. {gemcode-0.3.50 → gemcode-0.3.51}/src/gemcode.egg-info/top_level.txt +0 -0
  95. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_agent_instruction.py +0 -0
  96. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_autocompact.py +0 -0
  97. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_capability_routing.py +0 -0
  98. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_claude_web_adapter_sse.py +0 -0
  99. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_cli_init.py +0 -0
  100. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_computer_use_permissions.py +0 -0
  101. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_context_budget.py +0 -0
  102. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_context_warning.py +0 -0
  103. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_credentials.py +0 -0
  104. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_interactive_permission_ask.py +0 -0
  105. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_kairos_scheduler.py +0 -0
  106. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_modality_tools.py +0 -0
  107. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_model_error_retry.py +0 -0
  108. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_model_errors.py +0 -0
  109. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_model_routing.py +0 -0
  110. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_paths.py +0 -0
  111. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_permissions.py +0 -0
  112. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_prompt_suggestions.py +0 -0
  113. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_repl_commands.py +0 -0
  114. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_repl_slash.py +0 -0
  115. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_slash_commands.py +0 -0
  116. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_thinking_config.py +0 -0
  117. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_token_budget.py +0 -0
  118. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_tool_context_circulation.py +0 -0
  119. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_tools.py +0 -0
  120. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_tools_inspector.py +0 -0
  121. {gemcode-0.3.50 → gemcode-0.3.51}/tests/test_workspace_hints.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.50
3
+ Version: 0.3.51
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gemcode"
7
- version = "0.3.50"
7
+ version = "0.3.51"
8
8
  description = "Local-first coding agent on Google Gemini + ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -30,76 +30,82 @@ def session_db_path(cfg: GemCodeConfig) -> Path:
30
30
  return cfg.project_root / ".gemcode" / "sessions.sqlite"
31
31
 
32
32
 
33
- class _SafeComputerUseToolset:
33
+ def _make_safe_computer_toolset(computer):
34
34
  """
35
- Drop-in wrapper around ComputerUseToolset that catches Playwright startup
36
- failures (e.g. missing browser binary) and disables computer-use gracefully
37
- for the current session instead of crashing every turn with an unhandled error.
38
-
39
- When the underlying toolset fails for the *first* time a one-time warning is
40
- printed to stderr so the user knows they need to run ``playwright install``.
41
- Subsequent calls are silently no-ops so the session keeps working without
42
- browser tools — the agent can still use all other tools normally.
43
- """
44
-
45
- def __init__(self, computer) -> None:
46
- try:
47
- from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
48
- self._inner = ComputerUseToolset(computer=computer)
49
- except Exception:
50
- self._inner = None
51
- self._broken = False
52
- self._warned = False
53
-
54
- def _warn_once(self, error: Exception) -> None:
55
- if self._warned:
56
- return
57
- self._warned = True
58
- import sys
59
- msg = str(error)
60
- if "playwright install" in msg.lower() or "executable doesn't exist" in msg.lower():
61
- print(
62
- "\n[gemcode] Browser (computer-use) is unavailable — Playwright browsers are not installed.\n"
63
- " Run: playwright install chromium\n"
64
- " Then restart GemCode with /computer on (or --computer flag).\n"
65
- " Continuing without browser tools for this session.\n",
66
- file=sys.stderr,
67
- )
68
- else:
69
- print(
70
- f"\n[gemcode] Browser (computer-use) failed to start: {msg!s:.200}\n"
71
- " Continuing without browser tools for this session.\n",
72
- file=sys.stderr,
73
- )
35
+ Build a BaseToolset-compatible wrapper around ComputerUseToolset that catches
36
+ Playwright startup failures gracefully instead of crashing LlmAgent validation.
74
37
 
75
- # ── ADK toolset protocol ────────────────────────────────────────────────────
38
+ Must be a proper BaseToolset subclass (not a plain class) because ADK's Pydantic
39
+ model for LlmAgent validates each entry in `tools` against BaseTool | BaseToolset.
40
+ Returns a real BaseToolset subclass instance, or None if BaseToolset is unavailable.
41
+ """
42
+ try:
43
+ from google.adk.tools.base_toolset import BaseToolset
44
+ except ImportError:
45
+ return None
76
46
 
77
- async def process_llm_request(self, *, tool_context, llm_request) -> None:
78
- if self._broken or self._inner is None:
79
- return
80
- try:
81
- await self._inner.process_llm_request(
82
- tool_context=tool_context, llm_request=llm_request
83
- )
84
- except Exception as exc:
85
- self._broken = True
86
- self._warn_once(exc)
87
-
88
- async def get_tools(self, readonly_context=None):
89
- if self._broken or self._inner is None:
90
- return []
91
- try:
92
- return await self._inner.get_tools(readonly_context)
93
- except Exception as exc:
94
- self._broken = True
95
- self._warn_once(exc)
96
- return []
97
-
98
- def __getattr__(self, name: str):
99
- """Proxy all other attribute accesses to the inner toolset."""
100
- if self._inner is not None:
101
- return getattr(self._inner, name)
102
- raise AttributeError(name)
47
+ class _SafeComputerUseToolset(BaseToolset):
48
+ """Wraps ComputerUseToolset; degrades to a no-op if Playwright is missing."""
49
+
50
+ def __init__(self) -> None:
51
+ try:
52
+ from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
53
+ self._inner = ComputerUseToolset(computer=computer)
54
+ except Exception:
55
+ self._inner = None
56
+ self._broken = False
57
+ self._warned = False
58
+
59
+ def _warn_once(self, error: Exception) -> None:
60
+ if self._warned:
61
+ return
62
+ self._warned = True
63
+ import sys
64
+ msg = str(error)
65
+ if "playwright install" in msg.lower() or "executable doesn't exist" in msg.lower():
66
+ print(
67
+ "\n[gemcode] Browser (computer-use) is unavailable — Playwright browsers are not installed.\n"
68
+ " Run: playwright install chromium\n"
69
+ " Then restart GemCode with /computer on (or --computer flag).\n"
70
+ " Continuing without browser tools for this session.\n",
71
+ file=sys.stderr,
72
+ )
73
+ else:
74
+ print(
75
+ f"\n[gemcode] Browser (computer-use) failed to start: {msg!s:.200}\n"
76
+ " Continuing without browser tools for this session.\n",
77
+ file=sys.stderr,
78
+ )
79
+
80
+ async def process_llm_request(self, *, tool_context, llm_request) -> None:
81
+ if self._broken or self._inner is None:
82
+ return
83
+ try:
84
+ await self._inner.process_llm_request(
85
+ tool_context=tool_context, llm_request=llm_request
86
+ )
87
+ except Exception as exc:
88
+ self._broken = True
89
+ self._warn_once(exc)
90
+
91
+ async def get_tools(self, readonly_context=None):
92
+ if self._broken or self._inner is None:
93
+ return []
94
+ try:
95
+ return await self._inner.get_tools(readonly_context)
96
+ except Exception as exc:
97
+ self._broken = True
98
+ self._warn_once(exc)
99
+ return []
100
+
101
+ async def close(self) -> None:
102
+ if self._inner is not None:
103
+ try:
104
+ await self._inner.close()
105
+ except Exception:
106
+ pass
107
+
108
+ return _SafeComputerUseToolset()
103
109
 
104
110
 
105
111
  def _build_artifact_service(cfg: GemCodeConfig):
@@ -158,15 +164,15 @@ def create_runner(cfg: GemCodeConfig, extra_tools: list | None = None) -> Runner
158
164
  viewport_w = int(os.environ.get("GEMCODE_BROWSER_WIDTH", "1280"))
159
165
  viewport_h = int(os.environ.get("GEMCODE_BROWSER_HEIGHT", "720"))
160
166
  from gemcode.computer_use.browser_computer import BrowserComputer
161
- from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
162
167
 
163
168
  computer = BrowserComputer(
164
169
  headless=headless,
165
170
  viewport_size=(viewport_w, viewport_h),
166
171
  )
167
- computer_toolset = _SafeComputerUseToolset(computer=computer)
172
+ computer_toolset = _make_safe_computer_toolset(computer)
168
173
  merged_extra_tools = list(merged_extra_tools or [])
169
- merged_extra_tools.append(computer_toolset)
174
+ if computer_toolset is not None:
175
+ merged_extra_tools.append(computer_toolset)
170
176
 
171
177
  # Standalone read-only browser tools (browser_screenshot, browser_get_text, etc.)
172
178
  from gemcode.tools.browser import build_browser_inspection_tools
@@ -184,8 +184,18 @@ class GemCodeInputHandler:
184
184
  """Ctrl+C clears current line (like a real shell)."""
185
185
  event.app.current_buffer.reset()
186
186
 
187
- # Rows reserved for the / command popup. Too high (e.g. 20) leaves a huge
188
- # empty band above the prompt; 10–12 is enough for most slash lists.
187
+ @kb.add("enter")
188
+ def _submit(event):
189
+ """Enter always submits — even in multiline mode (for pasted code)."""
190
+ event.current_buffer.validate_and_handle()
191
+
192
+ @kb.add("escape", "enter", eager=True)
193
+ @kb.add("c-j") # Ctrl+J = manual newline inside a message
194
+ def _newline(event):
195
+ """Meta+Enter or Ctrl+J inserts a real newline without submitting."""
196
+ event.current_buffer.insert_text("\n")
197
+
198
+ # Rows reserved for the / command popup.
189
199
  try:
190
200
  _menu_rows = int(os.environ.get("GEMCODE_TUI_RESERVE_MENU_LINES", "12"))
191
201
  except ValueError:
@@ -202,8 +212,12 @@ class GemCodeInputHandler:
202
212
  mouse_support=False,
203
213
  complete_in_thread=True,
204
214
  reserve_space_for_menu=_menu_rows,
205
- # Single-column popup with description column (like VS Code)
206
215
  complete_style="COLUMN",
216
+ # Multiline=True so pasted code (with \n) lands in the buffer
217
+ # as one block rather than submitting line-by-line.
218
+ # Our Enter binding above overrides the default "add newline"
219
+ # behaviour so single-line prompts work exactly as before.
220
+ multiline=True,
207
221
  )
208
222
 
209
223
  def is_interactive(self) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.50
3
+ Version: 0.3.51
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes