Lobster: Typed Workflow Runtime for OpenClaw Pipelines
Lobster runs multi-step tool pipelines as one call with resumable approval and input gates. It is for builders who need repeatable, reviewable automation without a separate orchestration registry.
Read this when
- You want repeatable multi-step workflows with approvals or structured questions
- You need to resume a workflow without re-running earlier steps
Lobster executes multi-step tool pipelines as a single repeatable tool call, with explicit checkpoints for approval and input plus resume tokens. Those checkpoints are owned by the Lobster runner itself rather than by a separate orchestration registry.
Why
Without Lobster, a multi-step job turns into a long chain of tool round-trips, and the model has to orchestrate each step. Lobster shifts that orchestration into a typed runtime:
- One call instead of many: the entire pipeline comes back as a structured result from a single Lobster tool call.
- Approvals built in: side effects (send, post, delete) pause the workflow until someone explicitly approves them.
- Resumable: when a workflow halts, it hands back a token; you approve and resume without re-running the earlier steps.
Rather than a general scripting language, Lobster is a small, constrained DSL: approve/resume is a durable primitive built into it; pipelines are data (simple to log, diff, replay, and review); the tiny grammar keeps "creative" code paths in check so validation stays realistic; and timeouts, output caps, sandbox checks, and allowlists are enforced by the runtime instead of by each script. Any CLI or script can still be called from a step, so generate .lobster files from other tooling if you want a richer authoring language.
Without Lobster, a recurring email triage looks like:
User: "Check my email and draft replies"
→ openclaw calls gmail.list
→ LLM summarizes
→ User: "draft replies to #2 and #5"
→ LLM drafts
→ User: "send #2"
→ openclaw calls gmail.send
(repeat daily, no memory of what was triaged)
With Lobster, the same job becomes one call that halts for approval and then resumes:
{ "action": "run", "pipeline": "email.triage --limit 20", "timeoutMs": 30000 }
{
"ok": true,
"status": "needs_approval",
"output": [{ "summary": "5 need replies, 2 need action" }],
"requiresApproval": {
"type": "approval_request",
"prompt": "Send 2 draft replies?",
"items": [],
"resumeToken": "..."
}
}
How it works
The official @openclaw/lobster plugin, installed separately, executes Lobster workflows in-process through its embedded @clawdbot/lobster runtime. No external lobster subprocess gets spawned; the tool call returns a JSON envelope directly. When the pipeline halts for approval or input, Lobster persists its continuation and returns a resume token. Approval requests may also carry a short approval ID. The call stops at the checkpoint, and no process sits waiting for the user's answer.
Workflow input and LLM output schemas are validated by Lobster on Node, Bun, and AOT standalone hosts. Validation picks up the host runtime profile automatically, so no extra setting is needed to turn it on.
Enable
Lobster is an optional plugin tool: it is neither installed nor enabled by default. Install the official plugin:
openclaw plugins install @openclaw/lobster
A running Gateway picks up the installation automatically; otherwise it applies at the next startup. See Apply changes and inspect.
Next, allow the tool globally:
{
"tools": {
"alsoAllow": ["lobster"]
}
}
Or per-agent:
{
"agents": {
"entries": {
"main": {
"tools": {
"alsoAllow": ["lobster"]
}
}
}
}
}
Note
alsoAllowaddslobsteron top of the active tool profile without restricting other core tools. Usetools.allowonly if you want a restrictive allowlist mode instead.
Sandboxed tool contexts disable the tool entirely.
If you need the standalone Lobster CLI for development or external pipelines (outside the embedded gateway runner), install it from the Lobster repo and put lobster on PATH.
Pattern: small CLI + JSON pipes + approvals
Build tiny commands that speak JSON, then chain them into one Lobster call. (Command names below are examples; substitute your own.)
inbox list --json
inbox categorize --json
inbox apply --json
{
"action": "run",
"pipeline": "exec --json --shell 'inbox list --json' | exec --stdin json --shell 'inbox categorize --json' | exec --stdin json --shell 'inbox apply --json' | approve --preview-from-stdin --limit 5 --prompt 'Apply changes?'",
"timeoutMs": 30000
}
When the pipeline requests approval, resume with the token:
{
"action": "resume",
"token": "<resumeToken>",
"approve": true
}
Example: map input items into tool calls:
gog.gmail.search --query 'newer_than:1d' \
| openclaw.invoke --tool message --action send --each --item-key message --args-json '{"provider":"telegram","to":"..."}'
JSON-only LLM steps (llm-task)
To get a structured LLM step inside a workflow, enable the optional llm-task plugin tool and call it from Lobster:
{
"plugins": {
"entries": {
"llm-task": { "enabled": true }
}
},
"agents": {
"entries": {
"main": {
"tools": { "alsoAllow": ["llm-task"] }
}
}
}
}
Important limitation: embedded Lobster vs openclaw.invoke
The installed Lobster plugin runs workflows in-process inside the gateway. In that embedded mode, openclaw.invoke does not automatically inherit a gateway URL/auth context for nested OpenClaw CLI tool calls.
So this pattern is not currently reliable in the embedded runner:
openclaw.invoke --tool llm-task --action json --args-json '{ ... }'
Use the example below only when running the standalone Lobster CLI in an environment where openclaw.invoke is already configured with the correct gateway/auth context.
For openclaw.invoke and clawd.invoke, ambient OPENCLAW_TOKEN or CLAWD_TOKEN credentials are accepted only for localhost, 127.0.0.1, or [::1] destinations. Passing credentials to any other HTTP(S) endpoint requires --token to be given explicitly. The same rule covers embedded workflows that explicitly configure a remote connection. This command argument is the remote Gateway credential, not the Lobster tool's approval-resume token parameter. If an invocation times out or fails after dispatch, Lobster does not retry it automatically, since the Gateway may already have carried out the action.
openclaw.invoke --tool llm-task --action json --args-json '{
"prompt": "Given the input email, return intent and draft.",
"thinking": "low",
"input": { "subject": "Hello", "body": "Can you help?" },
"schema": {
"type": "object",
"properties": {
"intent": { "type": "string" },
"draft": { "type": "string" }
},
"required": ["intent", "draft"],
"additionalProperties": false
}
}'
If you are using the embedded Lobster plugin today, prefer either:
- a direct
llm-tasktool call outside Lobster, or - non-
openclaw.invokesteps inside the Lobster pipeline until a supported embedded bridge is added.
See LLM Task for details and configuration options.
Workflow files (.lobster)
Lobster can run YAML/JSON workflow files containing name, args, steps, env, condition, and approval fields. Point pipeline at the file path in the tool call.
name: inbox-triage
args:
tag:
default: "family"
steps:
- id: collect
command: inbox list --json
- id: categorize
command: inbox categorize --json
stdin: $collect.stdout
- id: approve
command: inbox apply --approve
stdin: $categorize.stdout
approval: required
- id: execute
command: inbox apply --execute
stdin: $categorize.stdout
condition: $approve.approved
Notes:
stdin: $step.stdoutandstdin: $step.jsonpass a prior step's output.condition(orwhen) can gate steps on$step.approved.
Injected environment variables
Every step shell starts from the parent environment and also gets the variables Lobster injects, which lets commands use resolved workflow args instead of hardcoding raw values into the command string:
LOBSTER_ARG_<NAME>- one per workflow arg. The name is uppercased, and every run of non-alphanumeric characters is collapsed to_, so arguser-idbecomesLOBSTER_ARG_USER_ID.LOBSTER_ARGS_JSON- all resolved args together as a single JSON string.
Those are the only injected variables. There are no per-step output variables like LOBSTER_STEP_<id>_STDOUT or LOBSTER_STEP_<id>_JSON_<field>; shells see those names as unset, and parameter-expansion defaults can therefore mask the mistake. To read a prior step's output, use step references instead - $step.stdout, $step.json, or $step.json.<field> - inside a stdin:, env:, or condition: value. (LOBSTER_STATE_DIR is a runtime setting for the state directory, separate from per-run args.)
Tool parameters
run
{
"action": "run",
"pipeline": "gog.gmail.search --query 'newer_than:1d' | email.triage",
"cwd": "workspace",
"timeoutMs": 30000,
"maxStdoutBytes": 512000
}
To run a workflow file with args:
{
"action": "run",
"pipeline": "/path/to/inbox-triage.lobster",
"argsJson": "{\"tag\":\"family\"}"
}
| Field | Default | Notes |
|---|---|---|
pipeline | required | Inline pipeline string, or a path ending in .lobster/.yaml/.yml/.json for a workflow file. |
cwd | gateway cwd | Relative working directory; must resolve inside the gateway working directory (absolute paths are rejected). |
timeoutMs | 20000 | Aborts the run if exceeded. |
maxStdoutBytes | 512000 | Aborts if captured stdout, stderr, or the embedded JSON result exceeds this size. |
argsJson | - | JSON string of args for a workflow file (ignored for inline pipelines). |
resume
{
"action": "resume",
"token": "<resumeToken>",
"approve": true
}
When handling approvals, take token or approvalId from requiresApproval together with a boolean approve. When handling input, take token from requiresInput and responseJson. To cancel a checkpoint of either kind, pass cancel: true rather than a decision. Exactly one of approve, responseJson, or cancel: true must be supplied.
Structured input
A workflow input step, or an inline ask stage, returns needs_input containing the question, a JSON Schema and a resume token. Suggested values and material to review can be supplied through the optional defaults and subject. For example:
{
"status": "needs_input",
"requiresInput": {
"type": "input_request",
"prompt": "What feedback should be included?",
"responseSchema": { "type": "string" },
"resumeToken": "<resumeToken>"
}
}
The agent puts the question to the user in chat, then sends back the user's answer as JSON:
{
"action": "resume",
"token": "<resumeToken>",
"responseJson": "\"Please shorten the introduction.\""
}
responseJson may encode any value the returned schema permits, not only an object. Before continuing, Lobster validates the answer. If the JSON is invalid, or the answer does not match the schema, the checkpoint stays available so it can be corrected. A resume may come back with another question or approval request.
This is a chat/tool interaction, not an Inbox card or form. Pending checkpoints are not listed by the plugin; keep the returned token so you can resume later. As with approval tokens, anyone allowed to use the tool who holds an input token can resume with it; tokens are not tied to an OpenClaw user or session.
Output envelope
Lobster returns a JSON envelope whose status is one of four values:
ok- completed without errorneeds_approval- suspended;requiresApprovalholds aresumeTokenplus a briefapprovalId, and either one is able to resume the runneeds_input- suspended; the question, answer schema andresumeTokenare carried inrequiresInputcancelled- denied outright or cancelled
Both content (pretty-printed JSON) and details
(the raw object) are ways the tool exposes the envelope.
Approvals
When requiresApproval is present, review the prompt and choose:
approve: true- continue the run, side effects includedapprove: false- finalize the workflow by cancelling it
approve --preview-from-stdin --limit N lets you attach a JSON preview to
approval requests, so no custom jq/heredoc glue is needed. The resume state
lives in small JSON files inside the Lobster state directory (~/.lobster/state
by default; override it with LOBSTER_STATE_DIR). That state is not encoded in the
token, which only points at it, not the full pipeline state.
Safety
- Local in-process only - execution happens within the gateway process; the plugin makes no network calls on its own.
- No secrets - OAuth is not managed by Lobster; the OpenClaw tools it calls handle that.
- Sandbox-aware - turned off whenever the tool context is sandboxed.
- Hardened - the embedded runner enforces timeouts and output caps.
Troubleshooting
| Error | Cause / fix |
|---|---|
lobster runtime timed out | The pipeline ran past timeoutMs. Raise that limit or split the pipeline. |
lobster stdout exceeded maxStdoutBytes (or stderr) | Captured output went over the cap. Either raise maxStdoutBytes or produce less output. |
lobster runtime result exceeded maxStdoutBytes | The JSON result went over the cap. Either raise maxStdoutBytes or produce less output. |
run --args-json must be valid JSON | Parsing failed for argsJson (workflow-file runs). Correct the JSON string. |
lobster runtime failed (or another runtime_error message) | An error envelope came back from the embedded runtime. Consult the gateway logs for details. |
Learn more
Case study: community workflows
A public example worth looking at: a "second brain" CLI paired with Lobster
pipelines that look after three Markdown vaults (personal, partner, shared).
Stats, inbox listings and stale scans are emitted as JSON by the CLI, and
Lobster chains those commands into workflows such as weekly-review, inbox-triage, memory-consolidation, and
shared-task-sync, each one gated by approvals. Where AI is available it makes the
judgment calls (categorization); where it isn't, fixed rules take over.
- Thread: https://x.com/plattenschieber/status/2014508656335770033
- Repo: https://github.com/bloomedai/brain-cli
Related
- Automation - every automation mechanism
- Tools Overview - every agent tool on offer
- Lobster plugin reference - the plugin's manifest, config, and tool reference