Install
openclaw skills install opcodeZero-token execution layer for AI agents. Define workflows once, run them free forever — persistent, scheduled, deterministic. 6 MCP tools over SSE. Supports DAG-based execution, 6 step types (action, condition, loop, parallel, wait, reasoning), 26 built-in actions, ${{}} interpolation, reasoning nodes for human-in-the-loop decisions, and secret vault. Use when defining workflows, running templates, checking status, sending signals, querying workflow history, or visualizing DAGs.
openclaw skills install opcodeExecution runtime for AI agents. You reason, OPCODE executes — zero tokens per run after the first define. Workflows persist across sessions, run on schedules, and coordinate multiple agents. Persistent SSE daemon: 1 server, N agents, 1 database. JSON-defined DAGs, level-by-level execution, automatic parallelism. 6 MCP tools over SSE (JSON-RPC).
Why use OPCODE instead of reasoning through each step yourself? Every repeated workflow burns tokens re-reasoning decisions you already made. OPCODE templates your reasoning once and executes it deterministically — zero inference cost, identical output every run, survives context resets.
| I want to... | Tool |
|---|---|
| Create/update a workflow template | opcode.define |
| Execute a workflow | opcode.run |
| Check status or pending decisions | opcode.status |
| Resolve a decision / cancel / retry | opcode.signal |
| List workflows, events, or templates | opcode.query |
| Visualize a workflow DAG | opcode.diagram |
Install:
go install github.com/rendis/opcode/cmd/opcode@latest
First-time setup (writes config and starts daemon):
opcode install --listen-addr :4100 --vault-key "my-passphrase"
Restart after stop: OPCODE_VAULT_KEY="my-passphrase" opcode
MCP client configuration:
{
"mcpServers": {
"mcpServers": {
"opcode": {
"type": "sse",
"url": "http://localhost:4100/sse"
}
}
}
Each agent self-identifies via agent_id in tool calls. Opcode auto-registers unknown agents. Choose a stable ID per agent (e.g., "content-writer", "deploy-bot").
Workflows survive restarts. On startup, orphaned active workflows become suspended. Query with opcode.query({ "resource": "workflows", "filter": { "status": "suspended" } }), then resume or cancel via opcode.signal.
See operations.md for full configuration, subcommands, SIGHUP hot-reload, security model, web panel, and benchmarks.
Registers a reusable workflow template. Version auto-increments (v1, v2, v3...).
| Param | Type | Required | Description |
|---|---|---|---|
name | string | yes | Template name |
definition | object | yes | Workflow definition (see below) |
agent_id | string | yes | Defining agent ID |
description | string | no | Template description |
input_schema | object | no | JSON Schema for input validation |
output_schema | object | no | JSON Schema for output validation |
triggers | object | no | Trigger config (seeworkflow-schema.md) |
Returns: { "name": "...", "version": "v1" }
Executes a workflow from a registered template.
| Param | Type | Required | Description |
|---|---|---|---|
template_name | string | yes | Template to execute |
agent_id | string | yes | Initiating agent ID |
version | string | no | Version (default: latest) |
params | object | no | Input parameters |
Returns:
{
"workflow_id": "uuid",
"status": "completed | suspended | failed",
"output": { ... },
"started_at": "RFC3339",
"completed_at": "RFC3339",
"steps": {
"step-id": { "step_id": "...", "status": "completed", "output": {...}, "duration_ms": 42 }
}
}
If status is "suspended", call opcode.status to see pending_decisions.
Gets workflow execution status.
| Param | Type | Required | Description |
|---|---|---|---|
workflow_id | string | yes | Workflow to query |
Returns:
{
"workflow_id": "uuid",
"status": "suspended",
"steps": { "step-id": { "status": "...", "output": {...} } },
"pending_decisions": [
{
"id": "uuid",
"step_id": "reason-step",
"context": { "prompt": "...", "data": {...} },
"options": [ { "id": "approve", "description": "Proceed" } ],
"timeout_at": "RFC3339",
"fallback": "reject",
"status": "pending"
}
],
"events": [ ... ]
}
Workflow statuses: pending, active, suspended, completed, failed, cancelled.
Sends a signal to a suspended workflow.
| Param | Type | Required | Description |
|---|---|---|---|
workflow_id | string | yes | Target workflow |
signal_type | enum | yes | decision / data / cancel / retry / skip |
payload | object | yes | Signal payload (see below) |
step_id | string | no | Target step |
agent_id | string | no | Signaling agent |
reasoning | string | no | Agent's reasoning |
Payload by signal type:
| Signal | step_id | Payload | Behavior |
|---|---|---|---|
decision | required | { "choice": "<option_id>" } | Resolves decision, auto-resumes |
data | optional | { "key": "value", ... } | Injects data into workflow |
cancel | no | {} | Cancels workflow |
retry | required | {} | Retries failed step |
skip | required | {} | Skips failed step |
Returns (decision): { "ok": true, "resumed": true, "status": "completed", ... }
Returns (other): { "ok": true, "workflow_id": "...", "signal_type": "..." }
Queries workflows, events, or templates.
| Param | Type | Required | Description |
|---|---|---|---|
resource | enum | yes | workflows / events / templates |
filter | object | no | Filter criteria |
Filter fields by resource:
| Resource | Fields |
|---|---|
workflows | status, agent_id, since (RFC3339), limit |
events | workflow_id, step_id, event_type, since, limit |
templates | name, agent_id, limit |
Note: event queries require either event_type or workflow_id in filter.
Returns: { "<resource>": [...] } -- results wrapped in object keyed by resource type.
Generates a visual DAG diagram from a template or running workflow.
| Param | Type | Required | Description |
|---|---|---|---|
template_name | string | no* | Template to visualize (structure preview) |
version | string | no | Template version (default: latest) |
workflow_id | string | no* | Workflow to visualize (with runtime status) |
format | enum | yes | ascii / mermaid / image |
include_status | bool | no | Show runtime status overlay (default: true if workflow_id) |
* One of template_name or workflow_id required.
template_name -- preview DAG structure before executionworkflow_id -- visualize with live step statusformat: "ascii" -- CLI-friendly text with box-drawing charactersformat: "mermaid" -- markdown-embeddable flowchart syntaxformat: "image" -- base64-encoded PNG for visual channelsReturns: { "format": "ascii", "diagram": "..." }
{
"steps": [ ... ],
"inputs": { "key": "value or ${{secrets.KEY}}" },
"context": { "intent": "...", "notes": "..." },
"timeout": "5m",
"on_timeout": "fail | suspend | cancel",
"on_complete": { /* step definition */ },
"on_error": { /* step definition */ },
"metadata": {}
}
| Field | Type | Required | Description |
|---|---|---|---|
steps | StepDefinition[] | yes | Workflow steps |
inputs | object | no | Input parameters (supports ${{}}) |
context | object | no | Workflow context, accessible via ${{context.*}} |
timeout | string | no | Workflow deadline (e.g.,"5m", "1h") |
on_timeout | string | no | fail (default), suspend, cancel |
on_complete | StepDefinition | no | Hook step after completion |
on_error | StepDefinition | no | Hook step on workflow failure |
metadata | object | no | Arbitrary metadata |
{
"id": "step-id",
"type": "action | condition | loop | parallel | wait | reasoning",
"action": "http.get",
"params": { ... },
"depends_on": ["other-step"],
"condition": "CEL guard expression",
"timeout": "30s",
"retry": { "max": 3, "backoff": "exponential", "delay": "1s", "max_delay": "30s" },
"on_error": { "strategy": "ignore | fail_workflow | fallback_step | retry", "fallback_step": "id" },
"config": { /* type-specific */ }
}
type defaults to action. See workflow-schema.md for all config blocks.
Executes a registered action. Set action to the action name, params for input.
Evaluates a CEL expression and branches.
{
"id": "route",
"type": "condition",
"config": {
"expression": "inputs.env",
"branches": { "prod": [...], "staging": [...] },
"default": [...]
}
}
Iterates over a collection or condition. Loop variables: ${{loop.item}}, ${{loop.index}}.
{
"id": "process-items",
"type": "loop",
"config": {
"mode": "for_each",
"over": "[\"a\",\"b\",\"c\"]",
"body": [
{
"id": "hash",
"action": "crypto.hash",
"params": { "data": "${{loop.item}}" }
}
],
"max_iter": 100
}
}
Modes: for_each (iterate over), while (loop while condition true), until (loop until condition true).
Executes branches concurrently.
{
"id": "fan-out",
"type": "parallel",
"config": {
"mode": "all",
"branches": [
[{ "id": "a", "action": "http.get", "params": {...} }],
[{ "id": "b", "action": "http.get", "params": {...} }]
]
}
}
Modes: all (wait for all branches), race (first branch wins).
Delays execution or waits for a named signal.
{ "id": "pause", "type": "wait", "config": { "duration": "5s" } }
Suspends workflow for agent decision. Empty options = free-form (any choice accepted).
{
"id": "review",
"type": "reasoning",
"config": {
"prompt_context": "Review data and decide",
"options": [
{ "id": "approve", "description": "Proceed" },
{ "id": "reject", "description": "Stop" }
],
"data_inject": { "analysis": "steps.analyze.output" },
"timeout": "1h",
"fallback": "reject",
"target_agent": ""
}
}
Syntax: ${{namespace.path}}
| Namespace | Example | Available fields |
|---|---|---|
steps | ${{steps.fetch.output.body}} | <id>.output.*, <id>.status |
inputs | ${{inputs.api_key}} | Keys from params in opcode.run |
workflow | ${{workflow.run_id}} | run_id, name, template_name, template_version, agent_id |
context | ${{context.intent}} | Keys from context in workflow definition |
secrets | ${{secrets.DB_PASS}} | Keys stored in vault |
loop | ${{loop.item}}, ${{loop.index}} | item (current element), index (0-based) |
Two-pass resolution: non-secrets first, then secrets via AES-256-GCM vault.
CEL gotcha: loop is a reserved word in CEL. Use iter.item / iter.index in CEL expressions. The ${{loop.item}} interpolation syntax is unaffected.
See expressions.md for CEL, GoJQ, Expr engine details.
| Category | Actions |
|---|---|
| HTTP | http.request, http.get, http.post |
| Filesystem | fs.read, fs.write, fs.append, fs.delete, fs.list, fs.stat, fs.copy, fs.move |
| Shell | shell.exec |
| Crypto | crypto.hash, crypto.hmac, crypto.uuid |
| Assert | assert.equals, assert.contains, assert.matches, assert.schema |
| Expression | expr.eval |
| Workflow | workflow.run, workflow.emit, workflow.context, workflow.fail, workflow.log, workflow.notify |
Quick reference (most-used actions):
http.get: url (req), headers, timeout, fail_on_error_status -- output: { status_code, headers, body, duration_ms }
shell.exec: command (req), args, stdin, timeout, env, workdir -- output: { stdout, stderr, exit_code, killed }
fs.read: path (req), encoding -- output: { path, content, encoding, size }
workflow.notify: message (req), data -- output: { notified: true/false } -- pushes real-time notification to agent via MCP SSE (best-effort)
expr.eval: expression (req), data -- output: { result: <value> } -- evaluates Expr expression against workflow scope (steps, inputs, workflow, context)
See actions.md for full parameter specs of all 26 actions.
shell.exec auto-parses JSON stdout. Convention: stdin=JSON, stdout=JSON, stderr=errors, non-zero exit=failure. Use stdout_raw for unprocessed text.
See patterns.md for language-specific templates (Bash, Python, Node, Go).
Workflow reaches a reasoning step
Executor creates PendingDecision, emits decision_requested event
Workflow status becomes suspended
Agent calls opcode.status to see pending decision with context and options
Agent resolves via opcode.signal:
{
"workflow_id": "...",
"signal_type": "decision",
"step_id": "reason-step",
"payload": { "choice": "approve" }
}
Workflow auto-resumes after signal
If timeout expires: fallback option auto-selected, or step fails if no fallback
See patterns.md for full JSON examples: linear pipeline, conditional branching, for-each loop, parallel fan-out, human-in-the-loop, error recovery, sub-workflows, and MCP lifecycle.
| Strategy | Behavior |
|---|---|
ignore | Step skipped, workflow continues |
fail_workflow | Entire workflow fails |
fallback_step | Execute fallback step |
retry | Defer to retry policy |
Backoff: none, linear, exponential, constant. Non-retryable errors (validation, permission, assertion) are never retried.
See error-handling.md for circuit breakers, timeout interactions, error codes.
10-step parallel workflows complete in ~50µs, 500-step in ~2.4ms. The event store sustains ~15k appends/sec with <12% drop under 100 concurrent writers. Worker pool overhead is ~0.85µs/task (>1M tasks/sec at any pool size).
Full benchmark charts, per-scenario breakdowns, and methodology: docs/benchmarks.md.