Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
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: default max_run_time=10 min, max_iter_num=100 steps)
- 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's `workspace_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 version `session_id` equals the
current workspace's `workspace_id` — read it via cloudrobo workspace current or the configured workspace_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 with failure_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): 1. User-created inference services (deployed viacloudrobo infer createincloudrobo-infer):exec_model_idequals the inference service's `service_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. 2. Platform prebuilt / external model-services show anext_-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 version
session_id == workspace_id (obtain via cloudrobo workspace current / configured workspace_id).
- Resolve robot & model — obtain
robot_id(from robot registration) andexec_model_id.
exec_model_id is the execution-model handle that drives the task — for a service you deployed, it is simply that service's service_id (see the "How to resolve exec_model_id" note below). Both are required in constraints.
- 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 offline failure reason. See cloudrobo-r2c to 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 a name. If the inference service was deployed with `strict:true`, the `task` text MUST match the predefined skill prompt; otherwise the service rejects it. (See cloudrobo-infer for deploying a service with skill_config.strict.)
- Create task with stop condition —
cloudrobo dispatch create-task --session-id <sid> --name <name> --task "<task>" --constraints-json '<json>'. The --constraints-json is required and carries model, robot_id, and the stop condition exec_constraints. Always set a stop condition (max_run_time and max_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 leaves RUNNING (i.e. reaches COMPLETED/FAILED/CANCELLED or any non-RUNNING state). Prefer wait-task over manual show-task polling — 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's `service_id` (read it fromcloudrobo infer list/ thecreateresponse). Platform prebuilt / external model-services use anext_-prefixed opaque handle instead. Resolve it by: 1. If the target model is one of your deployed inference services →cloudrobo infer list, take the serviceidasexec_model_id(verified equivalent on 2026-08). 2. 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 plus log_items. Supports --inverse, --limit, --offset for 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's workspace_id; obtain it from cloudrobo 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,
the task you 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 use session_id directly 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/robotid/execconstraints) |
| 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.md and 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 to `max_run_time=10` (minutes) and `max_iter_num=100` (steps) to avoid unbounded/long-running debug tasks. Valid ranges:max_run_time1–300 minutes,max_iter_num1–300000 steps.
`model` object shape (important) — increate-task'sconstraints.model, only include `exec_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_idList 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 asdata["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: raisesTimeoutError(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 manual show-task polling 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-task to inspect progress — but use wait-task when 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 |
| sessionid / taskid / robotid / execmodel_id | Never hardcoded; resolved dynamically (session_id from current workspace) |
| Cross-skill invocation | This skill does not call other skills; it consumes robotid (robot) and execmodel_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.shFunctional Testing
bash scripts/test-cli-commands.shTest 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 version session_id == workspace_id), and resolve robot_id / exec_model_id dynamically; never hardcode
- Always set a stop condition (
constraints.exec_constraints) when creating a task; if the user
gives none, use the defaults max_run_time=10 (minutes) and max_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 with `wait-task` instead of manually polling `show-task` — it blocks
(polling every 5s) until the status leaves RUNNING, then fetch the outcome with show-task-result. Use show-task only to inspect intermediate states when needed. In an agent setting, run wait-task as a background process and wait for it to return (it exits on its own once the task is terminal), rather than sleeping + show-task in a manual loop.
- Set an explicit
--timeoutonwait-taskwhen the task may run long; on timeout, re-check state
with show-task and report the outcome rather than retrying blindly
- `constraints.model` only takes `exec_model_id` — never pass
exec_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's
service_id (see cloudrobo infer list); only platform prebuilt/external model-services use ext_-prefixed handles. Prefer cloudrobo infer list over 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

