clearbox 0.2.2__tar.gz → 0.3.0__tar.gz
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.
- {clearbox-0.2.2 → clearbox-0.3.0}/PKG-INFO +125 -61
- {clearbox-0.2.2 → clearbox-0.3.0}/README.md +124 -60
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/__main__.py +10 -1
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/app.py +138 -14
- clearbox-0.3.0/clearbox/diffs.py +140 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/engine.py +101 -475
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/base.py +3 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/claude_sdk.py +2 -4
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/openai_compat.py +8 -9
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/probe.py +2 -3
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/registry.py +7 -9
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/mcp_bridge.py +19 -48
- clearbox-0.3.0/clearbox/mcp_tools.py +868 -0
- clearbox-0.3.0/clearbox/prompts.py +191 -0
- clearbox-0.3.0/clearbox/reviews.py +240 -0
- clearbox-0.3.0/clearbox/static/agents.js +298 -0
- clearbox-0.3.0/clearbox/static/app.js +449 -0
- clearbox-0.3.0/clearbox/static/core.js +166 -0
- clearbox-0.3.0/clearbox/static/drawer.js +561 -0
- clearbox-0.3.0/clearbox/static/graph.js +345 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/static/index.html +2 -1
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/store.py +33 -7
- clearbox-0.3.0/examples/demo_plan.py +85 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/examples/file_conflict_plan.py +15 -18
- {clearbox-0.2.2 → clearbox-0.3.0}/examples/mixed_plan.py +21 -8
- {clearbox-0.2.2 → clearbox-0.3.0}/pyproject.toml +11 -1
- clearbox-0.3.0/tests/test_agent_scope.py +204 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/tests/test_agents_api.py +3 -3
- {clearbox-0.2.2 → clearbox-0.3.0}/tests/test_file_conflicts.py +2 -2
- {clearbox-0.2.2 → clearbox-0.3.0}/tests/test_flow.py +109 -26
- clearbox-0.3.0/tests/test_host_check.py +85 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/tests/test_mixed_run.py +3 -3
- clearbox-0.3.0/tests/test_review_format.py +59 -0
- clearbox-0.3.0/tests/test_rework.py +177 -0
- clearbox-0.3.0/tests/unit/conftest.py +114 -0
- clearbox-0.3.0/tests/unit/test_scheduler.py +148 -0
- clearbox-0.3.0/tests/unit/test_store.py +41 -0
- clearbox-0.2.2/clearbox/agents.py +0 -12
- clearbox-0.2.2/clearbox/mcp_tools.py +0 -976
- clearbox-0.2.2/clearbox/static/app.js +0 -1730
- clearbox-0.2.2/examples/demo_plan.py +0 -71
- clearbox-0.2.2/tests/test_rework.py +0 -97
- {clearbox-0.2.2 → clearbox-0.3.0}/.gitignore +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/LICENSE +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/__init__.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/bootstrap.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/__init__.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/mock.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/openai_check.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/harness/tools.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/clearbox/static/style.css +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/tests/fake_openai.py +0 -0
- {clearbox-0.2.2 → clearbox-0.3.0}/tests/test_openai_harness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: clearbox
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Observable, reviewable agent work: DAG plans over MCP, executed node by node and watched live
|
|
5
5
|
Project-URL: Homepage, https://github.com/KaiHabermann/Clearbox
|
|
6
6
|
Project-URL: Repository, https://github.com/KaiHabermann/Clearbox
|
|
@@ -39,7 +39,7 @@ sessions attached to the nodes, and shows everything live in the browser: which
|
|
|
39
39
|
worked on right now, and — on click — the agent's session in that node as it happens.
|
|
40
40
|
|
|
41
41
|
```
|
|
42
|
-
model ──MCP──►
|
|
42
|
+
model ──MCP──► submit_plan (plan + nodes + edges) / start_plan
|
|
43
43
|
│
|
|
44
44
|
┌───────▼────────┐ SQLite (plans, nodes, edges, agent profiles, transcripts)
|
|
45
45
|
│ Clearbox │──► built-in agent per ready node, chosen by its agent profile:
|
|
@@ -69,7 +69,8 @@ uv run clearbox --mock # fake agents: try the UI without spending token
|
|
|
69
69
|
uv run python examples/demo_plan.py --start
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
Open <http://127.0.0.1:8765/>.
|
|
72
|
+
Open <http://127.0.0.1:8765/>. The port is `--port` / `CLEARBOX_PORT` (default `8765`); the
|
|
73
|
+
examples, the tests and this repo's `.mcp.json` follow `CLEARBOX_PORT` too.
|
|
73
74
|
|
|
74
75
|
For real agents drop `--mock`. Built-in agents run through the Claude Agent SDK, which ships its
|
|
75
76
|
own Claude Code CLI and uses your existing Claude login (or `ANTHROPIC_API_KEY`).
|
|
@@ -94,7 +95,8 @@ Without uv: `pip install -e .` then `clearbox ...` (or `python -m clearbox ...`)
|
|
|
94
95
|
|
|
95
96
|
### Connect a model
|
|
96
97
|
|
|
97
|
-
The MCP endpoint is `http://127.0.0.1:8765/mcp` (streamable HTTP
|
|
98
|
+
The MCP endpoint is `http://127.0.0.1:8765/mcp` (streamable HTTP; use your port if you passed
|
|
99
|
+
`--port`).
|
|
98
100
|
|
|
99
101
|
```powershell
|
|
100
102
|
claude mcp add --transport http clearbox http://127.0.0.1:8765/mcp
|
|
@@ -106,22 +108,16 @@ Then ask, e.g. *"Use clearbox to make a plan for … and start it."*
|
|
|
106
108
|
|
|
107
109
|
| Tool | What it does |
|
|
108
110
|
| --- | --- |
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
+
| `submit_plan(title, working_directory=None, description="", test_instructions=None, nodes=[…], edges=[…], parent_plan_id=None, parent_node_id=None, max_parallel=None)` | **The way to build a plan**: the plan, all its nodes and all its edges in one call → `{"plan_id": …, "nodes": {key: node_id}}`. `nodes` are `{key, title, description, files, results_from, kind, agent}` objects, where `key` is your own label; `edges` are `[from_key, to_key]` pairs and `results_from` names keys too. All or nothing: every problem (duplicate or unknown keys, cycles, a `results_from` the node does not depend on, a path outside the working directory, an unknown agent or kind, the working-directory and subplan rules) is reported in one error, and then nothing has been created. Plan fields as for `create_plan`; with a parent it becomes the **subplan** of that node. |
|
|
112
|
+
| `create_plan(title, description="", parent_plan_id=None, parent_node_id=None, working_directory=None, max_parallel=None, test_instructions=None)` | New empty plan → integer id (1, 2, 3 …). With a parent it becomes the **subplan** of that node. A top-level plan must name its `working_directory`; `test_instructions` is free text saying how the project's tests are run, see [Test levels](#test-levels-how-much-a-worker-agent-tests). Subplans inherit both and may not set them. |
|
|
113
|
+
| `update_plan(plan_id, max_parallel=None, test_instructions=None)` | Change a top-level plan's settings; only what you pass changes. `max_parallel=0` removes the limit, `test_instructions=""` removes the instructions. Same as `PATCH /api/plans/{plan_id}`. |
|
|
114
|
+
| `add_node(plan_id, title, description="", agent=None, files=None, results_from=None, kind=None)` | Extend or correct a plan one node at a time → `[plan_id, node_id]`. Node ids count up per plan. `agent` (profile name or id) pins the node to an agent profile; omitted = inherit. `files` declares the paths it will create or modify, see [File scoping](#file-scoping-avoiding-clobbered-edits). `results_from` lists earlier node ids whose result text it receives, see [Inherited results](#inherited-results-edges-order-results_from-passes). `kind="user"` makes it the user's own job, see [User-job nodes](#user-job-nodes-a-step-a-person-does); default `"agent"`. |
|
|
111
115
|
| `add_edge(plan_id, from_node_id, to_node_id)` | `from` must finish before `to` starts. Cycles are rejected. Passes no result; that is `results_from`. |
|
|
112
|
-
| `remove_edge`, `remove_node` | Corrections
|
|
113
|
-
| `update_node(plan_id, node_id, title=None, description=None, files=None, results_from=None)` | Change only what you pass
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `set_node_kind(plan_id, node_id, kind)` | Make a node the user's job (`"user"`) or an agent's (`"agent"`), see [User-job nodes](#user-job-nodes-a-step-a-person-does). Only before the node has run. |
|
|
117
|
-
| `get_plan(plan_id)`, `list_plans()` | Structure, status, results, progress. `get_plan` also shows `default_agent` and, per node, the resolved `agent` and its `agent_override`, plus `files` and `conflicts` (see [File scoping](#file-scoping-avoiding-clobbered-edits)), `results_from`, `kind` and `waiting_for_user` (a user node that is ready and waiting to be marked done). |
|
|
118
|
-
| `list_agents()` | Agent profiles (secrets redacted to `has_api_key`; `default: true` marks the server default), including each one's `capability` and `test_level`. |
|
|
119
|
-
| `create_agent(name, harness, model=None, base_url=None, api_key_env=None, params=None, tools=None, permission_mode=None, system_append=None, max_parallel=None, description="", capability=None, test_level=None)` | New agent profile. `capability` (`read` \| `run` \| `edit` \| `full`) and `test_level` (`none` \| `light` \| `standard` \| `thorough`) are explained under [Capability](#capability-what-an-agent-may-do) and [Test levels](#test-levels-how-much-a-worker-agent-tests); `permission_mode` and `tools` are raw escape hatches. Deliberately takes no literal `api_key`: secrets should not pass through a model's context. |
|
|
120
|
-
| `update_agent(agent, …same fields…)` | Change only the fields passed; `""` clears a text field (for `capability` / `test_level`: back to the default), `max_parallel=0` removes the limit, `params`/`tools` replace the whole object. |
|
|
121
|
-
| `delete_agent(agent)` | Delete a profile; nodes and plans that used it inherit again. |
|
|
122
|
-
| `set_node_agent(plan_id, node_id, agent)` | Per-node override; `null` clears it. Applies the next time the node starts. |
|
|
116
|
+
| `remove_edge`, `remove_node` | Corrections. |
|
|
117
|
+
| `update_node(plan_id, node_id, title=None, description=None, files=None, results_from=None, kind=None, agent=None)` | Change only what you pass, all or nothing. `files` / `results_from` omitted keeps the current value, `[]` clears it; they and `kind` change only before the node has run. `agent=""` clears the node's own agent so it inherits again; a new agent applies the next time the node starts. |
|
|
118
|
+
| `get_plan(plan_id)`, `list_plans()` | Structure, status, results, progress. `get_plan` also shows `working_directory`, `test_instructions` (the top-level plan's, for a subplan too), `default_agent` and, per node, the resolved `agent` and its `agent_override`, plus `files` and `conflicts` (see [File scoping](#file-scoping-avoiding-clobbered-edits)), `results_from`, `kind` and `waiting_for_user` (a user node that is ready and waiting to be marked done). |
|
|
119
|
+
| `list_agents()` | Agent profiles (secrets redacted to `has_api_key`; `default: true` marks the server default), including each one's `capability` and `test_level`. Creating, editing, deleting and testing profiles is setup work, done in the web UI's Agents panel or over REST (`/api/agents`), not over MCP. |
|
|
123
120
|
| `set_plan_agent(plan_id, agent)` | Default for the plan and its subplans; `null` clears it. |
|
|
124
|
-
| `test_agent(agent)` | Liveness check: for `openai` lists the endpoint's models (falls back to a tiny completion) → `{ok, detail, reachable, model_listed, models_sample, credential}`. |
|
|
125
121
|
| `start_plan(plan_id, executor="builtin" \| "external")`, `stop_plan(plan_id)` | Run / pause a top-level plan. |
|
|
126
122
|
| `claim_next_node(agent_name, plan_id=None)` | External agents: take the next ready node, with the same prompt a built-in agent would get. |
|
|
127
123
|
| `report_progress(plan_id, node_id, message, kind="text")` | Append to the node's live transcript. |
|
|
@@ -147,10 +143,11 @@ Then ask, e.g. *"Use clearbox to make a plan for … and start it."*
|
|
|
147
143
|
node done, see [User-job nodes](#user-job-nodes-a-step-a-person-does).
|
|
148
144
|
* **Subplans**: a node with a subplan skips the agent; when it is reached its subplan runs, and the
|
|
149
145
|
node is done once the whole subplan is done (its result summarises the subplan's final nodes).
|
|
150
|
-
Built-in agents also get
|
|
151
|
-
and *expand* it into a subplan (`
|
|
146
|
+
Built-in agents also get MCP tools, so an agent can decide mid-run that its node is too big
|
|
147
|
+
and *expand* it into a subplan (`submit_plan(parent_plan_id=…, parent_node_id=…, nodes=…)`); the node then
|
|
152
148
|
finishes after that subplan (disable with `--no-agent-mcp`). `openai` agents get the same tools
|
|
153
|
-
through a built-in MCP client, prefixed `vc_` (`
|
|
149
|
+
through a built-in MCP client, prefixed `vc_` (`vc_submit_plan`, …). These are a restricted set
|
|
150
|
+
on their own endpoint, see [Worker agents' MCP tools](#worker-agents-mcp-tools).
|
|
154
151
|
* All agents of one plan tree share a working directory: `<workdir>/plan-<root id>`
|
|
155
152
|
(default `./workspace/plan-N`).
|
|
156
153
|
* A failed node blocks its dependents. Use **Retry** in the UI. **Stop** cancels running built-in
|
|
@@ -161,7 +158,7 @@ Then ask, e.g. *"Use clearbox to make a plan for … and start it."*
|
|
|
161
158
|
### User-job nodes: a step a person does
|
|
162
159
|
|
|
163
160
|
Some steps of a plan are not an agent's to do: push a branch, open a merge request, deploy, approve
|
|
164
|
-
something. Mark such a node with `kind="user"` (`add_node
|
|
161
|
+
something. Mark such a node with `kind="user"` (on a `submit_plan` node, `add_node`, `update_node`, or the
|
|
165
162
|
**Done by** select in the node drawer; changeable while the node is pending or ready). Nothing else
|
|
166
163
|
about the node changes: it has edges, it can declare `files` and request `results_from`, and it
|
|
167
164
|
blocks its dependents like any other node.
|
|
@@ -251,9 +248,10 @@ work to build on, not to redo.
|
|
|
251
248
|
|
|
252
249
|
`results_from` only *passes*; it never orders. A node may only request nodes of its own plan that it
|
|
253
250
|
already depends on, directly or through other nodes — anything else might not have a result yet when
|
|
254
|
-
it runs, and is rejected with a message saying which edge is missing. `
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
it runs, and is rejected with a message saying which edge is missing. `submit_plan` checks it
|
|
252
|
+
against the edges of the same call; `add_node` accepts the list before the edges exist (you add
|
|
253
|
+
those next), so then the check runs when the edges are known: `update_node` and `start_plan`
|
|
254
|
+
reject it. Like `files`, it can only change
|
|
257
255
|
while the node is `pending` or `ready`.
|
|
258
256
|
|
|
259
257
|
The useful pattern: implementation nodes request nothing and start with a short prompt; a final docs or
|
|
@@ -261,11 +259,12 @@ summary node depends on all of them and requests every result, writing from them
|
|
|
261
259
|
rediscovering the work:
|
|
262
260
|
|
|
263
261
|
```
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
262
|
+
submit_plan(title, working_directory, nodes=[
|
|
263
|
+
{"key": "store", "title": "Store: add the column", …, "files": ["clearbox/store.py"]},
|
|
264
|
+
{"key": "engine", "title": "Engine: use it", …, "files": ["clearbox/engine.py"]},
|
|
265
|
+
{"key": "docs", "title": "Docs", "description": "Document the change in README.md …",
|
|
266
|
+
"files": ["README.md"], "results_from": ["store", "engine"]},
|
|
267
|
+
], edges=[["store", "engine"], ["engine", "docs"]]) # docs depends on store via engine
|
|
269
268
|
```
|
|
270
269
|
|
|
271
270
|
REST: `PUT /api/plans/{plan_id}/nodes/{node_id}/results-from` (body `{"node_ids": [...]}`), and
|
|
@@ -297,20 +296,31 @@ There are two levels, deliberately kept apart so each stays inside a context it
|
|
|
297
296
|
reviewer is told which case applies, so it can treat a missing change in the declared scope as
|
|
298
297
|
not made and stays cautious elsewhere. Agents are still asked to edit through the file tools: those
|
|
299
298
|
edits show up in the transcript as reviewable steps, and they are the only ones captured outside
|
|
300
|
-
the declared scope. The reviewer is asked to run the test suite before its verdict
|
|
301
|
-
|
|
302
|
-
allows for normal runs, see
|
|
303
|
-
|
|
299
|
+
the declared scope. The reviewer is asked to run the test suite before its verdict (with the
|
|
300
|
+
plan's `test_instructions`, if it has them), so reviews get capability `run` (read, search,
|
|
301
|
+
shell; no edits) whatever the profile allows for normal runs, see
|
|
302
|
+
[Capability](#capability-what-an-agent-may-do).
|
|
304
303
|
* **Plan review** (`review_plan` / `POST /api/plans/{id}/review`): once every node in the tree is
|
|
305
|
-
done, one reviewer looks at the plan as a whole — every node's title, description, result
|
|
306
|
-
step
|
|
304
|
+
done, one reviewer looks at the plan as a whole — every node's title, status, description, result
|
|
305
|
+
and step review — and asks what was missed, contradicted or left out across steps. This is
|
|
307
306
|
explicitly **not** a code review: it never reads diffs or source, only reads what the step
|
|
308
307
|
reviews and results already say.
|
|
309
308
|
|
|
310
309
|
Both levels run automatically once `set_plan_review_agent` names an agent (the plan review fires
|
|
311
310
|
once, right after the last node completes), and both can also be triggered on demand with an
|
|
312
311
|
optional one-off `agent=` override, whether or not the plan has one set — so a plan finished before
|
|
313
|
-
reviewing existed can still be reviewed after the fact.
|
|
312
|
+
reviewing existed can still be reviewed after the fact. An automatic and an on-demand review are
|
|
313
|
+
the same session: same prompt, same tools, same transcript (marked as a review), same record; an
|
|
314
|
+
on-demand one replaces a queued or running review of the same node or plan. Every reviewer ends
|
|
315
|
+
its reply with
|
|
316
|
+
|
|
317
|
+
VERDICT: ok | concerns | error
|
|
318
|
+
SUMMARY: <one or two sentences>
|
|
319
|
+
FINDINGS:
|
|
320
|
+
<bullet list, or "none">
|
|
321
|
+
|
|
322
|
+
Anything before the last `VERDICT:` line is dropped; a reply with no `VERDICT:` line at all is
|
|
323
|
+
recorded as `error` with the whole reply as its summary. `get_reviews(plan_id)` /
|
|
314
324
|
`GET /api/plans/{id}` read every verdict and finding back. The intended loop: read the plan-level
|
|
315
325
|
review's oversights, then turn each one into a follow-up node (or plan) rather than editing by
|
|
316
326
|
hand — reviews describe gaps, they don't fill them.
|
|
@@ -365,7 +375,8 @@ overwritten): `claude`, `mock`, and — only if `--openai-base-url`, `OPENAI_BAS
|
|
|
365
375
|
`OPENAI_API_KEY` is set — `openai`. The `claude` profile stores no model / permission mode / max
|
|
366
376
|
turns, so `--model`, `--permission-mode` and `--max-turns` keep applying to it; a value set on the
|
|
367
377
|
profile overrides the flag. Manage profiles in the UI (**Agents** in the sidebar, with a **Test
|
|
368
|
-
connection** button)
|
|
378
|
+
connection** button) or over REST (`/api/agents`). MCP only lists them (`list_agents`), so a
|
|
379
|
+
planning model can pick one; setting them up is not in its context.
|
|
369
380
|
|
|
370
381
|
```powershell
|
|
371
382
|
uv run clearbox --openai-base-url http://localhost:11434/v1 --openai-model qwen2.5-coder:14b
|
|
@@ -391,6 +402,31 @@ $env:OPENAI_API_KEY = "sk-…"; uv run clearbox --agent-profile openai # every
|
|
|
391
402
|
| `max_parallel` | Max nodes running on this profile at once, across all plans (on top of `--max-concurrency`). Useful for a local GPU. | no limit |
|
|
392
403
|
| `description` | Free text for humans. | `""` |
|
|
393
404
|
|
|
405
|
+
### Worker agents' MCP tools
|
|
406
|
+
|
|
407
|
+
A built-in worker never sees the `/mcp` endpoint your own session uses. When the engine starts a
|
|
408
|
+
built-in node it mints a random token for that run and hands the agent
|
|
409
|
+
`http://127.0.0.1:<port>/mcp-agent/<token>` instead; the token names the node and is revoked the
|
|
410
|
+
moment the run ends (finished, failed or stopped), after which the endpoint answers 404, as it
|
|
411
|
+
does for a token it never minted. That endpoint serves only what expanding a node needs:
|
|
412
|
+
`submit_plan`, `create_plan`, `add_node`, `add_edge`, `remove_edge`, `remove_node`, `update_node`
|
|
413
|
+
and `get_plan`, plus read-only `list_agents` and `report_progress` for the agent's own node. Every
|
|
414
|
+
plan argument must be inside the node's own subtree: `submit_plan` / `create_plan` attach a subplan
|
|
415
|
+
to the agent's own node or to a node of a subplan below it, and the other tools work only on those
|
|
416
|
+
subplans. There is no `start_plan` / `stop_plan`, no agent-profile admin, no review, rework or
|
|
417
|
+
working-directory tool, no `claim_next_node` / `complete_node` / `fail_node`, and no way to pick an
|
|
418
|
+
agent: `submit_plan`, `add_node` and `update_node` refuse an `agent` argument with an error.
|
|
419
|
+
Instead a subplan a worker creates gets **the creating node's resolved agent profile as its plan
|
|
420
|
+
default**, so expanding a node never runs the sub-work on a more capable agent than the node itself
|
|
421
|
+
had (see [Which agent runs a node](#which-agent-runs-a-node)). Reviewers get no clearbox tools at
|
|
422
|
+
all; external agents claiming over `/mcp` are trusted like you. The token never appears in a
|
|
423
|
+
transcript; only a `--mock` server hands it out, on a debug route the tests use.
|
|
424
|
+
|
|
425
|
+
This closes the tool layer, not the machine: an agent with capability `run` or `full` has a shell
|
|
426
|
+
and can reach `/mcp` on localhost directly. The scoping protects `read` and `edit` agents, and
|
|
427
|
+
stops a prompt injection from steering an agent through its own clearbox tools; it is no sandbox
|
|
428
|
+
for a shell-capable agent.
|
|
429
|
+
|
|
394
430
|
### Capability: what an agent may do
|
|
395
431
|
|
|
396
432
|
A profile's `capability` is one setting that means the same thing on every harness; the registry
|
|
@@ -433,11 +469,15 @@ Every worker prompt ends with a testing paragraph chosen by the profile's `test_
|
|
|
433
469
|
| `thorough` | run the full suite, and add tests for behaviour you added |
|
|
434
470
|
|
|
435
471
|
**`standard` is the default**, for new profiles and for every existing one (`null`), and for
|
|
436
|
-
external agents claiming through `claim_next_node`. `standard` and `thorough` also
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
472
|
+
external agents claiming through `claim_next_node`. `standard` and `thorough` also say how the
|
|
473
|
+
project's tests are run: the plan's `test_instructions` verbatim when it has them (set them on
|
|
474
|
+
`submit_plan`, `create_plan`, `update_plan` or the 🧪 pill in the UI's topbar), otherwise an
|
|
475
|
+
instruction to work it out from the repository itself (README, CLAUDE.md / AGENTS.md, CI config,
|
|
476
|
+
the test files) rather than invent a harness. Either way the agent is told that any server it starts for testing
|
|
477
|
+
gets a free port, never the port of the Clearbox server running the plan. The level is for worker
|
|
478
|
+
agents only: reviewers keep their own prompt and **always run the suite** before giving a verdict,
|
|
479
|
+
whatever the profile's level — which is why they need capability `run`. They get the same
|
|
480
|
+
how-to-test text.
|
|
441
481
|
|
|
442
482
|
### Which agent runs a node
|
|
443
483
|
|
|
@@ -447,6 +487,10 @@ the CLI-configured harness (`claude`, or `mock` with `--mock`) — exactly what
|
|
|
447
487
|
agent configuration did before profiles existed. The choice is made each time a node starts, so
|
|
448
488
|
editing a profile or an assignment affects the next node to start, not running ones.
|
|
449
489
|
|
|
490
|
+
A subplan that a **worker agent** creates by expanding its node starts with that node's resolved
|
|
491
|
+
profile as its plan default, so its nodes run on the same agent as the node that made them unless
|
|
492
|
+
you change it (see [Worker agents' MCP tools](#worker-agents-mcp-tools)).
|
|
493
|
+
|
|
450
494
|
In the UI the node drawer shows the resolved agent and where it came from (*set on this node*,
|
|
451
495
|
*plan default*, *inherited from plan N*, *server default*) and can override it; the topbar sets the
|
|
452
496
|
plan's default for the plan and its subplans. Graph nodes get a coloured chip only when their agent
|
|
@@ -458,8 +502,8 @@ Put api keys in environment variables of the Clearbox process and reference them
|
|
|
458
502
|
`api_key_env`; the value is read when a node starts and lives only in the HTTP client. A literal
|
|
459
503
|
key can be typed into the Agents panel (or sent as `api_key` to `POST`/`PATCH /api/agents`) for
|
|
460
504
|
convenience: it is stored in the database but **never returned** by REST, the stream, MCP or the
|
|
461
|
-
logs — they only show `has_api_key`. `""` clears it; leaving the field out keeps it. MCP
|
|
462
|
-
|
|
505
|
+
logs — they only show `has_api_key`. `""` clears it; leaving the field out keeps it. MCP has no
|
|
506
|
+
tool that creates or edits a profile, so a key never passes through a model's context. Error texts from endpoints are scrubbed of
|
|
463
507
|
the key before they reach a transcript. Endpoints on localhost or a private network get a
|
|
464
508
|
placeholder key when none is configured, since they rarely check it.
|
|
465
509
|
|
|
@@ -480,7 +524,7 @@ placeholder key when none is configured, since they rarely check it.
|
|
|
480
524
|
| `glob(pattern)` | Find files, e.g. `**/*.py`, newest first. | ✓ |
|
|
481
525
|
| `grep(pattern, path, glob, max_results=100)` | Regex search over file contents → `path:line: text`. | ✓ |
|
|
482
526
|
| `bash(command, timeout_ms=120000)` | Run a shell command (`cmd.exe` on Windows, `/bin/sh` elsewhere), max 600 000 ms; kills the process tree on timeout. | |
|
|
483
|
-
| `vc_*` | The clearbox MCP tools (`
|
|
527
|
+
| `vc_*` | The worker's scoped clearbox MCP tools (`vc_submit_plan`, `vc_add_node`, …, see [Worker agents' MCP tools](#worker-agents-mcp-tools)) so the agent can expand its node into a subplan; off with `tools.mcp=false` or `--no-agent-mcp`. | |
|
|
484
528
|
|
|
485
529
|
Walks skip `.git`, `node_modules`, `__pycache__` and virtualenvs; results are truncated like the
|
|
486
530
|
Claude harness's.
|
|
@@ -504,7 +548,7 @@ Common pitfalls (the transcript error says which one hit):
|
|
|
504
548
|
* **Small local models often loop** — re-reading the same file, repeating a call. Cap them with
|
|
505
549
|
`params.max_turns` (e.g. 15–25), keep their nodes mechanical, and set `max_parallel: 1` if they
|
|
506
550
|
share one GPU.
|
|
507
|
-
* Unsure? **Test connection** in the Agents panel (or `
|
|
551
|
+
* Unsure? **Test connection** in the Agents panel (or `POST /api/agents/{id}/test`) checks reachability, the key and
|
|
508
552
|
whether the model name is listed, without spending tokens.
|
|
509
553
|
|
|
510
554
|
### Walkthrough: a mixed-harness plan
|
|
@@ -523,10 +567,11 @@ It creates the agents it needs if missing and prints them: `cheap` (an `openai`
|
|
|
523
567
|
configured `openai` profile with preset `readonly`, `max_turns` 20, `max_parallel` 1) and `strong`
|
|
524
568
|
(`claude-sdk` with raw permission mode `default` — note that with no `capability` set this is
|
|
525
569
|
`full`, every tool pre-approved; set `capability="read"` on it for a genuinely read-only
|
|
526
|
-
reviewer), then
|
|
527
|
-
`cheap
|
|
528
|
-
|
|
529
|
-
|
|
570
|
+
reviewer) over REST, then tests
|
|
571
|
+
`cheap` (`POST /api/agents/{id}/test`). It builds the plan node by node, the incremental way, and
|
|
572
|
+
assigns the agents the three ways: `set_plan_agent` makes `cheap` the plan default (the inventory
|
|
573
|
+
nodes inherit it), `add_node(..., agent="strong")` pins the assessment, and
|
|
574
|
+
`update_node(..., agent="strong")` moves the review node. `--cheap NAME` / `--strong NAME` use profiles you already
|
|
530
575
|
have; `--repo PATH` reviews another directory. With no `openai` profile configured (e.g. against
|
|
531
576
|
`uv run clearbox --mock`) or with `--mock` it creates `mock-cheap` / `mock-strong` instead, so
|
|
532
577
|
the whole flow can be watched for free.
|
|
@@ -555,6 +600,8 @@ the whole flow can be watched for free.
|
|
|
555
600
|
key, choose **What may this agent do** (the [capability](#capability-what-an-agent-may-do)) and
|
|
556
601
|
its **Test level**, cap its parallelism, and **Test connection** before trusting it. The raw
|
|
557
602
|
**Permission mode** and **Tools preset** selects sit under *Advanced* as escape hatches.
|
|
603
|
+
* The topbar's 📁 pill shows a top-level plan's working directory (click to change it before the
|
|
604
|
+
plan starts); the 🧪 pill next to it holds its `test_instructions` (click to edit, any time).
|
|
558
605
|
* A reviewed node carries a small verdict marker (✓ / ! / ?) in its top-right corner; its full-view
|
|
559
606
|
**Review** tab shows the verdict, summary and findings and a button to (re-)review it on demand.
|
|
560
607
|
A review chip next to the plan status does the same for the plan-level review, see
|
|
@@ -595,6 +642,7 @@ and every later plan can use it. Two things worth knowing when a plan builds Cle
|
|
|
595
642
|
| Flag | Env | Default |
|
|
596
643
|
| --- | --- | --- |
|
|
597
644
|
| `--host`, `--port` | `CLEARBOX_HOST`, `CLEARBOX_PORT` | `127.0.0.1`, `8765` |
|
|
645
|
+
| `--allowed-host NAME[:PORT]` (repeatable) | `CLEARBOX_ALLOWED_HOSTS` (comma-separated) | loopback and `--host` only |
|
|
598
646
|
| `--db` | `CLEARBOX_DB` | `<data dir>/clearbox.db` |
|
|
599
647
|
| `--workdir` | `CLEARBOX_WORKDIR` | `<data dir>/workspace` |
|
|
600
648
|
| `--max-concurrency` | `CLEARBOX_MAX_CONCURRENCY` | `3` |
|
|
@@ -611,7 +659,7 @@ and every later plan can use it. Two things worth knowing when a plan builds Cle
|
|
|
611
659
|
`--model`, `--permission-mode` and `--max-turns` configure the `claude` profile's empty fields (and
|
|
612
660
|
any `claude-sdk` profile that leaves them empty). The three `--openai-*` flags only **bootstrap**
|
|
613
661
|
the `openai` profile the first time: once it exists they are ignored with a warning, because the
|
|
614
|
-
profile is the source of truth — edit it in the Agents panel or with `
|
|
662
|
+
profile is the source of truth — edit it in the Agents panel or with `PATCH /api/agents/{id}`. An
|
|
615
663
|
`--agent-profile` naming a profile that does not exist stops the server with a message listing the
|
|
616
664
|
ones that do.
|
|
617
665
|
|
|
@@ -624,17 +672,29 @@ prompts for Bash, and an SDK session has nobody to answer, so agents could not r
|
|
|
624
672
|
is what capability replaced. `bypassPermissions` lifts every check (only in a sandbox). For
|
|
625
673
|
`openai` agents the raw equivalent is the profile's `tools` preset.
|
|
626
674
|
|
|
675
|
+
**Security.** The API has no authentication: whoever can reach the port can run agents with
|
|
676
|
+
shell access on this machine. It listens on `127.0.0.1` by default, and every request must be
|
|
677
|
+
addressed to one of the server's own names (`127.0.0.1`, `localhost`, `[::1]`, the `--host` it
|
|
678
|
+
binds unless that is a wildcard, and each `--allowed-host`) or it gets a `421`; a request whose
|
|
679
|
+
`Origin` is not one of those gets a `403`. That keeps other web pages in your browser away from
|
|
680
|
+
it, including through DNS rebinding. Serving it under a name or on the network (`--host 0.0.0.0`,
|
|
681
|
+
which prints a warning) needs that name in `--allowed-host`, e.g. `--allowed-host mybox.lan`; a
|
|
682
|
+
name without a port is allowed with the server's port and without one (behind a reverse proxy).
|
|
683
|
+
Only do that on a network you trust.
|
|
684
|
+
|
|
627
685
|
## Layout of the code
|
|
628
686
|
|
|
629
687
|
```
|
|
630
688
|
clearbox/
|
|
631
689
|
store.py SQLite schema + DAG operations (ids, cycle check) + agent profiles
|
|
632
690
|
engine.py scheduler, node lifecycle, subplans, external-agent protocol
|
|
691
|
+
prompts.py what a worker agent is told: system prompt, test levels, the per-node context
|
|
692
|
+
diffs.py before-snapshots of a node's files and the diffs taken from them when it finishes
|
|
693
|
+
reviews.py step and plan reviews: reviewer prompts, reply parser, the review session
|
|
633
694
|
bootstrap.py built-in profiles, server default, startup banner and validation
|
|
634
695
|
mcp_bridge.py MCP client that hands the clearbox tools to non-Claude harnesses (vc_*)
|
|
635
|
-
mcp_tools.py MCP tool definitions
|
|
636
|
-
app.py FastAPI: REST, SSE live stream, MCP mounted at /mcp
|
|
637
|
-
agents.py backwards-compatible aliases; the implementations moved to harness/
|
|
696
|
+
mcp_tools.py MCP tool definitions, and the scoped subset worker agents get
|
|
697
|
+
app.py FastAPI: REST, SSE live stream, MCP mounted at /mcp and the workers' /mcp-agent/<token>
|
|
638
698
|
harness/
|
|
639
699
|
base.py the event contract every harness implements (AgentJob, Harness, HarnessError)
|
|
640
700
|
claude_sdk.py Claude Agent SDK harness
|
|
@@ -642,14 +702,18 @@ clearbox/
|
|
|
642
702
|
mock.py fake harness
|
|
643
703
|
tools.py sandboxed toolset for harnesses that bring none (read_file … bash)
|
|
644
704
|
registry.py picks the harness for a node; per-profile caches and limits
|
|
645
|
-
openai_check.py /models check behind
|
|
705
|
+
openai_check.py /models check behind POST /api/agents/{id}/test
|
|
646
706
|
probe.py tiny completion round trip when there is no /models listing
|
|
647
|
-
static/ UI (plain HTML/CSS
|
|
707
|
+
static/ UI (plain HTML/CSS and ES modules, no build step): app.js (entry: routing, plan
|
|
708
|
+
view, live feed), core.js (state, helpers), graph.js, drawer.js, agents.js
|
|
648
709
|
examples/demo_plan.py builds a demo plan through MCP
|
|
649
710
|
examples/mixed_plan.py builds a plan that mixes a cheap and a strong agent
|
|
711
|
+
tests/unit/ in-process unit tests of the scheduler and store: `uv run pytest`
|
|
650
712
|
tests/test_flow.py end-to-end test (run against a server started with --mock)
|
|
651
713
|
tests/test_agents_api.py REST agent lifecycle, incl. the secret-handling rules
|
|
652
714
|
tests/test_mixed_run.py per-node agents and max_parallel in a real run
|
|
715
|
+
tests/test_agent_scope.py the workers' scoped MCP endpoint: subtree rule, inherited agent, token lifetime
|
|
716
|
+
tests/test_host_check.py Host and Origin checks: foreign names and pages refused, own names and the stream pass
|
|
653
717
|
tests/test_openai_harness.py the agent loop against tests/fake_openai.py (no network, no DB)
|
|
654
718
|
```
|
|
655
719
|
|
|
@@ -693,7 +757,7 @@ it once on PyPI under *Publishing* with: owner `KaiHabermann`, repository `Clear
|
|
|
693
757
|
`release.yml`, environment `pypi`. Before the first release the project does not exist on PyPI
|
|
694
758
|
yet — add it as a *pending publisher* on your account instead, with the same four values.
|
|
695
759
|
|
|
696
|
-
`.github/workflows/ci.yml` runs the same build plus the
|
|
697
|
-
pull request, and installs the built wheel into a clean
|
|
698
|
-
that an editable install would hide. Action versions
|
|
699
|
-
Dependabot.
|
|
760
|
+
`.github/workflows/ci.yml` runs the same build plus the unit tests (`uv run pytest`) and the
|
|
761
|
+
mock-agent test suite on every push and pull request, and installs the built wheel into a clean
|
|
762
|
+
environment to catch a packaging mistake that an editable install would hide. Action versions
|
|
763
|
+
are pinned exactly and kept current by Dependabot.
|