| name | acp-task-dispatch |
| description | Dispatch a self-contained task to the OpenClaw-mcode-ACP HTTP server from inside MiniMax Code. Use when a task should be persisted, retried, observed over time, or processed by a worker pool instead of the current MiniMax Code session. |
| license | Apache-2.0 |
| compatibility | Requires MiniMax Code with Agent Plugins 1.0 support and an OpenClaw-mcode-ACP server reachable on http://127.0.0.1:9999. |
| metadata | {"author":"安天齐 (antianqi)","homepage":"https://github.com/antianqi/openclaw-mcode-acp","version":"0.2.0"} |
ACP Task Dispatch
Send a discrete, self-contained task to the OpenClaw-mcode-ACP server instead of running it inline in the current session. Useful when:
- The task is long-running and you do not want to block
- You want a persistent record (SQLite history) for later review
- A worker pool should pick it up off the queue
- You want to observe progress via SSE / WebSocket events
Setup
The Plugin ships its own HTTP client. There is no ACP_HOME to set, no external Python SDK to install, and no sys.path to mutate. The client lives at <plugin_root>/client/_acp_client.py and is resolved through the ACP_PLUGIN_ROOT environment variable (set automatically by the Plugin runtime) with a __file__-based fallback for ad-hoc invocations.
Authentication
The bundled client reads the bearer token from one of (first hit wins):
$ACP_TOKEN (recommended for CI and shells)
~/.acp_token (one line, no trailing newline)
<plugin_root>/.acp_token (one line; co-located fallback for fresh installs)
The client attaches Authorization: Bearer <token> to every request to http://127.0.0.1:9999/acp/*. Do not read, print, or pass the token yourself. The client also refuses to follow HTTP redirects (a hostile loopback server cannot exfiltrate the token via a 302) and refuses to talk to anything other than the loopback allow-list.
If the token cannot be located, the client raises ACPTokenMissing. Tell the user to set $ACP_TOKEN (or write one of the fallback files) and stop; do not retry.
Dispatch a task
import os, sys
_plugin_root = os.environ.get('ACP_PLUGIN_ROOT') or os.path.dirname(
os.path.dirname(os.path.abspath(__file__))
)
sys.path.insert(0, os.path.join(_plugin_root, 'client'))
from _acp_client import create_task, get_task, history
task_id = create_task(
prompt="用一句话回答:1+1=?",
workspace="D:/some/work/dir",
timeout=300,
)
print(task_id)
create_task is fire-and-forget. The server runs the task on a worker pool (default 3 concurrent) and persists every transition to SQLite.
Poll for completion
import time
while True:
state = get_task(task_id)
if state["status"] in ("succeeded", "failed", "timeout", "cancelled"):
break
time.sleep(2)
print(state.get("answer", state.get("error")))
For a blocking wait that returns the final task dict directly, use wait_task(task_id, timeout=600, poll_interval=2.0) from the same client.
Inspect history
for t in history(limit=20):
print(t["task_id"], t["status"], t.get("duration_ms"))
Stream progress (optional)
stream_task(task_id, on_event=lambda type, data: ...) consumes the server's SSE stream and yields {type, data} dicts. run_and_stream(prompt, workspace, ...) is a convenience that creates a task, streams its events, and returns the final task dict.
Constraints
- The
prompt is the entire instruction given to a fresh mcode subprocess. It must be self-contained — the subprocess has no memory of your session.
- The
workspace directory must exist; the server runs mcode with that as cwd.
- Default
timeout is 60 seconds. Raise it for longer work, but consider --permission full first if the task needs to write files.
- For multi-step peer work, prefer the
acp-collab Skill instead — this Skill is for one-shot fire-and-forget dispatch.
Failure handling
If create_task raises ACPError, the server is likely down or rejected the request. Verify the server is reachable on http://127.0.0.1:9999/acp/health and that the token matches. Stop and surface the error to the user; do not retry in a tight loop.