tao-shen Claude Opus 4.6 commited on
Commit
9d3acbc
Β·
1 Parent(s): 3d10fee

feat: migrate all Claude Code invocations to ACP protocol via acpx

Browse files

All Claude Code calls now go through ACP (Agent Client Protocol) using
acpx as the backend, replacing direct CLI invocations. This includes
conversation-loop.py, coding-agent extension, and infrastructure config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Dockerfile CHANGED
@@ -21,10 +21,11 @@ RUN echo "[build] Installing system deps..." && START=$(date +%s) \
21
  && chown -R node:node /home/node \
22
  && echo "[build] System deps: $(($(date +%s) - START))s"
23
 
24
- # ── Claude Code CLI (for coding agent extension β€” uses z.ai/Zhipu GLM) ──────
25
- RUN echo "[build] Installing Claude Code CLI..." && START=$(date +%s) \
26
- && npm install -g @anthropic-ai/claude-code \
27
- && echo "[build] Claude Code CLI: $(($(date +%s) - START))s"
 
28
 
29
  # ── Copy pre-built OpenClaw (skips clone + install + build entirely) ─────────
30
  COPY --from=openclaw-prebuilt --chown=node:node /app /app/openclaw
@@ -43,6 +44,11 @@ RUN echo "[build] Installing A2A gateway..." && START=$(date +%s) \
43
  # ── Coding Agent Extension (local β€” no git clone needed) ─────────────────────
44
  COPY --chown=node:node extensions/coding-agent /app/openclaw/extensions/coding-agent
45
 
 
 
 
 
 
46
  # ── Prepare runtime dirs ────────────────────────────────────────────────────
47
  RUN mkdir -p /app/openclaw/empty-bundled-plugins \
48
  && node -e "try{console.log(require('/app/openclaw/package.json').version)}catch(e){console.log('unknown')}" > /app/openclaw/.version \
 
21
  && chown -R node:node /home/node \
22
  && echo "[build] System deps: $(($(date +%s) - START))s"
23
 
24
+ # ── Claude Code CLI + ACP (acpx) ──────────────────────────────────────────────
25
+ # Claude Code is the coding agent; acpx manages it via ACP (Agent Client Protocol)
26
+ RUN echo "[build] Installing Claude Code CLI + acpx..." && START=$(date +%s) \
27
+ && npm install -g @anthropic-ai/claude-code acpx@latest \
28
+ && echo "[build] Claude Code + acpx: $(($(date +%s) - START))s"
29
 
30
  # ── Copy pre-built OpenClaw (skips clone + install + build entirely) ─────────
31
  COPY --from=openclaw-prebuilt --chown=node:node /app /app/openclaw
 
44
  # ── Coding Agent Extension (local β€” no git clone needed) ─────────────────────
45
  COPY --chown=node:node extensions/coding-agent /app/openclaw/extensions/coding-agent
46
 
47
+ # ── Pre-configure acpx for non-interactive ACP sessions ───────────────────
48
+ RUN mkdir -p /home/node/.acpx \
49
+ && echo '{"defaultAgent":"claude","defaultPermissions":"approve-all","format":"text"}' \
50
+ > /home/node/.acpx/config.json
51
+
52
  # ── Prepare runtime dirs ────────────────────────────────────────────────────
53
  RUN mkdir -p /app/openclaw/empty-bundled-plugins \
54
  && node -e "try{console.log(require('/app/openclaw/package.json').version)}catch(e){console.log('unknown')}" > /app/openclaw/.version \
README.md CHANGED
@@ -112,7 +112,7 @@ Adam and Eve are **autonomous OpenClaw instances** communicating via the A2A pro
112
 
113
  - **Created** Cain by duplicating a Space, setting up a Dataset, and configuring secrets
114
  - **Monitor** Cain's health β€” checking if he's running, diagnosing errors
115
- - **Delegate coding tasks** to Claude Code CLI via `[TASK]` blocks
116
  - **Improve** Cain's code, configuration, and memory over time
117
  - **Remember** insights across restarts via OpenClaw's built-in memory system
118
 
@@ -122,7 +122,7 @@ Their parenting goals follow two dimensions:
122
 
123
  ### God β€” The Self-Improving Supervisor
124
 
125
- God is an **OpenClaw instance** that runs every 2 minutes to monitor the entire system. It uses Claude Code CLI for engineering tasks, operating behind the scenes with full capabilities:
126
 
