ClawFlow: add runtime substrate (#58336)

Merged via squash.

Prepared head SHA: 6a6158179e
Reviewed-by: @mbelinky
This commit is contained in:
Mariano
2026-03-31 13:58:29 +02:00
committed by GitHub
parent f2d4089ca2
commit 607076d164
20 changed files with 1031 additions and 28 deletions

View File

@@ -0,0 +1,62 @@
---
name: clawflow-inbox-triage
description: Example ClawFlow authoring pattern for inbox triage. Use when messages need different treatment based on intent, with some routes notifying immediately, some waiting on outside answers, and others rolling into a later summary.
metadata: { "openclaw": { "emoji": "📥" } }
---
# ClawFlow inbox triage
This is a concrete example of how to think about ClawFlow without turning the core runtime into a DSL.
## Goal
Triage inbox items with one owner flow:
- business → post to Slack and wait for reply
- personal → notify the owner now
- everything else → keep for end-of-day summary
## Pattern
1. Create one flow for the inbox batch.
2. Run one detached task to classify new items.
3. Resume the flow when classification completes.
4. Route each item in the calling logic.
5. Persist only the summary bucket and the current wait target.
## Suggested flow outputs
- `business_threads`
- `personal_items`
- `eod_summary`
## Minimal runtime calls
```ts
const flow = createFlow({
ownerSessionKey,
goal: "triage inbox",
});
runTaskInFlow({
flowId: flow.flowId,
runtime: "acp",
task: "Classify inbox messages",
currentStep: "wait_for_classification",
});
resumeFlow({
flowId: flow.flowId,
currentStep: "route_items",
});
appendFlowOutput({
flowId: flow.flowId,
key: "eod_summary",
value: { subject: "Newsletter", route: "later" },
});
```
## Related example
- `skills/clawflow/examples/inbox-triage.lobster`

76
skills/clawflow/SKILL.md Normal file
View File

@@ -0,0 +1,76 @@
---
name: clawflow
description: Use when work should span one or more detached tasks but still behave like one job with a single owner context. ClawFlow is the runtime substrate under authoring layers like Lobster, acpx, or plain code. Keep conditional logic in the caller; use ClawFlow for flow identity, waiting, outputs, and user-facing emergence.
metadata: { "openclaw": { "emoji": "🪝" } }
---
# ClawFlow
Use ClawFlow when a job needs to outlive one prompt or one detached run, but you still want one owner session, one thread context, and one place to inspect or resume the work.
## When to use it
- Multi-step background work with one owner
- Work that waits on detached ACP or subagent tasks
- Jobs that may need to emit one clear update back to the owner
- Jobs that need a small persisted output bag between steps
## What ClawFlow owns
- flow identity
- owner session and return context
- waiting state
- small persisted outputs
- finish, fail, cancel, and blocked state
It does **not** own branching or business logic. Put that in Lobster, acpx, or the calling code.
## Runtime pattern
1. `createFlow(...)`
2. `runTaskInFlow(...)`
3. `setFlowWaiting(...)` or `setFlowOutput(...)`
4. `resumeFlow(...)`
5. `emitFlowUpdate(...)` only when needed
6. `finishFlow(...)` or `failFlow(...)`
## Example shape
```ts
const flow = createFlow({
ownerSessionKey,
goal: "triage inbox",
});
const classify = runTaskInFlow({
flowId: flow.flowId,
runtime: "acp",
task: "Classify inbox messages",
currentStep: "wait_for_classification",
});
resumeFlow({
flowId: flow.flowId,
currentStep: "route_results",
});
setFlowOutput({
flowId: flow.flowId,
key: "classification",
value: { route: "business" },
});
```
## Keep conditionals above the runtime
Use the flow runtime for state and task linkage. Keep decisions in the authoring layer:
- `business` → post to Slack and wait
- `personal` → notify the owner now
- `later` → append to an end-of-day summary bucket
## Examples
- See `skills/clawflow/examples/inbox-triage.lobster`
- See `skills/clawflow/examples/pr-intake.lobster`
- See `skills/clawflow-inbox-triage/SKILL.md` for a concrete routing pattern

View File

@@ -0,0 +1,33 @@
# Illustrative Lobster authoring example for a ClawFlow-style inbox triage job.
# Swap the placeholder commands for your own tools or scripts.
name: inbox-triage
steps:
- id: fetch
command: gog.gmail.search --query 'newer_than:1d' --max 20
- id: classify
command: >-
openclaw.invoke --tool llm-task --action json --args-json
'{"prompt":"Classify each inbox item as business, personal, or later. Return one JSON object per item with route and summary.","thinking":"low","schema":{"type":"object","properties":{"items":{"type":"array"}},"required":["items"],"additionalProperties":false}}'
stdin: $fetch.stdout
- id: post_business
command: slack-route --bucket business
stdin: $classify.stdout
condition: $classify.json.items[0].route == "business"
- id: wait_for_business_reply
command: echo '{"status":"waiting","reason":"slack_reply"}'
condition: $classify.json.items[0].route == "business"
- id: notify_personal
command: >-
openclaw.invoke --tool message --action send --args-json
'{"provider":"telegram","to":"owner-thread","content":"Personal inbox item needs attention."}'
condition: $classify.json.items[0].route == "personal"
- id: stash_for_eod
command: summary-append --bucket eod
stdin: $classify.stdout
condition: $classify.json.items[0].route == "later"

View File

@@ -0,0 +1,32 @@
# Illustrative Lobster authoring example for a ClawFlow-style PR intake lane.
# Replace the placeholder commands with repo-specific tooling.
name: pr-intake
steps:
- id: fetch
command: gh pr list --repo owner/repo --state open --json number,title,body,headRefName
- id: classify
command: >-
openclaw.invoke --tool llm-task --action json --args-json
'{"prompt":"Classify each PR as close, request_changes, refactor, or maintainer_review. Return intent and recommended next action.","thinking":"low","schema":{"type":"object","properties":{"items":{"type":"array"}},"required":["items"],"additionalProperties":false}}'
stdin: $fetch.stdout
- id: close_low_signal
command: pr-close-low-signal
stdin: $classify.stdout
condition: $classify.json.items[0].nextAction == "close"
- id: request_changes
command: pr-request-changes
stdin: $classify.stdout
condition: $classify.json.items[0].nextAction == "request_changes"
- id: refactor_branch
command: pr-refactor-branch
stdin: $classify.stdout
condition: $classify.json.items[0].nextAction == "refactor"
- id: escalate
command: echo '{"status":"notify","target":"maintainer"}'
condition: $classify.json.items[0].nextAction == "maintainer_review"