fix: intercept non-cli errors and allow agent to continue
This commit is contained in:
@@ -136,6 +136,18 @@ class BaseCLIAgent:
|
||||
if output_file_content and not stdout_text.strip():
|
||||
stdout_text = output_file_content
|
||||
|
||||
if return_code != 0:
|
||||
recovered = self._recover_from_error(
|
||||
returncode=return_code,
|
||||
stdout=stdout_text,
|
||||
stderr=stderr_text,
|
||||
sanitized_command=sanitized_command,
|
||||
duration_seconds=duration,
|
||||
output_file_content=output_file_content,
|
||||
)
|
||||
if recovered is not None:
|
||||
return recovered
|
||||
|
||||
if return_code != 0:
|
||||
raise CLIAgentError(
|
||||
f"CLI '{self.client.name}' exited with status {return_code}",
|
||||
@@ -177,3 +189,25 @@ class BaseCLIAgent:
|
||||
env = os.environ.copy()
|
||||
env.update(self.client.env)
|
||||
return env
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Error recovery hooks
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _recover_from_error(
|
||||
self,
|
||||
*,
|
||||
returncode: int,
|
||||
stdout: str,
|
||||
stderr: str,
|
||||
sanitized_command: list[str],
|
||||
duration_seconds: float,
|
||||
output_file_content: str | None,
|
||||
) -> AgentOutput | None:
|
||||
"""Hook for subclasses to convert CLI errors into successful outputs.
|
||||
|
||||
Return an AgentOutput to treat the failure as success, or None to signal
|
||||
that normal error handling should proceed.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
@@ -2,13 +2,85 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from clink.models import ResolvedCLIClient
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from .base import BaseCLIAgent
|
||||
from clink.models import ResolvedCLIClient
|
||||
from clink.parsers.base import ParsedCLIResponse
|
||||
|
||||
from .base import AgentOutput, BaseCLIAgent
|
||||
|
||||
|
||||
class GeminiAgent(BaseCLIAgent):
|
||||
"""Placeholder for Gemini-specific behaviour."""
|
||||
"""Gemini-specific behaviour."""
|
||||
|
||||
def __init__(self, client: ResolvedCLIClient):
|
||||
super().__init__(client)
|
||||
|
||||
def _recover_from_error(
|
||||
self,
|
||||
*,
|
||||
returncode: int,
|
||||
stdout: str,
|
||||
stderr: str,
|
||||
sanitized_command: list[str],
|
||||
duration_seconds: float,
|
||||
output_file_content: str | None,
|
||||
) -> AgentOutput | None:
|
||||
combined = "\n".join(part for part in (stderr, stdout) if part)
|
||||
if not combined:
|
||||
return None
|
||||
|
||||
brace_index = combined.find("{")
|
||||
if brace_index == -1:
|
||||
return None
|
||||
|
||||
json_candidate = combined[brace_index:]
|
||||
try:
|
||||
payload: dict[str, Any] = json.loads(json_candidate)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
error_block = payload.get("error")
|
||||
if not isinstance(error_block, dict):
|
||||
return None
|
||||
|
||||
code = error_block.get("code")
|
||||
err_type = error_block.get("type")
|
||||
detail_message = error_block.get("message")
|
||||
|
||||
prologue = combined[:brace_index].strip()
|
||||
lines: list[str] = []
|
||||
if prologue and (not detail_message or prologue not in detail_message):
|
||||
lines.append(prologue)
|
||||
if detail_message:
|
||||
lines.append(detail_message)
|
||||
|
||||
header = "Gemini CLI reported a tool failure"
|
||||
if code:
|
||||
header = f"{header} ({code})"
|
||||
elif err_type:
|
||||
header = f"{header} ({err_type})"
|
||||
|
||||
content_lines = [header.rstrip(".") + "."]
|
||||
content_lines.extend(lines)
|
||||
message = "\n".join(content_lines).strip()
|
||||
|
||||
metadata = {
|
||||
"cli_error_recovered": True,
|
||||
"cli_error_code": code,
|
||||
"cli_error_type": err_type,
|
||||
"cli_error_payload": payload,
|
||||
}
|
||||
|
||||
parsed = ParsedCLIResponse(content=message or header, metadata=metadata)
|
||||
return AgentOutput(
|
||||
parsed=parsed,
|
||||
sanitized_command=sanitized_command,
|
||||
returncode=returncode,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
duration_seconds=duration_seconds,
|
||||
parser_name=self._parser.name,
|
||||
output_file_content=output_file_content,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user