ghostlab 0.3.0__tar.gz → 0.4.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.
Files changed (180) hide show
  1. {ghostlab-0.3.0 → ghostlab-0.4.0}/PKG-INFO +205 -87
  2. {ghostlab-0.3.0 → ghostlab-0.4.0}/README.md +202 -84
  3. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/cli.md +327 -18
  4. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/concepts.md +23 -3
  5. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/configured-agent-lab.md +40 -10
  6. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/datasets-evaluation.md +5 -5
  7. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/getting-started.md +48 -10
  8. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/ghostlab-vision-gap-and-roadmap.md +35 -19
  9. ghostlab-0.4.0/docs/index.md +49 -0
  10. ghostlab-0.4.0/docs/run-an-agent.md +147 -0
  11. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/README.md +12 -0
  12. ghostlab-0.4.0/examples/agent-lab/task-agent.json +20 -0
  13. ghostlab-0.4.0/examples/copilot-agent.json +46 -0
  14. {ghostlab-0.3.0 → ghostlab-0.4.0}/llms.txt +12 -8
  15. {ghostlab-0.3.0 → ghostlab-0.4.0}/mkdocs.yml +2 -1
  16. {ghostlab-0.3.0 → ghostlab-0.4.0}/pyproject.toml +7 -1
  17. ghostlab-0.4.0/rehearsal/__init__.py +3 -0
  18. ghostlab-0.4.0/rehearsal/agent_exec.py +675 -0
  19. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/agent_profile.py +18 -5
  20. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/agent_sandbox.py +33 -18
  21. ghostlab-0.4.0/rehearsal/artifact_run.py +919 -0
  22. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/cli.py +1034 -103
  23. ghostlab-0.4.0/rehearsal/config.py +863 -0
  24. ghostlab-0.4.0/rehearsal/copilot_backend.py +256 -0
  25. ghostlab-0.4.0/rehearsal/copilot_llm_backend.py +299 -0
  26. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/evaluate.py +182 -6
  27. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/generate.py +38 -10
  28. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/hosts/__init__.py +3 -2
  29. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/jobs.py +311 -33
  30. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/lab.py +66 -17
  31. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/llm_backend.py +32 -12
  32. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/opencode_backend.py +28 -1
  33. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/orchestrator.py +125 -4
  34. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/profile.py +61 -9
  35. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/prompts.py +24 -0
  36. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/resolved_config.py +126 -29
  37. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/runner_presets.py +52 -16
  38. ghostlab-0.4.0/rehearsal/runners.py +604 -0
  39. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/sandbox.py +489 -16
  40. ghostlab-0.4.0/rehearsal/scorecard.py +617 -0
  41. ghostlab-0.4.0/rehearsal/scorer_wrapper.py +154 -0
  42. ghostlab-0.4.0/rehearsal/scorers.py +2425 -0
  43. ghostlab-0.4.0/rehearsal/session_provenance.py +40 -0
  44. ghostlab-0.4.0/rehearsal/skills.py +124 -0
  45. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/spec.py +45 -14
  46. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/tool_capture.py +211 -1
  47. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/ui/app.py +224 -20
  48. ghostlab-0.4.0/rehearsal/workspace_export.py +703 -0
  49. ghostlab-0.3.0/runners/codex-cortex-local-aut.json → ghostlab-0.4.0/runners/codex-aut.example.json +1 -1
  50. ghostlab-0.3.0/runners/codex-cortex-local-session.json → ghostlab-0.4.0/runners/codex-session.example.json +1 -1
  51. ghostlab-0.4.0/runners/copilot-aut.example.json +30 -0
  52. ghostlab-0.4.0/runners/copilot-user.example.json +28 -0
  53. ghostlab-0.4.0/tests/openshell_fake.py +212 -0
  54. ghostlab-0.4.0/tests/test_agent_exec.py +366 -0
  55. ghostlab-0.4.0/tests/test_artifact_run.py +1249 -0
  56. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_cli_spec_flow.py +2 -1
  57. ghostlab-0.4.0/tests/test_copilot_backend.py +608 -0
  58. ghostlab-0.4.0/tests/test_copilot_llm_backend.py +279 -0
  59. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_evaluate.py +44 -0
  60. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_generate.py +26 -0
  61. ghostlab-0.4.0/tests/test_harness_runners.py +118 -0
  62. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_jobs.py +30 -2
  63. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_opencode_backend.py +23 -0
  64. ghostlab-0.4.0/tests/test_sandbox.py +310 -0
  65. ghostlab-0.4.0/tests/test_scorecard.py +343 -0
  66. ghostlab-0.4.0/tests/test_scorers.py +2141 -0
  67. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_session_runner.py +28 -0
  68. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_skills.py +10 -0
  69. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_spec.py +12 -0
  70. ghostlab-0.3.0/docs/index.md +0 -36
  71. ghostlab-0.3.0/rehearsal/__init__.py +0 -3
  72. ghostlab-0.3.0/rehearsal/config.py +0 -221
  73. ghostlab-0.3.0/rehearsal/runners.py +0 -293
  74. ghostlab-0.3.0/rehearsal/scorecard.py +0 -266
  75. ghostlab-0.3.0/rehearsal/skills.py +0 -47
  76. ghostlab-0.3.0/runners/codex-cortex-aut.json +0 -20
  77. ghostlab-0.3.0/tests/test_sandbox.py +0 -132
  78. ghostlab-0.3.0/tests/test_scorecard.py +0 -112
  79. {ghostlab-0.3.0 → ghostlab-0.4.0}/.gitignore +0 -0
  80. {ghostlab-0.3.0 → ghostlab-0.4.0}/CONTRIBUTING.md +0 -0
  81. {ghostlab-0.3.0 → ghostlab-0.4.0}/LICENSE +0 -0
  82. {ghostlab-0.3.0 → ghostlab-0.4.0}/docs/publishing.md +0 -0
  83. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/AGENTS.md +0 -0
  84. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/agent.json +0 -0
  85. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/mcp.json +0 -0
  86. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/repo/CHANGELOG.md +0 -0
  87. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/repo/README.md +0 -0
  88. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/repo/src/app.py +0 -0
  89. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/skills/release-notes/SKILL.md +0 -0
  90. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent-lab/tiny-mcp.js +0 -0
  91. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/agent.json +0 -0
  92. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/demo-skill/SKILL.md +0 -0
  93. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/mcp-config.json +0 -0
  94. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/scenario.json +0 -0
  95. {ghostlab-0.3.0 → ghostlab-0.4.0}/examples/target.json +0 -0
  96. {ghostlab-0.3.0 → ghostlab-0.4.0}/ghostlab/__init__.py +0 -0
  97. {ghostlab-0.3.0 → ghostlab-0.4.0}/ghostlab/__main__.py +0 -0
  98. {ghostlab-0.3.0 → ghostlab-0.4.0}/ghostlab/cli.py +0 -0
  99. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/__main__.py +0 -0
  100. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/agents.py +0 -0
  101. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/__init__.py +0 -0
  102. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/assertions.py +0 -0
  103. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/executor.py +0 -0
  104. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/live.py +0 -0
  105. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/protocol.py +0 -0
  106. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/renderer.py +0 -0
  107. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/apps_host/report.py +0 -0
  108. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/cli_ui.py +0 -0
  109. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/codex_backend.py +0 -0
  110. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/compare.py +0 -0
  111. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/contract.py +0 -0
  112. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/critique.py +0 -0
  113. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/dashboard.py +0 -0
  114. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/dataset.py +0 -0
  115. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/hosts/base.py +0 -0
  116. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/hosts/direct.py +0 -0
  117. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/hosts/runner.py +0 -0
  118. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/inspect.py +0 -0
  119. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/logging.py +0 -0
  120. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/mcp_apps.py +0 -0
  121. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/mcp_client.py +0 -0
  122. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/mcp_config.py +0 -0
  123. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/mcp_targets.py +0 -0
  124. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/opencode_config.py +0 -0
  125. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/personas.py +0 -0
  126. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/plan.py +0 -0
  127. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/plan_generate.py +0 -0
  128. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/readiness.py +0 -0
  129. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/report.py +0 -0
  130. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/review.py +0 -0
  131. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/rollout_report.py +0 -0
  132. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/sampling.py +0 -0
  133. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/setup_runtime.py +0 -0
  134. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/__init__.py +0 -0
  135. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/db.py +0 -0
  136. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/hashing.py +0 -0
  137. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/ids.py +0 -0
  138. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/migrations/0001_initial.sql +0 -0
  139. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/redact.py +0 -0
  140. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/storage/repository.py +0 -0
  141. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/termcolor.py +0 -0
  142. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/testrun.py +0 -0
  143. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/types.py +0 -0
  144. {ghostlab-0.3.0 → ghostlab-0.4.0}/rehearsal/ui/__init__.py +0 -0
  145. {ghostlab-0.3.0 → ghostlab-0.4.0}/runners/claude-process.example.json +0 -0
  146. {ghostlab-0.3.0 → ghostlab-0.4.0}/runners/codex-process.example.json +0 -0
  147. {ghostlab-0.3.0 → ghostlab-0.4.0}/runners/codex-user-emulator.json +0 -0
  148. {ghostlab-0.3.0 → ghostlab-0.4.0}/runners/mock-aut.json +0 -0
  149. {ghostlab-0.3.0 → ghostlab-0.4.0}/runners/mock-user.json +0 -0
  150. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_agents.py +0 -0
  151. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_apps_host.py +0 -0
  152. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_apps_live.py +0 -0
  153. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_codex_backend_sandbox.py +0 -0
  154. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_compare.py +0 -0
  155. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_config.py +0 -0
  156. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_configured_agent.py +0 -0
  157. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_contract.py +0 -0
  158. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_critique.py +0 -0
  159. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_dataset.py +0 -0
  160. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_hosts_runner.py +0 -0
  161. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_inspect_lint.py +0 -0
  162. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_mcp_apps.py +0 -0
  163. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_mcp_client.py +0 -0
  164. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_mcp_targets.py +0 -0
  165. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_orchestrator.py +0 -0
  166. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_personas.py +0 -0
  167. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_plan.py +0 -0
  168. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_plan_generate.py +0 -0
  169. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_profile.py +0 -0
  170. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_prompts.py +0 -0
  171. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_readiness.py +0 -0
  172. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_review.py +0 -0
  173. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_runner_presets.py +0 -0
  174. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_sampling.py +0 -0
  175. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_sandbox_stdio.py +0 -0
  176. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_setup_runtime.py +0 -0
  177. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_storage.py +0 -0
  178. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_testrun.py +0 -0
  179. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_tool_capture.py +0 -0
  180. {ghostlab-0.3.0 → ghostlab-0.4.0}/tests/test_widgets_and_dashboard.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ghostlab
