diff --git a/config/backends/backend-instances/agy-cli-acp.default.yaml b/config/backends/backend-instances/agy-cli-acp.default.yaml index a56cb5620..c33d350cf 100644 --- a/config/backends/backend-instances/agy-cli-acp.default.yaml +++ b/config/backends/backend-instances/agy-cli-acp.default.yaml @@ -6,7 +6,9 @@ connector: agy-cli-acp extra: # workspace_path omitted: project root comes from session auto-detection (per-request project_dir). model: "auto" - process_timeout: 300 + # 4 hours. Forwarded to the wrapper as --timeout-seconds (agy --print-timeout). + # agy's own print-mode default is 5 minutes and will abort long tool waits. + process_timeout: 14400 idle_timeout: 120 skip_permissions: true mcp_servers: [] diff --git a/src/connectors/agy_cli_acp.py b/src/connectors/agy_cli_acp.py index b34cb10b3..d5d6ae844 100644 --- a/src/connectors/agy_cli_acp.py +++ b/src/connectors/agy_cli_acp.py @@ -27,6 +27,10 @@ logger = logging.getLogger(__name__) ACP_PROTOCOL_VERSION = 1 +# Match go-agy-acp-wrapper's 4-hour per-turn ceiling. This is both the ACP +# JSON-RPC wait and --timeout-seconds forwarded to the wrapper; agy's own +# --print-timeout default is only 5 minutes. +DEFAULT_AGY_PROCESS_TIMEOUT_SECONDS = 14400.0 CANONICAL_MODEL_ID_PATTERN = re.compile( r"^[a-z0-9][a-z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._-]*$" ) @@ -296,6 +300,7 @@ def __init__( self._skip_permissions = True self._mcp_servers: list[Any] = [] self._extra_wrapper_args: list[str] = [] + self._process_timeout = DEFAULT_AGY_PROCESS_TIMEOUT_SECONDS async def initialize(self, **kwargs: Any) -> None: try: diff --git a/tests/unit/connectors/test_agy_cli_acp.py b/tests/unit/connectors/test_agy_cli_acp.py index 73e607e99..9b2498609 100644 --- a/tests/unit/connectors/test_agy_cli_acp.py +++ b/tests/unit/connectors/test_agy_cli_acp.py @@ -9,6 +9,7 @@ from pydantic.types import JsonValue from src.connectors.acp_core.types import ACPNotification from src.connectors.agy_cli_acp import ( + DEFAULT_AGY_PROCESS_TIMEOUT_SECONDS, AgyCliAcpConnector, AgyCliConfiguredModelEnumerator, build_agy_acp_wrapper_command, @@ -172,6 +173,12 @@ def test_resolve_wrapper_prefers_existing_file(self, tmp_path: Path) -> None: exe.write_text("noop", encoding="utf-8") assert resolve_agy_acp_wrapper_executable(str(exe)) == str(exe.resolve()) + def test_connector_default_process_timeout_is_four_hours( + self, connector: AgyCliAcpConnector + ) -> None: + assert connector._process_timeout == DEFAULT_AGY_PROCESS_TIMEOUT_SECONDS + assert DEFAULT_AGY_PROCESS_TIMEOUT_SECONDS == 14400.0 + class TestAgyCliAcpInitialization: async def test_initialize_with_project_dir( @@ -190,6 +197,7 @@ async def test_initialize_with_project_dir( assert connector.is_backend_functional() is True assert connector._default_project_dir == temp_workspace.resolve() assert connector._model == "google/gemini-3.5-flash-medium" + assert connector._process_timeout == DEFAULT_AGY_PROCESS_TIMEOUT_SECONDS async def test_initialize_requires_existing_workspace( self, connector: AgyCliAcpConnector, tmp_path: Path