Add configurable server timeouts and file-based Claude Code prompt delivery#318
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #318 +/- ##
=======================================
Coverage ? 87.47%
=======================================
Files ? 92
Lines ? 11134
Branches ? 0
=======================================
Hits ? 9739
Misses ? 1395
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR introduces configurable “server tuning” settings (timeouts and queue sizes) loaded from settings.json, and updates the Claude Code provider to avoid OS command-line length limits by writing the system prompt and MCP config to files.
Changes:
- Add
get_server_settings()with defaults and wire it into server/client timeouts and EventBus queue sizing. - Update CLI providers to use a configurable provider initialization timeout (replacing hardcoded values).
- Switch Claude Code prompt/MCP config delivery from inline CLI args to file-based arguments; update unit tests and docs accordingly.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/providers/test_claude_code_unit.py | Updates expectations/parsing to validate file-based --append-system-prompt-file and --mcp-config. |
| src/cli_agent_orchestrator/services/settings_service.py | Adds get_server_settings() and default values for configurable server tuning. |
| src/cli_agent_orchestrator/services/event_bus.py | Reads EventBus subscriber queue maxsize from server settings. |
| src/cli_agent_orchestrator/providers/q_cli.py | Uses configurable provider init timeout instead of hardcoded shell init timeout. |
| src/cli_agent_orchestrator/providers/opencode_cli.py | Uses configurable provider init timeout. |
| src/cli_agent_orchestrator/providers/kiro_cli.py | Uses configurable provider init timeout (including recovery path). |
| src/cli_agent_orchestrator/providers/kimi_cli.py | Uses configurable provider init timeout. |
| src/cli_agent_orchestrator/providers/hermes.py | Uses configurable provider init timeout. |
| src/cli_agent_orchestrator/providers/gemini_cli.py | Uses configurable provider init timeout. |
| src/cli_agent_orchestrator/providers/cursor_cli.py | Uses configurable provider init timeout. |
| src/cli_agent_orchestrator/providers/copilot_cli.py | Uses configurable provider init timeout for both async and sync shell readiness checks. |
| src/cli_agent_orchestrator/providers/codex.py | Uses configurable provider init timeout. |
| src/cli_agent_orchestrator/providers/claude_code.py | Writes system prompt + MCP config to files and makes startup prompt handler timeout configurable. |
| src/cli_agent_orchestrator/mcp_server/server.py | Uses configurable MCP request timeout for requests to the CAO API. |
| src/cli_agent_orchestrator/cli/commands/launch.py | Uses configurable MCP request timeout for launch-time requests. |
| docs/settings.md | Documents the new server settings section in settings.json. |
| docs/CAO-LOCAL-GUIDE.html | Adds a large HTML guide file (appears machine-local). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| settings = _load() | ||
| saved = settings.get("server", {}) | ||
| result = dict(_SERVER_DEFAULTS) | ||
| result.update({k: v for k, v in saved.items() if k in _SERVER_DEFAULTS}) | ||
| return result |
| tmp_dir = Path.home() / ".aws" / "cli-agent-orchestrator" / "tmp" | ||
| tmp_dir.mkdir(parents=True, exist_ok=True) | ||
| prompt_file = tmp_dir / f"{self.terminal_id}.prompt" | ||
| prompt_file.write_text(system_prompt) | ||
| command_parts.extend(["--append-system-prompt-file", str(prompt_file)]) |
| tmp_dir = Path.home() / ".aws" / "cli-agent-orchestrator" / "tmp" | ||
| tmp_dir.mkdir(parents=True, exist_ok=True) | ||
| mcp_file = tmp_dir / f"{self.terminal_id}.mcp.json" | ||
| mcp_file.write_text(json.dumps({"mcpServers": mcp_config})) | ||
| command_parts.extend(["--mcp-config", str(mcp_file)]) |
| # shlex.join wraps in single quotes; strip them | ||
| if mcp_file_path.startswith("'") and mcp_file_path.endswith("'"): | ||
| mcp_file_path = mcp_file_path[1:-1] | ||
| mcp_data = json.loads(Path(mcp_file_path).read_text()) |
| mcp_file_path = parts[1].strip().split()[0] | ||
| if mcp_file_path.startswith("'") and mcp_file_path.endswith("'"): | ||
| mcp_file_path = mcp_file_path[1:-1] | ||
| mcp_data = json.loads(Path(mcp_file_path).read_text()) |
| mcp_file_path = parts[1].strip().split()[0] | ||
| if mcp_file_path.startswith("'") and mcp_file_path.endswith("'"): | ||
| mcp_file_path = mcp_file_path[1:-1] | ||
| mcp_data = json.loads(Path(mcp_file_path).read_text()) |
| if not await wait_for_shell(self.terminal_id, timeout=get_server_settings()["provider_init_timeout"]): | ||
| raise TimeoutError("Shell initialization timed out") |
| <h2 id="install">Installation (This Machine)</h2> | ||
| <pre><code># Installed from local patched clone | ||
| uv tool install /Users/JSR34/Repositories/cli-agent-orchestrator --upgrade | ||
|
|
||
| # Local clone location | ||
| /Users/JSR34/Repositories/cli-agent-orchestrator (branch: local/performance-patches) | ||
|
|
||
| # Installed binary location | ||
| /Users/JSR34/.local/bin/cao | ||
| /Users/JSR34/.local/bin/cao-server | ||
| /Users/JSR34/.local/bin/cao-mcp-server | ||
|
|
||
| # Data directory | ||
| ~/.aws/cli-agent-orchestrator/ | ||
| ├── agent-context/ → installed agent profiles (cao install) | ||
| ├── agent-store/ → custom agent profiles | ||
| ├── db/ → SQLite database | ||
| ├── fifos/ → Named pipes for terminal output streaming | ||
| ├── logs/ → Server logs + terminal scrollback | ||
| ├── memory/ → Agent memory wiki files | ||
| └── settings.json → Configuration</code></pre> |
70cb3f5 to
99633e4
Compare
|
@call-me-ram mind reviewing this ticket if get a chance ? |
| settings = _load() | ||
| saved = settings.get("server", {}) | ||
| result = dict(_SERVER_DEFAULTS) | ||
| result.update({k: v for k, v in saved.items() if k in _SERVER_DEFAULTS}) | ||
| # Validate types and ranges |
| logger.info("Set skipDangerousModePermissionPrompt in ~/.claude/settings.json") | ||
|
|
||
| def _handle_startup_prompts(self, timeout: float = 20.0) -> None: | ||
| def _handle_startup_prompts(self, timeout: float = None) -> None: |
| @@ -211,8 +216,14 @@ def _build_claude_command(self) -> str: | |||
| env["CAO_TERMINAL_ID"] = self.terminal_id | |||
| mcp_config[server_name]["env"] = env | |||
|
|
|||
| mcp_json = json.dumps({"mcpServers": mcp_config}) | |||
| command_parts.extend(["--mcp-config", mcp_json]) | |||
| tmp_dir = Path.home() / ".aws" / "cli-agent-orchestrator" / "tmp" | |||
| tmp_dir.mkdir(parents=True, exist_ok=True) | |||
| mcp_file = tmp_dir / f"{self.terminal_id}.mcp.json" | |||
| mcp_file.write_text( | |||
| json.dumps({"mcpServers": mcp_config}), encoding="utf-8" | |||
| ) | |||
| mcp_file.chmod(0o600) | |||
| command_parts.extend(["--mcp-config", str(mcp_file)]) | |||
| if not shell_ready: | ||
| raise TimeoutError("Shell initialization timed out after 30 seconds") | ||
| raise TimeoutError("Shell initialization timed out") |
| """Subscribe to a topic pattern (e.g., 'terminal.*.output'). Returns async queue.""" | ||
| queue: asyncio.Queue = asyncio.Queue(maxsize=EVENT_BUS_MAX_QUEUE_SIZE) | ||
| queue: asyncio.Queue = asyncio.Queue(maxsize=get_server_settings()["event_bus_max_queue_size"]) | ||
|
|
| assert get_extra_skill_dirs() == ["/skills"] | ||
| class TestGetServerSettings: |
| init_timeout = get_server_settings()["provider_init_timeout"] | ||
| if not await wait_for_shell(self.terminal_id, timeout=init_timeout): | ||
| raise TimeoutError(f"Shell initialization timed out after {init_timeout}s") |
| def _mcp_timeout() -> int: | ||
| """Get MCP request timeout from server settings.""" | ||
| return get_server_settings()["mcp_request_timeout"] | ||
|
|
||
| # Environment variable to enable/disable working_directory parameter | ||
| ENABLE_WORKING_DIRECTORY = os.getenv("CAO_ENABLE_WORKING_DIRECTORY", "false").lower() == "true" |
Closes #317
Summary
Add configurable server tuning settings and fix Claude Code command-line length limit.
Changes
Configurable server settings via
settings.jsonNew
"server"key in~/.aws/cli-agent-orchestrator/settings.json:{ "server": { "mcp_request_timeout": 120, "event_bus_max_queue_size": 8192, "provider_init_timeout": 90, "startup_prompt_handler_timeout": 5 } }mcp_request_timeout30event_bus_max_queue_size1024provider_init_timeout60startup_prompt_handler_timeout20All values retain original defaults for backward compatibility.
File-based prompt delivery for Claude Code
Replace inline
--append-system-promptand--mcp-configwith file-based equivalents to avoid OS command-line length limits (ref: anthropics/claude-code#3411):Files Changed
services/settings_service.py— newget_server_settings()functioncli/commands/launch.py— read MCP timeout from settingsservices/event_bus.py— read queue size from settingsmcp_server/server.py— read MCP timeout from settings via_mcp_timeout()providers/claude_code.py— file-based prompt/MCP config + configurable timeoutsproviders/*.py(all 9) — useprovider_init_timeoutfrom settingsdocs/settings.md— document new server tuning sectiontest/providers/test_claude_code_unit.py— updated for file-based behaviorTesting
settings.json(uses defaults)