3
- Version: 0.3.0
4
- Summary: OpenShell-first end-to-end evaluation lab for configured AI agents, MCP servers, skills, and MCP Apps.
3
+ Version: 0.4.0
4
+ Summary: Define an AI agent once (model, instructions, skills, MCPs) and run or evaluate it in Claude Code, Codex, Copilot, or OpenCode, sandboxed in NVIDIA OpenShell.
5
5
  Project-URL: Documentation, https://sajjadgg.github.io/Ghostlab/
6
6
  Project-URL: Homepage, https://github.com/sajjadGG/Ghostlab
7
7
  Project-URL: Repository, https://github.com/sajjadGG/Ghostlab
@@ -10,7 +10,7 @@ Project-URL: Changelog, https://github.com/sajjadGG/Ghostlab/releases
10
10
  Author: SajjadGG
11
11
  License: MIT
12
12
  License-File: LICENSE
13
- Keywords: agents,ai-agents,claude,cli,codex,end-to-end-testing,evaluation,llm,llm-evaluation,mcp,mcp-apps,model-context-protocol,testing
13
+ Keywords: agent-skills,agents,ai-agents,claude,claude-code,cli,codex,end-to-end-testing,evaluation,github-copilot,llm,llm-evaluation,mcp,mcp-apps,model-context-protocol,opencode,openshell,sandbox,testing
14
14
  Classifier: Development Status :: 3 - Alpha
