From 89f9651d0d5e7cd9f6d554d3f1332e309831c49a Mon Sep 17 00:00:00 2001 From: Wojak Date: Wed, 30 Sep 2026 15:19:46 +0700 Subject: [PATCH 1/5] fix(acp): launch the latest Codex bridge --- condor/acp/client.py | 2 +- condor/llm/readiness.py | 18 ++++++++++++++---- tests/test_model_readiness.py | 13 +++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/condor/acp/client.py b/condor/acp/client.py index e51441484..159332950 100644 --- a/condor/acp/client.py +++ b/condor/acp/client.py @@ -294,7 +294,7 @@ def _acp_ish(a: str) -> bool: "claude-acp": "claude-agent-acp", # model-configurable form: claude-acp: "gemini": "npx @google/gemini-cli --acp", "copilot": "npx @github/copilot --acp --stdio", - "codex": "npx @agentclientprotocol/codex-acp", + "codex": "npx --yes --prefer-online @agentclientprotocol/codex-acp@latest", } # Session markers Claude Code exports into the shells it spawns. If the bot was diff --git a/condor/llm/readiness.py b/condor/llm/readiness.py index 3eeed9a13..94e43223f 100644 --- a/condor/llm/readiness.py +++ b/condor/llm/readiness.py @@ -188,11 +188,18 @@ def acp_login_state(base: str) -> bool | None: return None # copilot and anything new: no documented on-disk marker +def _npx_package(parts: list[str]) -> str: + """Package spec after the boolean flags in Condor's npx commands.""" + return next((part for part in parts[1:] if not part.startswith("-")), "") + + def install_command(cmd: str) -> str: """How to install the CLI behind an ACP command line.""" - parts = cmd.split() + parts = shlex.split(cmd) if len(parts) > 1 and parts[0] == "npx": - return f"npm install -g {parts[1]}" + package = _npx_package(parts) + if package: + return f"npm install -g {shlex.quote(package)}" if parts and parts[0] in _BARE_INSTALL: return _BARE_INSTALL[parts[0]] return f"install `{cmd.split()[0] if parts else cmd}` and put it on PATH" @@ -275,11 +282,14 @@ def acp_bridges() -> list[dict]: npx_packages = npx_packages_installed() out: list[dict] = [] for base, cmd in ACP_COMMANDS.items(): - parts = cmd.split() + parts = shlex.split(cmd) if not parts: available = False elif parts[0] == "npx": - available = parts[1] in npx_packages if len(parts) > 1 else False + # A latest tag is resolved online at launch; local presence only + # proves the package is installed, not that it is already current. + package = _npx_package(parts).removesuffix("@latest") + available = package in npx_packages else: available = shutil.which(parts[0]) is not None out.append( diff --git a/tests/test_model_readiness.py b/tests/test_model_readiness.py index 94a56c2e1..22fc39c06 100644 --- a/tests/test_model_readiness.py +++ b/tests/test_model_readiness.py @@ -154,6 +154,19 @@ def test_acp_bridges_surface_login_state(monkeypatch): assert by_key["gemini"]["logged_in"] is False +def test_codex_latest_bridge_readiness_parses_flags_and_tag(monkeypatch): + package = "@agentclientprotocol/codex-acp" + monkeypatch.setattr(readiness, "acp_login_state", lambda base: True) + monkeypatch.setattr(readiness, "npx_packages_installed", lambda: {package}) + bridge = next(row for row in readiness.acp_bridges() if row["agent_key"] == "codex") + assert bridge["available"] is True + assert bridge["logged_in"] is True + assert ( + readiness.install_command(bridge["command"]) + == f"npm install -g {package}@latest" + ) + + def test_install_command_names_the_package(): assert readiness.install_command("npx @google/gemini-cli --acp") == ( "npm install -g @google/gemini-cli" From 7ad3cb2b0c9e0fbaef546f6f7735d48e4e8c8403 Mon Sep 17 00:00:00 2001 From: Wojak Date: Wed, 30 Sep 2026 15:56:42 +0700 Subject: [PATCH 2/5] fix(setup): refresh the Codex bridge through an explicit make target --- Makefile | 8 +++++++- README.md | 1 + condor/acp/client.py | 2 +- condor/llm/readiness.py | 18 ++++-------------- tests/test_model_readiness.py | 13 ------------- 5 files changed, 13 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index fb00b8a17..d69ba9304 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ export PATH := $(HOME)/.local/bin:$(HOME)/.cargo/bin:$(PATH) # "Entering/Leaving directory" line around every sub-invocation. MAKEFLAGS += --no-print-directory -.PHONY: help setup install run run-fg stop restart logs status check-stopped test lint build-frontend setup-chrome pick-model doctor +.PHONY: help setup install run run-fg stop restart logs status check-stopped test lint build-frontend setup-chrome pick-model doctor refresh-codex-bridge # tmux session Condor runs in SESSION := condor @@ -26,6 +26,7 @@ help: @echo "" @echo " make setup - Interactive setup wizard" @echo " make pick-model - Choose the AI model Condor thinks with" + @echo " make refresh-codex-bridge - Update the Codex ACP bridge and its bundled CLI" @echo " make install - Setup + install all dependencies" @echo " make run - Start Condor in the '$(SESSION)' tmux session" @echo " make run-fg - Run in the foreground (debugging)" @@ -43,6 +44,11 @@ setup: pick-model: uv run python -m condor.setup_llm +refresh-codex-bridge: + npm install -g @agentclientprotocol/codex-acp@latest + npm list -g @agentclientprotocol/codex-acp @openai/codex --depth=1 + @echo "Start a new Codex session in Condor to use the updated bridge." + install: setup uv sync --dev @bash -c ' \ diff --git a/README.md b/README.md index 9b6ebf0c3..ddbef35f6 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ The following applies after **Install Condor**. If you used **Install only Hummi - In Telegram, use **`/servers`** for Hummingbot API URLs and auth, **`/keys`** for exchange credentials, and **`/gateway`** for DEX setup (or **`/start`** for the setup shortcuts) so commands like `/portfolio` and `/trade` can reach your stack. - If Condor and the API are on **different machines**, install [Tailscale](https://tailscale.com/download) on the Condor host and add the API in **`/servers`** with host **`hummingbot-api`** (not a public IP). See [Secure Connection via Tailscale](#secure-connection-via-tailscale) below. - **Check the install:** `make doctor` re-verifies dependencies, `.env`/`config.yml`, the AI model, the dashboard's network binding, Tailscale, and whether every configured Hummingbot API server is actually reachable and authenticating. Run it any time something stops working — it is read-only. +- **Refresh Codex:** If a newer model fails because the Codex ACP bridge is outdated, run `make refresh-codex-bridge` to update the bridge and its bundled Codex CLI, then start a new Codex session in Condor. Existing sessions keep the bridge they already started. - If something fails, see **Troubleshooting** below. ## Local mode (no Telegram) diff --git a/condor/acp/client.py b/condor/acp/client.py index 159332950..e51441484 100644 --- a/condor/acp/client.py +++ b/condor/acp/client.py @@ -294,7 +294,7 @@ def _acp_ish(a: str) -> bool: "claude-acp": "claude-agent-acp", # model-configurable form: claude-acp: "gemini": "npx @google/gemini-cli --acp", "copilot": "npx @github/copilot --acp --stdio", - "codex": "npx --yes --prefer-online @agentclientprotocol/codex-acp@latest", + "codex": "npx @agentclientprotocol/codex-acp", } # Session markers Claude Code exports into the shells it spawns. If the bot was diff --git a/condor/llm/readiness.py b/condor/llm/readiness.py index 94e43223f..3eeed9a13 100644 --- a/condor/llm/readiness.py +++ b/condor/llm/readiness.py @@ -188,18 +188,11 @@ def acp_login_state(base: str) -> bool | None: return None # copilot and anything new: no documented on-disk marker -def _npx_package(parts: list[str]) -> str: - """Package spec after the boolean flags in Condor's npx commands.""" - return next((part for part in parts[1:] if not part.startswith("-")), "") - - def install_command(cmd: str) -> str: """How to install the CLI behind an ACP command line.""" - parts = shlex.split(cmd) + parts = cmd.split() if len(parts) > 1 and parts[0] == "npx": - package = _npx_package(parts) - if package: - return f"npm install -g {shlex.quote(package)}" + return f"npm install -g {parts[1]}" if parts and parts[0] in _BARE_INSTALL: return _BARE_INSTALL[parts[0]] return f"install `{cmd.split()[0] if parts else cmd}` and put it on PATH" @@ -282,14 +275,11 @@ def acp_bridges() -> list[dict]: npx_packages = npx_packages_installed() out: list[dict] = [] for base, cmd in ACP_COMMANDS.items(): - parts = shlex.split(cmd) + parts = cmd.split() if not parts: available = False elif parts[0] == "npx": - # A latest tag is resolved online at launch; local presence only - # proves the package is installed, not that it is already current. - package = _npx_package(parts).removesuffix("@latest") - available = package in npx_packages + available = parts[1] in npx_packages if len(parts) > 1 else False else: available = shutil.which(parts[0]) is not None out.append( diff --git a/tests/test_model_readiness.py b/tests/test_model_readiness.py index 22fc39c06..94a56c2e1 100644 --- a/tests/test_model_readiness.py +++ b/tests/test_model_readiness.py @@ -154,19 +154,6 @@ def test_acp_bridges_surface_login_state(monkeypatch): assert by_key["gemini"]["logged_in"] is False -def test_codex_latest_bridge_readiness_parses_flags_and_tag(monkeypatch): - package = "@agentclientprotocol/codex-acp" - monkeypatch.setattr(readiness, "acp_login_state", lambda base: True) - monkeypatch.setattr(readiness, "npx_packages_installed", lambda: {package}) - bridge = next(row for row in readiness.acp_bridges() if row["agent_key"] == "codex") - assert bridge["available"] is True - assert bridge["logged_in"] is True - assert ( - readiness.install_command(bridge["command"]) - == f"npm install -g {package}@latest" - ) - - def test_install_command_names_the_package(): assert readiness.install_command("npx @google/gemini-cli --acp") == ( "npm install -g @google/gemini-cli" From c7e4df9b253d7cda0e4374b0171e9b1751fe86c8 Mon Sep 17 00:00:00 2001 From: Wojak Date: Wed, 30 Sep 2026 16:08:14 +0700 Subject: [PATCH 3/5] docs(setup): shorten the Codex refresh command --- Makefile | 6 +++--- README.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d69ba9304..fbb6d7e6f 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ export PATH := $(HOME)/.local/bin:$(HOME)/.cargo/bin:$(PATH) # "Entering/Leaving directory" line around every sub-invocation. MAKEFLAGS += --no-print-directory -.PHONY: help setup install run run-fg stop restart logs status check-stopped test lint build-frontend setup-chrome pick-model doctor refresh-codex-bridge +.PHONY: help setup install run run-fg stop restart logs status check-stopped test lint build-frontend setup-chrome pick-model doctor refresh-codex # tmux session Condor runs in SESSION := condor @@ -26,7 +26,7 @@ help: @echo "" @echo " make setup - Interactive setup wizard" @echo " make pick-model - Choose the AI model Condor thinks with" - @echo " make refresh-codex-bridge - Update the Codex ACP bridge and its bundled CLI" + @echo " make refresh-codex - Update the Codex ACP bridge and its bundled CLI" @echo " make install - Setup + install all dependencies" @echo " make run - Start Condor in the '$(SESSION)' tmux session" @echo " make run-fg - Run in the foreground (debugging)" @@ -44,7 +44,7 @@ setup: pick-model: uv run python -m condor.setup_llm -refresh-codex-bridge: +refresh-codex: npm install -g @agentclientprotocol/codex-acp@latest npm list -g @agentclientprotocol/codex-acp @openai/codex --depth=1 @echo "Start a new Codex session in Condor to use the updated bridge." diff --git a/README.md b/README.md index ddbef35f6..ba849de77 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ The following applies after **Install Condor**. If you used **Install only Hummi - In Telegram, use **`/servers`** for Hummingbot API URLs and auth, **`/keys`** for exchange credentials, and **`/gateway`** for DEX setup (or **`/start`** for the setup shortcuts) so commands like `/portfolio` and `/trade` can reach your stack. - If Condor and the API are on **different machines**, install [Tailscale](https://tailscale.com/download) on the Condor host and add the API in **`/servers`** with host **`hummingbot-api`** (not a public IP). See [Secure Connection via Tailscale](#secure-connection-via-tailscale) below. - **Check the install:** `make doctor` re-verifies dependencies, `.env`/`config.yml`, the AI model, the dashboard's network binding, Tailscale, and whether every configured Hummingbot API server is actually reachable and authenticating. Run it any time something stops working — it is read-only. -- **Refresh Codex:** If a newer model fails because the Codex ACP bridge is outdated, run `make refresh-codex-bridge` to update the bridge and its bundled Codex CLI, then start a new Codex session in Condor. Existing sessions keep the bridge they already started. +- **Refresh Codex:** If a newer model fails because the Codex ACP bridge is outdated, run `make refresh-codex` to update the bridge and its bundled Codex CLI, then start a new Codex session in Condor. Existing sessions keep the bridge they already started. - If something fails, see **Troubleshooting** below. ## Local mode (no Telegram) From d3c8e315ef6bcc9b9b2444381ef6f2509cc17c03 Mon Sep 17 00:00:00 2001 From: Wojak Date: Wed, 30 Sep 2026 16:10:08 +0700 Subject: [PATCH 4/5] style(make): align help command descriptions --- Makefile | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index fbb6d7e6f..98868588e 100644 --- a/Makefile +++ b/Makefile @@ -24,19 +24,19 @@ endef help: @echo "Condor - Available Commands" @echo "" - @echo " make setup - Interactive setup wizard" - @echo " make pick-model - Choose the AI model Condor thinks with" - @echo " make refresh-codex - Update the Codex ACP bridge and its bundled CLI" - @echo " make install - Setup + install all dependencies" - @echo " make run - Start Condor in the '$(SESSION)' tmux session" - @echo " make run-fg - Run in the foreground (debugging)" - @echo " make logs - Attach to the session (detach: Ctrl+B then D)" - @echo " make stop - Stop Condor" - @echo " make restart - Stop and start again" - @echo " make status - Is Condor running?" - @echo " make doctor - Verify dependencies, config, and Hummingbot API access" - @echo " make test - Run tests" - @echo " make lint - Run black + isort" + @echo " make setup - Interactive setup wizard" + @echo " make pick-model - Choose the AI model Condor thinks with" + @echo " make refresh-codex - Update the Codex ACP bridge and its bundled CLI" + @echo " make install - Setup + install all dependencies" + @echo " make run - Start Condor in the '$(SESSION)' tmux session" + @echo " make run-fg - Run in the foreground (debugging)" + @echo " make logs - Attach to the session (detach: Ctrl+B then D)" + @echo " make stop - Stop Condor" + @echo " make restart - Stop and start again" + @echo " make status - Is Condor running?" + @echo " make doctor - Verify dependencies, config, and Hummingbot API access" + @echo " make test - Run tests" + @echo " make lint - Run black + isort" setup: @chmod +x setup-environment.sh && ./setup-environment.sh From f865589b1ba497e80cd5a2567bd1483332da4e90 Mon Sep 17 00:00:00 2001 From: Wojak Date: Wed, 30 Sep 2026 16:19:01 +0700 Subject: [PATCH 5/5] fix(make): load nvm when the Codex refresh cannot find npm --- Makefile | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 98868588e..aae8b269d 100644 --- a/Makefile +++ b/Makefile @@ -45,9 +45,19 @@ pick-model: uv run python -m condor.setup_llm refresh-codex: - npm install -g @agentclientprotocol/codex-acp@latest - npm list -g @agentclientprotocol/codex-acp @openai/codex --depth=1 - @echo "Start a new Codex session in Condor to use the updated bridge." + @if ! command -v npm >/dev/null 2>&1; then \ + export NVM_DIR="$$HOME/.nvm"; \ + if [ -s "$$NVM_DIR/nvm.sh" ]; then \ + . "$$NVM_DIR/nvm.sh"; \ + fi; \ + fi; \ + command -v npm >/dev/null 2>&1 || { \ + echo "npm not found. Install Node.js or configure nvm."; \ + exit 1; \ + }; \ + npm install -g @agentclientprotocol/codex-acp@latest && \ + npm list -g @agentclientprotocol/codex-acp @openai/codex --depth=1 && \ + echo "Start a new Codex session in Condor to use the updated bridge." install: setup uv sync --dev