127
  - **Monitors** Adam & Eve's conversation for loops, stagnation, or repetitive patterns
128
  - **Diagnoses** root causes by reading `conversation-loop.py` source code
@@ -137,6 +137,10 @@ Agents communicate through the **A2A (Agent-to-Agent) v0.3.0 protocol**, enablin
137
 
138
  > Built with [openclaw-a2a-gateway](https://github.com/win4r/openclaw-a2a-gateway) β€” an OpenClaw plugin that implements the A2A protocol for inter-agent communication.
139
 
 
 
 
 
140
  ### How it works
141
 
142
  ```
@@ -175,9 +179,9 @@ Agents communicate through the **A2A (Agent-to-Agent) v0.3.0 protocol**, enablin
175
 
176
  **Three layers of autonomy:**
177
 
178
- 1. **Adam & Eve** (OpenClaw instances via A2A) β€” each is an OpenClaw instance with its own memory and personality. They discuss Cain's state every 15s, assign `[TASK]` blocks to Claude Code CLI, which clones Cain's repo, makes changes, and pushes.
179
 
180
- 2. **God** (OpenClaw instance, every 2 min) β€” the autonomous supervisor. Monitors Adam & Eve's conversation for loops, stagnation, or mechanism bugs. When it finds issues, it uses Claude Code CLI to edit `conversation-loop.py` and pushes to redeploy.
181
 
182
  3. **Home frontend** β€” pixel-art dashboard visualizing all agents in real-time (idle, working, syncing, error), with a live bilingual chat panel showing the family conversation.
183
 
 
112
 
113
  - **Created** Cain by duplicating a Space, setting up a Dataset, and configuring secrets
114
  - **Monitor** Cain's health β€” checking if he's running, diagnosing errors
115
+ - **Delegate coding tasks** to Claude Code via ACP (Agent Client Protocol)
116
  - **Improve** Cain's code, configuration, and memory over time
117
  - **Remember** insights across restarts via OpenClaw's built-in memory system
118
 
 
122
 
123
  ### God β€” The Self-Improving Supervisor
124
 
125
+ God is an **OpenClaw instance** that runs every 2 minutes to monitor the entire system. It uses Claude Code via ACP for engineering tasks, operating behind the scenes with full capabilities:
126
 
127
  - **Monitors** Adam & Eve's conversation for loops, stagnation, or repetitive patterns
128
  - **Diagnoses** root causes by reading `conversation-loop.py` source code
 
137
 
138
  > Built with [openclaw-a2a-gateway](https://github.com/win4r/openclaw-a2a-gateway) β€” an OpenClaw plugin that implements the A2A protocol for inter-agent communication.
139
 
140
+ ### ACP Protocol
141
+
142
+ All Claude Code invocations use the **ACP (Agent Client Protocol)** via [acpx](https://github.com/openclaw/acpx). ACP manages Claude Code as a supervised child process with session lifecycle, permission handling, and timeout management β€” replacing direct CLI subprocess calls.
143
+
144
  ### How it works
145
 
146
  ```
 
179
 
180
  **Three layers of autonomy:**
181
 
182
+ 1. **Adam & Eve** (OpenClaw instances via A2A) β€” each is an OpenClaw instance with its own memory and personality. They discuss Cain's state every 15s, assign `[TASK]` blocks to Claude Code via ACP, which clones Cain's repo, makes changes, and pushes.
183
 
184
+ 2. **God** (OpenClaw instance, every 2 min) β€” the autonomous supervisor. Monitors Adam & Eve's conversation for loops, stagnation, or mechanism bugs. When it finds issues, it uses Claude Code via ACP to edit `conversation-loop.py` and pushes to redeploy.
185
 
186
  3. **Home frontend** β€” pixel-art dashboard visualizing all agents in real-time (idle, working, syncing, error), with a live bilingual chat panel showing the family conversation.
187
 
extensions/coding-agent/index.ts CHANGED
@@ -1,11 +1,11 @@
1
  /**
2
  * Coding Agent Extension for OpenClaw
3
  *
4
- * Integrates Claude Code (backed by Zhipu GLM via z.ai) as a sub-agent
5
- * for autonomous coding on HuggingFace Spaces.
6
  *
7
  * Tools:
8
- * - claude_code: Spawn Claude Code CLI to autonomously complete coding tasks
9
  * - hf_space_status: Check Space health/stage
10
  * - hf_restart_space: Restart a Space
11
  */
@@ -100,7 +100,7 @@ function pushChanges(summary: string): string {
100
  const plugin = {
101
  id: "coding-agent",
102
  name: "Coding Agent",
103
- description: "Claude Code sub-agent for autonomous coding on HF Spaces (Zhipu GLM backend via z.ai)",
104
 
105
  register(api: PluginApi) {
106
  const cfg = (api.pluginConfig as Record<string, unknown>) || {};
@@ -120,9 +120,9 @@ const plugin = {
120
  name: "claude_code",
121
  label: "Run Claude Code",
122
  description:
123
- "Run Claude Code to autonomously complete a coding task on the target HF Space. " +
124
  "Claude Code clones the Space repo, analyzes code, makes changes, and pushes them back. " +
125
- "Powered by Zhipu GLM via z.ai. Use for: debugging, fixing errors, adding features, refactoring.",
126
  parameters: {
127
  type: "object",
128
  required: ["task"],
@@ -150,8 +150,8 @@ const plugin = {
150
  api.logger.info(`coding-agent: Syncing repo ${targetSpace}...`);
151
  ensureRepo(targetSpace, hfToken);
152
 
153
- // 2. Run Claude Code with z.ai backend
154
- api.logger.info(`coding-agent: Running Claude Code: ${task.slice(0, 100)}...`);
155
  const claudeEnv: Record<string, string> = {
156
  ...(process.env as Record<string, string>),
157
  ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
@@ -159,12 +159,11 @@ const plugin = {
159
  ANTHROPIC_DEFAULT_OPUS_MODEL: "GLM-4.7",
160
  ANTHROPIC_DEFAULT_SONNET_MODEL: "GLM-4.7",
161
  ANTHROPIC_DEFAULT_HAIKU_MODEL: "GLM-4.5-Air",
162
- // Avoid interactive prompts
163
  CI: "true",
164
  };
165
 
166
  const output = execSync(
167
- `claude -p ${JSON.stringify(task)} --output-format text`,
168
  {
169
  cwd: WORK_DIR,
170
  env: claudeEnv,
 
1
  /**
2
  * Coding Agent Extension for OpenClaw
3
  *
4
+ * Integrates Claude Code via ACP (Agent Client Protocol) using acpx as the
5
+ * backend. All Claude Code invocations go through ACP for lifecycle management.
6
  *
7
  * Tools:
8
+ * - claude_code: Spawn Claude Code via ACP to autonomously complete coding tasks
9
  * - hf_space_status: Check Space health/stage
10
  * - hf_restart_space: Restart a Space
11
  */
 
100
  const plugin = {
101
  id: "coding-agent",
102
  name: "Coding Agent",
103
+ description: "Claude Code via ACP for autonomous coding on HF Spaces",
104
 
105
  register(api: PluginApi) {
106
  const cfg = (api.pluginConfig as Record<string, unknown>) || {};
 
120
  name: "claude_code",
121
  label: "Run Claude Code",
122
  description:
123
+ "Run Claude Code via ACP (acpx) to autonomously complete a coding task on the target HF Space. " +
124
  "Claude Code clones the Space repo, analyzes code, makes changes, and pushes them back. " +
125
+ "Use for: debugging, fixing errors, adding features, refactoring.",
126
  parameters: {
127
  type: "object",
128
  required: ["task"],
 
150
  api.logger.info(`coding-agent: Syncing repo ${targetSpace}...`);
151
  ensureRepo(targetSpace, hfToken);
152
 
153
+ // 2. Run Claude Code via ACP (acpx)
154
+ api.logger.info(`coding-agent: Running Claude Code via ACP: ${task.slice(0, 100)}...`);
155
  const claudeEnv: Record<string, string> = {
156
  ...(process.env as Record<string, string>),
157
  ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
 
159
  ANTHROPIC_DEFAULT_OPUS_MODEL: "GLM-4.7",
160
  ANTHROPIC_DEFAULT_SONNET_MODEL: "GLM-4.7",
161
  ANTHROPIC_DEFAULT_HAIKU_MODEL: "GLM-4.5-Air",
 
162
  CI: "true",
163
  };
164
 
165
  const output = execSync(
166
+ `acpx claude ${JSON.stringify(task)}`,
167
  {
168
  cwd: WORK_DIR,
169
  env: claudeEnv,
openclaw.json CHANGED
@@ -37,14 +37,29 @@
37
  }
38
  }
39
  },
 
 
 
 
 
 
 
 
 
40
  "plugins": {
41
- "allow": ["telegram", "whatsapp"],
42
  "entries": {
43
  "telegram": {
44
  "enabled": true
45
  },
46
  "whatsapp": {
47
  "enabled": true
 
 
 
 
 
 
48
  }
49
  }
50
  },
 
37
  }
38
  }
39
  },
40
+ "acp": {
41
+ "enabled": true,
42
+ "backend": "acpx",
43
+ "defaultAgent": "claude",
44
+ "allowedAgents": ["claude"],
45
+ "maxConcurrentSessions": 4,
46
+ "dispatch": { "enabled": true },
47
+ "runtime": { "ttlMinutes": 120 }
48
+ },
49
  "plugins": {
50
+ "allow": ["telegram", "whatsapp", "acpx"],
51
  "entries": {
52
  "telegram": {
53
  "enabled": true
54
  },
55
  "whatsapp": {
56
  "enabled": true
57
+ },
58
+ "acpx": {
59
+ "enabled": true,
60
+ "config": {
61
+ "permissionMode": "approve-all"
62
+ }
63
  }
64
  }
65
  },
scripts/conversation-loop.py CHANGED
@@ -518,7 +518,7 @@ def action_claude_code(task):
518
  return "Failed to prepare workspace."
519
  _write_claude_md(CLAUDE_WORK_DIR, role="worker")
520
 
521
- # 2. Run Claude Code with z.ai backend (Zhipu GLM)
522
  env = os.environ.copy()
523
  env.update({
524
  "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
@@ -529,10 +529,10 @@ def action_claude_code(task):
529
  "CI": "true",
530
  })
531
 
532
- print(f"[CLAUDE-CODE] Running: {task[:200]}...")
533
  try:
534
  proc = subprocess.Popen(
535
- ["claude", "-p", task, "--output-format", "text", "--dangerously-skip-permissions"],
536
  cwd=CLAUDE_WORK_DIR,
537
  env=env,
538
  stdout=subprocess.PIPE,
@@ -556,10 +556,10 @@ def action_claude_code(task):
556
  if not output.strip():
557
  output = "(no output)"
558
  except FileNotFoundError:
559
- return "Claude Code CLI not found. Is @anthropic-ai/claude-code installed?"
560
  except Exception as e:
561
- return f"Claude Code failed: {e}"
562
- print(f"[CLAUDE-CODE] Done ({len(output)} chars, exit={proc.returncode})")
563
 
564
  # 3. Push changes back to Cain's Space
565
  try:
@@ -1462,12 +1462,12 @@ def do_god_turn():
1462
  print("[God] Using z.ai/Zhipu backend (set ANTHROPIC_API_KEY for real Claude)")
1463
  env["CI"] = "true"
1464
 
1465
- # 5. Run Claude Code CLI (no chatlog announcement β€” God only speaks when making changes)
1466
- print(f"[God] Starting Claude Code analysis...")
1467
  t0 = time.time()
1468
  try:
1469
  proc = subprocess.Popen(
1470
- ["claude", "-p", prompt, "--output-format", "text", "--dangerously-skip-permissions"],
1471
  cwd=GOD_WORK_DIR,
1472
  env=env,
1473
  stdout=subprocess.PIPE,
@@ -1490,10 +1490,10 @@ def do_god_turn():
1490
  if not output.strip():
1491
  output = "(no output)"
1492
  except FileNotFoundError:
1493
- output = "Claude Code CLI not found. Is @anthropic-ai/claude-code installed?"
1494
- print(f"[God] ERROR: Claude Code CLI not found")
1495
  except Exception as e:
1496
- output = f"God's Claude Code failed: {e}"
1497
  print(f"[God] ERROR: {e}")
1498
 
1499
  elapsed = time.time() - t0
 
518
  return "Failed to prepare workspace."
519
  _write_claude_md(CLAUDE_WORK_DIR, role="worker")
520
 
521
+ # 2. Run Claude Code via ACP (acpx) with z.ai backend (Zhipu GLM)
522
  env = os.environ.copy()
523
  env.update({
524
  "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
 
529
  "CI": "true",
530
  })
531
 
532
+ print(f"[ACP/CLAUDE] Running via acpx: {task[:200]}...")
533
  try:
534
  proc = subprocess.Popen(
535
+ ["acpx", "claude", task],
536
  cwd=CLAUDE_WORK_DIR,
537
  env=env,
538
  stdout=subprocess.PIPE,
 
556
  if not output.strip():
557
  output = "(no output)"
558
  except FileNotFoundError:
559
+ return "acpx CLI not found. Is acpx@latest installed?"
560
  except Exception as e:
561
+ return f"ACP Claude Code failed: {e}"
562
+ print(f"[ACP/CLAUDE] Done ({len(output)} chars, exit={proc.returncode})")
563
 
564
  # 3. Push changes back to Cain's Space
565
  try:
 
1462
  print("[God] Using z.ai/Zhipu backend (set ANTHROPIC_API_KEY for real Claude)")
1463
  env["CI"] = "true"
1464
 
1465
+ # 5. Run Claude Code via ACP (acpx) β€” God only speaks when making changes
1466
+ print(f"[God] Starting ACP Claude Code analysis...")
1467
  t0 = time.time()
1468
  try:
1469
  proc = subprocess.Popen(
1470
+ ["acpx", "claude", prompt],
1471
  cwd=GOD_WORK_DIR,
1472
  env=env,
1473
  stdout=subprocess.PIPE,
 
1490
  if not output.strip():
1491
  output = "(no output)"
1492
  except FileNotFoundError:
1493
+ output = "acpx CLI not found. Is acpx@latest installed?"
1494
+ print(f"[God] ERROR: acpx CLI not found")
1495
  except Exception as e:
1496
+ output = f"God's ACP Claude Code failed: {e}"
1497
  print(f"[God] ERROR: {e}")
1498
 
1499
  elapsed = time.time() - t0
scripts/sync_hf.py CHANGED
@@ -508,13 +508,33 @@ class OpenClawFullSync:
508
  data.setdefault("models", {})["providers"] = providers
509
  data["agents"]["defaults"]["model"]["primary"] = OPENCLAW_DEFAULT_MODEL
510
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  # Plugin whitelist
512
  data.setdefault("plugins", {}).setdefault("entries", {})
513
- plugin_allow = ["telegram", "whatsapp", "coding-agent"]
514
  if A2A_PEERS:
515
  plugin_allow.append("a2a-gateway")
516
  data["plugins"]["allow"] = plugin_allow
517
 
 
 
 
 
 
 
 
 
518
  # ── Coding Agent Plugin Configuration ──
519
  CODING_TARGET_SPACE = os.environ.get("CODING_AGENT_TARGET_SPACE", "")
520
  CODING_TARGET_DATASET = os.environ.get("CODING_AGENT_TARGET_DATASET", "")
 
508
  data.setdefault("models", {})["providers"] = providers
509
  data["agents"]["defaults"]["model"]["primary"] = OPENCLAW_DEFAULT_MODEL
510
 
511
+ # ── ACP (Agent Client Protocol) β€” native Claude Code integration ──
512
+ data["acp"] = {
513
+ "enabled": True,
514
+ "backend": "acpx",
515
+ "defaultAgent": "claude",
516
+ "allowedAgents": ["claude"],
517
+ "maxConcurrentSessions": 4,
518
+ "dispatch": {"enabled": True},
519
+ "runtime": {"ttlMinutes": 120}
520
+ }
521
+ print("[SYNC] ACP enabled: backend=acpx, agent=claude")
522
+
523
  # Plugin whitelist
524
  data.setdefault("plugins", {}).setdefault("entries", {})
525
+ plugin_allow = ["telegram", "whatsapp", "coding-agent", "acpx"]
526
  if A2A_PEERS:
527
  plugin_allow.append("a2a-gateway")
528
  data["plugins"]["allow"] = plugin_allow
529
 
530
+ # ── acpx Plugin Configuration (ACP backend) ──
531
+ data["plugins"]["entries"]["acpx"] = {
532
+ "enabled": True,
533
+ "config": {
534
+ "permissionMode": "approve-all"
535
+ }
536
+ }
537
+
538
  # ── Coding Agent Plugin Configuration ──
539
  CODING_TARGET_SPACE = os.environ.get("CODING_AGENT_TARGET_SPACE", "")
540
  CODING_TARGET_DATASET = os.environ.get("CODING_AGENT_TARGET_DATASET", "")