15
15
  Classifier: Environment :: Console
16
16
  Classifier: Intended Audience :: Developers
@@ -42,48 +42,42 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  # Ghostlab
44
44
 
45
- > A local, end-to-end **testing lab for configured AI agents** — coding agents role-play
46
- > real users, drive your tools over multiple turns, and the harness captures
47
- > traces, scores outcomes, and even **renders and clicks through MCP Apps UI
48
- > widgets**.
45
+ > **Define an agent once — model, instructions, skills, MCPs, workspace — and run
46
+ > it in any coding-agent harness, sandboxed in NVIDIA OpenShell.** Use it to get
47
+ > a task done, or to evaluate how well the agent does its job.
49
48
 
50
49
  [![CI](https://github.com/sajjadGG/Ghostlab/actions/workflows/ci.yml/badge.svg)](https://github.com/sajjadGG/Ghostlab/actions)
50
+ [![PyPI](https://img.shields.io/pypi/v/ghostlab)](https://pypi.org/project/ghostlab/)
51
51
  [![Docs](https://img.shields.io/badge/docs-wiki-blue)](https://sajjadgg.github.io/Ghostlab/)
52
52
  [![Python](https://img.shields.io/badge/python-3.10%E2%80%933.13-blue)](pyproject.toml)
53
53
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
54
54
  [![llms.txt](https://img.shields.io/badge/llms.txt-✓-purple)](llms.txt)
55
55
 
56
- **Test an agent the way it is actually used** — not only with unit tests against
57
- the protocol, but with a real coding agent (Codex / Claude) that picks tools,
58
- makes mistakes, and tries to accomplish goals, while a second agent plays the
59
- user. Protocol-level checks (schema errors, a tool call that 500s) are useful
60
- sanity checks, but they aren't the real test — the real test is whether an
61
- agent can actually get a task done with its configured MCPs, skills, workspace,
62
- instructions, and runner, end to end.
63
-
64
56
  📖 **Docs wiki:** https://sajjadgg.github.io/Ghostlab/ · 🤖 **For agents:** [`llms.txt`](llms.txt) · 🛠 **Contributing:** [`CONTRIBUTING.md`](CONTRIBUTING.md)
65
57
 
66
- > **Naming:** the project and repo are **Ghostlab** (formerly *Rehearsal*). The CLI
67
- > is `ghostlab`, with `rehearsal` kept as an alias, and the installed Python
68
- > package is `rehearsal` — all the same project.
69
-
70
58
  <p align="center">
71
59
  <img src="static/ghostlablogo.png" width="400" alt="Logo" align="center">
72
60
  </p>
73
61
 
74
- ## Quickstart
62
+ Ghostlab does three things with one agent definition:
75
63
 
76
- ```bash
77
- python3 -m venv .venv # Python 3.10+
78
- .venv/bin/pip install -e . # add '.[ui]' for the web UI, '.[apps]' for widget rendering
64
+ | | Command | What you get |
65
+ | --- | --- | --- |
66
+ | **Run** | `ghostlab exec` | The agent does a task in Claude Code, Codex, GitHub Copilot CLI, or OpenCode — inside a throwaway OpenShell sandbox — and you get its answer, tool trace, and any files it produced. |
67
+ | **Evaluate** | `ghostlab lab` / `create` → `test` → `review` | A second agent plays realistic users, drives the configured agent over multiple turns, and a judge scores the outcome against a release gate. |
68
+ | **Build eval sets** | `ghostlab create --target` / `--skill`, `generate-dataset` | Persona × scenario test sets generated from an MCP server's tools or a skill's `SKILL.md`, curated as files, rerunnable in CI. |
79
69
 
80
- ghostlab create # evaluate an MCP server
81
- ghostlab lab # evaluate a configured agent (model, skills, MCPs, code)
70
+ > **Naming:** the CLI is `ghostlab` (with `rehearsal` kept as an alias) and the
71
+ > installed Python package is `rehearsal` — all the same project.
72
+
73
+ ## Install
74
+
75
+ ```bash
76
+ pip install ghostlab # add [ui] for the web UI, [apps] for MCP Apps rendering
77
+ ghostlab doctor # checks OpenShell and the agent CLIs
82
78
  ```
83
79
 
84
- Pick `create` when the thing under test is an **MCP server**, and
85
- [`lab`](#evaluate-a-configured-agent-ghostlab-lab) when it is an **agent** — a
86
- model plus instructions, skills, MCPs, permissions, and a codebase.
80
+ From a checkout: `python3 -m venv .venv && .venv/bin/pip install -e .`
87
81
 
88
82
  Ghostlab uses [NVIDIA OpenShell](https://docs.nvidia.com/openshell/latest/)
89
83
  as its default execution boundary. Install OpenShell, start a supported compute
@@ -103,6 +97,65 @@ brew services restart openshell
103
97
  openshell status
104
98
  ```
105
99
 
100
+ ## Run an agent: `ghostlab exec`
101
+
102
+ Describe the agent in one file:
103
+
104
+ ```json
105
+ {
106
+ "id": "release-helper",
107
+ "instructions": "Be concise. Never claim something was published.",
108
+ "runtime": { "backend": "claude", "model": "sonnet" },
109
+ "workspace": "repo",
110
+ "inputs": {
111
+ "mcps": [{ "config_ref": "mcp.json", "server": "tiny" }],
112
+ "skills": ["skills/release-notes"]
113
+ },
114
+ "sandbox": { "backend": "openshell", "providers": ["anthropic"] }
115
+ }
116
+ ```
117
+
118
+ Then hand it a task:
119
+
120
+ ```bash
121
+ ghostlab exec --agent agent.json "Draft release notes from CHANGELOG.md"
122
+ ghostlab exec --agent agent.json --backend copilot --model gpt-5-mini --provider copilot "..."
123
+ ghostlab exec --agent agent.json --prompt-file task.md --export /sandbox/repo/NOTES.md > answer.md
124
+ ```
125
+
126
+ The answer goes to stdout; status goes to stderr; the full trace — composed
127
+ prompt, raw harness stream, every MCP and built-in tool call, and
128
+ `exec.json` — is kept under `runs/`. The same definition runs in every harness:
129
+
130
+ | `runtime.backend` | Harness | How MCPs attach | How skills attach |
131
+ | --- | --- | --- | --- |
132
+ | `claude` | Claude Code (`claude -p`) | `--mcp-config` (strict), MCP tools pre-approved | Prompt preamble |
133
+ | `codex` | Codex (`codex exec --json`) | `-c mcp_servers.*` overrides | Prompt preamble |
134
+ | `copilot` | GitHub Copilot CLI | `--additional-mcp-config` | Prompt preamble |
135
+ | `opencode` | OpenCode (`opencode run`) | Generated `opencode.json` | Native `skills.paths` |
136
+ | `runner` | Any CLI in `runner.command` | Your command | Prompt preamble |
137
+
138
+ Under OpenShell (the default), the agent CLI, its stdio MCP servers, its skills,
139
+ and an uploaded copy of its workspace all live in one sandbox that is deleted
140
+ afterwards; credentials arrive through OpenShell providers (`--provider`) and
141
+ nothing on the host is touched. The OpenShell `base` image already ships all
142
+ four harness CLIs. `--sandbox local` runs on your machine and edits the
143
+ workspace in place — use it only for trusted agents. See
144
+ [Run an Agent](docs/run-an-agent.md).
145
+
146
+ ## Evaluate an agent, an MCP server, or a skill
147
+
148
+ ```bash
149
+ ghostlab lab # evaluate a configured agent (model, skills, MCPs, code)
150
+ ghostlab create # evaluate an MCP server or a skill
151
+ ```
152
+
153
+ Pick [`lab`](#evaluate-a-configured-agent-ghostlab-lab) when the thing under
154
+ test is an **agent** — a model plus instructions, skills, MCPs, permissions,
155
+ and a codebase — and `create` when it is an **MCP server** or a **skill**. The agent under test
156
+ can run in Codex, OpenCode, GitHub Copilot CLI, or Claude Code
157
+ (`--aut-backend claude`).
158
+
106
159
  OpenShell is the default; there is no `--local` flag. Use the explicit
107
160
  `--sandbox local` escape hatch only for trusted code that you intentionally
108
161
  want to execute directly on the host:
@@ -121,11 +174,12 @@ logs with the run artifacts, and deletes the sandboxes at teardown. Local stdio
121
174
  MCP processes in the job pipeline are routed through the same boundary. OpenShell failures remain
122
175
  `harness_error`s and never silently fall back to host execution.
123
176
 
124
- That's the whole flow. Interactive `ghostlab create` guides you through the
177
+ Interactive `ghostlab create` guides you through the
125
178
  evaluation subject (agent/MCP/skill), OpenShell image/providers, generation
126
- size, four model roles (AUT, user emulator, generation, and judge), Codex
127
- approval/sandbox policy, runner lifecycle/timeout, release gate, and whether to
128
- run immediately. The Questionary/Rich terminal UI provides arrow-key selection,
179
+ size, four model roles (AUT, user emulator, generation, and judge), separate AUT
180
+ and user runner backends, Copilot/VS Code custom-agent names, runner policy,
181
+ lifecycle/timeout, release gate, and whether to run immediately. The
182
+ Questionary/Rich terminal UI provides arrow-key selection,
129
183
  multi-select suite picking, color, and numbered progress. The same choices
130
184
  remain available as flags for scripts and CI.
131
185
 
@@ -135,9 +189,10 @@ remain available as flags for scripts and CI.
135
189
  skills, workspace, and assets. `--target` and `--skill` remain simple shorthands.
136
190
  2. **Discover** — connects to the target, lints its contract (schema errors,
137
191
  risk labels), and probes any MCP Apps `ui://` widgets.
138
- 3. **Configure semantic testing** — wires the configured runner (Codex by
139
- default) as the **agent-under-test** and displays its exact command, model,
140
- approval mode, nested sandbox, parser, and timeout.
192
+ 3. **Configure semantic testing** — wires the configured runner (Codex,
193
+ OpenCode, or GitHub Copilot CLI) as the **agent-under-test** and displays its
194
+ exact command, model, custom agent, policy, parser, and timeout. The user
195
+ emulator receives a separate runner and never receives the AUT's target MCP.
141
196
  4. **Generate a test plan** — personas × scenarios for the semantic/security
142
197
  suites, plus deterministic coverage for every discovered tool
143
198
  (`test-plan.yaml`), all editable afterward.
@@ -229,31 +284,6 @@ reasoning, and the full dual-agent transcript with inline tool calls:
229
284
  <img src="static/case-report-transcript.png" width="760" alt="Per-case report with judge verdict and conversation transcript">
230
285
  </p>
231
286
 
232
- ## Goal
233
-
234
- Build a repeatable, sandboxed tester that can:
235
-
236
- - Run arbitrary configured agents inside NVIDIA OpenShell.
237
- - Compose zero or more MCPs, skills, instructions, workspace files, and assets.
238
- - Launch one coding-agent session as the **agent-under-test**.
239
- - Launch another coding-agent session as the **user emulator** (persona + goal driven).
240
- - Drive multi-turn interactions between them.
241
- - Capture full traces, tool activity, failures, and outcomes.
242
-
243
- This lets you test with your existing Codex/Claude usage path, instead of wiring a separate LLM provider deployment just for E2E testing.
244
-
245
- ## Scope
246
-
247
- Ghostlab is intentionally **app-agnostic**:
248
-
249
- - Treats an agent configuration—not a single MCP—as the evaluation boundary.
250
- - Works with MCP servers reachable by stdio/SSE/streamable HTTP and local skills.
251
- - Supports local or remote MCP endpoints.
252
- - Supports multiple coding-agent runners (Codex, OpenCode, Claude Code, and future adapters).
253
- - Expresses an OpenCode agent's full configuration — model, instructions, skills, subagents, tool permissions, and any number of MCPs — and runs all of it inside the sandbox.
254
-
255
- No Cortex-specific assumptions are required in the core harness.
256
-
257
287
  ## Reference
258
288
 
259
289
  Everything below is the individual-command reference and advanced usage —
@@ -311,8 +341,9 @@ primary discovery input for one-MCP/one-skill jobs:
311
341
  - `capabilities`: optional expected tools/resources/prompts
312
342
  - `startup`: optional health checks and boot timeout
313
343
 
314
- The canonical `agent` section contains `runner`, `instructions`, and composable
315
- `inputs.mcps`, `inputs.skills`, and assets. The sibling `sandbox` section defaults
344
+ The canonical `agent` section contains a `runtime` (harness backend, model,
345
+ and harness-specific settings) or a raw `runner`, `instructions`, and composable
346
+ `inputs.mcps`, `inputs.skills`, and assets — the same file `ghostlab exec` runs. The sibling `sandbox` section defaults
316
347
  to OpenShell and controls the image, uploads, workdir, policy, resource limits,
317
348
  network mode, environment allowlist, providers, logs, and cleanup.
318
349
  `backend: local` is explicit unsandboxed compatibility—not a fallback.
@@ -321,6 +352,7 @@ network mode, environment allowlist, providers, logs, and cleanup.
321
352
 
322
353
  The package installs two equivalent console scripts: `ghostlab` and `rehearsal`.
323
354
 
355
+ - `ghostlab exec` — run a configured agent on one task in any harness (Claude Code, Codex, Copilot, OpenCode), inside OpenShell, and print its answer.
324
356
  - `ghostlab lab` — guided setup for a **configured agent** (model, instructions, skills, MCPs, permissions, code), then generate scenarios from its inferred purpose and run them fully sandboxed.
325
357
  - `ghostlab create` — the end-to-end wizard described above.
326
358
  - `ghostlab init` — advanced: scaffold a standalone `ghostlab.yaml` **spec** from a target JSON (see [spec vs job](#spec-vs-job) — most users want `ghostlab create`).
@@ -335,7 +367,9 @@ The package installs two equivalent console scripts: `ghostlab` and `rehearsal`.
335
367
  - `ghostlab run` — run one dual-agent E2E scenario directly.
336
368
  - `ghostlab evaluate` — score a run into a pass/fail verdict (codex judge).
337
369
  - `ghostlab critique` — rate a run's tool ergonomics from the agent's perspective (codex).
338
- - `ghostlab scorecard` — roll run verdicts and critiques into a summary scorecard.
370
+ - `ghostlab scorecard` — roll run verdicts and critiques into a summary scorecard, or aggregate benchmark attempts with `--attempts`.
371
+ - `ghostlab artifact-run` — run one configured agent once on a mutable workspace and export what it produced.
372
+ - `ghostlab scorer-run` — score an exported candidate workspace with a hidden scorer package.
339
373
  - `ghostlab compare` — diff two dataset runs for regressions.
340
374
  - `ghostlab apps-probe` / `apps-render` — probe/render MCP Apps `ui://` widgets.
341
375
  - `ghostlab doctor` — check the sandbox and both LLM backends (`--probe` for a live check).
@@ -553,13 +587,13 @@ persona-specific scenarios, and the pairs become runnable cases:
553
587
  ghostlab generate-dataset \
554
588
  --profile runs/<id>-inspect/capabilities.json \
555
589
  --personas 3 --scenarios-per-persona 3 --seed 7 \
556
- --name cortex
590
+ --name my-mcp
557
591
  ```
558
592
 
559
593
  This writes a self-contained dataset directory:
560
594
 
561
595
  ```text
562
- datasets/cortex/
596
+ datasets/my-mcp/
563
597
  dataset.json manifest: mcp, seed, cases[]
564
598
  personas/<id>.json
565
599
  scenarios/<id>.json persona-namespaced; inline `persona` is a situational note
@@ -571,7 +605,7 @@ Before spending agent credits, check that the dataset makes sense:
571
605
 
572
606
  ```bash
573
607
  ghostlab review-dataset \
574
- --dataset datasets/cortex \
608
+ --dataset datasets/my-mcp \
575
609
  --profile runs/<id>-inspect/capabilities.json
576
610
  ```
577
611
 
@@ -581,7 +615,7 @@ tools, personas with no scenarios). Curation is file-first — each case gets a
581
615
  `status` in `dataset.json` (`pending` / `approved` / `rejected` / `needs-edit`):
582
616
 
583
617
  ```bash
584
- ghostlab review-dataset --dataset datasets/cortex \
618
+ ghostlab review-dataset --dataset datasets/my-mcp \
585
619
  --approve case-a case-b --reject case-c
586
620
  ```
587
621
 
@@ -589,9 +623,9 @@ ghostlab review-dataset --dataset datasets/cortex \
589
623
 
590
624
  ```bash
591
625
  ghostlab run-dataset \
592
- --dataset datasets/cortex \
626
+ --dataset datasets/my-mcp \
593
627
  --target target.json \
594
- --aut-runner runners/codex-cortex-aut.json \
628
+ --aut-runner runners/codex-aut.example.json \
595
629
  --user-runner runners/codex-user-emulator.json \
596
630
  --provider openai \
597
631
  --limit 2
@@ -634,6 +668,70 @@ ghostlab compare --base runs/<base>-summary --candidate runs/<cand>-summary \
634
668
  Diffs case-by-case on verdict, listing regressions first, then fixes, then
635
669
  other changes. Exits non-zero when there are regressions, so it can gate CI.
636
670
 
671
+ ### Benchmark a coding agent: `artifact-run` / `scorer-run`
672
+
673
+ `run` evaluates a conversation. A benchmark task is the opposite shape: one
674
+ message, a repository the agent may rewrite, and a verdict that depends only on
675
+ the state it left behind. Two commands cover it, and neither one creates a user
676
+ emulator.
677
+
678
+ ```bash
679
+ ghostlab artifact-run \
680
+ --agent agents/candidate.json \
681
+ --workspace <materialized-base-repo> \
682
+ --prompt-file tasks/<task_id>/public/prompt.txt \
683
+ --sandbox-image 'project@sha256:<digest>' \
684
+ --setup-command '["python3","-m","venv",".venv"]' \
685
+ --optional-export /sandbox/output/notes.json=notes.json \
686
+ --export-workspace candidate-state.tar.zst \
687
+ --run-dir attempts/<attempt_id>
688
+
689
+ ghostlab scorer-run \
690
+ --task tasks/<task_id>/public/task.json \
691
+ --scorer tasks/<task_id>/private/scorer/scorer.json \
692
+ --candidate attempts/<attempt_id>/candidate-state.tar.zst \
693
+ --trace attempts/<attempt_id>/events.jsonl \
694
+ --output attempts/<attempt_id>/score-report.json
695
+ ```
696
+
697
+ The workspace is uploaded into an OpenShell sandbox, so the agent edits a
698
+ throwaway copy. Before that sandbox is deleted, `--export-workspace` takes a
699
+ canonical export: sorted paths with modes, sizes, and SHA-256 hashes in
700
+ `status.json`, a `diff.patch`, an `untracked.json`, and a deterministic archive
701
+ of exactly those files. Changed and untracked files are kept; `.git/` and the
702
+ usual build and cache directories are not. The post-run workspace hash is
703
+ recorded even without an archive. Required `--export` paths fail the run when
704
+ absent; `--optional-export` paths are downloaded only when present.
705
+ `--sandbox-image` replaces the agent definition's image with the task's pinned
706
+ project environment, and repeatable `--setup-command` values are JSON argument
707
+ arrays executed in that same sandbox before the agent turn. Artifact runs
708
+ require a root-owned `/usr/bin/python3` with the standard library; Ghostlab
709
+ validates that trusted exporter runtime before it sends the prompt and verifies
710
+ the downloaded archive against `status.json` before accepting the candidate
711
+ state.
712
+
713
+ `scorer-run` never reuses the agent's sandbox. The candidate is mounted
714
+ read-only at the path supplied by `repo_path`, hidden fixtures live at
715
+ `GHOSTLAB_FIXTURES_ROOT`, and only `GHOSTLAB_OUTPUT_ROOT` and `/tmp` are
716
+ writable. OpenShell 0.0.80 requires these roots below `/sandbox`; scorers must
717
+ use the score input and `GHOSTLAB_*_ROOT` variables rather than hard-code the
718
+ logical manifest paths. A fail-closed Landlock launcher applies this boundary
719
+ to the scorer process. A scorer that executes candidate code must invoke it
720
+ through `GHOSTLAB_SECURE_EXEC` (or an equivalently restrictive nested sandbox)
721
+ without granting fixture, input, scorer, or report access. The agent's trace is
722
+ redacted down to tool and timing evidence first, so the scorer cannot see who
723
+ produced the candidate. The deterministic phase runs with no network and no
724
+ provider credentials; a hybrid scorer's residual judge gets provider access
725
+ in a second sandbox that cannot execute anything. Ghostlab computes the total
726
+ outside both sandboxes: weights must sum to `1.0`, a failed hard gate forces
727
+ `0`, and unscored soft components are reported rather than renormalized away.
728
+ Scorer crashes, timeouts, judge outages, and unusable candidate artifacts get
729
+ their own statuses and are never turned into a zero.
730
+
731
+ `ghostlab scorecard --attempts <dir>` then rolls attempts up with equal weight
732
+ per rollout source, reporting pass rate, per-component means, seed spread,
733
+ valid coverage, and the error counts it deliberately excluded from the score.
734
+
637
735
  ### MCP Apps: `apps-probe` / `apps-render`
638
736
 
639
737
  Some MCPs ship **MCP Apps UI** resources — a tool's `_meta.ui.resourceUri`
@@ -667,21 +765,21 @@ if the render errored or any assertion failed.
667
765
  ### Session runner (one live agent across turns)
668
766
 
669
767
  By default each turn spawns a fresh agent process and the orchestrator replays
670
- the transcript. The session runner (`"kind": "codex-session"`) instead keeps
671
- one codex session alive: turn 1 records the `thread_id`, and later turns run
672
- `codex exec resume <thread_id>` so codex retains context — fewer tokens, no
673
- repeated cold-start noise.
768
+ the transcript. Stateful runners keep native agent context: `codex-session`
769
+ captures a Codex `thread_id` and resumes it, while `copilot-session` assigns one
770
+ Copilot `--session-id` to every turn. Both avoid repeated cold starts and prompt
771
+ replay.
674
772
 
675
773
  ```bash
676
774
  ghostlab run --target target.json --scenario <scenario.json> \
677
- --aut-runner runners/codex-cortex-local-session.json --user-runner <user.json>
775
+ --aut-runner runners/codex-session.example.json --user-runner <user.json>
678
776
  ```
679
777
 
680
778
  ### Validate your setup: `doctor`
681
779
 
682
780
  ```bash
683
781
  ghostlab doctor # validates runners/*.json
684
- ghostlab doctor --runners runners/codex-cortex-local-session.json
782
+ ghostlab doctor --runners runners/codex-session.example.json
685
783
  ghostlab doctor --sandbox local # trusted local mode; skips OpenShell checks
686
784
  ```
687
785
 
@@ -690,7 +788,7 @@ validates each runner's kind, command, and parser, and checks the OpenShell
690
788
  CLI/gateway by default. Add `--probe` to verify a backend can actually answer
691
789
  instead of only confirming its binary exists.
692
790
 
693
- ### Coding-agent backends (codex or opencode)
791
+ ### Generation and judge backends (codex or opencode)
694
792
 
695
793
  Ghostlab drives a coding-agent CLI for generation, the agent-under-test, and
696
794
  judging. `inspect` needs no agent — it is a direct MCP client.
@@ -742,12 +840,12 @@ pass/fail/skip verdicts, gate failures). Set `NO_COLOR=1` (or
742
840
  `GHOSTLAB_COLOR=0`) to disable it, `GHOSTLAB_COLOR=1` to force it on (e.g.
743
841
  piping into a pager that groks ANSI).
744
842
 
745
- Use `ghostlab config --job <name>` to see the exact effective Codex command,
746
- model, approval policy, nested sandbox, runner parser/timeout, all four model
747
- roles, composed MCP/skill inputs, and OpenShell configuration. Add `--json` for
748
- machine-readable output. If a command omits `-m`, Ghostlab resolves the
749
- top-level model from Codex's `config.toml` and identifies that source. The
750
- Streamlit Overview and Configure tab expose the same values.
843
+ Use `ghostlab config --job <name>` to see the exact effective AUT and user
844
+ runner commands, backend/model/custom-agent selection, policy, parser/timeout,
845
+ all four model roles, composed MCP/skill inputs, and OpenShell configuration.
846
+ Add `--json` for machine-readable output. If a Codex command omits `-m`,
847
+ Ghostlab resolves the top-level model from Codex's `config.toml` and identifies
848
+ that source. The Streamlit Overview and Configure tab expose the same values.
751
849
 
752
850
  ## Runner Configs
753
851
 
@@ -771,7 +869,9 @@ Process runner:
771
869
 
772
870
  The process runner starts one fresh process per turn. `prompt_mode` can be
773
871
  `stdin`, `append-arg`, or `replace-placeholder`. `ghostlab create` synthesizes
774
- one of these automatically for the agent-under-test host (wiring the target
872
+ separate `jobs/<name>/runners/aut.json` and
873
+ `jobs/<name>/runners/user.json` files. The AUT receives the target MCP while
874
+ the user emulator explicitly does not. Codex wires the target
775
875
  MCP in via codex's `-c mcp_servers.<id>...` overrides, including
776
876
  `bearer_token_env_var` for `Bearer ${VAR}`-style auth headers) — see
777
877
  `jobs/<name>/runners/aut.json` after running it. To use Claude Code or another
@@ -780,12 +880,30 @@ agent as the AUT instead, hand-write a runner JSON (see
780
880
  `ghostlab create`/`ghostlab plan`, or add it directly under `hosts:` in
781
881
  `job.yaml`.
782
882
 
783
- ## Install from PyPI
883
+ GitHub Copilot CLI is a native runner, including custom agents shared with VS
884
+ Code:
784
885
 
785
886
  ```bash
786
- pip install ghostlab # add [ui] and/or [apps] for those extras
787
- ghostlab --help
788
- ```
887
+ ghostlab create --name copilot-eval --target https://example.com/mcp \
888
+ --aut-backend copilot --user-backend copilot \
889
+ --model gpt-5.4 --user-model gpt-5-mini \
890
+ --aut-agent release-reviewer --user-agent realistic-user \
891
+ --aut-reasoning-effort high --aut-context long_context \
892
+ --sandbox local --no-discover --yes
893
+ ```
894
+
895
+ `--aut-agent`/`--user-agent` map to Copilot's `--agent`; a
896
+ `.github/agents/release-reviewer.agent.md` used in VS Code can therefore be
897
+ selected as `release-reviewer`. Use repeatable `--aut-copilot-arg` /
898
+ `--user-copilot-arg` and `--aut-runner-env` / `--user-runner-env` for settings
899
+ not represented by a dedicated flag. The declarative `agent.runtime` and
900
+ `test.user_runtime` objects in `job.yaml` expose model, agent, effort, context,
901
+ permissions, MCP/plugin settings, environment, and `extra_args`; the UI
902
+ Configure tab provides full JSON editors for both.
903
+
904
+ See `runners/copilot-aut.example.json`, `runners/copilot-user.example.json`,
905
+ and `examples/copilot-agent.json`. To use another process as either role, pass
906
+ low-level JSON with `--aut-runner` and/or `--user-runner`.
789
907
 
790
908
  ## Packaging & Release
791
909