ghostlab 0.3.0__tar.gz → 0.5.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.
- ghostlab-0.5.0/CONTRIBUTING.md +57 -0
- ghostlab-0.5.0/PKG-INFO +148 -0
- ghostlab-0.5.0/README.md +112 -0
- ghostlab-0.5.0/docker/agent-sandbox.Dockerfile +38 -0
- ghostlab-0.5.0/docs/agents.md +123 -0
- ghostlab-0.5.0/docs/cli.md +58 -0
- ghostlab-0.5.0/docs/getting-started.md +62 -0
- ghostlab-0.5.0/docs/index.md +26 -0
- ghostlab-0.5.0/docs/pipelines.md +121 -0
- ghostlab-0.5.0/examples/README.md +20 -0
- ghostlab-0.5.0/examples/agents/claude.yaml +14 -0
- ghostlab-0.5.0/examples/agents/codex.yaml +11 -0
- ghostlab-0.5.0/examples/agents/copilot.yaml +11 -0
- ghostlab-0.5.0/examples/agents/opencode.yaml +14 -0
- ghostlab-0.5.0/examples/batch-label/agents/labeler.yaml +10 -0
- ghostlab-0.5.0/examples/batch-label/pipeline.py +37 -0
- ghostlab-0.5.0/examples/batch-label/tickets.jsonl +5 -0
- ghostlab-0.5.0/examples/mcp-eval/agents/assistant.yaml +9 -0
- ghostlab-0.5.0/examples/mcp-eval/agents/helper.yaml +10 -0
- ghostlab-0.5.0/examples/mcp-eval/pipeline.py +137 -0
- ghostlab-0.5.0/examples/mcp-eval/targets.jsonl +1 -0
- ghostlab-0.5.0/examples/release-notes/agent.yaml +16 -0
- ghostlab-0.5.0/ghostlab/__init__.py +27 -0
- {ghostlab-0.3.0/rehearsal → ghostlab-0.5.0/ghostlab}/__main__.py +0 -1
- ghostlab-0.5.0/ghostlab/_util.py +96 -0
- ghostlab-0.5.0/ghostlab/agent.py +231 -0
- ghostlab-0.5.0/ghostlab/cli.py +164 -0
- ghostlab-0.5.0/ghostlab/harnesses.py +394 -0
- ghostlab-0.5.0/ghostlab/parsers.py +262 -0
- ghostlab-0.5.0/ghostlab/pipeline.py +321 -0
- ghostlab-0.5.0/ghostlab/sandbox.py +446 -0
- ghostlab-0.5.0/ghostlab/session.py +198 -0
- ghostlab-0.5.0/llms.txt +33 -0
- {ghostlab-0.3.0 → ghostlab-0.5.0}/mkdocs.yml +3 -5
- {ghostlab-0.3.0 → ghostlab-0.5.0}/pyproject.toml +17 -25
- ghostlab-0.5.0/tests/conftest.py +62 -0
- ghostlab-0.5.0/tests/fakes/fake_agent.py +52 -0
- ghostlab-0.5.0/tests/fakes/fake_openshell.py +77 -0
- ghostlab-0.5.0/tests/test_agent.py +90 -0
- ghostlab-0.5.0/tests/test_cli.py +57 -0
- ghostlab-0.5.0/tests/test_harnesses.py +126 -0
- ghostlab-0.5.0/tests/test_parsers.py +83 -0
- ghostlab-0.5.0/tests/test_pipeline.py +216 -0
- ghostlab-0.5.0/tests/test_session.py +101 -0
- ghostlab-0.3.0/CONTRIBUTING.md +0 -114
- ghostlab-0.3.0/PKG-INFO +0 -817
- ghostlab-0.3.0/README.md +0 -775
- ghostlab-0.3.0/docs/cli.md +0 -543
- ghostlab-0.3.0/docs/concepts.md +0 -143
- ghostlab-0.3.0/docs/configured-agent-lab.md +0 -238
- ghostlab-0.3.0/docs/datasets-evaluation.md +0 -113
- ghostlab-0.3.0/docs/getting-started.md +0 -350
- ghostlab-0.3.0/docs/ghostlab-vision-gap-and-roadmap.md +0 -181
- ghostlab-0.3.0/docs/index.md +0 -36
- ghostlab-0.3.0/examples/README.md +0 -75
- ghostlab-0.3.0/examples/agent-lab/agent.json +0 -22
- ghostlab-0.3.0/examples/agent-lab/mcp.json +0 -5
- ghostlab-0.3.0/examples/agent.json +0 -50
- ghostlab-0.3.0/examples/demo-skill/SKILL.md +0 -9
- ghostlab-0.3.0/examples/mcp-config.json +0 -7
- ghostlab-0.3.0/examples/scenario.json +0 -18
- ghostlab-0.3.0/examples/target.json +0 -14
- ghostlab-0.3.0/ghostlab/__init__.py +0 -5
- ghostlab-0.3.0/ghostlab/__main__.py +0 -4
- ghostlab-0.3.0/ghostlab/cli.py +0 -5
- ghostlab-0.3.0/llms.txt +0 -71
- ghostlab-0.3.0/rehearsal/__init__.py +0 -3
- ghostlab-0.3.0/rehearsal/agent_profile.py +0 -290
- ghostlab-0.3.0/rehearsal/agent_sandbox.py +0 -324
- ghostlab-0.3.0/rehearsal/agents.py +0 -251
- ghostlab-0.3.0/rehearsal/apps_host/__init__.py +0 -30
- ghostlab-0.3.0/rehearsal/apps_host/assertions.py +0 -102
- ghostlab-0.3.0/rehearsal/apps_host/executor.py +0 -125
- ghostlab-0.3.0/rehearsal/apps_host/live.py +0 -356
- ghostlab-0.3.0/rehearsal/apps_host/protocol.py +0 -246
- ghostlab-0.3.0/rehearsal/apps_host/renderer.py +0 -270
- ghostlab-0.3.0/rehearsal/apps_host/report.py +0 -123
- ghostlab-0.3.0/rehearsal/cli.py +0 -3438
- ghostlab-0.3.0/rehearsal/cli_ui.py +0 -118
- ghostlab-0.3.0/rehearsal/codex_backend.py +0 -153
- ghostlab-0.3.0/rehearsal/compare.py +0 -117
- ghostlab-0.3.0/rehearsal/config.py +0 -221
- ghostlab-0.3.0/rehearsal/contract.py +0 -501
- ghostlab-0.3.0/rehearsal/critique.py +0 -243
- ghostlab-0.3.0/rehearsal/dashboard.py +0 -520
- ghostlab-0.3.0/rehearsal/dataset.py +0 -302
- ghostlab-0.3.0/rehearsal/evaluate.py +0 -513
- ghostlab-0.3.0/rehearsal/generate.py +0 -230
- ghostlab-0.3.0/rehearsal/hosts/__init__.py +0 -84
- ghostlab-0.3.0/rehearsal/hosts/base.py +0 -80
- ghostlab-0.3.0/rehearsal/hosts/direct.py +0 -251
- ghostlab-0.3.0/rehearsal/hosts/runner.py +0 -320
- ghostlab-0.3.0/rehearsal/inspect.py +0 -243
- ghostlab-0.3.0/rehearsal/jobs.py +0 -530
- ghostlab-0.3.0/rehearsal/lab.py +0 -296
- ghostlab-0.3.0/rehearsal/llm_backend.py +0 -90
- ghostlab-0.3.0/rehearsal/logging.py +0 -17
- ghostlab-0.3.0/rehearsal/mcp_apps.py +0 -518
- ghostlab-0.3.0/rehearsal/mcp_client.py +0 -361
- ghostlab-0.3.0/rehearsal/mcp_config.py +0 -36
- ghostlab-0.3.0/rehearsal/mcp_targets.py +0 -128
- ghostlab-0.3.0/rehearsal/opencode_backend.py +0 -201
- ghostlab-0.3.0/rehearsal/opencode_config.py +0 -223
- ghostlab-0.3.0/rehearsal/orchestrator.py +0 -389
- ghostlab-0.3.0/rehearsal/personas.py +0 -147
- ghostlab-0.3.0/rehearsal/plan.py +0 -619
- ghostlab-0.3.0/rehearsal/plan_generate.py +0 -151
- ghostlab-0.3.0/rehearsal/profile.py +0 -240
- ghostlab-0.3.0/rehearsal/prompts.py +0 -326
- ghostlab-0.3.0/rehearsal/readiness.py +0 -386
- ghostlab-0.3.0/rehearsal/report.py +0 -68
- ghostlab-0.3.0/rehearsal/resolved_config.py +0 -131
- ghostlab-0.3.0/rehearsal/review.py +0 -248
- ghostlab-0.3.0/rehearsal/rollout_report.py +0 -327
- ghostlab-0.3.0/rehearsal/runner_presets.py +0 -246
- ghostlab-0.3.0/rehearsal/runners.py +0 -293
- ghostlab-0.3.0/rehearsal/sampling.py +0 -300
- ghostlab-0.3.0/rehearsal/sandbox.py +0 -527
- ghostlab-0.3.0/rehearsal/scorecard.py +0 -266
- ghostlab-0.3.0/rehearsal/setup_runtime.py +0 -331
- ghostlab-0.3.0/rehearsal/skills.py +0 -47
- ghostlab-0.3.0/rehearsal/spec.py +0 -664
- ghostlab-0.3.0/rehearsal/storage/__init__.py +0 -19
- ghostlab-0.3.0/rehearsal/storage/db.py +0 -136
- ghostlab-0.3.0/rehearsal/storage/hashing.py +0 -19
- ghostlab-0.3.0/rehearsal/storage/ids.py +0 -44
- ghostlab-0.3.0/rehearsal/storage/migrations/0001_initial.sql +0 -349
- ghostlab-0.3.0/rehearsal/storage/redact.py +0 -46
- ghostlab-0.3.0/rehearsal/storage/repository.py +0 -1036
- ghostlab-0.3.0/rehearsal/termcolor.py +0 -96
- ghostlab-0.3.0/rehearsal/testrun.py +0 -298
- ghostlab-0.3.0/rehearsal/tool_capture.py +0 -326
- ghostlab-0.3.0/rehearsal/types.py +0 -30
- ghostlab-0.3.0/rehearsal/ui/__init__.py +0 -1
- ghostlab-0.3.0/rehearsal/ui/app.py +0 -733
- ghostlab-0.3.0/runners/claude-process.example.json +0 -7
- ghostlab-0.3.0/runners/codex-cortex-aut.json +0 -20
- ghostlab-0.3.0/runners/codex-cortex-local-aut.json +0 -20
- ghostlab-0.3.0/runners/codex-cortex-local-session.json +0 -20
- ghostlab-0.3.0/runners/codex-process.example.json +0 -7
- ghostlab-0.3.0/runners/codex-user-emulator.json +0 -16
- ghostlab-0.3.0/runners/mock-aut.json +0 -4
- ghostlab-0.3.0/runners/mock-user.json +0 -4
- ghostlab-0.3.0/tests/test_agents.py +0 -83
- ghostlab-0.3.0/tests/test_apps_host.py +0 -203
- ghostlab-0.3.0/tests/test_apps_live.py +0 -126
- ghostlab-0.3.0/tests/test_cli_spec_flow.py +0 -608
- ghostlab-0.3.0/tests/test_codex_backend_sandbox.py +0 -47
- ghostlab-0.3.0/tests/test_compare.py +0 -53
- ghostlab-0.3.0/tests/test_config.py +0 -67
- ghostlab-0.3.0/tests/test_configured_agent.py +0 -383
- ghostlab-0.3.0/tests/test_contract.py +0 -216
- ghostlab-0.3.0/tests/test_critique.py +0 -107
- ghostlab-0.3.0/tests/test_dataset.py +0 -72
- ghostlab-0.3.0/tests/test_evaluate.py +0 -211
- ghostlab-0.3.0/tests/test_generate.py +0 -50
- ghostlab-0.3.0/tests/test_hosts_runner.py +0 -304
- ghostlab-0.3.0/tests/test_inspect_lint.py +0 -80
- ghostlab-0.3.0/tests/test_jobs.py +0 -241
- ghostlab-0.3.0/tests/test_mcp_apps.py +0 -231
- ghostlab-0.3.0/tests/test_mcp_client.py +0 -72
- ghostlab-0.3.0/tests/test_mcp_targets.py +0 -110
- ghostlab-0.3.0/tests/test_opencode_backend.py +0 -286
- ghostlab-0.3.0/tests/test_orchestrator.py +0 -53
- ghostlab-0.3.0/tests/test_personas.py +0 -72
- ghostlab-0.3.0/tests/test_plan.py +0 -201
- ghostlab-0.3.0/tests/test_plan_generate.py +0 -105
- ghostlab-0.3.0/tests/test_profile.py +0 -39
- ghostlab-0.3.0/tests/test_prompts.py +0 -32
- ghostlab-0.3.0/tests/test_readiness.py +0 -169
- ghostlab-0.3.0/tests/test_review.py +0 -92
- ghostlab-0.3.0/tests/test_runner_presets.py +0 -60
- ghostlab-0.3.0/tests/test_sampling.py +0 -187
- ghostlab-0.3.0/tests/test_sandbox.py +0 -132
- ghostlab-0.3.0/tests/test_sandbox_stdio.py +0 -213
- ghostlab-0.3.0/tests/test_scorecard.py +0 -112
- ghostlab-0.3.0/tests/test_session_runner.py +0 -46
- ghostlab-0.3.0/tests/test_setup_runtime.py +0 -160
- ghostlab-0.3.0/tests/test_skills.py +0 -39
- ghostlab-0.3.0/tests/test_spec.py +0 -160
- ghostlab-0.3.0/tests/test_storage.py +0 -322
- ghostlab-0.3.0/tests/test_testrun.py +0 -276
- ghostlab-0.3.0/tests/test_tool_capture.py +0 -186
- ghostlab-0.3.0/tests/test_widgets_and_dashboard.py +0 -143
- {ghostlab-0.3.0 → ghostlab-0.5.0}/.gitignore +0 -0
- {ghostlab-0.3.0 → ghostlab-0.5.0}/LICENSE +0 -0
- {ghostlab-0.3.0 → ghostlab-0.5.0}/docs/publishing.md +0 -0
- {ghostlab-0.3.0/examples/agent-lab → ghostlab-0.5.0/examples/mcp-eval}/tiny-mcp.js +0 -0
- {ghostlab-0.3.0/examples/agent-lab → ghostlab-0.5.0/examples/release-notes}/AGENTS.md +0 -0
- {ghostlab-0.3.0/examples/agent-lab → ghostlab-0.5.0/examples/release-notes}/repo/CHANGELOG.md +0 -0
- {ghostlab-0.3.0/examples/agent-lab → ghostlab-0.5.0/examples/release-notes}/repo/README.md +0 -0
- {ghostlab-0.3.0/examples/agent-lab → ghostlab-0.5.0/examples/release-notes}/repo/src/app.py +0 -0
- {ghostlab-0.3.0/examples/agent-lab → ghostlab-0.5.0/examples/release-notes}/skills/release-notes/SKILL.md +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Contributing to Ghostlab
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve Ghostlab! This guide is written for both humans and
|
|
4
|
+
coding agents. For a machine-readable map of the project, see
|
|
5
|
+
[`llms.txt`](llms.txt).
|
|
6
|
+
|
|
7
|
+
## Project shape
|
|
8
|
+
|
|
9
|
+
- Package and CLI: `ghostlab`. Its only runtime dependency is PyYAML.
|
|
10
|
+
- Layers, bottom up: `sandbox` (local / OpenShell boxes) → `harnesses` +
|
|
11
|
+
`parsers` (one class per agent CLI) → `session` (a run, single or
|
|
12
|
+
multi-turn) → `pipeline` (stages, checkpoints, resume) → `cli`.
|
|
13
|
+
- Ghostlab is infrastructure. Job-specific logic belongs in pipelines under
|
|
14
|
+
`examples/`, not in the package.
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
python3 -m venv .venv # Python 3.10+
|
|
20
|
+
.venv/bin/pip install -e '.[dev]'
|
|
21
|
+
.venv/bin/pytest
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Sandboxed runs need [NVIDIA OpenShell](https://docs.nvidia.com/openshell/latest/)
|
|
25
|
+
(`openshell status`). The test suite does not: `tests/fakes/` provides a fake
|
|
26
|
+
agent CLI and a fake `openshell` CLI.
|
|
27
|
+
|
|
28
|
+
## Making a change
|
|
29
|
+
|
|
30
|
+
1. Branch off `main` (`feat/…`, `fix/…`, `docs/…`).
|
|
31
|
+
2. Make the change with tests.
|
|
32
|
+
3. Open a PR against `main` that says what changed and why.
|
|
33
|
+
|
|
34
|
+
To add a harness, subclass `Harness` in `ghostlab/harnesses.py`, add its parser
|
|
35
|
+
to `ghostlab/parsers.py`, register both, and document its options in
|
|
36
|
+
`docs/agents.md`.
|
|
37
|
+
|
|
38
|
+
When you add or change a CLI command, update `ghostlab/cli.py`, `tests/`,
|
|
39
|
+
`README.md`, and `docs/cli.md`.
|
|
40
|
+
|
|
41
|
+
## Pull request checklist
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
.venv/bin/pytest # always
|
|
45
|
+
.venv/bin/mkdocs build --strict # if you touched docs/ or mkdocs.yml
|
|
46
|
+
.venv/bin/python -m build # if you touched packaging
|
|
47
|
+
.venv/bin/twine check dist/* # "
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Releases
|
|
51
|
+
|
|
52
|
+
Publishing to PyPI is automated by `.github/workflows/publish.yml` via PyPI
|
|
53
|
+
Trusted Publishing. It runs when a GitHub Release is published.
|
|
54
|
+
|
|
55
|
+
1. Bump `__version__` in `ghostlab/__init__.py`.
|
|
56
|
+
2. Validate: `.venv/bin/pytest && .venv/bin/python -m build && .venv/bin/twine check dist/*`.
|
|
57
|
+
3. `gh release create v<x.y.z> --generate-notes`.
|
ghostlab-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ghostlab
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A headless runner for coding agents (Claude Code, Codex, Copilot, OpenCode), sandboxed in NVIDIA OpenShell, with a Python SDK for checkpointed, resumable agent pipelines: evals, labeling, and batch jobs.
|
|
5
|
+
Project-URL: Documentation, https://sajjadgg.github.io/Ghostlab/
|
|
6
|
+
Project-URL: Homepage, https://github.com/sajjadGG/Ghostlab
|
|
7
|
+
Project-URL: Repository, https://github.com/sajjadGG/Ghostlab
|
|
8
|
+
Project-URL: Issues, https://github.com/sajjadGG/Ghostlab/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/sajjadGG/Ghostlab/releases
|
|
10
|
+
Author: SajjadGG
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: agents,ai-agents,batch,claude-code,codex,evaluation,github-copilot,headless,labeling,llm-evaluation,mcp,opencode,openshell,pipeline,sandbox
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: pyyaml>=6
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
30
|
+
Requires-Dist: mkdocs>=1.6; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
33
|
+
Provides-Extra: docs
|
|
34
|
+
Requires-Dist: mkdocs>=1.6; extra == 'docs'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
<p align="center"><img src="static/ghostlablogo.png" alt="Ghostlab" width="160"></p>
|
|
38
|
+
|
|
39
|
+
# Ghostlab
|
|
40
|
+
|
|
41
|
+
**A headless runner for coding agents, and a Python SDK for composing them
|
|
42
|
+
into checkpointed, resumable jobs.**
|
|
43
|
+
|
|
44
|
+
Define an agent once — harness, model, instructions, skills, MCP servers,
|
|
45
|
+
workspace — and run it in **Claude Code, Codex, GitHub Copilot CLI, or
|
|
46
|
+
OpenCode**, on your machine or inside an **NVIDIA OpenShell** sandbox. Then
|
|
47
|
+
chain agent calls into pipelines for evaluations, labeling, testing, or any
|
|
48
|
+
batch job: every item and every agent call is checkpointed, so an interrupted
|
|
49
|
+
run picks up where it stopped.
|
|
50
|
+
|
|
51
|
+
Ghostlab is infrastructure. It has no opinion about what your job does — an
|
|
52
|
+
MCP evaluation is just [one example pipeline](examples/mcp-eval/pipeline.py).
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install ghostlab
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Run an agent
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
# agent.yaml
|
|
62
|
+
name: reviewer
|
|
63
|
+
harness: claude # claude | codex | copilot | opencode | command
|
|
64
|
+
model: sonnet
|
|
65
|
+
instructions: Be concise.
|
|
66
|
+
skills: [skills/review]
|
|
67
|
+
mcps:
|
|
68
|
+
docs: {command: node, args: [docs-mcp.js]}
|
|
69
|
+
workspace: repo # the agent works on a private copy
|
|
70
|
+
sandbox: {backend: openshell, providers: [anthropic]}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ghostlab exec --agent agent.yaml "Review src/app.py" --collect REVIEW.md
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from ghostlab import Agent
|
|
79
|
+
|
|
80
|
+
agent = Agent.load("agent.yaml")
|
|
81
|
+
result = agent.run("Review src/app.py and reply with JSON")
|
|
82
|
+
result.status, result.answer, result.tool_calls, result.json()
|
|
83
|
+
|
|
84
|
+
with agent.session() as chat: # multi-turn: the harness session resumes
|
|
85
|
+
chat.send("Find the bug")
|
|
86
|
+
chat.send("Now fix it")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Compose a pipeline
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from ghostlab import Agent, Pipeline, stage
|
|
93
|
+
|
|
94
|
+
@stage # one output per item
|
|
95
|
+
def describe(ctx, target):
|
|
96
|
+
explorer = ctx.agent("assistant").with_mcp(target["id"], target["mcp"])
|
|
97
|
+
return {**target, "capabilities": ctx.run(explorer, "What can this MCP do? JSON.").json()}
|
|
98
|
+
|
|
99
|
+
@stage(fan_out=True) # a list: each element becomes an item
|
|
100
|
+
def scenarios(ctx, target):
|
|
101
|
+
return ctx.run(ctx.agent("planner"), f"Write test goals for {target}. JSON list.").json()
|
|
102
|
+
|
|
103
|
+
@stage(concurrency=4, retries=1) # parallel, retried on failure
|
|
104
|
+
def evaluate(ctx, scenario):
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
@stage(gather=True) # every surviving item at once
|
|
108
|
+
def report(ctx, results):
|
|
109
|
+
...
|
|
110
|
+
|
|
111
|
+
pipeline = Pipeline("mcp-eval", [describe, scenarios, evaluate, report],
|
|
112
|
+
params={"assistant": Agent.load("agents/assistant.yaml"),
|
|
113
|
+
"planner": Agent.load("agents/planner.yaml")})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
ghostlab run pipeline.py --input targets.jsonl # run
|
|
118
|
+
ghostlab run pipeline.py # resume
|
|
119
|
+
ghostlab run pipeline.py --param assistant=agents/codex.yaml # swap an agent
|
|
120
|
+
ghostlab status runs/mcp-eval
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- **Checkpointed:** each item's outcome is saved as it finishes. So is each
|
|
124
|
+
agent call inside an item, so an item that failed halfway reuses the calls
|
|
125
|
+
that completed.
|
|
126
|
+
- **Resumable:** re-running skips done items and retries failed ones. A changed
|
|
127
|
+
input, parameter, or stage `version` recomputes only what it affects.
|
|
128
|
+
- **Classified failures:** `timed_out`, `model_unavailable`, `sandbox_error`,
|
|
129
|
+
`harness_error`, and `agent_error` are kept apart, so "the agent got it wrong"
|
|
130
|
+
never looks like "the infrastructure broke".
|
|
131
|
+
|
|
132
|
+
## Examples
|
|
133
|
+
|
|
134
|
+
| Example | What it shows |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| [`examples/mcp-eval`](examples/mcp-eval/pipeline.py) | describe an MCP → generate scenarios → emulated-user conversations judged by an agent → report |
|
|
137
|
+
| [`examples/batch-label`](examples/batch-label/pipeline.py) | label a dataset with eight agents in parallel, validating every answer |
|
|
138
|
+
| [`examples/release-notes`](examples/release-notes/agent.yaml) | one configured agent with instructions, a skill, and a workspace |
|
|
139
|
+
| [`examples/agents`](examples/agents) | a starting config for each harness |
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
<https://sajjadgg.github.io/Ghostlab/> — [getting started](docs/getting-started.md),
|
|
144
|
+
[agents](docs/agents.md), [pipelines](docs/pipelines.md), [CLI](docs/cli.md).
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
ghostlab-0.5.0/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<p align="center"><img src="static/ghostlablogo.png" alt="Ghostlab" width="160"></p>
|
|
2
|
+
|
|
3
|
+
# Ghostlab
|
|
4
|
+
|
|
5
|
+
**A headless runner for coding agents, and a Python SDK for composing them
|
|
6
|
+
into checkpointed, resumable jobs.**
|
|
7
|
+
|
|
8
|
+
Define an agent once — harness, model, instructions, skills, MCP servers,
|
|
9
|
+
workspace — and run it in **Claude Code, Codex, GitHub Copilot CLI, or
|
|
10
|
+
OpenCode**, on your machine or inside an **NVIDIA OpenShell** sandbox. Then
|
|
11
|
+
chain agent calls into pipelines for evaluations, labeling, testing, or any
|
|
12
|
+
batch job: every item and every agent call is checkpointed, so an interrupted
|
|
13
|
+
run picks up where it stopped.
|
|
14
|
+
|
|
15
|
+
Ghostlab is infrastructure. It has no opinion about what your job does — an
|
|
16
|
+
MCP evaluation is just [one example pipeline](examples/mcp-eval/pipeline.py).
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install ghostlab
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Run an agent
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
# agent.yaml
|
|
26
|
+
name: reviewer
|
|
27
|
+
harness: claude # claude | codex | copilot | opencode | command
|
|
28
|
+
model: sonnet
|
|
29
|
+
instructions: Be concise.
|
|
30
|
+
skills: [skills/review]
|
|
31
|
+
mcps:
|
|
32
|
+
docs: {command: node, args: [docs-mcp.js]}
|
|
33
|
+
workspace: repo # the agent works on a private copy
|
|
34
|
+
sandbox: {backend: openshell, providers: [anthropic]}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ghostlab exec --agent agent.yaml "Review src/app.py" --collect REVIEW.md
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ghostlab import Agent
|
|
43
|
+
|
|
44
|
+
agent = Agent.load("agent.yaml")
|
|
45
|
+
result = agent.run("Review src/app.py and reply with JSON")
|
|
46
|
+
result.status, result.answer, result.tool_calls, result.json()
|
|
47
|
+
|
|
48
|
+
with agent.session() as chat: # multi-turn: the harness session resumes
|
|
49
|
+
chat.send("Find the bug")
|
|
50
|
+
chat.send("Now fix it")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Compose a pipeline
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from ghostlab import Agent, Pipeline, stage
|
|
57
|
+
|
|
58
|
+
@stage # one output per item
|
|
59
|
+
def describe(ctx, target):
|
|
60
|
+
explorer = ctx.agent("assistant").with_mcp(target["id"], target["mcp"])
|
|
61
|
+
return {**target, "capabilities": ctx.run(explorer, "What can this MCP do? JSON.").json()}
|
|
62
|
+
|
|
63
|
+
@stage(fan_out=True) # a list: each element becomes an item
|
|
64
|
+
def scenarios(ctx, target):
|
|
65
|
+
return ctx.run(ctx.agent("planner"), f"Write test goals for {target}. JSON list.").json()
|
|
66
|
+
|
|
67
|
+
@stage(concurrency=4, retries=1) # parallel, retried on failure
|
|
68
|
+
def evaluate(ctx, scenario):
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@stage(gather=True) # every surviving item at once
|
|
72
|
+
def report(ctx, results):
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
pipeline = Pipeline("mcp-eval", [describe, scenarios, evaluate, report],
|
|
76
|
+
params={"assistant": Agent.load("agents/assistant.yaml"),
|
|
77
|
+
"planner": Agent.load("agents/planner.yaml")})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
ghostlab run pipeline.py --input targets.jsonl # run
|
|
82
|
+
ghostlab run pipeline.py # resume
|
|
83
|
+
ghostlab run pipeline.py --param assistant=agents/codex.yaml # swap an agent
|
|
84
|
+
ghostlab status runs/mcp-eval
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- **Checkpointed:** each item's outcome is saved as it finishes. So is each
|
|
88
|
+
agent call inside an item, so an item that failed halfway reuses the calls
|
|
89
|
+
that completed.
|
|
90
|
+
- **Resumable:** re-running skips done items and retries failed ones. A changed
|
|
91
|
+
input, parameter, or stage `version` recomputes only what it affects.
|
|
92
|
+
- **Classified failures:** `timed_out`, `model_unavailable`, `sandbox_error`,
|
|
93
|
+
`harness_error`, and `agent_error` are kept apart, so "the agent got it wrong"
|
|
94
|
+
never looks like "the infrastructure broke".
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
| Example | What it shows |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| [`examples/mcp-eval`](examples/mcp-eval/pipeline.py) | describe an MCP → generate scenarios → emulated-user conversations judged by an agent → report |
|
|
101
|
+
| [`examples/batch-label`](examples/batch-label/pipeline.py) | label a dataset with eight agents in parallel, validating every answer |
|
|
102
|
+
| [`examples/release-notes`](examples/release-notes/agent.yaml) | one configured agent with instructions, a skill, and a workspace |
|
|
103
|
+
| [`examples/agents`](examples/agents) | a starting config for each harness |
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
<https://sajjadgg.github.io/Ghostlab/> — [getting started](docs/getting-started.md),
|
|
108
|
+
[agents](docs/agents.md), [pipelines](docs/pipelines.md), [CLI](docs/cli.md).
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Sandbox image for running a configured agent under test.
|
|
2
|
+
#
|
|
3
|
+
# The agent CLI must run *inside* the sandbox, not on the host: an agent
|
|
4
|
+
# configured with bash/edit permissions is exactly the configuration worth
|
|
5
|
+
# testing and the least safe to run on a developer's machine. The host's own
|
|
6
|
+
# Agent binaries cannot be uploaded — they are platform-specific executables —
|
|
7
|
+
# so the Linux builds are installed here instead.
|
|
8
|
+
#
|
|
9
|
+
# Build is handled by OpenShell:
|
|
10
|
+
# sandbox:
|
|
11
|
+
# image: docker/agent-sandbox.Dockerfile
|
|
12
|
+
# which resolves to `openshell sandbox create --from <this file>`.
|
|
13
|
+
FROM ghcr.io/nvidia/openshell-community/sandboxes/base@sha256:aeef1c63f00e2913ea002ccb3aaf925f338b5c5d70e63576f0d95c16a138044e
|
|
14
|
+
|
|
15
|
+
USER root
|
|
16
|
+
|
|
17
|
+
# Pinned so a sandbox rebuild cannot silently change the agent runtime under an
|
|
18
|
+
# evaluation. Bump deliberately, and re-record the version in run artifacts.
|
|
19
|
+
ARG OPENCODE_VERSION=1.4.3
|
|
20
|
+
ARG COPILOT_VERSION=1.0.80
|
|
21
|
+
RUN npm install -g \
|
|
22
|
+
"opencode-ai@${OPENCODE_VERSION}" \
|
|
23
|
+
"@github/copilot@${COPILOT_VERSION}" \
|
|
24
|
+
&& opencode --version \
|
|
25
|
+
&& copilot --version
|
|
26
|
+
|
|
27
|
+
# OpenCode keeps credentials and session state under XDG paths. Fixing them
|
|
28
|
+
# outside $HOME keeps them off the uploaded workspace, so an agent with `edit`
|
|
29
|
+
# permission cannot read or rewrite its own auth file.
|
|
30
|
+
ENV XDG_DATA_HOME=/opt/agent/share \
|
|
31
|
+
XDG_CONFIG_HOME=/opt/agent/config \
|
|
32
|
+
XDG_CACHE_HOME=/opt/agent/cache
|
|
33
|
+
RUN mkdir -p /opt/agent/share/opencode /opt/agent/config /opt/agent/cache \
|
|
34
|
+
&& chown -R 998:998 /opt/agent \
|
|
35
|
+
&& chmod 700 /opt/agent/share/opencode
|
|
36
|
+
|
|
37
|
+
USER 998
|
|
38
|
+
WORKDIR /sandbox
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Agents
|
|
2
|
+
|
|
3
|
+
An agent is a harness plus what it is given to work with. Load one from YAML
|
|
4
|
+
or JSON with `Agent.load(path)`, or build it in Python with `Agent(...)`.
|
|
5
|
+
Relative paths in a config file resolve against that file.
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
name: reviewer # default: the file name
|
|
9
|
+
harness: claude # claude | codex | copilot | opencode | command
|
|
10
|
+
model: sonnet # passed to the harness as-is
|
|
11
|
+
instructions: Be concise. # free text
|
|
12
|
+
instruction_files: [AGENTS.md] # files whose contents are instructions
|
|
13
|
+
skills: [skills/review] # SKILL.md files or their folders
|
|
14
|
+
mcps: # the `mcpServers` shape every client uses
|
|
15
|
+
docs: {command: node, args: [docs-mcp.js], env: {LEVEL: "1"}}
|
|
16
|
+
hub: {url: "https://hub.example/mcp", headers: {Authorization: "Bearer ${HUB_TOKEN}"}}
|
|
17
|
+
mcp_config: .mcp.json # or load servers from an existing config file
|
|
18
|
+
workspace: repo # a directory the agent works on (always a copy)
|
|
19
|
+
env: {API_BASE: "${API_BASE}"} # extra environment; ${VAR} expands on the host
|
|
20
|
+
timeout: 900 # seconds per turn
|
|
21
|
+
options: {} # harness-specific settings, below
|
|
22
|
+
sandbox: {backend: openshell} # where it runs, below
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Agents are immutable. Derive variants with `with_` and `with_mcp`:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
judge = helper.with_(model="opus", instructions="You are a strict grader.")
|
|
29
|
+
tester = assistant.with_mcp("tiny", {"command": "node", "args": ["tiny-mcp.js"]})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Running
|
|
33
|
+
|
|
34
|
+
| Call | Does |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `agent.run(prompt, run_dir=None, collect=())` | one task; returns a `Result` |
|
|
37
|
+
| `agent.session(run_dir=None)` | a multi-turn `Session`; `session.send(prompt)` per turn |
|
|
38
|
+
|
|
39
|
+
A session keeps one sandbox and one harness conversation alive. Later turns
|
|
40
|
+
resume it with `--resume` (Claude Code), `exec resume` (Codex),
|
|
41
|
+
`--session-id`/`--continue` (Copilot), or `--session` (OpenCode). Instructions
|
|
42
|
+
and skills are sent on the first turn only.
|
|
43
|
+
|
|
44
|
+
`collect` copies workspace paths into the turn directory before the sandbox is
|
|
45
|
+
torn down; they are listed in `result.files`.
|
|
46
|
+
|
|
47
|
+
### Result
|
|
48
|
+
|
|
49
|
+
| Field | Meaning |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `status` | `completed`, `agent_error`, `timed_out`, `model_unavailable`, `sandbox_error`, `harness_error` |
|
|
52
|
+
| `ok` | `status == "completed"` |
|
|
53
|
+
| `answer` | the agent's final message |
|
|
54
|
+
| `json()` | the JSON value in the answer (tolerates prose and code fences) |
|
|
55
|
+
| `check()` | returns the result, or raises `AgentError` if it did not complete |
|
|
56
|
+
| `tool_calls` / `builtin_calls` | MCP calls and built-in tool calls: server, tool, status, arguments, result, error |
|
|
57
|
+
| `session_id`, `turn`, `duration`, `exit_code`, `error`, `files`, `dir` | the rest of the trace |
|
|
58
|
+
|
|
59
|
+
Each turn writes `prompt.txt`, `stdout.txt`, `stderr.txt`, `answer.md`,
|
|
60
|
+
`result.json`, and `files/` under `<run_dir>/turn-N/`.
|
|
61
|
+
|
|
62
|
+
## Harnesses
|
|
63
|
+
|
|
64
|
+
Instruction files and skills are rendered into a preamble ahead of the task
|
|
65
|
+
for every harness except OpenCode, which loads them natively. Every harness
|
|
66
|
+
accepts `options.bin` (host binary) and `options.extra_args` (appended
|
|
67
|
+
verbatim).
|
|
68
|
+
|
|
69
|
+
| Harness | Command | MCPs via | Options |
|
|
70
|
+
| --- | --- | --- | --- |
|
|
71
|
+
| `claude` | `claude -p --output-format stream-json` | `--mcp-config` (pre-approved) | `permission_mode`, `max_turns`, `allow_tools`, `deny_tools` |
|
|
72
|
+
| `codex` | `codex exec --json` | `-c mcp_servers.<name>.*` | `sandbox_mode`, `approval`, `reasoning_effort` |
|
|
73
|
+
| `copilot` | `copilot --output-format json --prompt` | `--additional-mcp-config` | `agent`, `reasoning_effort`, `mode`, `allow_all`, `allow_all_tools`, `allow_tools`, `deny_tools`, `add_dirs`, `allow_urls`, `deny_urls`, `plugin_dirs`, ... |
|
|
74
|
+
| `opencode` | `opencode run --format json` | its own config (`OPENCODE_CONFIG_CONTENT`) | `default_agent`, `config` (merged into `opencode.json`) |
|
|
75
|
+
| `command` | `options.command` | — | `command` (argv), `prompt` (`stdin` or `arg`), `output` (`text`, or a harness name to parse its stream) |
|
|
76
|
+
|
|
77
|
+
Codex reads `Bearer ${VAR}` and `${VAR}` headers from the environment itself,
|
|
78
|
+
so those secrets never appear in its argv. OpenCode denies `bash`, `edit`, and
|
|
79
|
+
`webfetch` unless `options.config.permission` allows them.
|
|
80
|
+
|
|
81
|
+
Coding-agent sessions are tagged as synthetic: Codex gets
|
|
82
|
+
`originator: ghostlab`, and Copilot session ids start with Ghostlab's reserved
|
|
83
|
+
UUID prefix, so rollout collectors can exclude them.
|
|
84
|
+
|
|
85
|
+
## Sandbox
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
sandbox:
|
|
89
|
+
backend: openshell # or local
|
|
90
|
+
image: base # an OpenShell image or Dockerfile
|
|
91
|
+
providers: [anthropic] # OpenShell providers holding credentials
|
|
92
|
+
policy: egress.yaml # an OpenShell policy file
|
|
93
|
+
env_allowlist: [GH_TOKEN] # host variables forwarded into the sandbox
|
|
94
|
+
uploads: [{source: data, target: /sandbox/data}]
|
|
95
|
+
files: {/opt/agent/share/opencode/auth.json: ~/.local/share/opencode/auth.json}
|
|
96
|
+
cpu: "2"
|
|
97
|
+
memory: 4Gi
|
|
98
|
+
keep: false # leave the sandbox running for debugging
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`sandbox: local` is shorthand for `{backend: local}`.
|
|
102
|
+
|
|
103
|
+
**OpenShell** (the default) creates one sandbox per session. It uploads the
|
|
104
|
+
workspace to `/sandbox/<name>`, every stdio MCP's program to `/sandbox/mcp`,
|
|
105
|
+
and any files the harness reads by path to `/sandbox/agent`. Paths in the
|
|
106
|
+
command are rewritten to those locations. `files` are copied in after creation
|
|
107
|
+
with mode 600, so credentials can live outside the workspace. With no
|
|
108
|
+
providers the sandbox is created with `--no-auto-providers`. For OpenCode,
|
|
109
|
+
when no `policy` is given, Ghostlab generates a least-privilege egress policy
|
|
110
|
+
for the model's provider. The sandbox log is saved as `openshell.log`, and the
|
|
111
|
+
sandbox is deleted unless `keep` is set. OpenShell failures are reported as
|
|
112
|
+
`sandbox_error` and never fall back to local execution.
|
|
113
|
+
|
|
114
|
+
**Local** runs the harness on the host, in a private copy of the workspace,
|
|
115
|
+
with the full host environment. The copy sits in a neutral temporary directory
|
|
116
|
+
while the agent runs — harnesses tell the model their working directory, and a
|
|
117
|
+
run directory's path names the job and item — and is moved to
|
|
118
|
+
`<run_dir>/workspace` afterwards. `uploads`, `files`,
|
|
119
|
+
and the other OpenShell settings do not apply. Use it only for agents and MCPs
|
|
120
|
+
you trust.
|
|
121
|
+
|
|
122
|
+
The image in [`docker/agent-sandbox.Dockerfile`](https://github.com/sajjadGG/Ghostlab/blob/main/docker/agent-sandbox.Dockerfile)
|
|
123
|
+
pins OpenCode and Copilot CLI builds and keeps their state outside the workspace.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CLI
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
ghostlab exec run one agent on one task
|
|
5
|
+
ghostlab run run or resume a pipeline
|
|
6
|
+
ghostlab status summarize a pipeline run
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Exit codes: `0` success, `1` the task or some items failed, `2` invalid
|
|
10
|
+
configuration.
|
|
11
|
+
|
|
12
|
+
## `ghostlab exec`
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
ghostlab exec --agent agent.yaml "Draft release notes into RELEASE.md" --collect RELEASE.md
|
|
16
|
+
echo "Summarize this" | ghostlab exec --agent agent.yaml -
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Flag | Meaning |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `--agent PATH` | agent config (required) |
|
|
22
|
+
| `TASK` / `-` / `--prompt-file PATH` | the task, inline, from stdin, or from a file |
|
|
23
|
+
| `--harness`, `--model`, `--timeout`, `--workspace` | override the config |
|
|
24
|
+
| `--sandbox openshell\|local` | override the execution boundary |
|
|
25
|
+
| `--provider NAME` | attach an OpenShell provider (repeatable) |
|
|
26
|
+
| `--collect PATH` | copy a workspace path into the run dir (repeatable) |
|
|
27
|
+
| `--run-dir DIR` | where to keep the trace (default `runs/<timestamp>-<agent>`) |
|
|
28
|
+
| `--json` | print the full result instead of the answer |
|
|
29
|
+
|
|
30
|
+
The answer goes to stdout, and the status line and run directory go to stderr.
|
|
31
|
+
|
|
32
|
+
## `ghostlab run`
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
ghostlab run examples/mcp-eval/pipeline.py --input examples/mcp-eval/targets.jsonl
|
|
36
|
+
ghostlab run examples/mcp-eval/pipeline.py # resume
|
|
37
|
+
ghostlab run pipeline.py:nightly --params params.yaml --param max_turns=6
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Flag | Meaning |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `FILE.py[:NAME]` | the module holding the `Pipeline` (`NAME` when it defines several) |
|
|
43
|
+
| `--input PATH` | items as `.jsonl`, JSON, or YAML (repeatable); omit to reuse the recorded inputs |
|
|
44
|
+
| `--param KEY=VALUE` | a parameter; `VALUE` is parsed as JSON when it can be, otherwise kept as a string |
|
|
45
|
+
| `--params PATH` | parameters from a YAML/JSON mapping |
|
|
46
|
+
| `--run-dir DIR` | run directory (default `runs/<pipeline name>`, so re-running resumes) |
|
|
47
|
+
|
|
48
|
+
An agent parameter can be a config path (`--param judge=agents/judge.yaml`) or
|
|
49
|
+
inline JSON (`--param 'judge={"harness": "claude", "model": "opus"}'`).
|
|
50
|
+
|
|
51
|
+
## `ghostlab status`
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
ghostlab status runs/mcp-eval
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Prints done, cached, and failed counts per stage and lists the failed items.
|
|
58
|
+
Exits `1` when the run is incomplete or has failures.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install ghostlab # Python 3.10+
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
You also need the CLI of at least one harness (`claude`, `codex`, `copilot`,
|
|
10
|
+
or `opencode`). For sandboxed runs, install
|
|
11
|
+
[NVIDIA OpenShell](https://docs.nvidia.com/openshell/latest/) and check it
|
|
12
|
+
with `openshell status`.
|
|
13
|
+
|
|
14
|
+
## Run one agent
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
# helper.yaml
|
|
18
|
+
harness: claude
|
|
19
|
+
model: sonnet
|
|
20
|
+
instructions: Be concise.
|
|
21
|
+
sandbox: local # or {backend: openshell, providers: [anthropic]}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ghostlab exec --agent helper.yaml "What is 17 * 23? Reply with the number only."
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The answer goes to stdout. The full trace (prompt, raw stream, stderr,
|
|
29
|
+
`result.json`) is kept in `runs/<timestamp>-helper/turn-1/`.
|
|
30
|
+
|
|
31
|
+
The same from Python:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from ghostlab import Agent
|
|
35
|
+
|
|
36
|
+
helper = Agent.load("helper.yaml")
|
|
37
|
+
result = helper.run("What is 17 * 23?")
|
|
38
|
+
print(result.status, result.answer)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Run one pipeline
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
# label.py
|
|
45
|
+
from ghostlab import Agent, Pipeline, stage
|
|
46
|
+
|
|
47
|
+
@stage(concurrency=8, retries=2)
|
|
48
|
+
def label(ctx, ticket):
|
|
49
|
+
reply = ctx.run(ctx.agent("labeler"),
|
|
50
|
+
f"Label as bug, billing, or other. JSON {{\"label\": ...}}.\n\n{ticket['text']}")
|
|
51
|
+
return {**ticket, **reply.json()}
|
|
52
|
+
|
|
53
|
+
pipeline = Pipeline("label", [label], params={"labeler": Agent.load("helper.yaml")})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ghostlab run label.py --input tickets.jsonl # writes runs/label/results.jsonl
|
|
58
|
+
ghostlab run label.py # resume: done items are skipped
|
|
59
|
+
ghostlab status runs/label
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Next: the [agent reference](agents.md) and the [pipeline guide](pipelines.md).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Ghostlab
|
|
2
|
+
|
|
3
|
+
Ghostlab runs coding agents headlessly and composes them into checkpointed,
|
|
4
|
+
resumable jobs.
|
|
5
|
+
|
|
6
|
+
- **One agent definition, any harness.** Claude Code, Codex, GitHub Copilot
|
|
7
|
+
CLI, OpenCode, or any CLI you point it at. Instructions, skills, MCP servers,
|
|
8
|
+
and a workspace mean the same thing in each.
|
|
9
|
+
- **Sandboxed by default.** The agent CLI, its stdio MCPs, and a copy of its
|
|
10
|
+
workspace run inside an NVIDIA OpenShell sandbox that is deleted afterwards.
|
|
11
|
+
Running on the host (`sandbox: local`) must be asked for.
|
|
12
|
+
- **Pipelines in plain Python.** Stages map, fan out, or gather items; each
|
|
13
|
+
stage calls agents through a context that caches every call. Re-running a
|
|
14
|
+
pipeline resumes it.
|
|
15
|
+
|
|
16
|
+
Ghostlab is the infrastructure layer. Evaluations, labeling runs, test
|
|
17
|
+
generation, and migrations are pipelines you write on top of it. See
|
|
18
|
+
[the MCP evaluation example](https://github.com/sajjadGG/Ghostlab/blob/main/examples/mcp-eval/pipeline.py).
|
|
19
|
+
|
|
20
|
+
| Page | Read it for |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| [Getting started](getting-started.md) | install, run one agent, run one pipeline |
|
|
23
|
+
| [Agents](agents.md) | the agent config, every harness's options, the sandbox |
|
|
24
|
+
| [Pipelines](pipelines.md) | stages, the context, checkpointing and resume |
|
|
25
|
+
| [CLI](cli.md) | `ghostlab exec`, `run`, `status` |
|
|
26
|
+
| [Publishing](publishing.md) | releasing the package and this wiki |
|