Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Record image-affecting changes to `manager/`, `worker/`, `copaw/`, `hermes/`, `o
**Branding and Compatibility**

- **QwenPaw 2.0 runtime compatibility**: Adapt the QwenPaw Worker image, Matrix overlay, and native plugins to QwenPaw 2.0.0.post3 startup and schema contracts.
- **QwenPaw 2.0 Manager runtime**: Migrate the Manager container from copaw 1.0.2 to QwenPaw 2.0.1 with a separate venv to avoid agentscope version conflicts, register projectflow/taskflow/message/filesync tools through a QwenPaw plugin instead of monkey-patching CoPawAgent, replace the physical Matrix channel overlay with the QwenPaw plugin system, read Matrix credentials directly from agent.json so the manager tools work without importing copaw at runtime, align CMS observability packages and env vars with the Worker image, inject session-file privacy policy into prompt files, set approval_level=AUTO in the agent template, bridge YOLO mode to QwenPaw approval_level=OFF, disable the built-in QA Agent, rename start-copaw-manager.sh to start-qwenpaw-manager.sh with dual copaw|qwenpaw runtime value support, and remove dead PYTHONPATH entries.
- **Complete AgentTeams runtime rename**: Rename installer and Helm entrypoints, the controller Go module and CLI, and container filesystem paths to AgentTeams while preserving thin compatibility aliases and upgrade migration for existing HiClaw installations. ([3121f5f](https://github.com/agentscope-ai/AgentTeams/commit/3121f5f))
- **Hard-cut AgentTeams naming**: Remove retired-brand installer wrappers, environment fallbacks, CLI aliases, Helm naming branches, runtime path migrations, and active source paths so fresh AgentTeams deployments use one canonical contract end to end. ([d20e606](https://github.com/agentscope-ai/AgentTeams/commit/d20e606617edefbbc42c28c1201c5629fa73fd88))
- **Hard-cut Team and Worker resources**: Make Worker CRs the sole owners of runtime configuration and lifecycle, make Team CRs reference existing Workers through `spec.workerMembers`, and remove inline-member, registry, migration, and dependent-script compatibility paths. ([b3cf360](https://github.com/agentscope-ai/AgentTeams/commit/b3cf360))
Expand Down
33 changes: 27 additions & 6 deletions copaw/src/copaw_worker/hooks/tools/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,19 @@ def _sanitize_session_filename(name: str) -> str:


def _resolve_copaw_working_dir() -> Path:
configured = os.environ.get("COPAW_WORKING_DIR")
configured = (
os.environ.get("COPAW_WORKING_DIR")
or os.environ.get("QWENPAW_WORKING_DIR")
)
if configured:
return Path(configured).expanduser().resolve()

from copaw.constant import WORKING_DIR
# copaw is the legacy name for qwenpaw; the package was renamed.
# In the qwenpaw 2.0 venv only the new name exists.
try:
from qwenpaw.constant import WORKING_DIR
except ImportError:
from copaw.constant import WORKING_DIR

return Path(WORKING_DIR).expanduser().resolve()

Expand Down Expand Up @@ -339,11 +347,24 @@ async def _record_matrix_outbound_to_session(


def _matrix_config_for_agent(account_id: str) -> tuple[str, str, str]:
from copaw.config.config import load_agent_config
# Read agent.json directly to avoid a hard dependency on
# copaw.config.config, which is unavailable in the QwenPaw 2.0 venv.
# bridge.py writes homeserver/access_token/user_id into
# workspaces/<id>/agent.json regardless of runtime.
working_dir = _resolve_copaw_working_dir()
agent_json = (
working_dir / "workspaces" / (account_id or "default") / "agent.json"
)
try:
with open(agent_json, "r", encoding="utf-8") as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as exc:
raise MessageToolError(
f"cannot read agent config at {agent_json}: {exc}",
)

agent_config = load_agent_config(account_id or "default")
channels = _read_config_value(agent_config, "channels") or {}
matrix_cfg = _read_config_value(channels, "matrix") or {}
channels = data.get("channels") or {}
matrix_cfg = channels.get("matrix") or {}

homeserver = _read_config_value(matrix_cfg, "homeserver") or ""
access_token = _read_config_value(matrix_cfg, "access_token", "accessToken") or ""
Expand Down
47 changes: 39 additions & 8 deletions copaw/src/copaw_worker/hooks/tools/taskflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,46 @@ def _current_actor() -> str | None:
return configured.strip()

try:
from copaw.config.config import load_agent_config

agent_config = load_agent_config("default")
channels = _read_config_value(agent_config, "channels") or {}
matrix_cfg = _read_config_value(channels, "matrix") or {}
user_id = _read_config_value(matrix_cfg, "user_id", "userId")
return str(user_id).strip() if user_id else None
# Read agent.json directly (works in both copaw and qwenpaw venvs
# without importing either framework's config module).
working_dir = _resolve_working_dir()
if working_dir:
agent_json = working_dir / "workspaces" / "default" / "agent.json"
with open(agent_json, "r", encoding="utf-8") as f:
data = json.load(f)
matrix_cfg = (data.get("channels") or {}).get("matrix") or {}
user_id = matrix_cfg.get("user_id") or matrix_cfg.get("userId")
if user_id:
return str(user_id).strip()
except Exception:
return None
pass

return None


def _resolve_working_dir() -> Path | None:
"""Resolve the QwenPaw/CoPaw working directory.

Mirrors message.py's _resolve_copaw_working_dir() so that both tools
use the same path resolution logic, including the qwenpaw.constant
fallback when env vars are not set.
"""
configured = (
os.getenv("COPAW_WORKING_DIR")
or os.getenv("QWENPAW_WORKING_DIR")
)
if configured:
return Path(configured).expanduser().resolve()

try:
from qwenpaw.constant import WORKING_DIR
except ImportError:
try:
from copaw.constant import WORKING_DIR
except ImportError:
return None

return Path(WORKING_DIR).expanduser().resolve()


def _read_config_value(obj: Any, *names: str) -> Any:
Expand Down
16 changes: 11 additions & 5 deletions copaw/src/copaw_worker/run_copaw_app.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
"""Run the upstream CoPaw app with AgentTeams runtime hooks installed."""
"""Run the QwenPaw 2.0 app for AgentTeams Manager.

Previously this module called ``install_tool_hooks()`` to monkey-patch
``CoPawAgent._create_toolkit``. On QwenPaw 2.0 that approach no longer
works — ``QwenPawAgent`` does not inherit from ``CoPawAgent`` and has no
``_create_toolkit`` method. The four manager tools (projectflow, taskflow,
message, filesync) are now registered via the ``agentteams-manager-tools``
QwenPaw plugin (``api.register_tool()``), so this wrapper simply launches
QwenPaw.
"""

from __future__ import annotations

import runpy

from copaw_worker.hooks import install_tool_hooks


def main() -> None:
install_tool_hooks()
runpy.run_module("copaw", run_name="__main__", alter_sys=True)
runpy.run_module("qwenpaw", run_name="__main__", alter_sys=True)


if __name__ == "__main__":
Expand Down
7 changes: 4 additions & 3 deletions copaw/src/copaw_worker/templates/agent.manager.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
"id": "default",
"name": "Manager",
"language": "zh",
"approval_level": "AUTO",
"system_prompt_files": [
"AGENTS.md",
"SOUL.md",
"PROFILE.md",
"TOOLS.md"
],
"running": {
"max_iters": 200
},
"heartbeat": {
"enabled": true,
"every": "30m"
Expand All @@ -25,8 +29,5 @@
"group_allow_from": [],
"groups": {}
}
},
"running": {
"max_iters": 200
}
}
12 changes: 8 additions & 4 deletions copaw/tests/test_message_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,18 +510,22 @@ async def query_handler(self, msgs, request=None, **kwargs):
assert ("taskflow", "override") in names


def test_run_copaw_app_installs_hooks_before_start(monkeypatch):
def test_run_copaw_app_starts_qwenpaw(monkeypatch):
"""run_copaw_app.main() should launch qwenpaw without tool hooks.

The copaw hooks (install_tool_hooks) were removed because
QwenPaw 2.0's QwenPawAgent does not have _create_toolkit.
Tools are now registered via the agentteams-manager-tools plugin.
"""
import copaw_worker.run_copaw_app as app

calls = []

monkeypatch.setattr(app, "install_tool_hooks", lambda: calls.append("hooks"))

def fake_run_module(name, *, run_name, alter_sys):
calls.append((name, run_name, alter_sys))

monkeypatch.setattr(app.runpy, "run_module", fake_run_module)

app.main()

assert calls == ["hooks", ("copaw", "__main__", True)]
assert calls == [("qwenpaw", "__main__", True)]
70 changes: 47 additions & 23 deletions manager/Dockerfile.copaw
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,6 @@ RUN pip install --no-cache-dir \
"copaw==${COPAW_VERSION}" \
"matrix-nio[e2e]>=0.24.0"

# ---- CoPaw CMS Plugin (observability) ----
RUN pip install --no-cache-dir \
-i "${PIP_INDEX_URL}" \
--trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \
"wrapt<2.0" \
loongsuite-site-bootstrap \
loongsuite-distro \
opentelemetry-exporter-otlp \
loongsuite-instrumentation-copaw \
loongsuite-instrumentation-agentscope

# ---- Overlay AgentTeams Matrix channel (not yet merged into CoPaw release) ----
# Replace CoPaw's built-in Matrix channel with AgentTeams-enhanced version
COPY copaw/src/matrix/ /tmp/matrix-overlay/
RUN SITE=$(python3 -c "import copaw; import os; print(os.path.dirname(copaw.__file__))") \
&& rm -rf "$SITE/app/channels/matrix" \
&& mkdir -p "$SITE/app/channels/matrix" \
&& cp /tmp/matrix-overlay/channel.py /tmp/matrix-overlay/__init__.py "$SITE/app/channels/matrix/" \
&& if [ -f /tmp/matrix-overlay/config.py ]; then \
cp /tmp/matrix-overlay/config.py "$SITE/config/config.py"; \
fi \
&& rm -rf /tmp/matrix-overlay

# ---- CoPaw worker bridge module (for config conversion) ----
# Stage the copaw-worker package metadata for dependency resolution
COPY copaw/pyproject.toml copaw/README.md /tmp/copaw-worker/
Expand All @@ -115,6 +92,53 @@ 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/

# ---- QwenPaw 2.0 Runtime (Manager) ----
# Separate venv: copaw 1.0.2 requires agentscope==1.0.20, qwenpaw 2.0.1
# requires agentscope>=2.0.0. Mixing them in one venv breaks copaw's
# ReActAgent import. The copaw venv above stays pristine for bridge.py;
# qwenpaw runs from this venv.
ARG QWENPAW_PIP_SPEC=qwenpaw==2.0.1
RUN python3 -m venv /opt/venv/qwenpaw \
&& /opt/venv/qwenpaw/bin/pip install --no-cache-dir \
-i "${PIP_INDEX_URL}" \
--trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \
"${QWENPAW_PIP_SPEC}"

# Overlay copaw_worker source into qwenpaw venv.
# The 4 manager tools (projectflow/taskflow/message/filesync) depend only on
# agentscope 2.0.x and copaw_worker internal modules — not on copaw 1.0.2.
RUN cp -a /opt/copaw-venv/lib/python3.11/site-packages/copaw_worker \
/opt/venv/qwenpaw/lib/python3.11/site-packages/

# ---- CMS Observability Plugin (in qwenpaw venv) ----
# Aligned with qwenpaw Worker Dockerfile: installed into the runtime venv
# (qwenpaw) so that loongsuite-site-bootstrap is active when the Manager
# process starts. instrumentation-qwenpaw (not -copaw) +
# loongsuite-otel-util-genai for GenAI semantic conventions.
RUN /opt/venv/qwenpaw/bin/pip install --no-cache-dir \
-i "${PIP_INDEX_URL}" \
--trusted-host "$(echo ${PIP_INDEX_URL} | sed 's|https\?://||;s|/.*||')" \
"wrapt<2.0" \
loongsuite-site-bootstrap \
loongsuite-distro \
loongsuite-otel-util-genai \
opentelemetry-exporter-otlp \
loongsuite-instrumentation-qwenpaw \
loongsuite-instrumentation-agentscope

# qwenpaw CLI on PATH
RUN ln -sf /opt/venv/qwenpaw/bin/qwenpaw /usr/local/bin/qwenpaw

# qwenpaw venv takes priority for Manager runtime
ENV PATH="/opt/venv/qwenpaw/bin:/opt/copaw-venv/bin:${PATH}"

# AgentTeams plugins: Matrix channel + Manager tools
# Stored in image-local path (/opt/agentteams/plugins/). At runtime,
# start-qwenpaw-manager.sh copies them into the working dir's plugins/
# directory because /root/manager-workspace is a host-mounted volume.
COPY plugins/agentteams-matrix-channel/ /opt/agentteams/plugins/agentteams-matrix-channel/
COPY plugins/agentteams-manager-tools/ /opt/agentteams/plugins/agentteams-manager-tools/

# ---- Copy Manager agent definitions ----
COPY manager/agent/ /opt/agentteams/agent/
COPY manager/configs/ /opt/agentteams/configs/
Expand Down
17 changes: 10 additions & 7 deletions manager/scripts/init/start-manager-agent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
#
# Runtime selection:
# AGENTTEAMS_MANAGER_RUNTIME=openclaw (default) - OpenClaw gateway mode
# AGENTTEAMS_MANAGER_RUNTIME=copaw - CoPaw workspace mode
# (hermes runtime is supported for Workers only; Managers run openclaw or copaw.)
# AGENTTEAMS_MANAGER_RUNTIME=copaw|qwenpaw - QwenPaw workspace mode
# "copaw" is the legacy runtime identifier still used by the controller,
# installer, and CI; "qwenpaw" is the forward-looking name. Both map to
# the same QwenPaw 2.0 startup script.
# (hermes runtime is supported for Workers only.)

source /opt/agentteams/scripts/lib/agentteams-env.sh

Expand All @@ -16,8 +19,8 @@ source /opt/agentteams/scripts/lib/agentteams-env.sh
# ============================================================
MANAGER_RUNTIME="${AGENTTEAMS_MANAGER_RUNTIME:-openclaw}"
case "${MANAGER_RUNTIME}" in
copaw)
log "Manager runtime: CoPaw (Python workspace)"
copaw|qwenpaw)
log "Manager runtime: QwenPaw (Python workspace, runtime=${MANAGER_RUNTIME})"
;;
*)
log "Manager runtime: OpenClaw (Node.js gateway)"
Expand Down Expand Up @@ -1162,9 +1165,9 @@ fi
# ============================================================
# Runtime-specific startup
# ============================================================
if [ "${MANAGER_RUNTIME}" = "copaw" ]; then
# Delegate to CoPaw startup script
exec /opt/agentteams/scripts/init/start-copaw-manager.sh
if [ "${MANAGER_RUNTIME}" = "copaw" ] || [ "${MANAGER_RUNTIME}" = "qwenpaw" ]; then
# Delegate to QwenPaw startup script
exec /opt/agentteams/scripts/init/start-qwenpaw-manager.sh
else
# ── OpenClaw Runtime ─────────────────────────────────────────────────────
log "Starting OpenClaw Manager..."
Expand Down
Loading