@enderfga/claw-orchestrator 7.14.0 → 7.15.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 +160 -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 +71 -40
- 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 +23 -1
- 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/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 +117 -51
- package/dist/src/index.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 +8 -1
- package/dist/src/kernel/nodes/goal.js +40 -19
- 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 +6 -3
- package/dist/src/kernel/templates/index.js +8 -3
- package/dist/src/kernel/templates/index.js.map +1 -1
- package/dist/src/kernel/types.d.ts +30 -10
- 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/run-ledger.d.ts +3 -3
- package/dist/src/session-manager.d.ts +34 -1
- package/dist/src/session-manager.js +160 -30
- 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 +141 -35
- package/skills/references/claude-cli-tracking.md +8 -6
- package/skills/references/cli.md +122 -93
- 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 +209 -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 +227 -93
- package/assets/banner.jpg +0 -0
package/skills/references/acp.md
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
# Agent Client Protocol (ACP)
|
|
2
2
|
|
|
3
|
-
Claw Orchestrator
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
The model selector spans several engines, and
|
|
9
|
-
orchestration shape: a single agent, a council, a
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
as `kimi acp` or `dsh --profile acp`,
|
|
13
|
-
[
|
|
3
|
+
Claw Orchestrator speaks ACP in both directions:
|
|
4
|
+
|
|
5
|
+
- **As an ACP agent** (`clawo acp`): a coding agent that any ACP client drives over
|
|
6
|
+
JSON-RPC stdio. Zed, JetBrains IDEs, Neovim, Emacs and the VS Code ACP extension
|
|
7
|
+
are clients; so is `dsh`, whose `@deepseek-ai/dsh-subagent-acp` provider spawns an
|
|
8
|
+
arbitrary command as a subagent. The model selector spans several engines, and
|
|
9
|
+
session modes select the orchestration shape: a single agent, a council, a
|
|
10
|
+
planning pass, or a review. Most of this page covers this direction.
|
|
11
|
+
- **As an ACP client** (the `acp` engine): the orchestrator drives an agent that
|
|
12
|
+
serves ACP, such as `kimi acp` or `dsh --profile acp`, as one of its own sessions.
|
|
13
|
+
See [The `acp` engine](#the-acp-engine-driving-other-acp-agents) below.
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
clawo acp # or the dedicated binary:
|
|
17
17
|
clawo-acp
|
|
18
18
|
# without a global install:
|
|
19
|
-
npx -y @enderfga/claw-orchestrator acp
|
|
19
|
+
npx -y -p @enderfga/claw-orchestrator clawo-acp
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Requires Node ≥ 22 and at least one engine CLI (`claude`, `codex`, `agy`,
|
|
23
23
|
`grok`) installed and logged in.
|
|
24
24
|
|
|
25
25
|
Both read the protocol from stdin and write it to stdout. **Stdout carries nothing
|
|
26
|
-
but protocol frames** — all logging goes to stderr.
|
|
26
|
+
but protocol frames** — all logging goes to stderr. The agent exits when the client
|
|
27
|
+
closes its stdin, stopping the engine processes it started.
|
|
27
28
|
|
|
28
29
|
## Protocol version
|
|
29
30
|
|
|
@@ -35,13 +36,25 @@ Implemented: `initialize`, `authenticate`, `session/new`, `session/resume`,
|
|
|
35
36
|
`session/list`, `session/prompt`, `session/set_mode`, `session/set_config_option`,
|
|
36
37
|
`session/cancel`.
|
|
37
38
|
|
|
39
|
+
`session/new` requires an absolute `cwd`.
|
|
40
|
+
|
|
38
41
|
## Configuration
|
|
39
42
|
|
|
40
|
-
| Env var
|
|
41
|
-
|
|
|
42
|
-
| `CLAWO_ACP_MODEL`
|
|
43
|
-
| `CLAWO_ACP_PERMISSION`
|
|
44
|
-
| `
|
|
43
|
+
| Env var | Default | Meaning |
|
|
44
|
+
| ----------------------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
|
|
45
|
+
| `CLAWO_ACP_MODEL` | `claude-sonnet-4-6` | Model (and therefore engine) for a new session |
|
|
46
|
+
| `CLAWO_ACP_PERMISSION` | `acceptEdits` | `plan` \| `acceptEdits` \| `bypassPermissions`; an unrecognised value is ignored with a warning |
|
|
47
|
+
| `OPENCLAW_SERVE_MAX_SESSIONS` | `32` | Maximum number of engine sessions running at once |
|
|
48
|
+
| `OPENCLAW_SERVE_TTL_MINUTES` | `60` | Idle minutes before an engine session is stopped |
|
|
49
|
+
| `OPENCLAW_LOG_LEVEL` | `info` | Set `warn` to quieten the stderr channel |
|
|
50
|
+
|
|
51
|
+
## Prompt content
|
|
52
|
+
|
|
53
|
+
Only `text` and `resource_link` blocks are read from a prompt. `text` blocks are
|
|
54
|
+
concatenated; a `resource_link` becomes a textual `[resource_link name=… uri=…]`
|
|
55
|
+
reference that the engine may open with its own tools; other blocks are dropped
|
|
56
|
+
(`initialize` advertises no image, audio or embedded-context support). A prompt
|
|
57
|
+
that yields no text this way is refused with `invalid params`.
|
|
45
58
|
|
|
46
59
|
## Session modes
|
|
47
60
|
|
|
@@ -56,12 +69,15 @@ slash command, advertised through `available_commands_update` when the session o
|
|
|
56
69
|
|
|
57
70
|
That is the only way to reach a mode in a client that does not draw the picker.
|
|
58
71
|
|
|
59
|
-
| Mode | What a turn does
|
|
60
|
-
| ------------- |
|
|
61
|
-
| `single` | One engine answers. Text streams as `agent_message_chunk`; tool use becomes `tool_call`.
|
|
62
|
-
| `council` | Several engines debate in isolated git worktrees. Each agent becomes a `tool_call` the client can collapse, each round emits a `plan`, and the synthesis arrives as text.
|
|
63
|
-
| `ultraplan` | Long-horizon planning pass. Result arrives as a `plan` update and as text.
|
|
64
|
-
| `ultrareview` | Parallel reviewers sweep the working tree; findings arrive the same way.
|
|
72
|
+
| Mode | What a turn does |
|
|
73
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
74
|
+
| `single` | One engine answers. Text streams as `agent_message_chunk`; tool use becomes `tool_call`. The turn ends with a `usage_update` (context used and size, session cost) when the model's context window is known. |
|
|
75
|
+
| `council` | Several engines debate in isolated git worktrees. Each agent becomes a `tool_call` the client can collapse, each round emits a `plan`, and the synthesis arrives as text. |
|
|
76
|
+
| `ultraplan` | Long-horizon planning pass. Result arrives as a `plan` update and as text. |
|
|
77
|
+
| `ultrareview` | Parallel reviewers sweep the working tree; findings arrive the same way. |
|
|
78
|
+
|
|
79
|
+
A `council`, `ultraplan` or `ultrareview` turn gives up after 30 minutes; a council
|
|
80
|
+
is aborted at that point.
|
|
65
81
|
|
|
66
82
|
### Council
|
|
67
83
|
|
|
@@ -76,28 +92,37 @@ Treat council as a deliberate action, not a default.
|
|
|
76
92
|
Agent deltas are buffered per agent and delivered on that agent's `tool_call_update`
|
|
77
93
|
rather than streamed into the text channel. Several agents speak at once, and a
|
|
78
94
|
consumer that only reads text — `dsh`'s ACP subagent reads nothing else — would
|
|
79
|
-
otherwise receive them interleaved into one unreadable blob.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
otherwise receive them interleaved into one unreadable blob. An agent that fails a
|
|
96
|
+
round has its `tool_call_update` marked `failed` with the error; the council carries
|
|
97
|
+
on with the others.
|
|
98
|
+
|
|
99
|
+
Council works in git at the session `cwd`: a directory that is not a repository, or
|
|
100
|
+
has no commits yet, is initialised and given an initial commit of what it holds,
|
|
101
|
+
and each agent gets its own worktree under `.worktrees/` and a `council/<agent>`
|
|
102
|
+
branch. A run that fails before any agent answers — a too-short task, a `cwd`
|
|
103
|
+
inside the orchestrator's own install directory, a worktree that cannot be
|
|
104
|
+
created — ends the prompt with an `invalid params` error naming the reason. A run
|
|
105
|
+
that fails later reports the reason as text and ends with stop reason `refusal`.
|
|
84
106
|
|
|
85
107
|
**Consensus parks the run rather than finishing it.** The turn ends at the gate, and
|
|
86
|
-
the decision becomes a slash command, advertised through `available_commands_update
|
|
108
|
+
the decision becomes a slash command, advertised through `available_commands_update`.
|
|
109
|
+
Neither command merges anything: the agents merge their own work into the main branch
|
|
110
|
+
during the rounds.
|
|
87
111
|
|
|
88
|
-
| Command
|
|
89
|
-
|
|
|
90
|
-
| `/council_accept`
|
|
91
|
-
| `/council_reject <feedback
|
|
112
|
+
| Command | Effect |
|
|
113
|
+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
114
|
+
| `/council_accept` | Accept the work the agents have merged on the main branch, and clean up: the worktrees, the `council/*` branches, `plan.md` and `reviews/` are removed. |
|
|
115
|
+
| `/council_reject [<feedback>]` | Reject the work: `plan.md` is rewritten with the feedback and committed. Worktrees and branches are kept so the council can retry. Feedback is optional. |
|
|
92
116
|
|
|
93
|
-
Any other prompt while a council is parked is
|
|
94
|
-
over the same worktrees.
|
|
117
|
+
Any other prompt while a council is parked is answered with a reminder of these two
|
|
118
|
+
commands and runs nothing, so a second run cannot start over the same worktrees.
|
|
119
|
+
Either command with no council parked is refused with `invalid params`.
|
|
95
120
|
|
|
96
121
|
### Ultraplan and ultrareview
|
|
97
122
|
|
|
98
123
|
Neither emits events — they are started and then polled — so progress is reported as
|
|
99
|
-
`agent_thought_chunk` heartbeats.
|
|
100
|
-
cancelling one abandons the poll
|
|
124
|
+
`agent_thought_chunk` heartbeats. The agent does not stop either run on a cancel:
|
|
125
|
+
cancelling one abandons the poll, and the run carries on.
|
|
101
126
|
|
|
102
127
|
## Cross-engine model selector
|
|
103
128
|
|
|
@@ -110,20 +135,22 @@ another engine starts a new one, since one engine cannot read another's.
|
|
|
110
135
|
|
|
111
136
|
Other engines are absent for different reasons:
|
|
112
137
|
|
|
113
|
-
- **`gemini`** — the Gemini CLI is sunset and
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
- **`opencode`** — its models are open-ended `provider/model` strings
|
|
118
|
-
|
|
119
|
-
|
|
138
|
+
- **`gemini`** and **`cursor`** — legacy engines (the Gemini CLI is sunset and
|
|
139
|
+
superseded by Antigravity; Cursor is superseded by Grok Build in this lineup).
|
|
140
|
+
Their models are filtered out of the list; a model name that routes to one, set
|
|
141
|
+
through `CLAWO_ACP_MODEL`, still works.
|
|
142
|
+
- **`opencode`** — its models are open-ended `provider/model` strings with nothing
|
|
143
|
+
in the registry to enumerate, and no model name routes to it (a `provider/model`
|
|
144
|
+
string resolves to the Claude Code engine), so the ACP agent does not start
|
|
145
|
+
opencode sessions.
|
|
146
|
+
- **`acp`** — an ACP agent picks its own model.
|
|
120
147
|
|
|
121
148
|
## Generated images
|
|
122
149
|
|
|
123
150
|
When the engine generates an image in a turn (Codex does, when asked for one), the agent names the
|
|
124
151
|
file's path in a text chunk and then sends the image itself as an `image` content block, so a client
|
|
125
|
-
that renders images shows it and one that does not still gets the path. Files over 8 MB
|
|
126
|
-
only.
|
|
152
|
+
that renders images shows it and one that does not still gets the path. Files over 8 MB, and files
|
|
153
|
+
that are not PNG, JPEG, WebP or GIF, are named only.
|
|
127
154
|
|
|
128
155
|
## Resuming sessions
|
|
129
156
|
|
|
@@ -135,7 +162,9 @@ Sessions outlive the agent process. `initialize` advertises
|
|
|
135
162
|
- **`session/resume`** reattaches a session by id after the editor or the agent
|
|
136
163
|
restarts, on the engine and model it was using, and the next prompt continues
|
|
137
164
|
its conversation. The `cwd` must be the one the session was created in — the
|
|
138
|
-
engines keep conversations per directory.
|
|
165
|
+
engines keep conversations per directory. A session resumed into a new agent
|
|
166
|
+
process starts from the default permission setting and mode again
|
|
167
|
+
(`CLAWO_ACP_PERMISSION`, `single`).
|
|
139
168
|
|
|
140
169
|
`session/load` is not offered (`loadSession: false`): it requires replaying the
|
|
141
170
|
earlier conversation to the client, and no engine returns its history in a form
|
|
@@ -143,6 +172,11 @@ that can be replayed. A resumed session therefore opens with an empty view in th
|
|
|
143
172
|
client while the engine still remembers it. Sessions are kept for 7 days after
|
|
144
173
|
their last use.
|
|
145
174
|
|
|
175
|
+
Within a running agent, the session layer stops an engine session that has sat
|
|
176
|
+
idle for `OPENCLAW_SERVE_TTL_MINUTES` (default 60) and keeps its resume handle. The
|
|
177
|
+
next prompt or `session/resume` on that ACP session starts it again on the same
|
|
178
|
+
engine, model and permission, and the conversation continues.
|
|
179
|
+
|
|
146
180
|
## Permission
|
|
147
181
|
|
|
148
182
|
ACP can ask the user mid-turn through `session/request_permission`. This agent does
|
|
@@ -151,20 +185,36 @@ permission is resolved once into engine CLI flags at session start, and
|
|
|
151
185
|
`permissionPromptTool` routes to an MCP tool the caller hosts rather than back
|
|
152
186
|
through the manager. Offering a prompt that cannot be honoured would be worse than
|
|
153
187
|
saying so, so the choice is a session config option instead — which also suits
|
|
154
|
-
`dsh-subagent-acp`, whose default is to auto-reject permission requests.
|
|
188
|
+
`dsh-subagent-acp`, whose default is to auto-reject permission requests. The option
|
|
189
|
+
accepts `plan`, `acceptEdits` and `bypassPermissions`; any other value is refused
|
|
190
|
+
with `invalid params` and the session is left as it was. Changing it restarts the
|
|
191
|
+
underlying session on the same engine, which continues the conversation.
|
|
155
192
|
|
|
156
193
|
## Cancellation
|
|
157
194
|
|
|
158
|
-
`session/cancel`
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
195
|
+
What `session/cancel` does depends on the mode:
|
|
196
|
+
|
|
197
|
+
- **`single`** — the in-flight prompt settles as `cancelled` immediately. The
|
|
198
|
+
underlying session is torn down and recreated, and the recreated session
|
|
199
|
+
resumes the engine's conversation from the saved resume handle. The handle
|
|
200
|
+
exists only once the engine has reported it, so a cancel in the very first turn
|
|
201
|
+
of a session can leave the next prompt on a fresh conversation.
|
|
202
|
+
- **`council`** — the council run is aborted, and the turn ends with the
|
|
203
|
+
council's status message and stop reason `end_turn` or `refusal`, not
|
|
204
|
+
`cancelled`.
|
|
205
|
+
- **`ultraplan`** / **`ultrareview`** — the poll, which checks every 3 seconds,
|
|
206
|
+
stops at its next check and the prompt settles as `cancelled`; the run carries
|
|
207
|
+
on.
|
|
208
|
+
|
|
209
|
+
A cancel with **nothing in flight does nothing**.
|
|
210
|
+
|
|
211
|
+
In council mode a cancel aborts the run, which removes its worktrees, and the prompt
|
|
212
|
+
ends with `cancelled`. In ultraplan and ultrareview mode it ends the prompt with
|
|
213
|
+
`cancelled` and abandons the poll.
|
|
164
214
|
|
|
165
215
|
The session layer has **no mid-turn cancel** — `stopSession()` is the only lever and
|
|
166
|
-
it destroys the session rather than pausing the turn. So the ACP
|
|
167
|
-
promptly while the engine subprocess may take a moment longer to die, and any
|
|
216
|
+
it destroys the session rather than pausing the turn. So in `single` mode the ACP
|
|
217
|
+
turn returns promptly while the engine subprocess may take a moment longer to die, and any
|
|
168
218
|
partial work in that turn is lost.
|
|
169
219
|
|
|
170
220
|
## Use from `dsh`
|
|
@@ -178,20 +228,49 @@ needed:
|
|
|
178
228
|
config:
|
|
179
229
|
providerName: clawo
|
|
180
230
|
command: npx
|
|
181
|
-
args: ['-y', '@enderfga/claw-orchestrator', 'acp']
|
|
231
|
+
args: ['-y', '-p', '@enderfga/claw-orchestrator', 'clawo-acp']
|
|
182
232
|
permission: reject
|
|
183
233
|
```
|
|
184
234
|
|
|
235
|
+
With a global install, `command: clawo-acp` and `args: []` does the same.
|
|
236
|
+
|
|
185
237
|
That provider collects **only `agent_message_chunk` text** into its result — it
|
|
186
238
|
ignores tool calls and other updates — and runs a fresh subprocess per run with no
|
|
187
239
|
parent context. The adapter therefore keeps the text stream self-sufficient: an
|
|
188
240
|
engine that never streams still gets its whole answer emitted as one final chunk.
|
|
189
241
|
|
|
242
|
+
## The `acp` engine: driving other ACP agents
|
|
243
|
+
|
|
244
|
+
In the other direction, `engine: 'acp'` starts an ACP agent as a session and drives
|
|
245
|
+
it as a client. One wrapper covers every such agent, with no per-agent parsing:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
clawo engines # lists the bundled presets
|
|
249
|
+
clawo session-start kimi -e acp --acp-agent kimi-code -d ./project
|
|
250
|
+
clawo session-send kimi "Fix the failing test in tests/test_io.py"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
- `acpAgent` names the agent: a bundled preset id (`kimi-code`, `dsh`), or, from a
|
|
254
|
+
local caller only, an inline config (`name`, `command`, and optionally
|
|
255
|
+
`commandEnv`, `args`, `env`, `contextWindow`, `pricing`).
|
|
256
|
+
- One agent process per session; each send is one `session/prompt` on one ACP
|
|
257
|
+
session. The client advertises no `fs` or `terminal` capability, so the agent
|
|
258
|
+
reads, writes and runs commands with its own tools.
|
|
259
|
+
- `session/request_permission` is answered at once from the session's permission
|
|
260
|
+
mode: `bypassPermissions`, `acceptEdits`, `auto` and `dontAsk` allow, any other
|
|
261
|
+
mode (such as `plan`) rejects.
|
|
262
|
+
- A resumed session reattaches the agent's own session id with `session/resume`, or
|
|
263
|
+
`session/load` when that is all the agent offers.
|
|
264
|
+
|
|
265
|
+
The full reference — turn outcomes, cost, each preset's environment — is in
|
|
266
|
+
[multi-engine.md](./multi-engine.md#acp-agents-engine-acp).
|
|
267
|
+
|
|
190
268
|
## Development notes
|
|
191
269
|
|
|
192
|
-
Unit tests cover the translation helpers
|
|
193
|
-
|
|
194
|
-
|
|
270
|
+
Unit tests cover the translation helpers, the config-option shapes, and session
|
|
271
|
+
and council behaviour over an in-process client connection
|
|
272
|
+
(`src/__tests__/acp-server.test.ts`, `src/__tests__/acp-session-resume.test.ts`).
|
|
273
|
+
They cannot catch a corrupted frame stream, so also drive the real binary:
|
|
195
274
|
|
|
196
275
|
```bash
|
|
197
276
|
npm run build
|
|
@@ -28,12 +28,14 @@ This page is the operator reference.
|
|
|
28
28
|
| **Coder** | claude / sonnet | workspace | code changes, eval execution |
|
|
29
29
|
| **Reviewer** | claude / sonnet | `<workspace>/tasks/<run_id>/reviewer_sandbox/` | distrust audit; advance / hold / rollback |
|
|
30
30
|
|
|
31
|
-
Each role can use any built-in engine
|
|
32
|
-
local caller (custom engines name an executable, so the HTTP API
|
|
33
|
-
them — see [tools.md](./tools.md)).
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
Each role can use any built-in engine except `acp`, or a `custom` engine config
|
|
32
|
+
supplied by a local caller (custom engines name an executable, so the HTTP API
|
|
33
|
+
does not accept them — see [tools.md](./tools.md)). `acp` is refused for every
|
|
34
|
+
role: a role is configured by engine name alone and has no field for the ACP
|
|
35
|
+
agent to start. If a non-Claude role omits `model`, that CLI uses its own default
|
|
36
|
+
model rather than receiving the Claude `opus` / `sonnet` defaults. Claude receives
|
|
37
|
+
each role's instructions as its system prompt; every other engine receives them
|
|
38
|
+
in-band, at the top of each message to that role.
|
|
37
39
|
|
|
38
40
|
Each role also accepts an optional fixed reasoning effort at run start:
|
|
39
41
|
`planner_effort`, `coder_effort`, and `reviewer_effort`. Accepted values are
|
|
@@ -46,14 +48,15 @@ their existing behavior.
|
|
|
46
48
|
Engines without native multi-turn conversation (one-shot custom engines) spawn a
|
|
47
49
|
fresh process per send with nothing to resume, so the dispatcher replays that
|
|
48
50
|
role's transcript in-band as a `<conversation_history>` block, oldest turns dropped
|
|
49
|
-
past a character budget. Claude, Codex
|
|
50
|
-
resume their own conversation
|
|
51
|
+
past a character budget. Claude, Codex (`codex` and `codex-app`), Antigravity,
|
|
52
|
+
Grok and OpenCode each resume their own conversation and get no replay — see
|
|
51
53
|
`engineHasNativeConversation` in `types.ts`, which is the single source of truth for
|
|
52
54
|
this and is checked with a two-turn recall test per engine.
|
|
53
55
|
|
|
54
56
|
The Planner runs read-only so strategy cannot turn into source edits, and that is
|
|
55
57
|
enforced by the engine rather than requested politely: Claude uses plan mode,
|
|
56
|
-
Antigravity uses its plan mode, and
|
|
58
|
+
Antigravity uses its plan mode, Codex and Grok run in their own read-only
|
|
59
|
+
sandbox, and OpenCode gets a generated
|
|
57
60
|
`clawo-readonly` agent that denies `edit`/`bash`/`external_directory` (its built-in
|
|
58
61
|
`plan` agent is a user-overridable preset that denies neither, so a "read-only"
|
|
59
62
|
session could otherwise still author files through a shell heredoc). A custom
|
|
@@ -90,7 +93,9 @@ failing with "Session not found".
|
|
|
90
93
|
## UX flow
|
|
91
94
|
|
|
92
95
|
```
|
|
93
|
-
1. autoloop_start { run_id, workspace } → Planner session ready
|
|
96
|
+
1. autoloop_start { run_id, workspace } → Planner session ready
|
|
97
|
+
(a Git workspace must be clean,
|
|
98
|
+
see below)
|
|
94
99
|
2. autoloop_chat { run_id, "<your goal>" } → Planner reads workspace,
|
|
95
100
|
drafts plan.md + goal.json,
|
|
96
101
|
asks "ready to spawn?"
|
|
@@ -105,6 +110,38 @@ failing with "Session not found".
|
|
|
105
110
|
Stopping at a target or at `max_iters` from `goal.json` is the Planner's
|
|
106
111
|
decision: the runtime does not evaluate `goal.json`.
|
|
107
112
|
|
|
113
|
+
### Starting on a Git workspace
|
|
114
|
+
|
|
115
|
+
On a Git workspace, `autoloop_start` refuses with _Autoloop workspace has
|
|
116
|
+
uncommitted changes; commit or stash them before starting_ when the worktree has
|
|
117
|
+
any tracked, staged, untracked or submodule change. Repository configuration
|
|
118
|
+
cannot hide a change from this check (`status.showUntrackedFiles=no` included),
|
|
119
|
+
and a status listing too large to buffer, or any unexpected `git status` failure,
|
|
120
|
+
also refuses. The one exception is an earlier run's ledger: untracked (`??`) or
|
|
121
|
+
index-clean modified (` M`) files under `tasks/<id>/` are ignored when
|
|
122
|
+
`tasks/<id>/goal.json` is a regular file, so a second run can start after a
|
|
123
|
+
committed ledger was appended to. This also holds when the workspace is a
|
|
124
|
+
subdirectory of the repository; changes outside the workspace still refuse.
|
|
125
|
+
Ignored paths and non-Git workspaces start normally.
|
|
126
|
+
|
|
127
|
+
Once that check passes, start keeps the run's ledger out of Git before writing
|
|
128
|
+
anything else to it:
|
|
129
|
+
|
|
130
|
+
- It creates `tasks/<runId>/.gitignore` containing `*` as the ledger's first
|
|
131
|
+
file. An existing file whose content is exactly `*`, `*\n` or `*\r\n` is
|
|
132
|
+
accepted without being overwritten.
|
|
133
|
+
- It proves with `git check-ignore` that the ledger is ignored.
|
|
134
|
+
- It refuses with the fixed error _Autoloop could not isolate the run ledger
|
|
135
|
+
from Git_ when the ledger is tracked, the ignore file holds anything else, or
|
|
136
|
+
`tasks` / `tasks/<runId>` is not a real directory inside the workspace (for
|
|
137
|
+
example a committed `tasks` symlink).
|
|
138
|
+
|
|
139
|
+
Nothing is deleted on failure: a failed proof or a later start failure leaves the
|
|
140
|
+
run's directories and `.gitignore` in place. Start never writes
|
|
141
|
+
`.git/info/exclude` or any repository-wide ignore. The same boundary runs on the
|
|
142
|
+
first boot of a workflow `autoloop` node (`visits === 1`); `autoloopResume` /
|
|
143
|
+
`workflowResume` and later visits skip it.
|
|
144
|
+
|
|
108
145
|
## Timeout hierarchy and recoverable sends
|
|
109
146
|
|
|
110
147
|
Autoloop has three independent start-time controls. Their bounds are inclusive,
|
|
@@ -261,7 +298,8 @@ for the `CustomEngineConfig` shape.
|
|
|
261
298
|
| on_decision_needed | decision / both |
|
|
262
299
|
|
|
263
300
|
The runtime fires five of these itself: `on_stall_30min` (30 minutes without
|
|
264
|
-
activity), `on_metric_regression_2
|
|
301
|
+
activity), `on_metric_regression_2` (two `rollback` verdicts in a row),
|
|
302
|
+
`on_reviewer_reject_2` (two `hold` or `rollback` verdicts in a row),
|
|
265
303
|
`on_phase_error`, and `on_target_hit` (only when an acceptance contract passes,
|
|
266
304
|
see [Acceptance contracts](#acceptance-contracts)). `on_start`,
|
|
267
305
|
`on_iter_done_ok` and `on_decision_needed` are policy entries for the Planner's
|
|
@@ -302,7 +340,8 @@ CLIs expose no such command. The threshold is still meaningful there because
|
|
|
302
340
|
`contextPercent` tracks real occupancy, but crossing it cannot free space:
|
|
303
341
|
the session emits a single warning on its log channel the first time compaction
|
|
304
342
|
is requested, then the thread keeps growing until the CLI refuses the request.
|
|
305
|
-
Treat that warning as the signal to start a fresh session.
|
|
343
|
+
Treat that warning as the signal to start a fresh session. `codex-app` does
|
|
344
|
+
compact, through the app-server's own thread compaction.
|
|
306
345
|
|
|
307
346
|
## Phase-error circuit
|
|
308
347
|
|
|
@@ -344,6 +383,8 @@ decisions:
|
|
|
344
383
|
| `update_push_policy` | Planner mutates the policy |
|
|
345
384
|
| `policy_silence_blocked` | Planner tried to silence a critical channel |
|
|
346
385
|
| `phase_error` | Surfaced from dispatcher to runner |
|
|
386
|
+
| `send_timeout` | A role's send hit `send_timeout_ms`; run paused |
|
|
387
|
+
| `timeout_migration` | A resume raised `send_timeout_ms` (see above) |
|
|
347
388
|
| `terminate` | Run ends (planner reason or `phase_error_circuit`) |
|
|
348
389
|
|
|
349
390
|
JSONL, one entry per line, ts-prefixed.
|
|
@@ -353,19 +394,18 @@ JSONL, one entry per line, ts-prefixed.
|
|
|
353
394
|
```
|
|
354
395
|
<workspace>/tasks/<run_id>/
|
|
355
396
|
├── .gitignore # run-local `*`; first file on Git start; keeps the ledger out of status/add
|
|
356
|
-
├── plan.md # Planner-authored (workspace-root copies are what iters commit)
|
|
357
|
-
├── goal.json # Planner-authored (workspace-root copies are what iters commit)
|
|
358
397
|
├── push_log.jsonl # every notify_user attempt + channel used
|
|
359
398
|
├── decisions.jsonl # runner / dispatcher audit trail (see above)
|
|
360
|
-
├── chat.jsonl # Planner
|
|
399
|
+
├── chat.jsonl # chat for all three panes (user, Planner, Coder, Reviewer), replayed by /chat_history
|
|
400
|
+
├── coder_log.jsonl # one row per Coder `coder_log` call: { ts, iter, message }
|
|
361
401
|
├── evidence/iter-<n>/ # acceptance-contract bundle, when a contract is configured
|
|
362
402
|
├── reviewer_sandbox/ # Reviewer cwd; restaged per iter
|
|
363
|
-
│ ├── plan.md # copy
|
|
364
|
-
│ ├── goal.json # copy
|
|
403
|
+
│ ├── plan.md # copy of the workspace-root file
|
|
404
|
+
│ ├── goal.json # copy of the workspace-root file
|
|
365
405
|
│ ├── iter-N/ # this iter's directive + diff + eval
|
|
366
406
|
│ ├── prior_verdict.json
|
|
367
407
|
│ ├── reviewer_memory.md # persistent (frozen-injected at session start)
|
|
368
|
-
│ └── reviewer_log.jsonl # persistent
|
|
408
|
+
│ └── reviewer_log.jsonl # persistent; one row per Reviewer `reviewer_log` call: { ts, iter, message }
|
|
369
409
|
└── iter/<n>/
|
|
370
410
|
├── directive.json # Planner → Coder (schema_version: 1)
|
|
371
411
|
├── eval_output.json # what Coder reported (schema_version: 1)
|
|
@@ -374,9 +414,10 @@ JSONL, one entry per line, ts-prefixed.
|
|
|
374
414
|
└── coder_summary.txt
|
|
375
415
|
```
|
|
376
416
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
417
|
+
`plan.md` and `goal.json` live at the workspace root, where `write_plan` /
|
|
418
|
+
`write_goal` put them; the Reviewer gets copies in its sandbox. On a Git
|
|
419
|
+
workspace the ledger is kept out of Git by its own `.gitignore` (see
|
|
420
|
+
[Starting on a Git workspace](#starting-on-a-git-workspace)); it does not rely on
|
|
380
421
|
`.git/info/exclude` or a root `.gitignore` entry for `tasks/`.
|
|
381
422
|
|
|
382
423
|
The orchestrator git-commits each iter automatically. Coder must NOT call
|
|
@@ -393,18 +434,83 @@ Every JSON artifact in the ledger carries a `schema_version` field (currently
|
|
|
393
434
|
|
|
394
435
|
## Backend HTTP / SSE
|
|
395
436
|
|
|
396
|
-
| Endpoint | Returns
|
|
397
|
-
| ---------------------------------------- |
|
|
398
|
-
| `GET /autoloop/list` | `{ ok, runs: AutoloopState[] }`
|
|
399
|
-
| `POST /autoloop/new` | `{ ok, run_id, planner_session }` —
|
|
400
|
-
| `GET /autoloop/<id>/state` | `{ ok, state: AutoloopState, live }` — `live` is `true` only when the run is running in this process. For a run that is not live here, `state` is the last state recorded in the run store, so historical runs open with their real iteration count. 404 when there is no such run.
|
|
401
|
-
| `GET /autoloop/<id>/push_log` | `{ ok, entries: PushLogEntry[] }` — served from the ledger via `autoloopStatus`, so historical runs work the same as live ones.
|
|
402
|
-
| `GET /autoloop/<id>/chat_history` | `{ ok, entries: ChatEntry[] }` — replays `<ledger>/chat.jsonl
|
|
403
|
-
| `GET /autoloop/<id>/events` | SSE
|
|
404
|
-
| `POST /autoloop/<id>/chat` | **202** `{ ok, queued: true }` —
|
|
405
|
-
| `GET /autoloop/<id>/resume-requirements` | `{ ok, runId, rolesNeedingCustomEngine }` — the roles whose engine was `custom`, so a caller knows which secret references a resume needs. Role names only; nothing sensitive. 404 when there is no such run.
|
|
406
|
-
| `POST /autoloop/<id>/resume` | `{ ok, state }` —
|
|
407
|
-
| `POST /autoloop/<id>/delete` | `{ ok }` — stops the loop if still live, deletes the run record from the run store, and purges the role sessions' persisted resume ids so the run cannot be resumed. The ledger directory under `<workspace>/tasks/<run_id>/` is kept on disk. 404 when there is no such run.
|
|
437
|
+
| Endpoint | Returns |
|
|
438
|
+
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
439
|
+
| `GET /autoloop/list` | `{ ok, runs: AutoloopState[] }` |
|
|
440
|
+
| `POST /autoloop/new` | `{ ok, run_id, planner_session }` — see [Starting a run](#starting-a-run). 409 when the workspace refuses the start (see [Starting on a Git workspace](#starting-on-a-git-workspace)). |
|
|
441
|
+
| `GET /autoloop/<id>/state` | `{ ok, state: AutoloopState, live }` — `live` is `true` only when the run is running in this process. For a run that is not live here, `state` is the last state recorded in the run store, so historical runs open with their real iteration count. 404 when there is no such run. |
|
|
442
|
+
| `GET /autoloop/<id>/push_log` | `{ ok, entries: PushLogEntry[] }` — served from the ledger via `autoloopStatus`, so historical runs work the same as live ones. |
|
|
443
|
+
| `GET /autoloop/<id>/chat_history` | `{ ok, entries: ChatEntry[] }` — replays `<ledger>/chat.jsonl`, so the dashboard's panes survive a page refresh, another process, or re-opening a terminated run. `[]` when the file doesn't exist. |
|
|
444
|
+
| `GET /autoloop/<id>/events` | SSE — see [Event stream](#event-stream). |
|
|
445
|
+
| `POST /autoloop/<id>/chat` | **202** `{ ok, queued: true }` — see [Chat over HTTP](#chat-over-http). |
|
|
446
|
+
| `GET /autoloop/<id>/resume-requirements` | `{ ok, runId, rolesNeedingCustomEngine }` — the roles whose engine was `custom`, so a caller knows which secret references a resume needs. Role names only; nothing sensitive. 404 when there is no such run. |
|
|
447
|
+
| `POST /autoloop/<id>/resume` | `{ ok, state }` — see [Resume over HTTP](#resume-over-http). 404 when there is no such run. |
|
|
448
|
+
| `POST /autoloop/<id>/delete` | `{ ok }` — stops the loop if still live, deletes the run record from the run store, and purges the role sessions' persisted resume ids so the run cannot be resumed. The ledger directory under `<workspace>/tasks/<run_id>/` is kept on disk. 404 when there is no such run. |
|
|
449
|
+
|
|
450
|
+
### Starting a run
|
|
451
|
+
|
|
452
|
+
`POST /autoloop/new` takes `{ workspace, run_id?, planner_engine?, planner_model?,
|
|
453
|
+
planner_effort?, coder_engine?, coder_model?, coder_effort?, reviewer_engine?,
|
|
454
|
+
reviewer_model?, reviewer_effort?, send_timeout_ms?, activity_lease_ms?,
|
|
455
|
+
autoloop_hard_timeout_ms? }`. A `run_id` that is missing or does not match
|
|
456
|
+
`^[a-zA-Z0-9_-]{1,64}$` is replaced by a generated `auto-<timestamp>-<hex>`.
|
|
457
|
+
Timeout fields use the defaults and inclusive bounds documented above; malformed
|
|
458
|
+
or out-of-range values return 400 before a run starts. Custom-engine configs are
|
|
459
|
+
not accepted over HTTP, so a `custom` role can only be started by a local caller.
|
|
460
|
+
|
|
461
|
+
### Event stream
|
|
462
|
+
|
|
463
|
+
`GET /autoloop/<id>/events` emits `snapshot` / `message` / `state` / `push` /
|
|
464
|
+
`iter_done` / `planner_reply` / `planner_error` / `coder_reply` /
|
|
465
|
+
`reviewer_reply` / `compact` / `terminated`.
|
|
466
|
+
|
|
467
|
+
For a run that is NOT in this process's memory (terminated, or live in another
|
|
468
|
+
process), the endpoint emits a single-shot `snapshot` + `terminated` then closes —
|
|
469
|
+
the dashboard's existing handlers render history without hanging. A run still in
|
|
470
|
+
memory that has already reached `terminated` or `crashed` gets the same
|
|
471
|
+
single-shot pair instead of an open stream that would never receive another
|
|
472
|
+
event. Every such stream sets `retry: 864000000`, so an `EventSource` does not
|
|
473
|
+
keep reconnecting to a stream that can only end again.
|
|
474
|
+
|
|
475
|
+
### Chat over HTTP
|
|
476
|
+
|
|
477
|
+
`POST /autoloop/<id>/chat` takes `{ text }` and is fire-and-forget: the
|
|
478
|
+
Planner's reply streams back on `/events` as a `planner_reply` event (or
|
|
479
|
+
`planner_error` on failure). The HTTP response intentionally does not wait for
|
|
480
|
+
it, because first-contact replies routinely exceed reverse-proxy idle limits
|
|
481
|
+
(e.g. Cloudflare Tunnel cuts at ~100s → 524). The MCP `autoloop_chat` tool keeps
|
|
482
|
+
the synchronous await-and-return-reply semantics (it runs in-process).
|
|
483
|
+
|
|
484
|
+
400 on empty text. 404 when the run is not in this process's memory: if the
|
|
485
|
+
store still holds it, the error says so and names `POST /autoloop/<id>/resume`;
|
|
486
|
+
an unknown or malformed id is plain `not found`.
|
|
487
|
+
|
|
488
|
+
### Resume over HTTP
|
|
489
|
+
|
|
490
|
+
`POST /autoloop/<id>/resume` returns `{ ok, state }`.
|
|
491
|
+
|
|
492
|
+
- **A live ordinary or activity-lease pause** (`pending_dispatch` null): an empty
|
|
493
|
+
timeout body continues that run. The response returns once status is
|
|
494
|
+
`planning` or `running` again, the lease re-arms, the hard deadline stays put,
|
|
495
|
+
and parked Planner input is then delivered once, in order.
|
|
496
|
+
- **A live `awaiting_resume` pause** is unchanged unless the body increases the
|
|
497
|
+
send timeout.
|
|
498
|
+
- **Otherwise** the role engine/model choices are restored from the run's spec
|
|
499
|
+
and the dispatcher and runner are re-created. Existing engine-specific
|
|
500
|
+
conversation resume is reused where supported; `chat.jsonl` remains the visual
|
|
501
|
+
history fallback.
|
|
502
|
+
|
|
503
|
+
For recoverable send timeouts, body fields `send_timeout_ms` and
|
|
504
|
+
`pending_dispatch_id` apply the increase-only migration described above;
|
|
505
|
+
`allow_decrease`, lease overrides, and hard-cap overrides are rejected.
|
|
506
|
+
|
|
507
|
+
A custom-engine config is never persisted and never accepted over HTTP, so a role
|
|
508
|
+
using `custom` is re-supplied by **reference**: `plannerCustomEngineRef` /
|
|
509
|
+
`coderCustomEngineRef` / `reviewerCustomEngineRef` name an environment variable
|
|
510
|
+
`CLAWO_CUSTOM_ENGINE_<NAME>` on the orchestrator host, whose value is the JSON
|
|
511
|
+
custom-engine config; the server reads and resolves it. The name is not
|
|
512
|
+
sensitive, the value never crosses the wire, and an unknown name is an error
|
|
513
|
+
rather than a silent start without credentials.
|
|
408
514
|
|
|
409
515
|
The dashboard's 3-pane autoloop view (`/dashboard`) consumes these endpoints:
|
|
410
516
|
|
|
@@ -519,8 +625,8 @@ retries it.
|
|
|
519
625
|
- **One-way push.** Replies to a push are not routed back to the Planner;
|
|
520
626
|
answer with `autoloop_chat`.
|
|
521
627
|
- **No fork / population mode.** Single linear iter trajectory per run.
|
|
522
|
-
- **Cross-run knowledge isolated.** Each run's `reviewer_memory.md`
|
|
523
|
-
|
|
628
|
+
- **Cross-run knowledge isolated.** Each run's `reviewer_memory.md` lives in
|
|
629
|
+
that run's ledger; there is no shared store.
|
|
524
630
|
- **No cost cap.** `maxBudgetUsd` is not exposed for autoloop. Wall-clock time
|
|
525
631
|
is bounded by `autoloop_hard_timeout_ms` (default 24 h); the iteration count
|
|
526
632
|
is bounded only by the Planner honouring `max_iters` in `goal.json`.
|