From b3d1e5b7a58a9f6eb763e6f31aabcc7b244d4c6b Mon Sep 17 00:00:00 2001 From: LUOSENGWA Date: Tue, 7 Jul 2026 14:00:33 +0000 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20migrate=20copaw=E2=86=92qwenpaw=20f?= =?UTF-8?q?or=20both=20Worker=20and=20Manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Worker changes: - copaw/Dockerfile: install qwenpaw after copaw-worker (standard venv) - entrypoint.sh: set QWENPAW_WORKING_DIR for qwenpaw config path - worker.py: switch copaw.app._app → qwenpaw.app._app (console + headless) - bridge.py: inject agents.profiles into config.json (required by qwenpaw) - matrix_channel.py: dual fallback — try qwenpaw BaseChannel first, then copaw - health.py: update docstring reference Manager changes: - Dockerfile.copaw: install qwenpaw after copaw-worker overlay (agentscope 1.0.20) - start-copaw-manager.sh: export QWENPAW_WORKING_DIR + launch python3 -m qwenpaw app CI health check changes: - test-helpers.sh: pgrep checks for 'copaw app' OR 'qwenpaw app' - test-01-manager-boot.sh: same dual pgrep pattern --- copaw/Dockerfile | 6 ++++ copaw/scripts/copaw-worker-entrypoint.sh | 4 +++ copaw/src/copaw_worker/bridge.py | 11 ++++++ copaw/src/copaw_worker/health.py | 2 +- copaw/src/copaw_worker/matrix_channel.py | 37 ++++++++++++++++----- copaw/src/copaw_worker/worker.py | 19 ++++++----- manager/Dockerfile.copaw | 10 ++++++ manager/scripts/init/start-copaw-manager.sh | 9 +++-- tests/lib/test-helpers.sh | 3 +- tests/test-01-manager-boot.sh | 2 +- 10 files changed, 81 insertions(+), 22 deletions(-) diff --git a/copaw/Dockerfile b/copaw/Dockerfile index baeb45fa5..bf6a09088 100644 --- a/copaw/Dockerfile +++ b/copaw/Dockerfile @@ -90,6 +90,12 @@ RUN python -m venv /opt/venv/standard \ --trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \ /tmp/copaw-worker/ +# --- Install QwenPaw (migrate from copaw to qwenpaw) --- +RUN /opt/venv/standard/bin/pip install --no-cache-dir \ + --index-url "${PIP_INDEX_URL}" \ + --trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \ + qwenpaw + # --- Patch CoPaw 1.0.2 Matrix channel indentation bug --- # _sync_loop method def is indented one level too deep inside # _refresh_matrix_token, causing IndentationError that prevents diff --git a/copaw/scripts/copaw-worker-entrypoint.sh b/copaw/scripts/copaw-worker-entrypoint.sh index cdf333bed..fff600ff0 100755 --- a/copaw/scripts/copaw-worker-entrypoint.sh +++ b/copaw/scripts/copaw-worker-entrypoint.sh @@ -25,6 +25,10 @@ WORKER_NAME="${AGENTTEAMS_WORKER_NAME:-${HICLAW_WORKER_NAME:-}}" INSTALL_DIR="/root/.copaw-worker" CONSOLE_PORT="${AGENTTEAMS_CONSOLE_PORT:-${HICLAW_CONSOLE_PORT:-}}" +# QwenPaw uses QWENPAW_WORKING_DIR to locate workspace config files. +# Align it with bridge.py output path (config.json written to .copaw/). +export QWENPAW_WORKING_DIR="${INSTALL_DIR}/${WORKER_NAME}/.copaw" + log() { echo "[hiclaw-copaw-worker $(date '+%Y-%m-%d %H:%M:%S')] $1" } diff --git a/copaw/src/copaw_worker/bridge.py b/copaw/src/copaw_worker/bridge.py index fef88b17b..4ee90f0e2 100644 --- a/copaw/src/copaw_worker/bridge.py +++ b/copaw/src/copaw_worker/bridge.py @@ -261,6 +261,17 @@ def _write_config_json( "max_input_length" ] = context_window + # Ensure agents.profiles section exists (required by qwenpaw for + # channel routing — maps agent ID to workspace directory). + agents = existing.setdefault("agents", {}) + agents.setdefault("active_agent", "default") + agents.setdefault("profiles", { + "default": { + "id": "default", + "workspace_dir": str(working_dir / "workspaces" / "default"), + } + }) + with open(config_path, "w") as f: json.dump(existing, f, indent=2, ensure_ascii=False) diff --git a/copaw/src/copaw_worker/health.py b/copaw/src/copaw_worker/health.py index 3b296c59b..30b1f626a 100644 --- a/copaw/src/copaw_worker/health.py +++ b/copaw/src/copaw_worker/health.py @@ -12,7 +12,7 @@ - Component health detection strategy: * copaw: Startup health: - - check: start uvicorn.Server for "copaw.app._app:app". + - check: start uvicorn.Server for "qwenpaw.app._app:app". - check: after starting the server, the worker performs one bounded startup probe against the native CoPaw health endpoint. - healthy: the startup probe gets HTTP 200 from diff --git a/copaw/src/copaw_worker/matrix_channel.py b/copaw/src/copaw_worker/matrix_channel.py index fd4ac9262..cd9206751 100644 --- a/copaw/src/copaw_worker/matrix_channel.py +++ b/copaw/src/copaw_worker/matrix_channel.py @@ -44,12 +44,36 @@ TOKEN_REFRESH_BACKOFF_S = 5 # --------------------------------------------------------------------------- -# Lazy import of CoPaw base types so this file can be syntax-checked without -# copaw installed (it's only executed inside a copaw environment). +# Lazy import of runtime base types. Try qwenpaw first (migrated runtime), +# then copaw (legacy). This file is used as a custom channel in both runtimes. # --------------------------------------------------------------------------- try: - from copaw.app.channels.base import BaseChannel - from copaw.app.channels.schema import ChannelType + from qwenpaw.app.channels.base import BaseChannel + from qwenpaw.app.channels.schema import ChannelType + _QWENPAW_AVAILABLE = True + _COPAW_AVAILABLE = True +except ImportError: + _QWENPAW_AVAILABLE = False + try: + from copaw.app.channels.base import BaseChannel + from copaw.app.channels.schema import ChannelType + _COPAW_AVAILABLE = True + except ImportError: # pragma: no cover + _COPAW_AVAILABLE = False + BaseChannel = object # type: ignore[assignment,misc] + ChannelType = str # type: ignore[assignment] + +# Content types — try qwenpaw first, then agentscope_runtime (copaw fallback) +try: + from qwenpaw.app.channels.schema import ( + AudioContent, + ContentType, + FileContent, + ImageContent, + TextContent, + VideoContent, + ) +except ImportError: from agentscope_runtime.engine.schemas.agent_schemas import ( AudioContent, ContentType, @@ -58,11 +82,6 @@ TextContent, VideoContent, ) - _COPAW_AVAILABLE = True -except ImportError: # pragma: no cover - _COPAW_AVAILABLE = False - BaseChannel = object # type: ignore[assignment,misc] - ChannelType = str # type: ignore[assignment] CHANNEL_KEY = "matrix" diff --git a/copaw/src/copaw_worker/worker.py b/copaw/src/copaw_worker/worker.py index b1600307b..3866eedc9 100644 --- a/copaw/src/copaw_worker/worker.py +++ b/copaw/src/copaw_worker/worker.py @@ -195,7 +195,9 @@ async def _run_copaw(self) -> None: async def _run_copaw_with_console(self, port: int) -> None: """Run CoPaw's full FastAPI app (runner + channels + web console).""" import uvicorn - from copaw.app.channels.registry import clear_builtin_channel_cache + # Use qwenpaw runtime (copaw→qwenpaw migration). + # qwenpaw.app._app:app is the FastAPI ASGI application. + from qwenpaw.app.channels.registry import clear_builtin_channel_cache clear_builtin_channel_cache() @@ -257,7 +259,7 @@ async def _readiness(): await api_server.start() uv_config = uvicorn.Config( - "copaw.app._app:app", + "qwenpaw.app._app:app", host="0.0.0.0", port=port, log_level="info", @@ -275,12 +277,13 @@ async def _readiness(): await api_server.stop() async def _run_copaw_headless(self) -> None: - """Start CoPaw's AgentRunner + ChannelManager (no HTTP server).""" - from copaw.app.runner.runner import AgentRunner - from copaw.config.utils import load_config - from copaw.app.channels.manager import ChannelManager - from copaw.app.channels.utils import make_process_from_runner - from copaw.app.channels.registry import clear_builtin_channel_cache + """Start QwenPaw's AgentRunner + ChannelManager (no HTTP server).""" + # Use qwenpaw runtime (copaw→qwenpaw migration). + from qwenpaw.app.runner.runner import AgentRunner + from qwenpaw.config.utils import load_config + from qwenpaw.app.channels.manager import ChannelManager + from qwenpaw.app.channels.utils import make_process_from_runner + from qwenpaw.app.channels.registry import clear_builtin_channel_cache # Force registry reload so newly installed matrix_channel.py is picked up clear_builtin_channel_cache() diff --git a/manager/Dockerfile.copaw b/manager/Dockerfile.copaw index 546e763ff..1514e0118 100644 --- a/manager/Dockerfile.copaw +++ b/manager/Dockerfile.copaw @@ -115,6 +115,16 @@ RUN SITE=/opt/copaw-venv/lib/python3.11/site-packages \ && cp -a /tmp/copaw-worker/src/copaw_worker/* "$SITE/copaw_worker/" \ && rm -rf /tmp/copaw-worker/ +# ---- Install QwenPaw (migrate Manager from copaw to qwenpaw) ---- +# Must be installed AFTER copaw-worker source overlay to prevent +# copaw-worker's copaw>=1.0.2 dependency from downgrading agentscope. +# Installed last → qwenpaw's agentscope==1.0.20 wins. +RUN /opt/copaw-venv/bin/pip install --no-cache-dir \ + -i "${PIP_INDEX_URL}" \ + --trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \ + qwenpaw \ + && ln -sf /opt/copaw-venv/bin/qwenpaw /usr/local/bin/qwenpaw + # ---- Copy Manager agent definitions ---- COPY manager/agent/ /opt/hiclaw/agent/ COPY manager/configs/ /opt/hiclaw/configs/ diff --git a/manager/scripts/init/start-copaw-manager.sh b/manager/scripts/init/start-copaw-manager.sh index 40569b6ce..254540ff8 100644 --- a/manager/scripts/init/start-copaw-manager.sh +++ b/manager/scripts/init/start-copaw-manager.sh @@ -13,6 +13,9 @@ source /opt/hiclaw/scripts/lib/hiclaw-env.sh # ============================================================ OPENCLAW_WORKSPACE="${HOME}" COPAW_WORKING_DIR="${HOME}/.copaw" +# QwenPaw reads config from QWENPAW_WORKING_DIR. +# Align with bridge.py output path (same as COPAW_WORKING_DIR). +export QWENPAW_WORKING_DIR="${COPAW_WORKING_DIR}" # ============================================================ # 1. Create CoPaw directory structure @@ -239,5 +242,7 @@ export COPAW_LOG_LEVEL # Set PYTHONPATH to include copaw_worker module export PYTHONPATH="/opt/hiclaw/copaw/src:${PYTHONPATH:-}" -# Use uvicorn to run CoPaw FastAPI app (enables AgentConfigWatcher for hot-reload) -exec python3 -m copaw app --host 0.0.0.0 --port 18799 +# Launch QwenPaw Manager (copaw→qwenpaw migration). +# QwenPaw reads config from QWENPAW_WORKING_DIR (set above, same as .copaw/). +# The qwenpaw CLI is installed by pip (provides the same CLI as copaw). +exec python3 -m qwenpaw app --host 0.0.0.0 --port 18799 diff --git a/tests/lib/test-helpers.sh b/tests/lib/test-helpers.sh index b8c6a4192..5ad2de3a1 100755 --- a/tests/lib/test-helpers.sh +++ b/tests/lib/test-helpers.sh @@ -203,7 +203,8 @@ wait_for_manager_agent_ready() { while [ "${elapsed}" -lt "${timeout}" ]; do case "${manager_runtime}" in copaw) - if docker exec "${agent_container}" pgrep -f "copaw app" >/dev/null 2>&1 && \ + # Accept either "copaw app" (legacy) or "qwenpaw app" (migrated runtime). + if docker exec "${agent_container}" sh -c 'pgrep -f "copaw app" || pgrep -f "qwenpaw app"' >/dev/null 2>&1 && \ docker exec "${agent_container}" curl -sf http://127.0.0.1:18799/ >/dev/null 2>&1; then runtime_ready=true break diff --git a/tests/test-01-manager-boot.sh b/tests/test-01-manager-boot.sh index 91fbb402a..63295b80e 100755 --- a/tests/test-01-manager-boot.sh +++ b/tests/test-01-manager-boot.sh @@ -118,7 +118,7 @@ case "${MANAGER_RUNTIME}" in log_fail "CoPaw agent.json valid" fi - if docker exec "${_AGENT_CTR}" pgrep -f "copaw app" >/dev/null 2>&1; then + if docker exec "${_AGENT_CTR}" sh -c 'pgrep -f "copaw app" || pgrep -f "qwenpaw app"' >/dev/null 2>&1; then log_pass "CoPaw process running" else log_fail "CoPaw process running" From eb8f6e7192821d2919981af3728e36bd2b057e4c Mon Sep 17 00:00:00 2001 From: LUOSENGWA Date: Tue, 7 Jul 2026 15:32:57 +0000 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20address=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20changelog,=20agents.profiles=20nested=20merge,=20qw?= =?UTF-8?q?enpaw=20symlink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - changelog/current.md: add copaw→qwenpaw migration entry - copaw/Dockerfile: ln -sf qwenpaw to /usr/local/bin for CLI PATH - bridge.py: nested setdefault merge for agents.profiles (handle partially-populated blocks instead of only top-level setdefault) --- changelog/current.md | 1 + copaw/Dockerfile | 3 ++- copaw/src/copaw_worker/bridge.py | 14 ++++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/changelog/current.md b/changelog/current.md index f3ae6b4eb..658a2c894 100644 --- a/changelog/current.md +++ b/changelog/current.md @@ -5,6 +5,7 @@ Record image-affecting changes to `manager/`, `worker/`, `copaw/`, `openclaw-bas --- - feat(qwenpaw): add the QwenPaw worker runtime Python package baseline with runtime config sync, storage sync, heartbeat reporting, Matrix channel overlay, and focused unit tests. +- feat(copaw): migrate Worker and Manager runtime from copaw 1.0.2 to qwenpaw (pip install qwenpaw, agents.profiles injection, QWENPAW_WORKING_DIR alignment, CI pgrep dual-compatible with `copaw app || qwenpaw app`) - fix(controller): surface Kubernetes Pod container failures in Worker backend status and status API responses. - feat(controller): expose low-cardinality AgentTeams controller metrics and optional Helm ServiceMonitor. - feat(controller): add Matrix AppService Human SSO identity provisioning with hash-derived Matrix IDs, AppService login, deletion deactivation, and Team admin/member identity resolution from Human status. diff --git a/copaw/Dockerfile b/copaw/Dockerfile index bf6a09088..8acd6be60 100644 --- a/copaw/Dockerfile +++ b/copaw/Dockerfile @@ -94,7 +94,8 @@ RUN python -m venv /opt/venv/standard \ RUN /opt/venv/standard/bin/pip install --no-cache-dir \ --index-url "${PIP_INDEX_URL}" \ --trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \ - qwenpaw + qwenpaw \ + && ln -sf /opt/venv/standard/bin/qwenpaw /usr/local/bin/qwenpaw # --- Patch CoPaw 1.0.2 Matrix channel indentation bug --- # _sync_loop method def is indented one level too deep inside diff --git a/copaw/src/copaw_worker/bridge.py b/copaw/src/copaw_worker/bridge.py index 4ee90f0e2..26cf00446 100644 --- a/copaw/src/copaw_worker/bridge.py +++ b/copaw/src/copaw_worker/bridge.py @@ -263,14 +263,16 @@ def _write_config_json( # Ensure agents.profiles section exists (required by qwenpaw for # channel routing — maps agent ID to workspace directory). + # Use nested merge so partially-populated agents/profiles blocks + # are completed rather than skipped. agents = existing.setdefault("agents", {}) agents.setdefault("active_agent", "default") - agents.setdefault("profiles", { - "default": { - "id": "default", - "workspace_dir": str(working_dir / "workspaces" / "default"), - } - }) + profiles = agents.setdefault("profiles", {}) + default_profile = profiles.setdefault("default", {}) + default_profile.setdefault("id", "default") + default_profile.setdefault( + "workspace_dir", str(working_dir / "workspaces" / "default") + ) with open(config_path, "w") as f: json.dump(existing, f, indent=2, ensure_ascii=False) From 9d7f67a30b55144996865f21517862adbfc02188 Mon Sep 17 00:00:00 2001 From: LUOSENGWA Date: Tue, 7 Jul 2026 15:40:38 +0000 Subject: [PATCH 3/4] fix: install qwenpaw in lite venv for headless mode The worker Dockerfile has two venvs: standard (console mode) and lite (headless mode, default when no CONSOLE_PORT is set). Both worker.py code paths (_run_copaw_with_console and _run_copaw_headless) now import from qwenpaw, but qwenpaw was only installed in the standard venv. Lite mode would crash with ImportError on startup. CI only tests standard mode (with console port), so this bug was invisible in CI. Production Workers without console ports would be affected. --- copaw/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/copaw/Dockerfile b/copaw/Dockerfile index 8acd6be60..3827cd7f7 100644 --- a/copaw/Dockerfile +++ b/copaw/Dockerfile @@ -117,7 +117,11 @@ RUN python -m venv /opt/venv/lite \ && /opt/venv/lite/bin/pip install --no-cache-dir \ --index-url "${PIP_INDEX_URL}" \ --trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \ - /tmp/copaw-worker/ + /tmp/copaw-worker/ \ + && /opt/venv/lite/bin/pip install --no-cache-dir \ + --index-url "${PIP_INDEX_URL}" \ + --trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \ + qwenpaw # --------------------------------------------------------------------------- # Patch installed packages in the lite venv to defer heavyweight modules that From f34fff3762adb362b1b67a91742e0c3600467691 Mon Sep 17 00:00:00 2001 From: LUOSENGWA Date: Wed, 8 Jul 2026 00:40:16 +0800 Subject: [PATCH 4/4] trigger CI re-run for flaky copaw/hermes shard