gemcode 0.3.41__tar.gz → 0.3.43__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.41/src/gemcode.egg-info → gemcode-0.3.43}/PKG-INFO +1 -1
- {gemcode-0.3.41 → gemcode-0.3.43}/pyproject.toml +1 -1
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/intent_classifier.py +17 -21
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tui/scrollback.py +95 -13
- {gemcode-0.3.41 → gemcode-0.3.43/src/gemcode.egg-info}/PKG-INFO +1 -1
- {gemcode-0.3.41 → gemcode-0.3.43}/LICENSE +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/MANIFEST.in +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/README.md +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/setup.cfg +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/agent.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/config.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/version.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_credentials.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_paths.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_permissions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_tools.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.41 → gemcode-0.3.43}/tests/test_workspace_hints.py +0 -0
|
@@ -41,35 +41,19 @@ INTENT_DESCRIPTIONS: dict[str, str] = {
|
|
|
41
41
|
INTENT_ANALYSIS: "Systematic audit or summarisation — thorough tool sweep, then synthesise.",
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
# One-line summaries for the TUI — same visual lane as ∴ Thinking (collapsed)
|
|
45
|
-
INTENT_THINKING_SUMMARY: dict[str, str] = {
|
|
46
|
-
INTENT_GREETING: "Greeting / chitchat — no tools",
|
|
47
|
-
INTENT_CONCEPT: "General knowledge — answer without repo reads if possible",
|
|
48
|
-
INTENT_PROJECT_QUESTION: "About this repo — a few read-only tools, then answer",
|
|
49
|
-
INTENT_ENGINEERING_TASK: "Engineering task — orient → plan → execute → verify",
|
|
50
|
-
INTENT_ANALYSIS: "Deep analysis — systematic read / grep / synthesise",
|
|
51
|
-
}
|
|
52
|
-
|
|
53
44
|
# How the intent was determined (for TUI suffix)
|
|
54
45
|
SOURCE_LOCAL = "local" # obvious greeting / heuristic, no classifier API call
|
|
55
46
|
SOURCE_LLM = "llm" # gemini-2.5-flash-lite classifier
|
|
56
47
|
SOURCE_OFF = "off" # classifier disabled
|
|
57
48
|
|
|
58
49
|
|
|
59
|
-
def
|
|
50
|
+
def show_intentifying_line(source: str) -> bool:
|
|
60
51
|
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
Whether to show the minimal ``∴ Intentifying…`` line in the TUI (same lane
|
|
53
|
+
as ``∴ Thinking``). Hidden only when intent classification is disabled
|
|
54
|
+
(``SOURCE_OFF``).
|
|
64
55
|
"""
|
|
65
|
-
|
|
66
|
-
return None
|
|
67
|
-
summary = INTENT_THINKING_SUMMARY.get(intent, intent)
|
|
68
|
-
if source == SOURCE_LOCAL:
|
|
69
|
-
tag = "instant"
|
|
70
|
-
else:
|
|
71
|
-
tag = "flash-lite classifier"
|
|
72
|
-
return f"{intent} — {summary} · {tag}"
|
|
56
|
+
return source != SOURCE_OFF
|
|
73
57
|
|
|
74
58
|
# ── Prompts ───────────────────────────────────────────────────────────────────
|
|
75
59
|
_CLASSIFY_PROMPT = """\
|
|
@@ -110,6 +94,16 @@ _OBVIOUS_GREETINGS: frozenset[str] = frozenset({
|
|
|
110
94
|
"what's up", "whats up", "wassup",
|
|
111
95
|
})
|
|
112
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
|
+
|
|
113
107
|
|
|
114
108
|
def _classifier_enabled() -> bool:
|
|
115
109
|
v = os.environ.get("GEMCODE_INTENT_CLASSIFY_ENABLED", "1")
|
|
@@ -144,6 +138,8 @@ async def classify_intent_with_source(message: str) -> tuple[str, str]:
|
|
|
144
138
|
|
|
145
139
|
# Fast local check for unambiguously short greetings — saves an API round-trip.
|
|
146
140
|
lower = stripped.lower()
|
|
141
|
+
if lower in _RESUME_WORDS:
|
|
142
|
+
return INTENT_ENGINEERING_TASK, SOURCE_LOCAL
|
|
147
143
|
if lower in _OBVIOUS_GREETINGS or (len(lower) <= 3 and lower.isalpha()):
|
|
148
144
|
return INTENT_GREETING, SOURCE_LOCAL
|
|
149
145
|
|
|
@@ -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,21 +555,28 @@ 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:
|
|
521
569
|
from gemcode.intent_classifier import (
|
|
522
570
|
classify_intent_with_source,
|
|
523
571
|
generate_greeting_reply,
|
|
524
|
-
|
|
572
|
+
show_intentifying_line,
|
|
525
573
|
INTENT_GREETING,
|
|
526
574
|
INTENT_DESCRIPTIONS,
|
|
527
575
|
)
|
|
528
576
|
_intent, _intent_src = await classify_intent_with_source(prompt)
|
|
529
|
-
|
|
530
|
-
if _intent_line:
|
|
577
|
+
if show_intentifying_line(_intent_src):
|
|
531
578
|
print(
|
|
532
|
-
f" \u23bf {ansi.dim}\u2234
|
|
579
|
+
f" \u23bf {ansi.dim}\u2234 Intentifying\u2026{ansi.reset}"
|
|
533
580
|
)
|
|
534
581
|
print("")
|
|
535
582
|
|
|
@@ -611,6 +658,7 @@ async def run_gemcode_scrollback_tui(
|
|
|
611
658
|
assistant_wrote_text = False
|
|
612
659
|
buffered_thought: list[str] = []
|
|
613
660
|
buffered_final: list[str] = []
|
|
661
|
+
last_tool_error = None
|
|
614
662
|
kwargs = dict(
|
|
615
663
|
user_id="local", session_id=current_session_id, new_message=current_message
|
|
616
664
|
)
|
|
@@ -708,6 +756,40 @@ async def run_gemcode_scrollback_tui(
|
|
|
708
756
|
console.print(
|
|
709
757
|
_RichPadding(_RichMarkdown(final_text), (0, 0, 0, 4)),
|
|
710
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
|
|
711
793
|
break
|
|
712
794
|
|
|
713
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
|
|
File without changes
|
|
File without changes
|