gemcode 0.3.42__tar.gz → 0.3.44__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.
- {gemcode-0.3.42/src/gemcode.egg-info → gemcode-0.3.44}/PKG-INFO +1 -1
- {gemcode-0.3.42 → gemcode-0.3.44}/pyproject.toml +1 -1
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/agent.py +4 -1
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/intent_classifier.py +12 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/session_runtime.py +73 -1
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tui/scrollback.py +92 -9
- {gemcode-0.3.42 → gemcode-0.3.44/src/gemcode.egg-info}/PKG-INFO +1 -1
- {gemcode-0.3.42 → gemcode-0.3.44}/LICENSE +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/MANIFEST.in +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/README.md +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/setup.cfg +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/config.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/version.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_credentials.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_paths.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_permissions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_tools.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.42 → gemcode-0.3.44}/tests/test_workspace_hints.py +0 -0
|
@@ -603,10 +603,13 @@ For tasks where quality matters:
|
|
|
603
603
|
4. Report done only when verified
|
|
604
604
|
|
|
605
605
|
## Error recovery
|
|
606
|
-
- **Test/build failures**: read the full error, identify the exact line, fix, re-run.
|
|
606
|
+
- **Test/build failures**: read the full error, identify the exact line, fix, re-run. Do NOT give up after one attempt.
|
|
607
|
+
- **Frontend / Next.js build errors**: read `src/app/page.tsx` (or the file in the error trace), fix the import/export precisely, then re-run the dev server.
|
|
608
|
+
- **lucide-react icon errors** (`Export X was not found`): The correct icon API for lucide-react ≥0.460 uses `Github` → `Github` is removed; use `GithubIcon` or find the right name by checking `node_modules/lucide-react/dist/esm/lucide-react.js` with `grep_content`. Always verify icon names before writing code.
|
|
607
609
|
- **Tool errors**: diagnose why it failed before retrying — don't repeat the exact same call.
|
|
608
610
|
- **After 2 failed attempts on the same problem**: stop and explain the blocker clearly.
|
|
609
611
|
- **Unexpected file content**: re-read the actual file rather than assuming your mental model is correct.
|
|
612
|
+
- **Compiler / linter errors pasted by the user**: extract the file path and line from the error, read that file, apply the minimal fix, and re-run the check. Never explain without fixing.
|
|
610
613
|
|
|
611
614
|
## Risk and permissions
|
|
612
615
|
- State destructive operations clearly before doing them (deletes, force-push, data truncation).
|
|
@@ -94,6 +94,16 @@ _OBVIOUS_GREETINGS: frozenset[str] = frozenset({
|
|
|
94
94
|
"what's up", "whats up", "wassup",
|
|
95
95
|
})
|
|
96
96
|
|
|
97
|
+
# Single-token "control" messages users often type after transient model errors.
|
|
98
|
+
# Treat these as "resume the last task", not greetings.
|
|
99
|
+
_RESUME_WORDS: frozenset[str] = frozenset({
|
|
100
|
+
"continue",
|
|
101
|
+
"go on",
|
|
102
|
+
"resume",
|
|
103
|
+
"retry",
|
|
104
|
+
"try again",
|
|
105
|
+
})
|
|
106
|
+
|
|
97
107
|
|
|
98
108
|
def _classifier_enabled() -> bool:
|
|
99
109
|
v = os.environ.get("GEMCODE_INTENT_CLASSIFY_ENABLED", "1")
|
|
@@ -128,6 +138,8 @@ async def classify_intent_with_source(message: str) -> tuple[str, str]:
|
|
|
128
138
|
|
|
129
139
|
# Fast local check for unambiguously short greetings — saves an API round-trip.
|
|
130
140
|
lower = stripped.lower()
|
|
141
|
+
if lower in _RESUME_WORDS:
|
|
142
|
+
return INTENT_ENGINEERING_TASK, SOURCE_LOCAL
|
|
131
143
|
if lower in _OBVIOUS_GREETINGS or (len(lower) <= 3 and lower.isalpha()):
|
|
132
144
|
return INTENT_GREETING, SOURCE_LOCAL
|
|
133
145
|
|
|
@@ -30,6 +30,78 @@ def session_db_path(cfg: GemCodeConfig) -> Path:
|
|
|
30
30
|
return cfg.project_root / ".gemcode" / "sessions.sqlite"
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
class _SafeComputerUseToolset:
|
|
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
|
+
)
|
|
74
|
+
|
|
75
|
+
# ── ADK toolset protocol ────────────────────────────────────────────────────
|
|
76
|
+
|
|
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)
|
|
103
|
+
|
|
104
|
+
|
|
33
105
|
def _build_artifact_service(cfg: GemCodeConfig):
|
|
34
106
|
"""
|
|
35
107
|
Return an ADK ArtifactService for this session, or None if disabled.
|
|
@@ -92,7 +164,7 @@ def create_runner(cfg: GemCodeConfig, extra_tools: list | None = None) -> Runner
|
|
|
92
164
|
headless=headless,
|
|
93
165
|
viewport_size=(viewport_w, viewport_h),
|
|
94
166
|
)
|
|
95
|
-
computer_toolset =
|
|
167
|
+
computer_toolset = _SafeComputerUseToolset(computer=computer)
|
|
96
168
|
merged_extra_tools = list(merged_extra_tools or [])
|
|
97
169
|
merged_extra_tools.append(computer_toolset)
|
|
98
170
|
|
|
@@ -251,6 +251,10 @@ async def run_gemcode_scrollback_tui(
|
|
|
251
251
|
get_cfg=lambda: cfg,
|
|
252
252
|
)
|
|
253
253
|
|
|
254
|
+
# Turn-scoped monitors/state.
|
|
255
|
+
# Declared up-front so nested render helpers can update them via `nonlocal`.
|
|
256
|
+
last_tool_error: dict | None = None
|
|
257
|
+
|
|
254
258
|
async def typewrite(text: str) -> None:
|
|
255
259
|
if not text:
|
|
256
260
|
return
|
|
@@ -386,6 +390,7 @@ async def run_gemcode_scrollback_tui(
|
|
|
386
390
|
|
|
387
391
|
def _render_tool_results(ev) -> None:
|
|
388
392
|
"""Show a one-line result summary; transition spinner to 'Querying…'."""
|
|
393
|
+
nonlocal last_tool_error
|
|
389
394
|
try:
|
|
390
395
|
frs: list = []
|
|
391
396
|
try:
|
|
@@ -409,6 +414,28 @@ async def run_gemcode_scrollback_tui(
|
|
|
409
414
|
summary = _fmt_tool_result(resp)
|
|
410
415
|
if summary:
|
|
411
416
|
print(f" ⎿ {ansi.dim}\u21b3 {summary}{ansi.reset}")
|
|
417
|
+
# Capture the most recent tool error (best-effort) so we can offer to fix it.
|
|
418
|
+
try:
|
|
419
|
+
d = resp if isinstance(resp, dict) else {}
|
|
420
|
+
inner = d.get("result", d)
|
|
421
|
+
if not isinstance(inner, dict):
|
|
422
|
+
inner = d
|
|
423
|
+
err = inner.get("error") or d.get("error")
|
|
424
|
+
exit_code = inner.get("exit_code")
|
|
425
|
+
stderr = inner.get("stderr") or ""
|
|
426
|
+
if err or (isinstance(exit_code, int) and exit_code != 0):
|
|
427
|
+
full = ""
|
|
428
|
+
if isinstance(err, str) and err.strip():
|
|
429
|
+
full = err.strip()
|
|
430
|
+
elif isinstance(stderr, str) and stderr.strip():
|
|
431
|
+
full = stderr.strip()
|
|
432
|
+
last_tool_error = {
|
|
433
|
+
"tool": getattr(fr, "name", "") or "tool",
|
|
434
|
+
"summary": (summary or str(err) or str(exit_code) or "error")[:120],
|
|
435
|
+
"full": full[:2000],
|
|
436
|
+
}
|
|
437
|
+
except Exception:
|
|
438
|
+
pass
|
|
412
439
|
# Restart spinner while model re-queries with the tool outputs.
|
|
413
440
|
_start_anim("Querying\u2026")
|
|
414
441
|
except Exception:
|
|
@@ -446,18 +473,24 @@ async def run_gemcode_scrollback_tui(
|
|
|
446
473
|
)
|
|
447
474
|
|
|
448
475
|
current_session_id = session_id
|
|
476
|
+
pending_prompt: str | None = None
|
|
477
|
+
last_user_prompt: str | None = None
|
|
449
478
|
|
|
450
479
|
while True:
|
|
451
|
-
|
|
452
|
-
prompt =
|
|
453
|
-
|
|
454
|
-
|
|
480
|
+
if pending_prompt:
|
|
481
|
+
prompt = pending_prompt
|
|
482
|
+
pending_prompt = None
|
|
483
|
+
else:
|
|
455
484
|
try:
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
485
|
+
prompt = await input_handler.prompt_async()
|
|
486
|
+
except EOFError:
|
|
487
|
+
print("")
|
|
488
|
+
try:
|
|
489
|
+
from gemcode.hooks import run_session_stop_hook
|
|
490
|
+
run_session_stop_hook(cfg.project_root, model=getattr(cfg, "model", "") or "")
|
|
491
|
+
except Exception:
|
|
492
|
+
pass
|
|
493
|
+
return
|
|
461
494
|
if not prompt:
|
|
462
495
|
continue
|
|
463
496
|
if prompt in (":q", "quit", "exit", "/exit"):
|
|
@@ -468,6 +501,13 @@ async def run_gemcode_scrollback_tui(
|
|
|
468
501
|
pass
|
|
469
502
|
return
|
|
470
503
|
|
|
504
|
+
# Plain-text resume command: rerun the last user message (useful after 503s).
|
|
505
|
+
if (prompt or "").strip().lower() in ("continue", "resume", "retry", "try again", "go on"):
|
|
506
|
+
if last_user_prompt:
|
|
507
|
+
print(f" ⎿ {ansi.dim}↻ Continuing last request…{ansi.reset}")
|
|
508
|
+
print("")
|
|
509
|
+
prompt = last_user_prompt
|
|
510
|
+
|
|
471
511
|
old_model = getattr(cfg, "model", "")
|
|
472
512
|
old_model_overridden = bool(getattr(cfg, "model_overridden", False))
|
|
473
513
|
|
|
@@ -515,6 +555,14 @@ async def run_gemcode_scrollback_tui(
|
|
|
515
555
|
continue
|
|
516
556
|
prompt = slash.model_prompt or prompt
|
|
517
557
|
|
|
558
|
+
# Track the last real user request so "continue" can rerun it later.
|
|
559
|
+
try:
|
|
560
|
+
pnorm = (prompt or "").strip()
|
|
561
|
+
if pnorm:
|
|
562
|
+
last_user_prompt = pnorm
|
|
563
|
+
except Exception:
|
|
564
|
+
pass
|
|
565
|
+
|
|
518
566
|
# ── LLM intent pre-classifier ────────────────────────────────────────────
|
|
519
567
|
# gemini-2.5-flash-lite classifies the message (same lane as Thinking)
|
|
520
568
|
try:
|
|
@@ -610,6 +658,7 @@ async def run_gemcode_scrollback_tui(
|
|
|
610
658
|
assistant_wrote_text = False
|
|
611
659
|
buffered_thought: list[str] = []
|
|
612
660
|
buffered_final: list[str] = []
|
|
661
|
+
last_tool_error = None
|
|
613
662
|
kwargs = dict(
|
|
614
663
|
user_id="local", session_id=current_session_id, new_message=current_message
|
|
615
664
|
)
|
|
@@ -707,6 +756,40 @@ async def run_gemcode_scrollback_tui(
|
|
|
707
756
|
console.print(
|
|
708
757
|
_RichPadding(_RichMarkdown(final_text), (0, 0, 0, 4)),
|
|
709
758
|
)
|
|
759
|
+
|
|
760
|
+
# If a tool error occurred during this turn, ask whether to resolve it.
|
|
761
|
+
if last_tool_error:
|
|
762
|
+
try:
|
|
763
|
+
tool_name = last_tool_error.get("tool") or "tool"
|
|
764
|
+
summary = last_tool_error.get("summary") or "an error"
|
|
765
|
+
full = last_tool_error.get("full") or ""
|
|
766
|
+
print("")
|
|
767
|
+
print(
|
|
768
|
+
f" ⎿ {ansi.blue_warn}{ansi.bold}Detected an error{ansi.reset} "
|
|
769
|
+
f"in {ansi.bold}{tool_name}{ansi.reset}: {ansi.dim}{summary}{ansi.reset}"
|
|
770
|
+
)
|
|
771
|
+
sys.stdout.flush()
|
|
772
|
+
prompt_str = (
|
|
773
|
+
f" ⎿ Try to resolve it now? "
|
|
774
|
+
f"[{ansi.blue_ok}y{ansi.reset} = yes "
|
|
775
|
+
f"{ansi.dim}any other key = no{ansi.reset}] "
|
|
776
|
+
)
|
|
777
|
+
sys.stdout.write(prompt_str)
|
|
778
|
+
sys.stdout.flush()
|
|
779
|
+
ok = await _read_permission_char(asyncio.get_running_loop())
|
|
780
|
+
sys.stdout.write(("y" if ok else "n") + "\n")
|
|
781
|
+
sys.stdout.flush()
|
|
782
|
+
if ok:
|
|
783
|
+
pending_prompt = (
|
|
784
|
+
"We encountered an error during the last turn.\n\n"
|
|
785
|
+
f"Tool: {tool_name}\n"
|
|
786
|
+
f"Summary: {summary}\n\n"
|
|
787
|
+
f"{full}\n\n"
|
|
788
|
+
"Please fix the issue. If a command needs to be run, propose it "
|
|
789
|
+
"and ask for confirmation."
|
|
790
|
+
)
|
|
791
|
+
except Exception:
|
|
792
|
+
pass
|
|
710
793
|
break
|
|
711
794
|
|
|
712
795
|
interactive_enabled = bool(getattr(cfg, "interactive_permission_ask", False))
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|