@enderfga/claw-orchestrator 7.14.0 → 7.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -135
- package/configs/autoloop-reviewer-prompt.md +1 -1
- package/configs/engines/README.md +31 -11
- package/dist/bin/acp-server.js +7 -12
- package/dist/bin/acp-server.js.map +1 -1
- package/dist/bin/cli.js +84 -46
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/mcp-server.js +2 -54
- package/dist/bin/mcp-server.js.map +1 -1
- package/dist/src/acp-server.d.ts +17 -9
- package/dist/src/acp-server.js +149 -30
- package/dist/src/acp-server.js.map +1 -1
- package/dist/src/autoloop/agent-tools.d.ts +2 -0
- package/dist/src/autoloop/agent-tools.js +7 -0
- package/dist/src/autoloop/agent-tools.js.map +1 -1
- package/dist/src/autoloop/dispatcher.d.ts +6 -0
- package/dist/src/autoloop/dispatcher.js +72 -9
- package/dist/src/autoloop/dispatcher.js.map +1 -1
- package/dist/src/autoloop/planner-tools.js +2 -0
- package/dist/src/autoloop/planner-tools.js.map +1 -1
- package/dist/src/claude-goal-session.d.ts +147 -0
- package/dist/src/claude-goal-session.js +355 -0
- package/dist/src/claude-goal-session.js.map +1 -0
- package/dist/src/council.d.ts +8 -0
- package/dist/src/council.js +45 -12
- package/dist/src/council.js.map +1 -1
- package/dist/src/dashboard/index.html +28 -14
- package/dist/src/embedded-server.js +27 -8
- package/dist/src/embedded-server.js.map +1 -1
- package/dist/src/engine-spawn.js +3 -1
- package/dist/src/engine-spawn.js.map +1 -1
- package/dist/src/fanout.d.ts +13 -3
- package/dist/src/fanout.js +10 -0
- package/dist/src/fanout.js.map +1 -1
- package/dist/src/inbox-manager.d.ts +9 -0
- package/dist/src/inbox-manager.js +8 -2
- package/dist/src/inbox-manager.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +135 -53
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/agent-step.d.ts +6 -0
- package/dist/src/kernel/agent-step.js.map +1 -1
- package/dist/src/kernel/conditions.d.ts +1 -1
- package/dist/src/kernel/conditions.js +1 -1
- package/dist/src/kernel/engine.d.ts +28 -8
- package/dist/src/kernel/engine.js +93 -19
- package/dist/src/kernel/engine.js.map +1 -1
- package/dist/src/kernel/nodes/agent.js +2 -0
- package/dist/src/kernel/nodes/agent.js.map +1 -1
- package/dist/src/kernel/nodes/council.js +9 -4
- package/dist/src/kernel/nodes/council.js.map +1 -1
- package/dist/src/kernel/nodes/fanout.js +5 -0
- package/dist/src/kernel/nodes/fanout.js.map +1 -1
- package/dist/src/kernel/nodes/goal.d.ts +19 -5
- package/dist/src/kernel/nodes/goal.js +81 -34
- package/dist/src/kernel/nodes/goal.js.map +1 -1
- package/dist/src/kernel/nodes/subflow.js +9 -1
- package/dist/src/kernel/nodes/subflow.js.map +1 -1
- package/dist/src/kernel/nodes/ultraapp.d.ts +1 -0
- package/dist/src/kernel/nodes/ultraapp.js +1 -0
- package/dist/src/kernel/nodes/ultraapp.js.map +1 -1
- package/dist/src/kernel/nodes/verifier.js +5 -1
- package/dist/src/kernel/nodes/verifier.js.map +1 -1
- package/dist/src/kernel/projections.d.ts +1 -0
- package/dist/src/kernel/projections.js +7 -1
- package/dist/src/kernel/projections.js.map +1 -1
- package/dist/src/kernel/templates/index.d.ts +16 -11
- package/dist/src/kernel/templates/index.js +14 -8
- package/dist/src/kernel/templates/index.js.map +1 -1
- package/dist/src/kernel/types.d.ts +37 -13
- package/dist/src/kernel/types.js.map +1 -1
- package/dist/src/logger.d.ts +6 -0
- package/dist/src/logger.js +14 -0
- package/dist/src/logger.js.map +1 -1
- package/dist/src/mcp-annotations.d.ts +13 -0
- package/dist/src/mcp-annotations.js +100 -0
- package/dist/src/mcp-annotations.js.map +1 -0
- package/dist/src/models.d.ts +7 -0
- package/dist/src/models.js +9 -0
- package/dist/src/models.js.map +1 -1
- package/dist/src/openai-compat.d.ts +11 -5
- package/dist/src/openai-compat.js +55 -54
- package/dist/src/openai-compat.js.map +1 -1
- package/dist/src/persistent-acp-session.d.ts +7 -0
- package/dist/src/persistent-acp-session.js +26 -2
- package/dist/src/persistent-acp-session.js.map +1 -1
- package/dist/src/persistent-codex-app-session.d.ts +2 -0
- package/dist/src/persistent-codex-app-session.js +18 -9
- package/dist/src/persistent-codex-app-session.js.map +1 -1
- package/dist/src/persistent-codex-session.d.ts +14 -3
- package/dist/src/persistent-codex-session.js +22 -19
- package/dist/src/persistent-codex-session.js.map +1 -1
- package/dist/src/persistent-custom-session.d.ts +9 -0
- package/dist/src/persistent-custom-session.js +41 -21
- package/dist/src/persistent-custom-session.js.map +1 -1
- package/dist/src/persistent-grok-session.js +1 -1
- package/dist/src/persistent-grok-session.js.map +1 -1
- package/dist/src/persistent-session.d.ts +9 -0
- package/dist/src/persistent-session.js +23 -0
- package/dist/src/persistent-session.js.map +1 -1
- package/dist/src/run-ledger.d.ts +3 -3
- package/dist/src/session-manager.d.ts +45 -1
- package/dist/src/session-manager.js +199 -34
- package/dist/src/session-manager.js.map +1 -1
- package/dist/src/types.d.ts +34 -11
- package/dist/src/types.js.map +1 -1
- package/dist/src/ultraapp/build.d.ts +5 -11
- package/dist/src/ultraapp/build.js +5 -11
- package/dist/src/ultraapp/build.js.map +1 -1
- package/dist/src/ultraapp/council-adapter.d.ts +5 -2
- package/dist/src/ultraapp/council-adapter.js +6 -6
- package/dist/src/ultraapp/council-adapter.js.map +1 -1
- package/dist/src/ultraapp/files.d.ts +7 -0
- package/dist/src/ultraapp/files.js +7 -1
- package/dist/src/ultraapp/files.js.map +1 -1
- package/dist/src/ultraapp/manager.d.ts +23 -3
- package/dist/src/ultraapp/manager.js +170 -72
- package/dist/src/ultraapp/manager.js.map +1 -1
- package/dist/src/ultraapp/narrator.js +1 -1
- package/dist/src/ultraapp/narrator.js.map +1 -1
- package/dist/src/ultraapp/versions.d.ts +27 -5
- package/dist/src/ultraapp/versions.js +58 -13
- package/dist/src/ultraapp/versions.js.map +1 -1
- package/openclaw.plugin.json +4 -13
- package/package.json +13 -6
- package/skills/SKILL.md +52 -24
- package/skills/references/acp.md +140 -61
- package/skills/references/autoloop.md +148 -38
- package/skills/references/claude-cli-tracking.md +9 -6
- package/skills/references/cli.md +139 -108
- package/skills/references/council.md +44 -22
- package/skills/references/dashboard.md +37 -21
- package/skills/references/getting-started.md +49 -15
- package/skills/references/inbox.md +13 -8
- package/skills/references/mcp.md +25 -17
- package/skills/references/multi-engine.md +161 -145
- package/skills/references/observability.md +90 -58
- package/skills/references/openai-compat.md +172 -82
- package/skills/references/sessions.md +101 -65
- package/skills/references/tools.md +217 -158
- package/skills/references/ultra.md +7 -7
- package/skills/references/ultraapp.md +101 -56
- package/skills/references/verification.md +112 -56
- package/skills/references/workflow.md +275 -97
- package/assets/banner.jpg +0 -0
|
@@ -5,10 +5,13 @@ fails, when to stop, and how to come back after the process dies.
|
|
|
5
5
|
|
|
6
6
|
## Every mode runs on it
|
|
7
7
|
|
|
8
|
-
`
|
|
9
|
-
`
|
|
10
|
-
|
|
11
|
-
`
|
|
8
|
+
`workflow_start` runs a spec you write or a [built-in template](#built-in-templates);
|
|
9
|
+
`clawo solve`, `clawo fanout` and `clawo goal` start the same templates from a
|
|
10
|
+
terminal. The mode tools run on the same kernel: `council_start`, `fanout_start`,
|
|
11
|
+
`ultraplan_start`, `ultrareview_start` and `autoloop_start` each create a kernel
|
|
12
|
+
run, and so does an UltraApp build. The modes' result shapes — `CouncilSession`,
|
|
13
|
+
`FanoutSession`, `UltraplanResult`, `UltrareviewResult`, `AutoloopState` — are
|
|
14
|
+
_projected_ from the run record, so `workflow_list` lists every one of them.
|
|
12
15
|
|
|
13
16
|
The mode engines (`Council`, `Fanout`, the autoloop planner/coder/reviewer
|
|
14
17
|
dispatcher) do the work; the kernel owns lifecycle, persistence and listing. A
|
|
@@ -24,7 +27,8 @@ Every state transition is checkpointed **before the next step begins**:
|
|
|
24
27
|
run.json the mutable checkpoint, rewritten atomically (tmp + rename)
|
|
25
28
|
events.jsonl append-only audit + SSE source
|
|
26
29
|
incarnation.json which creation of this run id this is, and its fence counter
|
|
27
|
-
lease.json who is executing it right now
|
|
30
|
+
lease.json who is executing it right now (removed when the run ends)
|
|
31
|
+
lease.lock the exclusive lock every write takes, present only while held
|
|
28
32
|
.tx/ a committed batch awaiting application (see below)
|
|
29
33
|
nodes/<id>/ per-node artifacts
|
|
30
34
|
evidence/<id>/ evidence bundles
|
|
@@ -32,8 +36,11 @@ Every state transition is checkpointed **before the next step begins**:
|
|
|
32
36
|
|
|
33
37
|
Splitting the immutable spec from the mutable checkpoint is what makes recovery
|
|
34
38
|
total: if `run.json` is missing or half-written, state is rebuilt by replaying
|
|
35
|
-
`events.jsonl` against `spec.json
|
|
36
|
-
|
|
39
|
+
`events.jsonl` against `spec.json`: the run's state and outcome, and each node's
|
|
40
|
+
state, attempts, visits, output preview and evidence id. Node payloads (`data`),
|
|
41
|
+
cost, consensus votes, the base commit and the test snapshot are only in
|
|
42
|
+
`run.json`. The atomic rewrite makes that path rare; the replay makes it
|
|
43
|
+
survivable anyway.
|
|
37
44
|
|
|
38
45
|
A kernel resumes at a **node boundary**, never mid-node, and at the node the run
|
|
39
46
|
was on — not at the first pending node, since a node on a branch the run never
|
|
@@ -53,8 +60,14 @@ again after the resume.
|
|
|
53
60
|
**This makes node execution at-least-once, not exactly-once.** There is no
|
|
54
61
|
idempotency key and no side-effect commit marker, so a node that wrote files and
|
|
55
62
|
then died before its checkpoint runs again from the top. Workflows whose nodes
|
|
56
|
-
are not safe to repeat need to make them safe.
|
|
57
|
-
|
|
63
|
+
are not safe to repeat need to make them safe.
|
|
64
|
+
|
|
65
|
+
Resume is explicit — `workflow_resume` or `clawo workflow resume` — not automatic.
|
|
66
|
+
Resuming a run that already ended returns it unchanged. Custom-engine configs are
|
|
67
|
+
never written to disk (they can carry credentials), so a run whose agents used one
|
|
68
|
+
is resumed through `workflow_resume` with `agentCustomEngineRefs:
|
|
69
|
+
{ "<agent>": "<REF>" }`, each config read as JSON from `CLAWO_CUSTOM_ENGINE_<REF>`
|
|
70
|
+
(upper-cased) on the orchestrator's host.
|
|
58
71
|
|
|
59
72
|
### One owner, and one way to write
|
|
60
73
|
|
|
@@ -111,10 +124,11 @@ names four things, and all four are checked on every durable write:
|
|
|
111
124
|
each has its own owner id, or both would read the other's claim as their own.
|
|
112
125
|
- **Atomic acquisition.** The check and the write happen inside the lock, so two
|
|
113
126
|
processes cannot both see "free" and both claim the run.
|
|
114
|
-
- **An independent heartbeat.** Renewed on a timer, not only at
|
|
115
|
-
run executing one long node makes no checkpoints, and must not
|
|
116
|
-
for it. On the same host a live pid is the authority and is never
|
|
117
|
-
for going quiet; the heartbeat is the fallback for a holder on
|
|
127
|
+
- **An independent heartbeat.** Renewed every 15 s on a timer, not only at
|
|
128
|
+
checkpoints: a run executing one long node makes no checkpoints, and must not
|
|
129
|
+
look abandoned for it. On the same host a live pid is the authority and is never
|
|
130
|
+
judged stale for going quiet; the heartbeat is the fallback for a holder on
|
|
131
|
+
another machine, whose lease goes stale after 60 s without a renewal.
|
|
118
132
|
- **In-process too.** Starting a run whose id is already live retires the
|
|
119
133
|
previous run first. A lease cannot see inside one process, and two live runs
|
|
120
134
|
sharing an id would write over each other's checkpoints.
|
|
@@ -138,28 +152,70 @@ is unrecoverable and reads back as "not found".
|
|
|
138
152
|
|
|
139
153
|
## Node kinds
|
|
140
154
|
|
|
141
|
-
| Kind
|
|
142
|
-
|
|
|
143
|
-
| `agent`
|
|
144
|
-
| `fanout`
|
|
145
|
-
| `council`
|
|
146
|
-
| `verifier`
|
|
147
|
-
| `human_gate`
|
|
148
|
-
| `router`
|
|
149
|
-
| `subflow`
|
|
150
|
-
| `autoloop`
|
|
151
|
-
| `goal`
|
|
152
|
-
| `
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
| Kind | Does |
|
|
156
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `agent` | One session, one turn. A new session per attempt unless `sessionName` pins one |
|
|
158
|
+
| `fanout` | N agents in parallel on a shared prompt, optional synthesis pass. Succeeds when at least one agent does |
|
|
159
|
+
| `council` | The council engine; its votes are recorded as advisory. Fails only when the council itself errors |
|
|
160
|
+
| `verifier` | Runs an acceptance contract, writes evidence — see [`verification.md`](./verification.md). `"contract": "run"` with no run-level contract passes through and verifies nothing; an inline contract with no recognised check is refused at start |
|
|
161
|
+
| `human_gate` | Parks the run until a person answers; a rejection fails the run |
|
|
162
|
+
| `router` | Picks the next node from declarative conditions; a backwards route is the loop |
|
|
163
|
+
| `subflow` | Runs another workflow as a child run and adopts its verdict, with the tree digest the verdict was taken against; a child that ends `unverified` hands up no verdict |
|
|
164
|
+
| `autoloop` | A long-lived Planner / Coder / Reviewer loop; its executor is injected. On the node's first visit (`visits === 1`) the shared fresh-start boundary applies (dirty-worktree refusal + run-local `tasks/<config.runId>/.gitignore`); resume and later visits skip it. |
|
|
165
|
+
| `goal` | A `/goal` on one Codex `codex-app` thread or Claude Code session, run until the goal finishes — see [below](#goal) |
|
|
166
|
+
| `ultraapp_synth`, `ultraapp_deploy` | UltraApp's synth and deploy stages; their executors are injected too |
|
|
167
|
+
|
|
168
|
+
### Spec and node fields
|
|
169
|
+
|
|
170
|
+
A `WorkflowSpec` is `{ name, nodes, cwd?, contract?, maxNodeVisits? }`. `cwd` is
|
|
171
|
+
the run's working directory (a start call's `cwd` overrides it), `contract` is the
|
|
172
|
+
run-level acceptance contract, and `maxNodeVisits` (default 50) bounds every loop.
|
|
173
|
+
A spec is checked before anything runs: a missing name, no nodes, a missing or
|
|
174
|
+
duplicate node id, a `next`, route or `default` naming a node that does not exist,
|
|
175
|
+
a route with no condition, a `retry.max` that is not a non-negative integer, a
|
|
176
|
+
`timeoutMs` that is not a positive number, and a `maxNodeVisits` that is not a
|
|
177
|
+
positive integer are all refused.
|
|
178
|
+
|
|
179
|
+
With a run-level contract the kernel appends one more node, the verifier
|
|
180
|
+
`__verify`, unless a node already declares `"contract": "run"`.
|
|
181
|
+
|
|
182
|
+
Every node has an `id` and a `kind`, plus these optional fields:
|
|
183
|
+
|
|
184
|
+
| Field | Default | Meaning |
|
|
185
|
+
| ----------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
186
|
+
| `next` | the next node in `nodes` order | Explicit successor; a router ignores it and uses `default`. The run ends after the last node in `nodes` order |
|
|
187
|
+
| `retry` | `{ "max": 0 }` | `max` extra attempts per visit, waiting `backoffMs` (default 1000) × the attempt number between them |
|
|
188
|
+
| `timeoutMs` | 30 min; none for `autoloop` and `goal` nodes | Wall clock for one attempt |
|
|
189
|
+
| `onFailure` | `fail` | `fail` ends the run; `continue` records the failure and moves on |
|
|
190
|
+
|
|
191
|
+
The kind-specific fields:
|
|
192
|
+
|
|
193
|
+
| Kind | Fields |
|
|
194
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
195
|
+
| `agent` | `prompt` (required); `engine`, `model`, `effort`, `acpAgent` (a bundled ACP preset id, for `engine: "acp"`); `cwd` (default: the run's); `sessionName`, to reuse one named session across nodes; `permissionMode` (default `bypassPermissions`); `appendSystemPrompt`, standing instructions for the session |
|
|
196
|
+
| `fanout` | `prompt` (required, the shared task) and `agents` (required); `synthesize`, `synthesisEngine` (default `claude`), `synthesisModel`, `synthesisAcpAgent`, `synthesisPermissionMode` (default `bypassPermissions`); `maxTurnsPerAgent` and `maxBudgetUsd`, per agent; `cwd`; `agentTimeoutMs` |
|
|
197
|
+
| `council` | `task` (required) and `agents` (required); `projectDir` (default: the run's cwd); `maxRounds` (default 15, as for `council_start`); `maxTurnsPerAgent` and `maxBudgetUsd`, per agent; `defaultPermissionMode`; `agentTimeoutMs` |
|
|
198
|
+
| `verifier` | `contract` (required): an inline contract, or `"run"` for the run-level one; `cwd` (default: the run's) |
|
|
199
|
+
| `human_gate` | `prompt` (required) |
|
|
200
|
+
| `router` | `routes` (required): `[{ "when": <condition>, "to": <node id> }]`, first match wins; `default`, taken when nothing matches (omitted: fall through to the next node) |
|
|
201
|
+
| `subflow` | `workflow` (required): an inline `WorkflowSpec`, or the id of an existing run, whose stored spec runs again as a new child run. Template names do not resolve here |
|
|
202
|
+
| `goal` | `objective` (required); `engine`, `cwd`, `model`, `effort`, `sandboxMode`, `launcher`, `threadId`, `tokenBudget`, `capacityRetry`, `stallMs` — see [Goal](#goal) |
|
|
203
|
+
| `autoloop` | `workspace` and `config` (the `autoloop_start` options). Started by `autoloop_start` |
|
|
204
|
+
| `ultraapp_synth`, `ultraapp_deploy` | `appRunId`, `runDir`, `slug`; the deploy stage adds `version` and `codebasePath`. Started by UltraApp builds |
|
|
205
|
+
|
|
206
|
+
An entry in a fan-out's or council's `agents` takes `name` (required), `engine`,
|
|
207
|
+
`model`, `effort`, `acpAgent`, `persona`, `prompt`, `permissionMode` and `baseUrl`;
|
|
208
|
+
a fan-out agent also takes `sandboxMode`, and a council agent `ultracode` and
|
|
209
|
+
`emoji` (shown with its name in its charter and the transcript). An agent
|
|
210
|
+
node and each of these entries use `engine`, `model` and `effort` the same way;
|
|
211
|
+
`effort` accepts `low`, `medium`, `high`, `xhigh`, `max`, `ultra`, or `auto`, and
|
|
212
|
+
omitting any of them preserves the session default. `customEngine` is not a spec
|
|
213
|
+
field — a spec is written to disk and a custom-engine config can carry
|
|
214
|
+
credentials — so custom engines reach a run in memory, through `fanout_start` or
|
|
215
|
+
`council_start`, and are re-supplied on resume as described above.
|
|
216
|
+
|
|
217
|
+
A council agent's `persona` is its role; one with no `persona` uses its `prompt`
|
|
218
|
+
instead. Fan-out agents may also set `persona` for role instructions. When no non-empty
|
|
163
219
|
per-agent `prompt` exists, the executor sends the persona followed by a
|
|
164
220
|
`## Shared task` section containing the node's shared prompt. A non-empty
|
|
165
221
|
per-agent `prompt` remains a complete override and takes precedence over both
|
|
@@ -193,11 +249,18 @@ surface. These closed forms are evaluated and nothing else:
|
|
|
193
249
|
{ "type": "and", "all": [ /* … */ ] }
|
|
194
250
|
```
|
|
195
251
|
|
|
196
|
-
`
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
252
|
+
`node_failed` and `node_succeeded` read the named node's current state (a node
|
|
253
|
+
that has not run is neither). `verified` holds when the named node produced
|
|
254
|
+
evidence and the run's current verdict is `verified`. `visits_lt` counts how many
|
|
255
|
+
times control has entered the node. Routes are tried in order and the first match
|
|
256
|
+
wins.
|
|
257
|
+
|
|
258
|
+
`and` is the only recursive one, and nesting is capped at six deep; an `and` with
|
|
259
|
+
an empty `all` is false. Use it rather than chaining two routers: **a router whose
|
|
260
|
+
routes all miss falls through to the next node** (or to its `default`), so a gate
|
|
261
|
+
that failed to match simply hands control to the router after it, which then
|
|
262
|
+
matches on its own. Chaining reads as AND and behaves as "whatever the last router
|
|
263
|
+
says".
|
|
201
264
|
|
|
202
265
|
`maxNodeVisits` (default 50) bounds every loop as a safety backstop. Budget loops
|
|
203
266
|
with `visits_lt`.
|
|
@@ -209,7 +272,7 @@ with `visits_lt`.
|
|
|
209
272
|
"name": "solve",
|
|
210
273
|
"cwd": "/repo",
|
|
211
274
|
"maxNodeVisits": 6,
|
|
212
|
-
"contract": { "checks": [{ "type": "command", "cmd": "npm", "args": ["test"] }] },
|
|
275
|
+
"contract": { "checks": [{ "spec": { "type": "command", "cmd": "npm", "args": ["test"] } }] },
|
|
213
276
|
"nodes": [
|
|
214
277
|
{
|
|
215
278
|
"id": "triage",
|
|
@@ -243,6 +306,13 @@ with `visits_lt`.
|
|
|
243
306
|
}
|
|
244
307
|
```
|
|
245
308
|
|
|
309
|
+
A contract written inside a spec — the run-level `contract`, or a verifier's
|
|
310
|
+
inline one — is used as written, so each check goes in the `{ "spec": { … } }`
|
|
311
|
+
wrapper shown here, with `id` and `required` beside `spec`. The `contract` argument
|
|
312
|
+
of `workflow_start` and `POST /workflow/new` is normalized first and also accepts
|
|
313
|
+
bare checks (`{ "type": "command", … }`); when given, it replaces the spec's own.
|
|
314
|
+
See [`verification.md`](./verification.md#the-one-rule-about-where-contracts-come-from).
|
|
315
|
+
|
|
246
316
|
When the router sends control back to `implement`, the agent is told what the
|
|
247
317
|
last verification found: each failing required check and the lines of its output
|
|
248
318
|
that say why, framed as data, ahead of the node's own prompt. This applies to any
|
|
@@ -254,32 +324,48 @@ test suite would double the most expensive part of the run to learn nothing new.
|
|
|
254
324
|
|
|
255
325
|
## Built-in templates
|
|
256
326
|
|
|
257
|
-
`workflow_start` accepts `template` instead of `spec
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
327
|
+
`workflow_start` accepts `template` and `task` instead of `spec` (`template`
|
|
328
|
+
defaults to `solve`); `clawo solve`, `clawo fanout` and `clawo goal` build the same
|
|
329
|
+
specs and post them.
|
|
330
|
+
|
|
331
|
+
| Template | Nodes | Arguments |
|
|
332
|
+
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
333
|
+
| `solve` | `triage` (fan-out) → `approve` (human gate, only with `humanGate`) → `implement` (agent) → `review` (fan-out, only with `reviewers`) → `verify` → `repair-gate` (router) | `agents` (the triage scouts; default one agent on the default engine), `implementer` (`engine`, `model`), `reviewers`, `humanGate`, `maxRepairs` (default 3), `contract` |
|
|
334
|
+
| `council` | `council`, then `__verify` when a contract is declared | `agents`, `maxRounds` (default 15), `contract` |
|
|
335
|
+
| `fanout` | `fanout`, then `__verify` when a contract is declared | `agents`, `contract` |
|
|
336
|
+
| `goal` | `goal` → `verify` → `repair-gate` (router); `task` is the objective. See [below](#goal) | `engine` (`codex` default, or `claude`), `model`, `tokenBudget`, `launcher`, `maxRepairs` (default 1), `contract` |
|
|
337
|
+
|
|
338
|
+
`solve` is the shape above: the triage and review fan-outs add a synthesis pass
|
|
339
|
+
when they have two or more agents, `implement` and `review` continue on failure so
|
|
340
|
+
the verifier always judges the tree, `verify` continues so a red result reaches
|
|
341
|
+
`repair-gate`, and `maxNodeVisits` is `maxRepairs + 3`. Through `workflow_start` a `fanout` template synthesizes when it
|
|
342
|
+
has two or more agents; `clawo fanout` synthesizes only with `--synthesize`.
|
|
343
|
+
`clawo goal` also takes `--effort`, `--sandbox`, `--thread`, `--goal-timeout`,
|
|
344
|
+
`--check-timeout` and `--objective-file`.
|
|
345
|
+
|
|
346
|
+
These are ordinary specs, not privileged paths. UltraApp's builds run one more
|
|
347
|
+
built-in, `ultraapp` (`synth` → `build`, a verifier → `deploy`, when there is
|
|
348
|
+
somewhere to deploy to), which only UltraApp starts.
|
|
268
349
|
|
|
269
350
|
## Goal
|
|
270
351
|
|
|
271
|
-
A `goal` node runs one
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
352
|
+
A `goal` node runs one `/goal` on one thread: a Codex `codex-app` thread by
|
|
353
|
+
default, or a Claude Code session with `"engine": "claude"` (see
|
|
354
|
+
[On Claude Code](#on-claude-code)). Codex's app-server runs the goal itself:
|
|
355
|
+
`thread/goal/set` starts its turn loop, which keeps going until the goal is
|
|
356
|
+
`complete`, out of budget, `blocked`, or stopped by a usage limit. The node adds
|
|
357
|
+
what an unattended run needs around that loop.
|
|
275
358
|
|
|
276
359
|
```jsonc
|
|
277
360
|
{
|
|
278
361
|
"id": "goal",
|
|
279
362
|
"kind": "goal",
|
|
280
363
|
"objective": "Reach the goal described in GOAL.md …",
|
|
364
|
+
"engine": "codex", // default; or "claude"
|
|
281
365
|
"cwd": "/work/task/ws",
|
|
282
366
|
"model": "gpt-6.1-sol", // optional; codex's own default otherwise
|
|
367
|
+
"effort": "high", // optional reasoning effort
|
|
368
|
+
"sandboxMode": "workspace-write", // default; or "read-only" / "danger-full-access"
|
|
283
369
|
"launcher": ["/work/goal/sandbox/sandbox.sh", "task", "0 1"], // optional argv prefix
|
|
284
370
|
"threadId": "019c…", // optional: start on this existing thread, replacing its goal
|
|
285
371
|
"tokenBudget": 50000000, // optional; the goal ends as budgetLimited past it
|
|
@@ -290,27 +376,33 @@ by a usage limit. The node adds what an unattended run needs around that loop.
|
|
|
290
376
|
```
|
|
291
377
|
|
|
292
378
|
- **The objective fits Codex's limit.** Codex accepts at most 4,000 characters;
|
|
293
|
-
a longer objective fails the node before a session opens
|
|
294
|
-
a
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
379
|
+
a longer objective fails the node before a session opens (and `clawo goal`
|
|
380
|
+
refuses it before starting a run). Put a long brief in a file in the working
|
|
381
|
+
directory and name the file in the objective.
|
|
382
|
+
- **The thread is checkpointed.** Node data holds `threadId`, the `objective`
|
|
383
|
+
currently set, `status`, `tokensUsed`, `timeUsedSeconds`, `resumes`,
|
|
384
|
+
`lastError`, the agent's last reply (`lastMessage`, at most 4,000 characters)
|
|
385
|
+
and the thread's spend (`costUsd`), updated as the goal moves (progress at most
|
|
386
|
+
every 10 s, status changes at once). `workflow_status` and `clawo workflow show`
|
|
387
|
+
read it while the goal runs. The spend is added to the run's `costUsd`.
|
|
301
388
|
- **Resume reattaches, it does not restart.** A resumed run hands the stored
|
|
302
389
|
thread id to `thread/resume`; a goal that had not finished continues where it
|
|
303
390
|
was. If the resume lands on a different thread the node fails rather than
|
|
304
391
|
starting the objective over. An app-server that exits mid-goal is reopened on
|
|
305
|
-
the same thread the same way.
|
|
392
|
+
the same thread the same way. A goal that had already ended (`complete`,
|
|
393
|
+
`budgetLimited`, `blocked`) when the process died ends the node with that
|
|
394
|
+
result on resume; it is not set again.
|
|
306
395
|
- **Stalls are resumed within a bound.** A goal that is `usageLimited` or
|
|
307
396
|
`paused`, or `active` with no turn running and either no news for `stallMs` or
|
|
308
|
-
|
|
309
|
-
`rateLimitExceeded`,
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
-
|
|
397
|
+
a capacity error the server will not retry (`serverOverloaded`,
|
|
398
|
+
`usageLimitExceeded`, `rateLimitExceeded`, `internalServerError`,
|
|
399
|
+
`responseTooManyFailedAttempts`, or a failed or dropped connection), is set back
|
|
400
|
+
to `active` after a backoff that starts at `capacityRetry.backoffMs` and doubles
|
|
401
|
+
to at most 15 min. Every attempt counts before it is made, reopening a dead
|
|
402
|
+
app-server included; past `capacityRetry.max` the node fails. The count is part
|
|
403
|
+
of the checkpoint, so it carries across process restarts and repair visits.
|
|
404
|
+
- **Repair on the same thread.** Revisited after a red verifier — a red verdict
|
|
405
|
+
other than the one in force when its current objective was set — the node clears
|
|
314
406
|
the finished goal and sets a new objective on the same thread: the failing
|
|
315
407
|
checks first (as diagnostic data, like an agent node's repair brief), then the
|
|
316
408
|
original objective. The agent keeps its context. The original objective is
|
|
@@ -325,14 +417,62 @@ by a usage limit. The node adds what an unattended run needs around that loop.
|
|
|
325
417
|
user input) is answered with an error at once; the sandbox mode is the
|
|
326
418
|
boundary.
|
|
327
419
|
- **Ends.** `complete` succeeds. `budgetLimited` and `blocked` fail with the
|
|
328
|
-
reason. Cancel interrupts the running turn and pauses the goal
|
|
329
|
-
`autoloop`, the node has no default timeout.
|
|
420
|
+
reason. Cancel interrupts the running turn and pauses the goal, and so does a
|
|
421
|
+
`timeoutMs` that runs out. Like `autoloop`, the node has no default timeout.
|
|
422
|
+
|
|
423
|
+
### On Claude Code
|
|
424
|
+
|
|
425
|
+
With `"engine": "claude"` the same node drives Claude Code's `/goal` (CLI
|
|
426
|
+
2.1.139 or later) through `ClaudeGoalSession`. Claude Code has no goal RPCs or
|
|
427
|
+
goal notifications, so the adapter reads the goal from what the CLI does in
|
|
428
|
+
stream-json mode:
|
|
429
|
+
|
|
430
|
+
- `/goal <objective>` sets the goal and starts working. A Stop hook — a separate
|
|
431
|
+
model judging the condition against the session — runs after every turn and
|
|
432
|
+
feeds the turn back while the condition does not hold, all inside one send;
|
|
433
|
+
one `result` arrives when the goal is met, and the goal clears itself.
|
|
434
|
+
- A bare `/goal` answers at once, without a model turn — `Goal active: …` or
|
|
435
|
+
`No goal set.` A failed turn clears the goal too, so a gone goal is not proof:
|
|
436
|
+
the goal is `complete` only when a turn that started with it set ended without
|
|
437
|
+
an error and it is gone after. A goal still set with nothing driving it, a
|
|
438
|
+
failed turn, and a reopened session whose goal is gone are `paused`, which the
|
|
439
|
+
node resumes within its bound. Resuming sends a short "continue" message when
|
|
440
|
+
the goal is still set and sets it again (`/goal <objective>`) when it is not,
|
|
441
|
+
so a goal that was already met costs one turn to confirm.
|
|
442
|
+
- A failed turn is read from its text: usage limits become `usageLimited`,
|
|
443
|
+
overloaded and 5xx errors the matching transient codes, so the node backs off
|
|
444
|
+
and resumes as it does for Codex. A failed login is `blocked` at once.
|
|
445
|
+
- When the Stop hook blocks the agent's stop nine times running, the CLI ends
|
|
446
|
+
the turn anyway and says so; the goal is then `blocked` (the agent keeps
|
|
447
|
+
asking to stop, and continuing only repeats that). The template's verifier
|
|
448
|
+
still judges the tree. Write the objective as a condition the agent can show
|
|
449
|
+
it met from inside the session; one that depends on something after the
|
|
450
|
+
agent stops can never be met.
|
|
451
|
+
- The session id is the thread id. A new session is started with
|
|
452
|
+
`--session-id` so it is known before the goal is set, and the session is kept
|
|
453
|
+
out of the registry with `skipPersistence` (not `noSessionPersistence`, which
|
|
454
|
+
would stop the CLI writing the transcript `--resume` needs). A reopened
|
|
455
|
+
process resumes it with `--resume`; the goal is still set there, so the node
|
|
456
|
+
continues it at once.
|
|
457
|
+
- Steers are written into the running turn, which the CLI folds in. Cancel and
|
|
458
|
+
pause send stream-json's `interrupt` control request; the goal stays set.
|
|
459
|
+
- `tokensUsed` counts the session's input, output and cache-read tokens, and is
|
|
460
|
+
carried across reopened processes. `tokenBudget` counts from when the current
|
|
461
|
+
objective was set; past it the turn is interrupted and the goal ends
|
|
462
|
+
`budgetLimited`. `sandboxMode` does not apply; put the boundary in `launcher`.
|
|
330
463
|
|
|
331
464
|
The `goal` template wraps this in `goal → verify → repair-gate`. The goal node
|
|
332
465
|
continues on failure, so a goal that ran out of budget is still judged. The
|
|
333
|
-
verifier's node timeout is sized from the contract
|
|
334
|
-
|
|
335
|
-
|
|
466
|
+
verifier's node timeout is sized from the contract — the sum of every check's
|
|
467
|
+
timeout (its default when unset), times one plus `fixOnFailureRounds`, plus five
|
|
468
|
+
minutes, and never under 30 min — so a judge that takes hours is not cut off at
|
|
469
|
+
the 30-minute node default. `maxRepairs` defaults to 1, and `maxNodeVisits` is
|
|
470
|
+
`maxRepairs + 3`.
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
clawo goal "Make the failing tests in tests/ pass; the brief is in GOAL.md" \
|
|
474
|
+
-c "npm test" --check-timeout 20 --token-budget 50000000 --max-repairs 2 --wait
|
|
475
|
+
```
|
|
336
476
|
|
|
337
477
|
## The verifier is a terminal barrier
|
|
338
478
|
|
|
@@ -344,7 +484,9 @@ may follow the verifier" would be the wrong rule anyway: what matters is not tha
|
|
|
344
484
|
a node ran, but that the tree moved. So the kernel measures. Each evidence bundle
|
|
345
485
|
records a content digest of the working tree (HEAD, the full `git diff HEAD`,
|
|
346
486
|
and the path and bytes of every untracked file), and when the run ends, if any
|
|
347
|
-
workspace-touching node ran after the verdict, the digest is recomputed
|
|
487
|
+
workspace-touching node ran after the verdict, the digest is recomputed in the
|
|
488
|
+
directory the checks ran in. Every kind except `verifier`, `router` and
|
|
489
|
+
`human_gate` counts as workspace-touching.
|
|
348
490
|
|
|
349
491
|
If it moved, the outcome drops from `verified` to `unverified` with the reason
|
|
350
492
|
recorded on the run. Not `refuted` — no check failed; we simply stopped knowing,
|
|
@@ -361,26 +503,57 @@ so anything the reviewers change is covered by the verdict.
|
|
|
361
503
|
## Completion
|
|
362
504
|
|
|
363
505
|
`RunState` is `pending | running | awaiting_human | verifying | completed |
|
|
364
|
-
failed | cancelled`.
|
|
506
|
+
failed | cancelled`. `verifying` is the state while a verifier node runs; the run
|
|
507
|
+
returns to `running` when it finishes, and ends in one of the last three.
|
|
365
508
|
|
|
366
509
|
`RunOutcome` is `verified | unverified | refuted` and answers a different
|
|
367
|
-
question: not "did it stop" but "did anything check it".
|
|
368
|
-
|
|
369
|
-
|
|
510
|
+
question: not "did it stop" but "did anything check it". The outcome is the last
|
|
511
|
+
verdict the run produced — each verifier visit replaces it, so a repair loop is
|
|
512
|
+
judged by its last check — and a `refuted` run ends `failed`, never `completed`.
|
|
513
|
+
A run with no contract completes as `unverified` — it says it does not know,
|
|
514
|
+
which is not the same as success.
|
|
515
|
+
|
|
516
|
+
The implicit `__verify` is the last node, and a run only ends `completed` after
|
|
517
|
+
its last node has run, so every such path is checked. A spec that places its own
|
|
518
|
+
`"contract": "run"` verifier gets no `__verify`, and must route every path through
|
|
519
|
+
that node: a run whose path skips it ends `completed` / `unverified`, with no
|
|
520
|
+
check run.
|
|
521
|
+
|
|
522
|
+
A run whose spec declares a run-level `contract` cannot complete without having
|
|
523
|
+
run it. The kernel appends a terminal verifier unless the spec places its own
|
|
524
|
+
`contract: "run"` verifier, but a router can still send control past that
|
|
525
|
+
verifier; a run that ends with no verdict from a `contract: "run"` verifier ends
|
|
526
|
+
`failed`, with outcome `unverified` (nothing ran, so nothing was refuted). A
|
|
527
|
+
pass from a verifier with an inline contract does not stand in for the declared
|
|
528
|
+
one.
|
|
370
529
|
|
|
371
530
|
## Control
|
|
372
531
|
|
|
373
|
-
| Action | Tool | HTTP | CLI
|
|
374
|
-
| ------------- | ------------------ | --------------------------------- |
|
|
375
|
-
| Start | `workflow_start` | `POST /workflow/new` | `clawo solve` / `fanout` / `goal`
|
|
376
|
-
| Poll | `workflow_status` | `GET /workflow/<id>/state` | `clawo workflow show <id>`
|
|
377
|
-
| List | `workflow_list` | `GET /workflow/list` | `clawo workflow list`
|
|
378
|
-
| Resume | `workflow_resume` | `POST /workflow/<id>/resume` | `clawo workflow resume <id>`
|
|
379
|
-
| Cancel | `workflow_cancel` | `POST /workflow/<id>/cancel` | `clawo workflow cancel <id>`
|
|
380
|
-
| Steer | `workflow_steer` | `POST /workflow/<id>/steer` | `clawo workflow steer <id> "<text>"`
|
|
381
|
-
| Answer a gate | `workflow_approve` | `POST /workflow/<id>/approve` | `clawo workflow approve <id> [reject]`
|
|
382
|
-
| Evidence | — | `GET /workflow/<id>/evidence` | `clawo verify <id>`
|
|
383
|
-
| Live events | — | `GET /workflow/<id>/events` (SSE) |
|
|
532
|
+
| Action | Tool | HTTP | CLI |
|
|
533
|
+
| ------------- | ------------------ | --------------------------------- | --------------------------------------- |
|
|
534
|
+
| Start | `workflow_start` | `POST /workflow/new` | `clawo solve` / `fanout` / `goal` |
|
|
535
|
+
| Poll | `workflow_status` | `GET /workflow/<id>/state` | `clawo workflow show <id>` |
|
|
536
|
+
| List | `workflow_list` | `GET /workflow/list` | `clawo workflow list` |
|
|
537
|
+
| Resume | `workflow_resume` | `POST /workflow/<id>/resume` | `clawo workflow resume <id>` |
|
|
538
|
+
| Cancel | `workflow_cancel` | `POST /workflow/<id>/cancel` | `clawo workflow cancel <id>` |
|
|
539
|
+
| Steer | `workflow_steer` | `POST /workflow/<id>/steer` | `clawo workflow steer <id> "<text>"` |
|
|
540
|
+
| Answer a gate | `workflow_approve` | `POST /workflow/<id>/approve` | `clawo workflow approve <id> [reject]` |
|
|
541
|
+
| Evidence | — | `GET /workflow/<id>/evidence` | `clawo verify <id>` |
|
|
542
|
+
| Live events | — | `GET /workflow/<id>/events` (SSE) | `--wait` on `solve` / `fanout` / `goal` |
|
|
543
|
+
|
|
544
|
+
`POST /workflow/new` takes `{ spec, cwd?, runId?, contract? }`; the templates are
|
|
545
|
+
built by the tool and by the CLI, which posts the spec it built. `workflow_status`
|
|
546
|
+
returns the run's state, outcome, `outcomeReason`, verdict, current node, evidence
|
|
547
|
+
id, cost, error, node records and consensus votes, plus its last N events with
|
|
548
|
+
`events: N`; `GET /workflow/<id>/state` and `clawo workflow show <id> --json` return
|
|
549
|
+
the whole record. The event stream opens with a `snapshot` (the
|
|
550
|
+
run's `workflow_list` entry), then sends each kernel event as a `workflow-event`. `GET /workflow/list` and
|
|
551
|
+
`workflow_list` filter by `workflow` and `state`, newest first, 25 by default.
|
|
552
|
+
|
|
553
|
+
Cancel and steer reach a run only while it executes in this process. Answering a
|
|
554
|
+
gate does not need that: a run parked by a process that has since exited is
|
|
555
|
+
resumed with the answer attached. On the CLI, `reject`, `false` or `no` as the
|
|
556
|
+
last argument of `clawo workflow approve` rejects; anything else approves.
|
|
384
557
|
|
|
385
558
|
In a running `goal` node, steer text goes into the current turn (`turn/steer`).
|
|
386
559
|
Otherwise, steer text is **prepended** to the next agent node's prompt: an instruction that
|
|
@@ -393,9 +566,12 @@ belong before the task.
|
|
|
393
566
|
in-flight agent turn is owned by the session layer and finishes on its own
|
|
394
567
|
schedule; the kernel does not pretend to kill it. The abandoned attempt keeps
|
|
395
568
|
running, so agent nodes name their session per attempt
|
|
396
|
-
(`<runId>-<nodeId>-a<n
|
|
397
|
-
|
|
398
|
-
|
|
569
|
+
(`<runId>-<nodeId>-a<n>`, unless `sessionName` pins one), and a verifier names
|
|
570
|
+
its fixer sessions per visit and attempt (`<runId>-<nodeId>-v<visit>a<n>-fix-<round>`)
|
|
571
|
+
— otherwise the dying attempt's teardown would stop the retry's session. A
|
|
572
|
+
`goal` node is the exception: it watches its signal, so a timeout interrupts its
|
|
573
|
+
turn. A node that writes to a fixed path outside the run directory can still race
|
|
574
|
+
its own retry; scope such writes per attempt.
|
|
399
575
|
|
|
400
576
|
Because such an attempt can still write, a run about to report `verified`
|
|
401
577
|
waits briefly for outstanding attempts to settle, and reports `unverified`
|
|
@@ -405,9 +581,11 @@ belong before the task.
|
|
|
405
581
|
- Cancel and a node timeout are different things. A timeout is a node failure
|
|
406
582
|
(it still gets its retries and still honours `onFailure`); cancelling ends the
|
|
407
583
|
run.
|
|
408
|
-
- Nothing prunes run directories. Delete
|
|
584
|
+
- Nothing prunes run directories. Delete a run that is not executing yourself
|
|
409
585
|
(`rm -rf ~/.claw-orchestrator/wf/<runId>`), or programmatically with
|
|
410
|
-
`SessionManager.workflowDelete(runId)
|
|
586
|
+
`SessionManager.workflowDelete(runId)`, which claims the run first. No tool or
|
|
587
|
+
CLI command deletes a workflow run; `POST /autoloop/<id>/delete` removes an
|
|
588
|
+
autoloop's.
|
|
411
589
|
|
|
412
590
|
## Related
|
|
413
591
|
|
package/assets/banner.jpg
DELETED
|
Binary file
|