Windows / PowerShell: Examples use bash syntax. To run on Windows PowerShell:
- Flatten
\line continuations to a single line, or end lines with a backtick.- Set env vars with
$env:NAME="value"instead ofexport NAME="value".- Single-quoted JSON
'{"a":"b"}'works as-is.
Overview
The cloudrobo-dispatch skill manages embodied task dispatch via the robo-dispatcher
service. It lets the agent create a task that runs on a robot (identified by robot_id) under
an execution model (exec_model_id) inside a session (session_id), monitor it, cancel it, and
retrieve the natural-language task result plus log items.
Applicable scenarios:
- Task dispatch — Create a task on a robot given a natural-language task description; always
provide a stop condition (
exec_constraints: defaultmax_run_time=10min,max_iter_num=100steps) - Task monitoring — List/show tasks in a session, filter by status/robot/infer-service
- Wait for task completion — Block until a dispatched task leaves
RUNNINGwithout manual polling (wait-task, the preferred way to wait for a task to finish) - Task cancellation — Cancel a running/pending task
- Result retrieval — Get the task result (task + log_items) of a finished task
- Cross-skill orchestration — Combine robots (
cloudrobo-robot) and inference models (cloudrobo-infer/cloudrobo-asset) into concrete robot-executable tasks.
Architecture:
Agent / LLM
│
├── CLI → cloudrobo dispatch <command>
└── SDK → DispatchClient (Python) — domain RoboDispatcherTaskManagement
│
▼
cloudrobo-service (REST API)
/v1/robo-dispatcher/sessions/{session_id}/tasks*
All operations target the cloudrobo-service backend, are scoped to a session_id, and require
a valid workspace_id. A session is a stable execution context; multiple tasks can be created
within one session across time.
session_id: In the current version,
session_idis identical to the current workspace'sworkspace_id. You can obtain it by reading the active workspace (cloudrobo workspace current) or the configuredworkspace_id, and reuse it directly as the--session-id. If a caller explicitly provides a differentsession_id, prefer confirming it against the current workspace.
Prerequisites
- See
references/cli-installation-guide.mdfor CLI installation, AK/SK authentication, and workspace configuration. - A valid
session_idfor all task operations. In the current versionsession_idequals the current workspace'sworkspace_id— read it viacloudrobo workspace currentor the configuredworkspace_id(there is no separate session-create API). - A valid
robot_id(fromcloudrobo-robot) andexec_model_idfor the constrained model service that drives the task. The robot must be ONLINE (i.e. its R2C edge client is connected) for a task to run — otherwise the backend rejects it withfailure_reason: "Robot offline".
How to obtain
exec_model_id(important) —exec_model_idis the execution-model handle that drives the task. Do not put the raw model/asset id (e.g.8b804b51-…) into it; the backend cannot resolve the asset id and the task will fail.Two kinds of
exec_model_id(verified 2026-08 on this platform):
- User-created inference services (deployed via
cloudrobo infer createincloudrobo-infer):exec_model_idequals the inference service'sservice_id— i.e. theidreturned bycloudrobo infer createand bycloudrobo infer list. Practical derivation: runcloudrobo infer list, pick the service you deployed (e.g.so101-real-demo-0828), and use itsid(<uuid>) directly asexec_model_id. No separate "model-service UUID" lookup is needed for user-deployed services.- Platform prebuilt / external model-services show an
ext_-prefixed opaque handle, e.g.ext_2dcc1539e72040c78650c554102debaf(name e.g.Claude-3.5-Updated) orext_1ee0bcc7…(name e.g.cloudrobo-real-rl-model-…). These are NOT infer-list services; obtain them from an existing task'sconstraints.model.exec_model_id.At any time you can confirm the exact value against an existing successful task in the same session:
cloudrobo dispatch list-tasks --session-id <sid> --content-match "<skill prompt>"and read itsconstraints.model.exec_model_id(the returnedexec_model_nameequals the source service name).
Workflow
Natural-Language-First Principle
Every workflow below starts from a user intent (1-2 sentences), not from manual CLI/SDK orchestration. The skill then drives the matching command chain and reports state feedback.
Task Dispatch Workflow (module + robot + model + workspace)
Scenario: "Tell robot A to go pick up the red cube and place it in the bin."
- Resolve session — read
session_idfrom the current workspace: in the current versionsession_id == workspace_id(obtain viacloudrobo workspace current/ configuredworkspace_id). - Resolve robot & model — obtain
robot_id(from robot registration) andexec_model_id.exec_model_idis the execution-model handle that drives the task — for a service you deployed, it is simply that service'sservice_id(see the "How to resolveexec_model_id" note below). Both are required inconstraints. - Verify robot reachable — the target robot must be ONLINE (r2c edge client connected).
A dispatch task on an offline/unconnected robot fails with a
Robot offlinefailure reason. Seecloudrobo-r2cto bring a (dummy/real) robot online before dispatching. - Describe task — collect the natural-language
tasktext (e.g. "pick up the red cube and place it in the bin") and aname. If the inference service was deployed withstrict:true, thetasktext MUST match the predefined skill prompt; otherwise the service rejects it. (Seecloudrobo-inferfor deploying a service withskill_config.strict.) - Create task with stop condition —
cloudrobo dispatch create-task --session-id <sid> --name <name> --task "<task>" --constraints-json '<json>'. The--constraints-jsonis required and carriesmodel,robot_id, and the stop conditionexec_constraints. Always set a stop condition (max_run_timeandmax_iter_num) so the task cannot run unboundedly. Defaults if the user does not specify:max_run_time=10(minutes),max_iter_num=100(steps). Confirm before submitting (mutating). - Wait for completion (preferred) — block until the task finishes with
cloudrobo dispatch wait-task --session-id <sid> --task-id <task_id> [--timeout <secs>]. This polls internally every 5s and returns once the task status leavesRUNNING(i.e. reachesCOMPLETED/FAILED/CANCELLEDor any non-RUNNINGstate). Preferwait-taskover manualshow-taskpolling — it replaces the old 20-30s manual polling loop. - Get result — on completion,
cloudrobo dispatch show-task-result --session-id <sid> --task-id <task_id>to read the natural-language result and log items.
Note:
robot_idandexec_model_idare required insideconstraints. Do not hardcode them; resolve from robot and infer/asset outputs.
How to resolve
exec_model_id—exec_model_idis NOT the model asset ID (the8b804b51-...-style asset UUID fromcloudrobo-asset). It is the execution-model handle the task drives. For a service you deployed yourself viacloudrobo infer create,exec_model_idequals that service'sservice_id(read it fromcloudrobo infer list/ thecreateresponse). Platform prebuilt / external model-services use anext_-prefixed opaque handle instead. Resolve it by:
- If the target model is one of your deployed inference services →
cloudrobo infer list, take the serviceidasexec_model_id(verified equivalent on 2026-08).- Otherwise, confirm the exact value against an existing successful task in the same session:
cloudrobo dispatch list-tasks --session-id <sid> --content-match "<deployed skill prompt>"and read itsconstraints.model.exec_model_id(exec_model_nameis the source service name, informational). Using the raw asset UUID inexec_model_idis a common root cause of dispatch failures.
Task Monitoring / Lookup Workflow (module + filters)
Scenario: "What tasks are running in my session?"
cloudrobo dispatch list-tasks --session-id <sid> [--status <status>] [--robot-id <rid>] [--infer-service-id <iid>] [--start-time <ms>] [--end-time <ms>] [--content-match <text>]with pagination (--limit/--offset) and sorting (--sort-key/--sort-dir).cloudrobo dispatch show-task --session-id <sid> --task-id <task_id>for detail.- Report task status, robot, model, and content.
Task Result Retrieval Workflow (module)
Scenario: "Show me the outcome of that pick-and-place task."
- Wait for the task to finish via
wait-task(or confirm it is terminal viashow-task). cloudrobo dispatch show-task-result --session-id <sid> --task-id <task_id>— returns the task object pluslog_items. Supports--inverse,--limit,--offsetfor pagination.
Task Cancellation Workflow (module)
Scenario: "Abort that task — the robot picked the wrong object."
- Confirm the task via
show-task. cloudrobo dispatch cancel-task --session-id <sid> --task-id <task_id>(mutating; confirm).- Verify via
show-taskthat the task moved to a cancelled state.
Session Context Note
- Tasks are always created under a
session_id. In the current versionsession_idis the same as the current workspace'sworkspace_id; obtain it fromcloudrobo workspace current. You may create many tasks within one session over time. - Stop condition: every task should be created with
constraints.exec_constraintsso it stops on time — see the Create a Task section for the required object shape and defaults. - strict services: if the driving inference service was deployed with
skill_config.strict:true, thetaskyou pass MUST match one of its predefined skill prompts; a mismatched prompt is rejected. - The old
create-session/exec_task/create-session-taskinterfaces are deprecated and must not be used. All operations usesession_iddirectly on the task endpoints.
CLI Command Format Standard
cloudrobo dispatch <command> [OPTIONS]
| Feature | Description |
|---|---|
| Command group | dispatch |
| Subcommand | kebab-case: create-task, list-tasks, show-task, cancel-task, show-task-result, wait-task |
| Session | --session-id <id> (all task operations; equals workspace_id in current version) |
| JSON params | --constraints-json '<object>' (create-task; required; holds model/robot_id/exec_constraints) |
| Dry-run | --dry-run (create-task/cancel-task) |
| Result pagination | --inverse, --limit, --offset (show-task-result) |
| Wait timeout | --timeout <secs> (wait-task; default 600, IntRange 1–3600) |
Full coverage: SDK exposes 6 methods, CLI exposes 6 commands (0 gaps). See
references/cli-installation-guide.mdand the acceptance criteria for the coverage mapping.
Core Commands
SDK Direct Calls: When CLI is inconvenient (dynamic JSON, cross-package queries), use the Python SDK directly.
DispatchClient(domain RoboDispatcherTaskManagement) exposes the 6 methods below.
Create a Task
cloudrobo dispatch create-task --session-id <session-id> --name <task-name> --task "<natural language task>" --constraints-json '{"model":{"exec_model_id":"<model-id>"},"robot_id":"<robot-id>","exec_constraints":{"max_run_time":10,"max_iter_num":100}}' [--dry-run]
Stop condition is required in practice (
constraints.exec_constraints). If the user does not specify values, default tomax_run_time=10(minutes) andmax_iter_num=100(steps) to avoid unbounded/long-running debug tasks. Valid ranges:max_run_time1–300 minutes,max_iter_num1–300000 steps.
modelobject shape (important) — increate-task'sconstraints.model, only includeexec_model_id. Do not addexec_model_name: the create API rejects it with400 Invalid parameter: exec_model_name.exec_model_nameis a response-only field (present inshow-task/list-tasks/show-task-resultoutput), never a request field. See the "How to resolveexec_model_id" note for whatexec_model_idshould be.
from cloudrobo_core.sdk import Config, HttpClient
from cloudrobo_dispatch import DispatchClient
config = Config()
http_client = HttpClient(config)
client = DispatchClient(http_client)
# session_id equals the current workspace_id in the current version
req = {
"name": "pick-red-cube",
"task": "pick up the red cube and place it in the bin",
"constraints": {
"model": {"exec_model_id": "<mid>"},
"robot_id": "<robot-id>",
# stop condition: use the given values, else default max_run_time=10, max_iter_num=100
"exec_constraints": {"max_run_time": 10, "max_iter_num": 100},
},
}
task = client.create_dispatcher_task("<session-id>", req)
print(task) # includes task_id
List Tasks
cloudrobo dispatch list-tasks --session-id <sid> [--status <status>] [--limit 20] [--offset 0]
tasks = client.list_dispatcher_tasks("<session-id>", status="RUNNING")
for t in tasks.get("items", []):
print(t["id"], t["status"], t["name"])
Show Task Detail
cloudrobo dispatch show-task --session-id <sid> --task-id <task-id>
task = client.show_dispatcher_task("<session-id>", "<task-id>")
print(task["name"], task["status"], task["robot_id"])
Cancel Task
cloudrobo dispatch cancel-task --session-id <sid> --task-id <task-id> [--dry-run]
client.cancel_dispatcher_task("<session-id>", "<task-id>")
Show Task Result
cloudrobo dispatch show-task-result --session-id <sid> --task-id <task-id> [--inverse] [--limit 100] [--offset 0]
result = client.show_dispatcher_task_result("<session-id>", "<task-id>")
print(result["task"]["result"])
for item in result.get("log_items", []):
print(item)
Wait for Task Completion
Purpose: Block until a dispatched task finishes (its status leaves RUNNING), so you do not
have to manually poll show-task in a loop. This is the preferred way to wait for a task in
non-interactive / automation scenarios.
cloudrobo dispatch wait-task --session-id <sid> --task-id <task-id> [--timeout <seconds>]
result = client.wait_dispatcher_task("<session-id>", "<task-id>", timeout=600)
# result is the full task dict once status != "RUNNING" (e.g. COMPLETED / FAILED / CANCELLED)
status = (result.get("task") or {}).get("status")
Behavior (authoritative, from source):
- Method:
wait_dispatcher_task(session_id, task_id, timeout=600).- Polling: every 5 seconds (
POLL_INTERVAL = 5); it callsshow_dispatcher_taskinternally (GET /v1/robo-dispatcher/sessions/{session_id}/tasks/{task_id}).- Return condition: returns the task dict as soon as
data["task"]["status"] != "RUNNING"— regardless of whether it reachedCOMPLETED,FAILED,CANCELLED, or another non-RUNNINGstate. Terminal states:COMPLETED,FAILED,CANCELLED.- Default timeout: 600 seconds.
--timeoutis anIntRange(1, 3600)— max 3600 s (1 hour).- On timeout: raises
TimeoutError(client); the CLI prints[ERROR] <msg>to stderr and exits with code 1. Always check the return status to distinguish success, failure, and timeout.- No independent REST API:
wait-taskis a client-side polling helper — it has no dedicated backend endpoint and simply wrapsshow-task.- Must create the task first:
wait-taskdoes not create a task. Callcreate-taskto get thetask_idfirst, thenwait-taskon it.
Recommendation (when to use wait-task):
- Use
wait-taskright aftercreate-task, then fetch the outcome withshow-task-result— this replaces the old manualshow-taskpolling every 20-30s. - It is ideal for non-interactive / automation flows that should block until a terminal state.
- In interactive flows where you want to observe intermediate states, you may still use
show-taskto inspect progress — but usewait-taskwhen you simply need to wait for the outcome.
Parameter Confirmation
| Parameter | Source | Required | Confirmation Needed |
|---|---|---|---|
--session-id |
Current workspace (workspace_id) |
Yes (all) | In current version session_id == workspace_id; verify against cloudrobo workspace current |
--task |
User | Yes (create) | Natural-language task description; if strict service, must match predefined skill prompt |
--name |
User | Yes (create) | Task name |
--constraints-json |
User/derived | Yes (create) | JSON object {model, robot_id, exec_constraints}; holds stop condition |
exec_constraints (in --constraints-json) |
User, else default | Yes in practice (create) | Default max_run_time=10 (min), max_iter_num=100 (steps); ranges 1–300 / 1–300000 |
--task-id |
User or prior output | Yes (show/cancel/result/wait) | Verify before cancel; must exist (from create-task) before wait-task |
--timeout |
Derivable | No (wait) | wait-task timeout in seconds; default 600, IntRange 1–3600 |
--dry-run |
— | No | Preview without executing |
Mutating operations (create-task / cancel-task) must prompt the user for confirmation before execution.
Reference Documents
- CLI Installation Guide — cloudrobo CLI installation and configuration
- IAM Policies — Least-privilege credential & access model
- Verification Method — Verification method details
- Dataflow Diagram — Mermaid data flow diagrams
- Acceptance Criteria — Acceptance criteria
Edge Cases
| Scenario | Handling |
|---|---|
Missing session_id |
In current version session_id == workspace_id; obtain from cloudrobo workspace current |
Missing robot_id / exec_model_id |
create-task requires both inside constraints; resolve from robot and the deployed model service (not the asset id) |
exec_model_id is wrong (asset id used) |
For a service you deployed, exec_model_id equals that service's service_id (read from cloudrobo infer list); using the raw asset id (8b804b51-...) is a common failure. Prebuilt/external model-services use an ext_-prefixed handle. Verify against an existing successful task via list-tasks --content-match "<prompt>" |
exec_model_name passed in create request |
Do not include exec_model_name in constraints.model — the create API returns 400 Invalid parameter: exec_model_name. exec_model_name is response-only; drop it and retry with only exec_model_id |
| Robot offline / not connected | A task on an offline robot fails with failure_reason: "Robot offline". Bring the robot ONLINE first via cloudrobo-r2c (dummy/real edge client) before dispatching |
| Missing stop condition | create-task should set constraints.exec_constraints; default max_run_time=10, max_iter_num=100 to avoid unbounded tasks |
| Over-long task | Respect ranges: max_run_time 1–300 min, max_iter_num 1–300000 steps; don't exceed to keep debug tasks bounded |
| strict:true service | If the inference service was deployed with skill_config.strict:true, the task MUST match a predefined skill prompt; otherwise rejected |
| Task not found | show/cancel/result return ResourceNotFoundError; verify task_id/session_id |
| Path traversal | validate_safe_id(session_id) / validate_safe_id(task_id) block ../ input |
| Cancelling a finished task | backend rejects; confirm status before cancel |
| Long-running task | Prefer wait-task (polls every 5s, returns once status leaves RUNNING); use show-task only to observe intermediate states, not in a tight manual loop |
wait-task timeout |
--timeout max is 3600s (1h); on timeout the CLI prints [ERROR] and exits 1 — raise/inform the user and re-check with show-task |
wait-task on a terminal task |
Returns immediately (status already non-RUNNING); safe to call after a task finished |
wait-task needs an existing task |
It does not create a task — call create-task first to obtain task_id, or it fails on an invalid/unknown task |
| Natural-language task content | Sanitize/inject-protect; do not echo raw content into logs unescaped |
| AK/SK not set | Operations fail at HTTP signing; set HUAWEI_CLOUD_AK/HUAWEI_CLOUD_SK |
| Deprecated interfaces | Do not use old create-session/exec_task/create-session-task/list-sessions |
| session_id / task_id / robot_id / exec_model_id | Never hardcoded; resolved dynamically (session_id from current workspace) |
| Cross-skill invocation | This skill does not call other skills; it consumes robot_id (robot) and exec_model_id (infer/asset) and reports task results |
| Mutating operations | create-task / cancel-task should be confirmed by the user |
Verification Method
Specification Compliance Verification
bash scripts/test-cli-commands.sh
Functional Testing
bash scripts/test-cli-commands.sh
Test Cases
See templates/test-vars.json for the full test case list covering dispatch, monitoring,
wait-for-completion, cancellation, result retrieval, and safety scenarios.
Verification Checklist
- After
create-task, task appears inlist-taskswith correct status - The created task carries a stop condition (
exec_constraints.max_run_time/max_iter_num); when the user gave none, they default to 10 min / 100 steps session_idused equals the current workspace'sworkspace_id- When the inference service is
strict:true, thetaskmatched the predefined skill prompt - After
wait-task, the command blocked (polling every 5s) and returned only once status was non-RUNNING; no manual 20-30s polling loop was used wait-taskwith an explicit--timeoutrespects the timeout and reports a clear error on expirywait-taskwas called with atask_idalready created bycreate-task(it does not create tasks)- After
show-task, detail returns the task with robot/model/status - After
show-task-result, natural-language result and log_items are returned - After
cancel-task,show-taskreflects the cancelled state - Path traversal (
../) is blocked byvalidate_safe_id - Deprecated interfaces (
create-session/exec_task) are not used - Mutating operations prompt user confirmation before executing
Best Practices
- Always resolve
session_idfrom the current workspace (cloudrobo workspace current; in the current versionsession_id == workspace_id), and resolverobot_id/exec_model_iddynamically; never hardcode - Always set a stop condition (
constraints.exec_constraints) when creating a task; if the user gives none, use the defaultsmax_run_time=10(minutes) andmax_iter_num=100(steps) to keep debug tasks bounded (respect ranges 1–300 / 1–300000) - If the driving inference service was deployed with
strict:true, make thetaskmatch the predefined skill prompt - Confirm before
create-task(it triggers real robot action) andcancel-task - After
create-task, wait withwait-taskinstead of manually pollingshow-task— it blocks (polling every 5s) until the status leavesRUNNING, then fetch the outcome withshow-task-result. Useshow-taskonly to inspect intermediate states when needed. In an agent setting, runwait-taskas a background process and wait for it to return (it exits on its own once the task is terminal), rather than sleeping +show-taskin a manual loop. - Set an explicit
--timeoutonwait-taskwhen the task may run long; on timeout, re-check state withshow-taskand report the outcome rather than retrying blindly constraints.modelonly takesexec_model_id— never passexec_model_namein the create request (400 error); it is a response-only field- For a model service you deployed (
cloudrobo infer create),exec_model_id= that service'sservice_id(seecloudrobo infer list); only platform prebuilt/external model-services useext_-prefixed handles. Prefercloudrobo infer listover guessing when resolving it - Use
--dry-runoncreate-task/cancel-taskto validate params before acting - Use
list-tasksfilters (--status,--robot-id,--infer-service-id,--content-match) to quickly locate tasks - Sanitize natural-language
taskcontent; do not echo raw content into logs unescaped - Combine with robot (
cloudrobo robot list/show) and infer (cloudrobo infer list) skills to resolve robot/model IDs needed for task creation