opencommand 0.1.1__tar.gz → 0.1.2__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.
- {opencommand-0.1.1 → opencommand-0.1.2}/PKG-INFO +15 -1
- {opencommand-0.1.1 → opencommand-0.1.2}/README.md +14 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/pyproject.toml +1 -1
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/cli.py +80 -11
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/core/config.py +19 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/system/automatic.py +11 -7
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/swarm/agents/commander.py +23 -4
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/__init__.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/core/agent.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/core/errors.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/core/logging.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/core/supervisor.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/core/tools.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/engine/__init__.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/engine/runner.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/knowledge.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/memory/MEMORY.md +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/skills/bash.md +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/skills/powershell.md +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/skills/python.md +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/skills/system.md +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/system/cron.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/system/notify.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/system/platform.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/templates.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/desktop.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/files.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/instructions.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/memory.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/playwright.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/research.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/system.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/modules/tools/terminal.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/providers/provider.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/swarm/agents/worker.py +0 -0
- {opencommand-0.1.1 → opencommand-0.1.2}/src/opencommand/tui/dashboard.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: opencommand
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Local-first swarm agent framework: a Commander plans and dispatches a swarm of Workers that research, code, test, and interactively drive the computer to complete long-horizon codebase goals.
|
|
5
5
|
Author: DSiENT
|
|
6
6
|
Author-email: DSiENT <w.caskey7@gmail.com>
|
|
@@ -55,12 +55,26 @@ playwright install # optional, for the playwright tool
|
|
|
55
55
|
```bash
|
|
56
56
|
opencommand models list # list built-in embedded models
|
|
57
57
|
opencommand models pull all # download GGUF models into ./models
|
|
58
|
+
opencommand config # interactive menu: workers, batch size, GPU, etc.
|
|
58
59
|
opencommand run "Add unit tests for the engine module."
|
|
60
|
+
opencommand run --goal-file Goal.md # run from a Goal.md spec (intended state)
|
|
59
61
|
opencommand run "Build a Panda3D FPS" --pipeline advanced --verify "uv run pytest -q"
|
|
60
62
|
opencommand tui # live swarm dashboard
|
|
61
63
|
opencommand cron add healthcheck "every 1h" "echo ok"
|
|
62
64
|
```
|
|
63
65
|
|
|
66
|
+
### Goal.md spec
|
|
67
|
+
|
|
68
|
+
Instead of an inline goal, point `run` at a `Goal.md` file that defines the
|
|
69
|
+
intended end state / requirements for the session. Its contents become the goal
|
|
70
|
+
fed to the swarm (and the advanced pipeline's review gate checks against it):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
opencommand run --goal-file Goal.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The file name is configurable via `opencommand config` (default `Goal.md`).
|
|
77
|
+
|
|
64
78
|
## Built-in models
|
|
65
79
|
|
|
66
80
|
| Role | Model |
|
|
@@ -32,12 +32,26 @@ playwright install # optional, for the playwright tool
|
|
|
32
32
|
```bash
|
|
33
33
|
opencommand models list # list built-in embedded models
|
|
34
34
|
opencommand models pull all # download GGUF models into ./models
|
|
35
|
+
opencommand config # interactive menu: workers, batch size, GPU, etc.
|
|
35
36
|
opencommand run "Add unit tests for the engine module."
|
|
37
|
+
opencommand run --goal-file Goal.md # run from a Goal.md spec (intended state)
|
|
36
38
|
opencommand run "Build a Panda3D FPS" --pipeline advanced --verify "uv run pytest -q"
|
|
37
39
|
opencommand tui # live swarm dashboard
|
|
38
40
|
opencommand cron add healthcheck "every 1h" "echo ok"
|
|
39
41
|
```
|
|
40
42
|
|
|
43
|
+
### Goal.md spec
|
|
44
|
+
|
|
45
|
+
Instead of an inline goal, point `run` at a `Goal.md` file that defines the
|
|
46
|
+
intended end state / requirements for the session. Its contents become the goal
|
|
47
|
+
fed to the swarm (and the advanced pipeline's review gate checks against it):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
opencommand run --goal-file Goal.md
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The file name is configurable via `opencommand config` (default `Goal.md`).
|
|
54
|
+
|
|
41
55
|
## Built-in models
|
|
42
56
|
|
|
43
57
|
| Role | Model |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "opencommand"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Local-first swarm agent framework: a Commander plans and dispatches a swarm of Workers that research, code, test, and interactively drive the computer to complete long-horizon codebase goals."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -57,7 +57,7 @@ def _load_cfg(path: str | None) -> OpenCommandConfig:
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
@click.group()
|
|
60
|
-
@click.version_option(version="0.1.
|
|
60
|
+
@click.version_option(version="0.1.1")
|
|
61
61
|
@click.option("-v", "--verbose", is_flag=True, help="Enable verbose (DEBUG) logging.")
|
|
62
62
|
def cli(verbose: bool) -> None:
|
|
63
63
|
"""OpenCommand - all-in-one local model runner & command center."""
|
|
@@ -145,6 +145,44 @@ def _build_rebuild_command(cmake_args: str) -> tuple:
|
|
|
145
145
|
return base, False, dict(os.environ, CMAKE_ARGS=cmake_args)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
@cli.command()
|
|
149
|
+
@click.argument("path", required=False)
|
|
150
|
+
def config(path: str | None) -> None:
|
|
151
|
+
"""Interactive menu to set advanced swarm settings (persisted to config.json)."""
|
|
152
|
+
cfg = _load_cfg(path)
|
|
153
|
+
click.echo(click.style("OpenCommand configuration", fg="cyan", bold=True))
|
|
154
|
+
click.echo(f"Workspace: {cfg.workspace}\n")
|
|
155
|
+
|
|
156
|
+
cfg.vision = click.confirm("Enable vision / desktop tool?", default=cfg.vision)
|
|
157
|
+
cfg.vision_model = click.prompt(
|
|
158
|
+
"Vision model role", type=click.Choice(["vision", "vision_small"]),
|
|
159
|
+
default=cfg.vision_model)
|
|
160
|
+
cfg.max_workers = click.prompt(
|
|
161
|
+
"Max workers (concurrent model pool size)", type=int, default=cfg.max_workers)
|
|
162
|
+
cfg.batch_size = click.prompt(
|
|
163
|
+
"Workers per swarm batch", type=int, default=cfg.batch_size)
|
|
164
|
+
cfg.max_retries = click.prompt(
|
|
165
|
+
"Max retries per worker", type=int, default=cfg.max_retries)
|
|
166
|
+
cfg.pipeline = click.prompt(
|
|
167
|
+
"Pipeline", type=click.Choice(["standard", "advanced"]), default=cfg.pipeline)
|
|
168
|
+
cfg.live_verify = click.confirm(
|
|
169
|
+
"Enable live-verify (interactive desktop check after tests pass)?",
|
|
170
|
+
default=cfg.live_verify)
|
|
171
|
+
cfg.max_iterations = click.prompt(
|
|
172
|
+
"Max automatic-mode iterations", type=int, default=cfg.max_iterations)
|
|
173
|
+
cfg.max_research_loops = click.prompt(
|
|
174
|
+
"Max research refinement loops / phase", type=int, default=cfg.max_research_loops)
|
|
175
|
+
cfg.max_test_loops = click.prompt(
|
|
176
|
+
"Max test+debug fix loops / phase", type=int, default=cfg.max_test_loops)
|
|
177
|
+
cfg.n_gpu_layers = click.prompt(
|
|
178
|
+
"GPU layers to offload (0=CPU, -1=all)", type=int, default=cfg.n_gpu_layers)
|
|
179
|
+
cfg.goal_file = click.prompt(
|
|
180
|
+
"Session goal spec file name", default=cfg.goal_file)
|
|
181
|
+
|
|
182
|
+
cfg.save()
|
|
183
|
+
click.echo(click.style(f"\nSaved config -> {cfg._path()}", fg="green"))
|
|
184
|
+
|
|
185
|
+
|
|
148
186
|
@cli.group()
|
|
149
187
|
def models() -> None:
|
|
150
188
|
"""Manage built-in embedded models."""
|
|
@@ -274,11 +312,15 @@ def cron_start(path: str | None) -> None:
|
|
|
274
312
|
|
|
275
313
|
|
|
276
314
|
@cli.command()
|
|
277
|
-
@click.argument("goal")
|
|
315
|
+
@click.argument("goal", required=False)
|
|
278
316
|
@click.option("--path", default=None, help="Workspace directory.")
|
|
279
|
-
@click.option("--
|
|
317
|
+
@click.option("--goal-file", default=None,
|
|
318
|
+
help="Path to a Goal.md spec defining the intended state/goal. "
|
|
319
|
+
"If omitted, the configured goal_file in the workspace is used.")
|
|
320
|
+
@click.option("--max-iter", default=None, type=int, help="Automatic-mode iterations.")
|
|
280
321
|
@click.option("--verify", default=None, help="Shell command to verify success (e.g. 'pytest -q').")
|
|
281
322
|
@click.option("--pool", default=None, type=int, help="Worker model pool size (parallelism).")
|
|
323
|
+
@click.option("--batch-size", default=None, type=int, help="Workers spawned per swarm batch.")
|
|
282
324
|
@click.option("--no-vision", is_flag=True, help="Disable the desktop/vision tool.")
|
|
283
325
|
@click.option("--vision-model", default=None,
|
|
284
326
|
type=click.Choice(["vision", "vision_small"]),
|
|
@@ -290,10 +332,16 @@ def cron_start(path: str | None) -> None:
|
|
|
290
332
|
"(Phase 6: scaffold + research-context + commander review gate).")
|
|
291
333
|
@click.option("--live-verify", is_flag=True,
|
|
292
334
|
help="After tests pass, run the desktop tool to interactively verify UI goals.")
|
|
293
|
-
def run(goal: str, path: str | None,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
335
|
+
def run(goal: str | None, path: str | None, goal_file: str | None, max_iter: int | None,
|
|
336
|
+
verify: str | None, pool: int | None, batch_size: int | None,
|
|
337
|
+
no_vision: bool, vision_model: str | None, pipeline: str | None,
|
|
338
|
+
live_verify: bool) -> None:
|
|
339
|
+
"""Run a GOAL autonomously using embedded models.
|
|
340
|
+
|
|
341
|
+
The GOAL may be passed inline, or defined in a Goal.md spec file (see
|
|
342
|
+
--goal-file) that describes the intended end state / requirements for the
|
|
343
|
+
session. When a goal file is used, its contents become the goal.
|
|
344
|
+
"""
|
|
297
345
|
import asyncio
|
|
298
346
|
|
|
299
347
|
from .modules.system.automatic import run_automatic
|
|
@@ -308,6 +356,23 @@ def run(goal: str, path: str | None, max_iter: int, verify: str | None,
|
|
|
308
356
|
cfg.pipeline = pipeline
|
|
309
357
|
if live_verify:
|
|
310
358
|
cfg.live_verify = True
|
|
359
|
+
if max_iter is not None:
|
|
360
|
+
cfg.max_iterations = max_iter
|
|
361
|
+
if batch_size is not None:
|
|
362
|
+
cfg.batch_size = batch_size
|
|
363
|
+
|
|
364
|
+
# Resolve the goal: inline arg wins; otherwise load from a Goal.md spec.
|
|
365
|
+
goal_path = Path(goal_file or cfg.goal_file)
|
|
366
|
+
if goal is None:
|
|
367
|
+
if not goal_path.is_absolute():
|
|
368
|
+
goal_path = cfg.workspace / goal_path
|
|
369
|
+
if not goal_path.exists():
|
|
370
|
+
raise click.UsageError(
|
|
371
|
+
f"No goal provided and no spec file found at {goal_path}. "
|
|
372
|
+
"Pass a GOAL argument or create a Goal.md spec.")
|
|
373
|
+
goal = goal_path.read_text(encoding="utf-8").strip()
|
|
374
|
+
click.echo(click.style(f"Loaded goal spec: {goal_path}", fg="green"))
|
|
375
|
+
|
|
311
376
|
engine = Engine(model_dir=cfg.workspace / "models",
|
|
312
377
|
pool_size=pool or cfg.max_workers)
|
|
313
378
|
sup = Supervisor(max_retries=cfg.max_retries)
|
|
@@ -320,8 +385,9 @@ def run(goal: str, path: str | None, max_iter: int, verify: str | None,
|
|
|
320
385
|
cron = CronScheduler.load(cron_file)
|
|
321
386
|
if cron._jobs:
|
|
322
387
|
cron.on_goal = lambda g: run_automatic(
|
|
323
|
-
commander, g, max_iterations=
|
|
324
|
-
pipeline=cfg.pipeline, live_verify=cfg.live_verify
|
|
388
|
+
commander, g, max_iterations=cfg.max_iterations, verify_cmd=verify,
|
|
389
|
+
pipeline=cfg.pipeline, live_verify=cfg.live_verify,
|
|
390
|
+
batch_size=cfg.batch_size)
|
|
325
391
|
click.echo(click.style(f"Loaded {len(cron._jobs)} cron job(s).", fg="green"))
|
|
326
392
|
|
|
327
393
|
async def _main() -> str:
|
|
@@ -329,8 +395,9 @@ def run(goal: str, path: str | None, max_iter: int, verify: str | None,
|
|
|
329
395
|
cron.start()
|
|
330
396
|
try:
|
|
331
397
|
return await run_automatic(
|
|
332
|
-
commander, goal, max_iterations=
|
|
333
|
-
pipeline=cfg.pipeline, live_verify=cfg.live_verify
|
|
398
|
+
commander, goal, max_iterations=cfg.max_iterations, verify_cmd=verify,
|
|
399
|
+
pipeline=cfg.pipeline, live_verify=cfg.live_verify,
|
|
400
|
+
batch_size=cfg.batch_size)
|
|
334
401
|
finally:
|
|
335
402
|
cron.stop()
|
|
336
403
|
sup.shutdown()
|
|
@@ -338,6 +405,8 @@ def run(goal: str, path: str | None, max_iter: int, verify: str | None,
|
|
|
338
405
|
click.echo(f"OpenCommand on {describe()}")
|
|
339
406
|
click.echo(click.style(f"Pipeline: {cfg.pipeline}"
|
|
340
407
|
+ (" + live-verify" if cfg.live_verify else ""), fg="cyan"))
|
|
408
|
+
click.echo(click.style(f"Batch size: {cfg.batch_size} Pool: {pool or cfg.max_workers}",
|
|
409
|
+
fg="cyan"))
|
|
341
410
|
click.echo(click.style("Loading embedded models...", fg="cyan"))
|
|
342
411
|
click.echo(click.style("Running automatic mode...", fg="cyan"))
|
|
343
412
|
report = asyncio.run(_main())
|
|
@@ -17,6 +17,13 @@ class OpenCommandConfig:
|
|
|
17
17
|
max_retries: int = 3
|
|
18
18
|
pipeline: str = "standard" # standard | advanced (Phase 6)
|
|
19
19
|
live_verify: bool = False # interactive desktop check after verify passes
|
|
20
|
+
# --- Advanced settings (config menu / Goal.md) -------------------------
|
|
21
|
+
max_iterations: int = 5 # outer automatic-mode iterations
|
|
22
|
+
max_research_loops: int = 3 # research refinement loops per phase
|
|
23
|
+
max_test_loops: int = 5 # test+debug fix loops per phase
|
|
24
|
+
batch_size: int = 4 # workers spawned per swarm batch
|
|
25
|
+
n_gpu_layers: int = 0 # 0 = CPU; -1 = all GPU layers
|
|
26
|
+
goal_file: str = "Goal.md" # session goal spec file name
|
|
20
27
|
|
|
21
28
|
def save(self) -> None:
|
|
22
29
|
self._path().parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -32,6 +39,12 @@ class OpenCommandConfig:
|
|
|
32
39
|
"max_retries": self.max_retries,
|
|
33
40
|
"pipeline": self.pipeline,
|
|
34
41
|
"live_verify": self.live_verify,
|
|
42
|
+
"max_iterations": self.max_iterations,
|
|
43
|
+
"max_research_loops": self.max_research_loops,
|
|
44
|
+
"max_test_loops": self.max_test_loops,
|
|
45
|
+
"batch_size": self.batch_size,
|
|
46
|
+
"n_gpu_layers": self.n_gpu_layers,
|
|
47
|
+
"goal_file": self.goal_file,
|
|
35
48
|
}, indent=2)
|
|
36
49
|
|
|
37
50
|
@classmethod
|
|
@@ -49,6 +62,12 @@ class OpenCommandConfig:
|
|
|
49
62
|
max_retries=d.get("max_retries", 3),
|
|
50
63
|
pipeline=d.get("pipeline", "standard"),
|
|
51
64
|
live_verify=d.get("live_verify", False),
|
|
65
|
+
max_iterations=d.get("max_iterations", 5),
|
|
66
|
+
max_research_loops=d.get("max_research_loops", 3),
|
|
67
|
+
max_test_loops=d.get("max_test_loops", 5),
|
|
68
|
+
batch_size=d.get("batch_size", 4),
|
|
69
|
+
n_gpu_layers=d.get("n_gpu_layers", 0),
|
|
70
|
+
goal_file=d.get("goal_file", "Goal.md"),
|
|
52
71
|
)
|
|
53
72
|
|
|
54
73
|
def _path(self) -> Path:
|
|
@@ -28,12 +28,13 @@ log = get_logger("system.automatic")
|
|
|
28
28
|
|
|
29
29
|
async def run_automatic(commander: Commander, goal: str, max_iterations: int = 5,
|
|
30
30
|
verify_cmd: str | None = None, pipeline: str = "standard",
|
|
31
|
-
live_verify: bool = False) -> str:
|
|
31
|
+
live_verify: bool = False, batch_size: int = 4) -> str:
|
|
32
32
|
"""Iteratively execute the goal, auto-fixing failures, until 100% met.
|
|
33
33
|
|
|
34
34
|
``pipeline`` is "standard" or "advanced" (scaffold + research loop +
|
|
35
35
|
commander review gate + test/debug loop). ``live_verify`` runs the desktop
|
|
36
36
|
tool as a final interactive check when the goal is UI/UX facing.
|
|
37
|
+
``batch_size`` is the number of workers spawned per swarm batch.
|
|
37
38
|
|
|
38
39
|
The loop only returns success when BOTH the exit-code verify gate AND the
|
|
39
40
|
Commander review gate pass (and, if enabled, the live-verify check). If the
|
|
@@ -47,10 +48,11 @@ async def run_automatic(commander: Commander, goal: str, max_iterations: int = 5
|
|
|
47
48
|
tags=["rocket", "opencommand"])
|
|
48
49
|
last = ""
|
|
49
50
|
for i in range(max_iterations):
|
|
50
|
-
log.info("iteration=%d/%d goal=%r pipeline=%s", i + 1,
|
|
51
|
-
original_goal, pipeline)
|
|
51
|
+
log.info("iteration=%d/%d goal=%r pipeline=%s batch=%d", i + 1,
|
|
52
|
+
max_iterations, original_goal, pipeline, batch_size)
|
|
52
53
|
last = await _execute(commander, original_goal, pipeline,
|
|
53
|
-
verify_cmd=verify_cmd, live_verify=live_verify
|
|
54
|
+
verify_cmd=verify_cmd, live_verify=live_verify,
|
|
55
|
+
batch_size=batch_size)
|
|
54
56
|
# Exit-code gate (real verification, not string matching).
|
|
55
57
|
if verify_cmd:
|
|
56
58
|
rc, out = _verify(commander, verify_cmd)
|
|
@@ -98,11 +100,13 @@ async def run_automatic(commander: Commander, goal: str, max_iterations: int = 5
|
|
|
98
100
|
|
|
99
101
|
|
|
100
102
|
async def _execute(commander: Commander, goal: str, pipeline: str,
|
|
101
|
-
verify_cmd: str | None = None, live_verify: bool = False
|
|
103
|
+
verify_cmd: str | None = None, live_verify: bool = False,
|
|
104
|
+
batch_size: int = 4) -> str:
|
|
102
105
|
if pipeline == "advanced":
|
|
103
106
|
return await commander.execute_advanced(
|
|
104
|
-
goal, verify_cmd=verify_cmd, live_verify=live_verify
|
|
105
|
-
|
|
107
|
+
goal, verify_cmd=verify_cmd, live_verify=live_verify,
|
|
108
|
+
batch_size=batch_size)
|
|
109
|
+
return await commander.execute(goal, batch_size=batch_size)
|
|
106
110
|
|
|
107
111
|
|
|
108
112
|
def _fix_prompt(original_goal: str, last: str) -> str:
|
|
@@ -184,7 +184,7 @@ class Commander:
|
|
|
184
184
|
|
|
185
185
|
# --- Standard pipeline (backward compatible) ---------------------------
|
|
186
186
|
|
|
187
|
-
async def execute(self, goal: str) -> str:
|
|
187
|
+
async def execute(self, goal: str, batch_size: int = 4) -> str:
|
|
188
188
|
"""Standard pipeline: plan -> research -> workers (no scaffold/review)."""
|
|
189
189
|
plan = await self.plan(goal)
|
|
190
190
|
save_note(self.workspace, "goal_plan",
|
|
@@ -201,7 +201,7 @@ class Commander:
|
|
|
201
201
|
worker_jobs = {f"worker-{i}": self._worker_job(i, t)
|
|
202
202
|
for i, t in enumerate(phase.workers)}
|
|
203
203
|
if worker_jobs:
|
|
204
|
-
for k, v in (await self.
|
|
204
|
+
for k, v in (await self._run_batched(worker_jobs, batch_size)).items():
|
|
205
205
|
report.append(f" [{k}] {v[:300]}")
|
|
206
206
|
save_note(self.workspace, "goal_report", "\n".join(report), tags=["report"])
|
|
207
207
|
return "\n".join(report)
|
|
@@ -211,7 +211,8 @@ class Commander:
|
|
|
211
211
|
async def execute_advanced(self, goal: str, max_research_loops: int = 3,
|
|
212
212
|
max_test_loops: int = 5,
|
|
213
213
|
verify_cmd: str | None = None,
|
|
214
|
-
live_verify: bool = False
|
|
214
|
+
live_verify: bool = False,
|
|
215
|
+
batch_size: int = 4) -> str:
|
|
215
216
|
"""Fully-featured autonomous loop for a GOAL.
|
|
216
217
|
|
|
217
218
|
Per phase:
|
|
@@ -257,7 +258,7 @@ class Commander:
|
|
|
257
258
|
worker_jobs = {f"worker-{i}": self._worker_job(i, t, context)
|
|
258
259
|
for i, t in enumerate(phase.workers)}
|
|
259
260
|
if worker_jobs:
|
|
260
|
-
for k, v in (await self.
|
|
261
|
+
for k, v in (await self._run_batched(worker_jobs, batch_size)).items():
|
|
261
262
|
report.append(f" [{k}] {v[:300]}")
|
|
262
263
|
# 4. TEST+DEBUG LOOP: reach a testable state, then fix until green.
|
|
263
264
|
if verify_cmd:
|
|
@@ -469,6 +470,24 @@ class Commander:
|
|
|
469
470
|
|
|
470
471
|
# --- Helpers -----------------------------------------------------------
|
|
471
472
|
|
|
473
|
+
async def _run_batched(self, jobs: dict[str, Callable[[], Awaitable[str]]],
|
|
474
|
+
batch_size: int = 4) -> dict[str, str]:
|
|
475
|
+
"""Run jobs in bounded batches so at most ``batch_size`` run at once.
|
|
476
|
+
|
|
477
|
+
Workers share a fixed-size runner pool, so capping concurrency here keeps
|
|
478
|
+
the pool from thrashing and bounds RAM/CPU usage on smaller hosts. Each
|
|
479
|
+
batch is dispatched through the Supervisor (which isolates every job with
|
|
480
|
+
crash containment), so behavior is identical to ``run_all`` but bounded.
|
|
481
|
+
"""
|
|
482
|
+
if batch_size <= 0:
|
|
483
|
+
batch_size = 1
|
|
484
|
+
results: dict[str, str] = {}
|
|
485
|
+
items = list(jobs.items())
|
|
486
|
+
for start in range(0, len(items), batch_size):
|
|
487
|
+
batch = dict(items[start:start + batch_size])
|
|
488
|
+
results.update(await self.supervisor.run_all(batch))
|
|
489
|
+
return results
|
|
490
|
+
|
|
472
491
|
def _scaffold(self, phase: Phase, idx: int) -> str:
|
|
473
492
|
"""Lay the foundation for a phase so workers fill in structure, not fight it.
|
|
474
493
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|