teamrunner 0.0.3__tar.gz → 0.0.4__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.
- {teamrunner-0.0.3 → teamrunner-0.0.4}/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/.github/ISSUE_TEMPLATE/question.yml +1 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/CHANGELOG.md +40 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/NOTICE +11 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/PKG-INFO +47 -8
- {teamrunner-0.0.3 → teamrunner-0.0.4}/README.md +46 -7
- {teamrunner-0.0.3 → teamrunner-0.0.4}/pyproject.toml +1 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/__init__.py +1 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/authoring/AGENTS.md +2 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/builtin_tools.py +103 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/checks.py +14 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/cli.py +11 -6
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/console.py +1 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/engine.py +12 -2
- teamrunner-0.0.4/src/teamrunner/flowgraph.py +136 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/run.py +79 -19
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/runfolder.py +9 -6
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/runs.py +82 -14
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/tool_registry.py +2 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/turns.py +1 -0
- teamrunner-0.0.4/src/teamrunner/ui/__init__.py +1 -0
- teamrunner-0.0.4/src/teamrunner/ui/api.py +297 -0
- teamrunner-0.0.4/src/teamrunner/ui/diagrams.py +131 -0
- teamrunner-0.0.4/src/teamrunner/ui/jobs.py +272 -0
- teamrunner-0.0.4/src/teamrunner/ui/runviews.py +231 -0
- teamrunner-0.0.4/src/teamrunner/ui/server.py +265 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/app.css +89 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/app.js +346 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/diagrams.js +53 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/index.html +134 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/run.js +144 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/runs.js +249 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/LICENSE +21 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/SOURCE.txt +5 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/dompurify-LICENSE.txt +202 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/dompurify-SOURCE.txt +5 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/dompurify.js +7 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/editor.css +344 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/editor.js +11383 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/marked-LICENSE.txt +44 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/marked-SOURCE.txt +5 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/marked.js +86 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/mermaid-LICENSE.txt +21 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/mermaid-SOURCE.txt +5 -0
- teamrunner-0.0.4/src/teamrunner/ui/static/vendor/mermaid.js +4329 -0
- teamrunner-0.0.4/src/teamrunner/ui/stream.py +78 -0
- teamrunner-0.0.4/src/teamrunner/ui/trash.py +79 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/README.txt +1 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-05_090000_debate/events.jsonl +9 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-05_090000_debate/output.md +14 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-05_090000_debate/run.json +26 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-06_100000_debate/events.jsonl +9 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-06_100000_debate/inputs/brief.txt +1 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-06_100000_debate/output.md +14 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-06_100000_debate/run.json +26 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-06_100000_debate/work/scores.csv +2 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-07_101500_review/events.jsonl +9 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-07_101500_review/output.md +14 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/runs/2026-11-07_101500_review/run.json +26 -0
- teamrunner-0.0.4/tests/fixtures/ui_history/team.yaml +2 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_run.py +10 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_tools.py +51 -0
- teamrunner-0.0.4/tests/test_ui.py +793 -0
- teamrunner-0.0.4/tests/test_ui_diagrams.py +128 -0
- teamrunner-0.0.4/tests/test_ui_review.py +145 -0
- teamrunner-0.0.4/tests/ui_transcript_check.js +44 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/uv.lock +1 -1
- {teamrunner-0.0.3 → teamrunner-0.0.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/.github/workflows/publish.yml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/.github/workflows/tests.yml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/.gitignore +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/CLA.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/CONTRIBUTING.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/LICENSE +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/PREVIEW.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/SECURITY.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/LICENSE +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/.gitignore +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/against.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/chair.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/fact_checker.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/for.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/judge.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/sandbox.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/team.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/fact_checked/web.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/chair.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/chen.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/clerk.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/diaz.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/models/member.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/okafor.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/hiring_committee/team.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/multi_agent_debate/.gitignore +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/multi_agent_debate/against.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/multi_agent_debate/chair.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/multi_agent_debate/for.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/multi_agent_debate/judge.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/multi_agent_debate/team.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/.gitignore +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/chair.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/derek.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/emma.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/lily.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/models/engineer.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/noah.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/team.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/orchestrators_lite/zoe.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/catalogue.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/chair.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/context_checker.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/editor.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/intake.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/library.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/metadata_checker.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/reader.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/reference_check/team.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/round_robin/.gitignore +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/round_robin/01_for.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/round_robin/02_against.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/round_robin/03_judge.md +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/examples/round_robin/team.yaml +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/__main__.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/authoring/LICENSE +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/authoring/__init__.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/build.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/describe.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/desk.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/environment.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/events.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/explain.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/flow.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/gather.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/ids.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/inputs.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/ir.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/keys.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/litellm_model.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/loader.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/minutes.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/model.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/new.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/prices.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/problems.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/rehearsal.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/sandbox/__init__.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/sandbox/run.js +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/schema_check.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/schemas/member.schema.json +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/schemas/model_profile.schema.json +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/schemas/team.schema.json +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/show.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/validate.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/src/teamrunner/vocabulary.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_authoring.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_engine.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_explain.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_flow.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_minutes.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_new.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_prices.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_runs.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_sandbox.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_show.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_turns.py +0 -0
- {teamrunner-0.0.3 → teamrunner-0.0.4}/tests/test_validate.py +0 -0
|
@@ -4,6 +4,46 @@ TeamRunner is in preview: 0.0.x versions are a soft launch, and until 0.1.0
|
|
|
4
4
|
is announced a version may change in ways that break teams and run folders
|
|
5
5
|
made with an earlier one. Each such change is listed here.
|
|
6
6
|
|
|
7
|
+
## 0.0.4
|
|
8
|
+
|
|
9
|
+
- **Local browser interface:** `teamrunner ui [folder]` (`gui` is an alias)
|
|
10
|
+
opens the team's files in a browser. Edit with offline syntax highlighting,
|
|
11
|
+
save, validate with clickable problems, inspect resolved instructions and
|
|
12
|
+
flow diagrams, create teams from examples, manage members and set up keys.
|
|
13
|
+
- Start one run at a time with defaults, a message, a file or a task file;
|
|
14
|
+
rehearse for free, follow events live, stop cleanly and re-run saved inputs
|
|
15
|
+
with the current team. Browse and search run history, read conversations,
|
|
16
|
+
outputs and records, inspect what members could read, and download raw files.
|
|
17
|
+
- Compare two runs' numbered member versions, outputs, cost and paths. New
|
|
18
|
+
event logs keep exact member source text and fingerprints, the declared
|
|
19
|
+
flow graph and the recorded engine path for source diffs and arrow counts.
|
|
20
|
+
Older logs remain readable and label missing historical data explicitly.
|
|
21
|
+
A saved `resolved/team.yaml`, when present, can be copied to a new team for
|
|
22
|
+
editing; ordinary runs do not yet save a complete resolved team.
|
|
23
|
+
- The interface uses the CLI's Python functions, adds no Python runtime
|
|
24
|
+
dependencies or database, and bundles its editor, Markdown renderer and
|
|
25
|
+
Mermaid diagrams for offline use. It binds only to `127.0.0.1`, requires a
|
|
26
|
+
random access token, confines editing to the opened folder, keeps run
|
|
27
|
+
records read-only and never displays stored key values.
|
|
28
|
+
- The README's tutorial gives a browser alternative beside each CLI step.
|
|
29
|
+
- UI review fixes: teams under an unrelated folder named `runs` remain editable;
|
|
30
|
+
actual run records stay read-only. Case-only renames work on macOS, rename
|
|
31
|
+
selects the old filename, and new-member names are checked before creation.
|
|
32
|
+
Non-UTF-8 files and validation in an empty workspace show friendly messages.
|
|
33
|
+
- Files changed on disk reload automatically when there are no unsaved edits.
|
|
34
|
+
The file tree refreshes automatically and after runs finish. macOS uses its
|
|
35
|
+
native Trash operation, preserving Finder's Put Back support. The runs list
|
|
36
|
+
keeps comparison controls aligned and offers column navigation on narrow screens.
|
|
37
|
+
- Live transcripts append new cards and populate read context on demand;
|
|
38
|
+
member-version history is cached in memory and invalidated when run files change.
|
|
39
|
+
The historical diagram helpers have been reformatted for readability.
|
|
40
|
+
- **Two email tools**: `read_email` hands a member one email from a folder of
|
|
41
|
+
`.eml` files in the package (`mailbox`, `max_chars`); `save_draft` saves a
|
|
42
|
+
reply in the run's `work/outbox/` and never sends it. By default a draft can
|
|
43
|
+
answer only the email's sender (`reply_only: false` lifts that).
|
|
44
|
+
- Runs started in the same second get their own folders instead of failing
|
|
45
|
+
with `FileExistsError`.
|
|
46
|
+
|
|
7
47
|
## 0.0.3
|
|
8
48
|
|
|
9
49
|
- **Backup models**: `model:` and `default_model:` may name several models,
|
|
@@ -5,7 +5,17 @@ TeamRunner is licensed under GPL-3.0-or-later (see LICENSE). The shipped
|
|
|
5
5
|
examples, the files `teamrunner new` writes and the authoring guide are
|
|
6
6
|
MIT-0 (see examples/LICENSE).
|
|
7
7
|
|
|
8
|
-
TeamRunner bundles
|
|
8
|
+
TeamRunner bundles CodeMirror 5.65.21 (MIT), by Marijn Haverbeke and others,
|
|
9
|
+
for offline browser editing. Its licence and pinned source information are
|
|
10
|
+
in src/teamrunner/ui/static/vendor/LICENSE and SOURCE.txt.
|
|
11
|
+
The offline run viewer also bundles Marked 18.1.3 (MIT) and DOMPurify
|
|
12
|
+
3.4.17 (Apache-2.0 OR MPL-2.0). Their licences and pinned source information
|
|
13
|
+
are in the same vendor folder, under marked-* and dompurify-*.
|
|
14
|
+
|
|
15
|
+
Offline flow diagrams bundle Mermaid 12.3.0 (MIT); its licence, source
|
|
16
|
+
information and bundled dependency notices are in vendor/mermaid-*.
|
|
17
|
+
|
|
18
|
+
It depends on these packages, which
|
|
9
19
|
pip installs separately, each under its own licence:
|
|
10
20
|
|
|
11
21
|
LiteLLM MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: teamrunner
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: The experimentation platform for agent organizations — run multi-agent orchestrations and factorial experiments entirely from text files.
|
|
5
5
|
Project-URL: Homepage, https://github.com/teamrunner/teamrunner
|
|
6
6
|
Project-URL: Repository, https://github.com/teamrunner/teamrunner
|
|
@@ -68,6 +68,7 @@ No Python. No graph-building API. No framework vocabulary. If you can edit a tex
|
|
|
68
68
|
- [Why we built it](#why-we-built-it)
|
|
69
69
|
- [The five ideas](#the-five-ideas)
|
|
70
70
|
- [Tutorial: your first debate in 10 minutes](#tutorial-your-first-debate-in-10-minutes)
|
|
71
|
+
- [Use TeamRunner in your browser](#use-teamrunner-in-your-browser)
|
|
71
72
|
- [What lands in a run folder](#what-lands-in-a-run-folder)
|
|
72
73
|
- [What your runs keep, and where your words go](#what-your-runs-keep-and-where-your-words-go)
|
|
73
74
|
- [Going further: the worked examples](#going-further-the-worked-examples)
|
|
@@ -164,7 +165,7 @@ Two more ideas are designed but not built yet — a whiteboard for who sees what
|
|
|
164
165
|
|
|
165
166
|
We'll build a three-agent debate, run it, give it rounds, a fact checker and the web. Zero code throughout. Turning it into a 40-run experiment comes with 0.2: see [PREVIEW.md](PREVIEW.md#experiments-v02).
|
|
166
167
|
|
|
167
|
-
>
|
|
168
|
+
> Every step works in a terminal, and from 0.0.4 also in your browser: see [Use TeamRunner in your browser](#use-teamrunner-in-your-browser).
|
|
168
169
|
|
|
169
170
|
### Step 0 — install
|
|
170
171
|
|
|
@@ -185,6 +186,8 @@ Keys stay with you, never with your teams: team folders get copied, zipped and s
|
|
|
185
186
|
|
|
186
187
|
Models on your own machine work too, free: install [Ollama](https://ollama.com), pull a model (`ollama pull gpt-oss:20b`), and write `default_model: ollama/gpt-oss:20b` in `team.yaml`. If Ollama runs somewhere other than this computer, put its address in the key file as `OLLAMA_API_BASE`. Choose one of about 20 billion parameters or more: the smallest models often fail at exactly what teams need most — answering in a fixed form and asking toolkeepers properly — so they test the wiring better than they stand in for a real team. Before the first turn of every run, TeamRunner checks that each model your team names is available and lists every problem it finds in one table, so you never pay for half a run. `teamrunner validate --online` runs the same check without a run.
|
|
187
188
|
|
|
189
|
+
Or open `teamrunner ui` and choose **Set up keys** after selecting a team. It saves keys to the same key file and shows whether each key is present, never its saved value. **Check keys and models online** is a separate check.
|
|
190
|
+
|
|
188
191
|
### Step 1 — make a team
|
|
189
192
|
|
|
190
193
|
```bash
|
|
@@ -199,6 +202,8 @@ Two other ways to start, when you'd rather not begin from nothing:
|
|
|
199
202
|
- `teamrunner new my_debate --from fact_checked` copies one of the examples that ship with TeamRunner (or any team folder you name), with fresh ids; each file remembers what it was copied from.
|
|
200
203
|
- `teamrunner new my_debate --describe "two debaters argue a topic, a fact checker checks each speech, and a judge decides"` asks a model to write the team for you. It checks its own work with `validate`, never overwrites files, tells you what it cost, and ends by printing `teamrunner explain`, so you can read what it made before you run it. (If you work with an AI coding assistant, `teamrunner skill` gives it the same instructions: it installs a skill for Claude Code and Codex, and `teamrunner skill --agents-md <folder>` writes an `AGENTS.md` for other assistants that read one.)
|
|
201
204
|
|
|
205
|
+
Or run `teamrunner ui` in the folder where you want to keep your teams. Choose **New team**, enter `debate`, and select an empty team or a shipped example. Select `team.yaml` on the left to edit it.
|
|
206
|
+
|
|
202
207
|
This tutorial starts from nothing, so you see every file.
|
|
203
208
|
|
|
204
209
|
`team.yaml` is the only required file. It marks the folder as a team, says what the team does, and names the model its agents run on:
|
|
@@ -220,6 +225,8 @@ Two things worth knowing now:
|
|
|
220
225
|
|
|
221
226
|
An agent file is Markdown. Frontmatter configures, body prompts.
|
|
222
227
|
|
|
228
|
+
In the browser, select the team, choose **New member** for each Markdown file below, and paste its contents into the editor. **Save** (Ctrl/Cmd+S), then use **Show as it will run** to read the instructions with inputs filled in.
|
|
229
|
+
|
|
223
230
|
```markdown
|
|
224
231
|
<!-- advocate.md -->
|
|
225
232
|
---
|
|
@@ -314,6 +321,8 @@ teamrunner run . --message "Four-day weeks make teams more productive"
|
|
|
314
321
|
|
|
315
322
|
The first runs the default topic; the second replaces it. Either way, the run log records the topic and whether it was given or the default.
|
|
316
323
|
|
|
324
|
+
Or do the editing, validation and running in `teamrunner ui .`: **Validate** checks the same files, and clicking a problem opens its file at the line to fix. Choose **Run…**, keep **Rehearse (free, no model)** checked for a free test, and start. To use a model, clear that checkbox; choose team defaults, a message, a file or a task file. The page shows the conversation as it happens and a **Stop run** button.
|
|
325
|
+
|
|
317
326
|
What you see while it runs (the words are invented; the layout is the real one):
|
|
318
327
|
|
|
319
328
|
```
|
|
@@ -341,6 +350,8 @@ Each turn shows who spoke, what they decided, how long it took, what it cost and
|
|
|
341
350
|
|
|
342
351
|
Later, `teamrunner runs` lists every run of this package — when, which team, the input, the result (`winner = advocate`), status, time and cost — and `teamrunner runs latest` (or `teamrunner runs 14:32`) shows one again exactly as it looked while it ran.
|
|
343
352
|
|
|
353
|
+
In the browser, choose **Runs** to reopen the conversation, output, path or record. Select two runs with the checkboxes and choose **Compare selected runs** to see member versions, outputs, cost and paths side by side.
|
|
354
|
+
|
|
344
355
|
That's a working multi-agent system: four text files, one command.
|
|
345
356
|
|
|
346
357
|
### Step 5 — take control of the flow
|
|
@@ -364,7 +375,7 @@ The flow grammar is deliberately tiny — it is a whiteboard sketch that happens
|
|
|
364
375
|
- **[v0.3]** `[a, b]` — **at the same time**: `editor -> [reviewer_1, reviewer_2] -> editor` sends the paper to both reviewers at once, and the editor waits for both reports. Neither sees the other's work in progress.
|
|
365
376
|
- `done` ends the flow — or, inside a named block, just that block; `stop` ends the whole run immediately.
|
|
366
377
|
|
|
367
|
-
Now the debate runs `advocate → critic → advocate → critic → advocate → critic → judge`. To see it as a picture, `teamrunner explain --diagram` opens the flow as a flowchart in your browser.
|
|
378
|
+
Now the debate runs `advocate → critic → advocate → critic → advocate → critic → judge`. To see it as a picture, `teamrunner explain --diagram` opens the flow as a flowchart in your browser. Or, in the local interface, refresh the file tree after adding `chair.md`, select it, and choose **Flow diagram**. Save an edit to redraw the diagram and plain-words summary.
|
|
368
379
|
|
|
369
380
|
### Step 6 — add a fact checker and a decision
|
|
370
381
|
|
|
@@ -459,10 +470,37 @@ A toolkeeper step means the member just before it must end its turn with a reque
|
|
|
459
470
|
|
|
460
471
|
`teamrunner explain .` prints, in plain English, who may use which toolkeeper and on what terms.
|
|
461
472
|
|
|
462
|
-
TeamRunner 0.1 has
|
|
473
|
+
TeamRunner 0.1 has six built-in tools: `web_search_brave` (Brave Search), `read_page` (a link in, the page's text out), `read_file` (a file the run was given with `--file`, its text out), `run_code` (Python in a sandbox: no internet unless the toolkeeper allows it, and files written only in the run's own work folder), `read_email` (one email from a folder of `.eml` files in the package) and `save_draft` (a reply saved in the run's `work/outbox/`, never sent, addressed only to the email's sender unless `reply_only: false`). The sandbox is Python running in WebAssembly, and it installs with TeamRunner — nothing else to set up; it can use packages built for it (numpy, pandas, scipy, sympy and many more), but not every package. `examples/fact_checked` gives the fact checker a second toolkeeper, `sandbox.md`, so it can check the sums in a claim by running them. A toolkeeper holds one tool in 0.1, so its limits always mean one thing; a member who needs two tools asks two toolkeepers, as the fact checker does here. `examples/reference_check` uses `read_page` and `read_file`: it checks every reference in a paper.
|
|
463
474
|
|
|
464
475
|
---
|
|
465
476
|
|
|
477
|
+
## Use TeamRunner in your browser
|
|
478
|
+
|
|
479
|
+
From 0.0.4, open a team folder with:
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
teamrunner ui debate
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
`teamrunner ui` opens the current folder when you leave out `debate`; `teamrunner gui` is an alias. To create teams, open their parent folder instead. The command opens a browser tab and prints its local address. Keep that terminal running while you work; press Ctrl+C there to stop the server. If the tab does not open, paste the full printed address, including its token, into your browser.
|
|
486
|
+
|
|
487
|
+
The page works on the same files as the CLI. Choose a team or file on the left. You can create an empty team or copy a shipped example, add members, edit and save, rename members, or move files to the system trash. The editor warns about unsaved edits and files changed by another editor. Validation points to the file, line and suggested fix. **Show as it will run** fills in a member's instructions; **Flow diagram** shows the team's flow and summary. For a ready-made flow to edit, start from the `fact_checked` example.
|
|
488
|
+
|
|
489
|
+
**Run…** accepts team defaults, a message, a file or a YAML task file. Rehearsal is selected by default for a team with no prior run; it uses placeholder words and random answers, needs no key and costs nothing. A model run uses your configured provider and its normal charges. The interface validates before starting, allows one active run at a time, streams the recorded events, and can stop the run cleanly. Key setup uses `~/.teamrunner/.env`; stored key values are never displayed.
|
|
490
|
+
|
|
491
|
+
**Runs** is the archive. Filter by team or status, search the message and output, then open a run:
|
|
492
|
+
|
|
493
|
+
- **Conversation:** member messages, decisions, tool calls, retries, tokens, models and cost, with a toggle for what each member could read.
|
|
494
|
+
- **Path:** the saved flow with taken arrows highlighted and transition counts. Older logs without a saved engine path show only observed completed-turn transitions, labelled accordingly.
|
|
495
|
+
- **Output:** the rendered `output.md`.
|
|
496
|
+
- **Record:** inputs, numbered member versions, recorded settings and files. Version numbers come from the available run history.
|
|
497
|
+
|
|
498
|
+
Select two runs to compare their outputs, cost, time, paths and member versions. Exact member source diffs are available for new logs that record the source and its fingerprint; older logs show their recorded prompts, which may include substituted inputs.
|
|
499
|
+
|
|
500
|
+
**Re-run with these inputs…** uses the current team with the old run's input values and copied input files. It does not restore old member versions. **Edit a copy of the saved team…** appears only when a run contains `resolved/team.yaml`; it creates an independent team with fresh IDs and lineage. Open the teams' parent folder to give that copy a place outside the original team. Ordinary runs do not yet save a complete resolved team, so their original files cannot be reconstructed from prompts alone.
|
|
501
|
+
|
|
502
|
+
Every run has **Open folder** and raw-file download links. The editor stays inside the folder you opened, and run records remain read-only. The server listens only on this computer at `127.0.0.1`, with a random access token. Editor, Markdown and diagram assets are bundled for offline use; model calls and online checks still need their usual services. The page adds no database or separate project format, and shows equivalent CLI commands alongside actions.
|
|
503
|
+
|
|
466
504
|
## What lands in a run folder
|
|
467
505
|
|
|
468
506
|
Every run is logged in a `runs/` folder at the top of the package you ran — even when you run just one subteam (**[v0.3]**). The folder is git-ignored, so sharing the package never shares its history.
|
|
@@ -483,12 +521,12 @@ runs/01JZXG7T3M9QKWP5RVB2C8HD6N/
|
|
|
483
521
|
|
|
484
522
|
| Event | What it records |
|
|
485
523
|
|---|---|
|
|
486
|
-
| `run_started` | the inputs, and every member exactly as it will run — model, settings, prompt |
|
|
524
|
+
| `run_started` | the inputs, and every member exactly as it will run — model, settings, prompt; from 0.0.4, also each member's source text, content fingerprints and the declared flow graph |
|
|
487
525
|
| `agent_message` | from, **to** (the *realized* topology), stage, attempt, **what it could read**, everything it said, typed outputs, the model actually used, tokens, cost, time |
|
|
488
526
|
| `route_decision` | who decided, which option, which arrow — the audit trail for "no hidden if-statements" |
|
|
489
527
|
| `tool_call` | who asked, which toolkeeper, the toolkeeper's yes or no, tool, arguments, the full result (for a search, how many results came back, unless `keep_results: true`) |
|
|
490
528
|
| `state_write` **[v0.3]** | item, old → new, writer |
|
|
491
|
-
| `check`, `retry`, `warning`, `error`, `run_finished` | the pre-run check, retries, warnings, failures, and the totals |
|
|
529
|
+
| `check`, `retry`, `warning`, `error`, `run_finished` | the pre-run check, retries, warnings, failures, and the totals; from 0.0.4, `run_finished` also saves the path taken, for arrow counts |
|
|
492
530
|
|
|
493
531
|
A field never changes its meaning between versions — later versions only add — so an analysis script you write today keeps working.
|
|
494
532
|
|
|
@@ -567,9 +605,10 @@ The recasting campaign is the point, not decoration: each one is run *before* th
|
|
|
567
605
|
|---|---|
|
|
568
606
|
| **Design** | Spec drafted; paper recasts written as pure text ✅ |
|
|
569
607
|
| **0.1 · The org package** ← now: in preview as 0.0.x | A folder that gets a job done: validation with friendly errors, its own runner, the event log, toolkeepers and four built-in tools, rehearsals, `run`, `runs`, `explain`, `new`, the authoring skill ✅ |
|
|
608
|
+
| **Before 0.2 · Local browser interface** | In 0.0.4 ✅ `teamrunner ui` / `gui`: edit, validate, rehearse or run, browse recorded conversations and paths, compare two runs |
|
|
570
609
|
| **0.2 · Experiments** | Experiments outside the package (`base:`), factorial designs, seeded replications, manifests, response cache, the answer-key metric, `teamrunner experiment`, export; `promote` / `export`. First real result: leader vs. flat |
|
|
571
610
|
| **0.3 · Richer organizations** | Managers and rules-table chairs, `whiteboard.yaml` (private information, sealed and conditional visibility, addressed messages), subteams, parallel work, announcers; the recasts run for real, and the Invisible Orchestrators replication |
|
|
572
|
-
| **0.4+ · Later** | LLM-as-judge metrics,
|
|
611
|
+
| **0.4+ · Later** | LLM-as-judge metrics, communication-network statistics and visualization, template library of canonical org structures, human-in-the-loop, automated improvement, running packages in production |
|
|
573
612
|
|
|
574
613
|
Human-in-the-loop nodes (`kind: human` — the runner pauses and asks a person) are for mixed human–agent studies.
|
|
575
614
|
|
|
@@ -597,7 +636,7 @@ Inside a package, these names are reserved. **Every one of them is optional exce
|
|
|
597
636
|
| `chair.md` | How a protocol team is run: the script, as the file's body, or a `kind: rules` table **[v0.3]** | Everyone may talk to everyone; members act in alphabetical filename order, one turn each |
|
|
598
637
|
| `manager.md` **[v0.3]** | How a managed team is run: an LLM that decides who acts next; only it may use input placeholders. A team has at most one of `chair.md` and `manager.md` | — |
|
|
599
638
|
| `whiteboard.yaml` **[v0.3]** | Who sees what: declared items with `read_by` / `written_by`, and who can read what each member says (`said`) | Everyone reads everything said; built-in items only (e.g. `stage`) |
|
|
600
|
-
| `tools/` | Reserved for a package's own tools, which come after 0.1. In 0.1 toolkeepers use the built-ins: `web_search_brave`, `read_page`, `read_file`, `run_code` | Built-ins only |
|
|
639
|
+
| `tools/` | Reserved for a package's own tools, which come after 0.1. In 0.1 toolkeepers use the built-ins: `web_search_brave`, `read_page`, `read_file`, `run_code`, `read_email`, `save_draft` | Built-ins only |
|
|
601
640
|
| `models/` | Model profiles: a model and its settings in one `.yaml`, named by agents as `model: careful.yaml` or by `team.yaml` as `default_model:` | Agents name provider models directly |
|
|
602
641
|
| `runs/` | Where every run of this package is logged, whichever team you started; git-ignored | Created on the first run |
|
|
603
642
|
| *subfolder with `team.yaml`* **[v0.3]** | A subteam — a node in the parent flow, with its own whiteboard; the parent hears only its report (`report_from:`) | — |
|
|
@@ -33,6 +33,7 @@ No Python. No graph-building API. No framework vocabulary. If you can edit a tex
|
|
|
33
33
|
- [Why we built it](#why-we-built-it)
|
|
34
34
|
- [The five ideas](#the-five-ideas)
|
|
35
35
|
- [Tutorial: your first debate in 10 minutes](#tutorial-your-first-debate-in-10-minutes)
|
|
36
|
+
- [Use TeamRunner in your browser](#use-teamrunner-in-your-browser)
|
|
36
37
|
- [What lands in a run folder](#what-lands-in-a-run-folder)
|
|
37
38
|
- [What your runs keep, and where your words go](#what-your-runs-keep-and-where-your-words-go)
|
|
38
39
|
- [Going further: the worked examples](#going-further-the-worked-examples)
|
|
@@ -129,7 +130,7 @@ Two more ideas are designed but not built yet — a whiteboard for who sees what
|
|
|
129
130
|
|
|
130
131
|
We'll build a three-agent debate, run it, give it rounds, a fact checker and the web. Zero code throughout. Turning it into a 40-run experiment comes with 0.2: see [PREVIEW.md](PREVIEW.md#experiments-v02).
|
|
131
132
|
|
|
132
|
-
>
|
|
133
|
+
> Every step works in a terminal, and from 0.0.4 also in your browser: see [Use TeamRunner in your browser](#use-teamrunner-in-your-browser).
|
|
133
134
|
|
|
134
135
|
### Step 0 — install
|
|
135
136
|
|
|
@@ -150,6 +151,8 @@ Keys stay with you, never with your teams: team folders get copied, zipped and s
|
|
|
150
151
|
|
|
151
152
|
Models on your own machine work too, free: install [Ollama](https://ollama.com), pull a model (`ollama pull gpt-oss:20b`), and write `default_model: ollama/gpt-oss:20b` in `team.yaml`. If Ollama runs somewhere other than this computer, put its address in the key file as `OLLAMA_API_BASE`. Choose one of about 20 billion parameters or more: the smallest models often fail at exactly what teams need most — answering in a fixed form and asking toolkeepers properly — so they test the wiring better than they stand in for a real team. Before the first turn of every run, TeamRunner checks that each model your team names is available and lists every problem it finds in one table, so you never pay for half a run. `teamrunner validate --online` runs the same check without a run.
|
|
152
153
|
|
|
154
|
+
Or open `teamrunner ui` and choose **Set up keys** after selecting a team. It saves keys to the same key file and shows whether each key is present, never its saved value. **Check keys and models online** is a separate check.
|
|
155
|
+
|
|
153
156
|
### Step 1 — make a team
|
|
154
157
|
|
|
155
158
|
```bash
|
|
@@ -164,6 +167,8 @@ Two other ways to start, when you'd rather not begin from nothing:
|
|
|
164
167
|
- `teamrunner new my_debate --from fact_checked` copies one of the examples that ship with TeamRunner (or any team folder you name), with fresh ids; each file remembers what it was copied from.
|
|
165
168
|
- `teamrunner new my_debate --describe "two debaters argue a topic, a fact checker checks each speech, and a judge decides"` asks a model to write the team for you. It checks its own work with `validate`, never overwrites files, tells you what it cost, and ends by printing `teamrunner explain`, so you can read what it made before you run it. (If you work with an AI coding assistant, `teamrunner skill` gives it the same instructions: it installs a skill for Claude Code and Codex, and `teamrunner skill --agents-md <folder>` writes an `AGENTS.md` for other assistants that read one.)
|
|
166
169
|
|
|
170
|
+
Or run `teamrunner ui` in the folder where you want to keep your teams. Choose **New team**, enter `debate`, and select an empty team or a shipped example. Select `team.yaml` on the left to edit it.
|
|
171
|
+
|
|
167
172
|
This tutorial starts from nothing, so you see every file.
|
|
168
173
|
|
|
169
174
|
`team.yaml` is the only required file. It marks the folder as a team, says what the team does, and names the model its agents run on:
|
|
@@ -185,6 +190,8 @@ Two things worth knowing now:
|
|
|
185
190
|
|
|
186
191
|
An agent file is Markdown. Frontmatter configures, body prompts.
|
|
187
192
|
|
|
193
|
+
In the browser, select the team, choose **New member** for each Markdown file below, and paste its contents into the editor. **Save** (Ctrl/Cmd+S), then use **Show as it will run** to read the instructions with inputs filled in.
|
|
194
|
+
|
|
188
195
|
```markdown
|
|
189
196
|
<!-- advocate.md -->
|
|
190
197
|
---
|
|
@@ -279,6 +286,8 @@ teamrunner run . --message "Four-day weeks make teams more productive"
|
|
|
279
286
|
|
|
280
287
|
The first runs the default topic; the second replaces it. Either way, the run log records the topic and whether it was given or the default.
|
|
281
288
|
|
|
289
|
+
Or do the editing, validation and running in `teamrunner ui .`: **Validate** checks the same files, and clicking a problem opens its file at the line to fix. Choose **Run…**, keep **Rehearse (free, no model)** checked for a free test, and start. To use a model, clear that checkbox; choose team defaults, a message, a file or a task file. The page shows the conversation as it happens and a **Stop run** button.
|
|
290
|
+
|
|
282
291
|
What you see while it runs (the words are invented; the layout is the real one):
|
|
283
292
|
|
|
284
293
|
```
|
|
@@ -306,6 +315,8 @@ Each turn shows who spoke, what they decided, how long it took, what it cost and
|
|
|
306
315
|
|
|
307
316
|
Later, `teamrunner runs` lists every run of this package — when, which team, the input, the result (`winner = advocate`), status, time and cost — and `teamrunner runs latest` (or `teamrunner runs 14:32`) shows one again exactly as it looked while it ran.
|
|
308
317
|
|
|
318
|
+
In the browser, choose **Runs** to reopen the conversation, output, path or record. Select two runs with the checkboxes and choose **Compare selected runs** to see member versions, outputs, cost and paths side by side.
|
|
319
|
+
|
|
309
320
|
That's a working multi-agent system: four text files, one command.
|
|
310
321
|
|
|
311
322
|
### Step 5 — take control of the flow
|
|
@@ -329,7 +340,7 @@ The flow grammar is deliberately tiny — it is a whiteboard sketch that happens
|
|
|
329
340
|
- **[v0.3]** `[a, b]` — **at the same time**: `editor -> [reviewer_1, reviewer_2] -> editor` sends the paper to both reviewers at once, and the editor waits for both reports. Neither sees the other's work in progress.
|
|
330
341
|
- `done` ends the flow — or, inside a named block, just that block; `stop` ends the whole run immediately.
|
|
331
342
|
|
|
332
|
-
Now the debate runs `advocate → critic → advocate → critic → advocate → critic → judge`. To see it as a picture, `teamrunner explain --diagram` opens the flow as a flowchart in your browser.
|
|
343
|
+
Now the debate runs `advocate → critic → advocate → critic → advocate → critic → judge`. To see it as a picture, `teamrunner explain --diagram` opens the flow as a flowchart in your browser. Or, in the local interface, refresh the file tree after adding `chair.md`, select it, and choose **Flow diagram**. Save an edit to redraw the diagram and plain-words summary.
|
|
333
344
|
|
|
334
345
|
### Step 6 — add a fact checker and a decision
|
|
335
346
|
|
|
@@ -424,10 +435,37 @@ A toolkeeper step means the member just before it must end its turn with a reque
|
|
|
424
435
|
|
|
425
436
|
`teamrunner explain .` prints, in plain English, who may use which toolkeeper and on what terms.
|
|
426
437
|
|
|
427
|
-
TeamRunner 0.1 has
|
|
438
|
+
TeamRunner 0.1 has six built-in tools: `web_search_brave` (Brave Search), `read_page` (a link in, the page's text out), `read_file` (a file the run was given with `--file`, its text out), `run_code` (Python in a sandbox: no internet unless the toolkeeper allows it, and files written only in the run's own work folder), `read_email` (one email from a folder of `.eml` files in the package) and `save_draft` (a reply saved in the run's `work/outbox/`, never sent, addressed only to the email's sender unless `reply_only: false`). The sandbox is Python running in WebAssembly, and it installs with TeamRunner — nothing else to set up; it can use packages built for it (numpy, pandas, scipy, sympy and many more), but not every package. `examples/fact_checked` gives the fact checker a second toolkeeper, `sandbox.md`, so it can check the sums in a claim by running them. A toolkeeper holds one tool in 0.1, so its limits always mean one thing; a member who needs two tools asks two toolkeepers, as the fact checker does here. `examples/reference_check` uses `read_page` and `read_file`: it checks every reference in a paper.
|
|
428
439
|
|
|
429
440
|
---
|
|
430
441
|
|
|
442
|
+
## Use TeamRunner in your browser
|
|
443
|
+
|
|
444
|
+
From 0.0.4, open a team folder with:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
teamrunner ui debate
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
`teamrunner ui` opens the current folder when you leave out `debate`; `teamrunner gui` is an alias. To create teams, open their parent folder instead. The command opens a browser tab and prints its local address. Keep that terminal running while you work; press Ctrl+C there to stop the server. If the tab does not open, paste the full printed address, including its token, into your browser.
|
|
451
|
+
|
|
452
|
+
The page works on the same files as the CLI. Choose a team or file on the left. You can create an empty team or copy a shipped example, add members, edit and save, rename members, or move files to the system trash. The editor warns about unsaved edits and files changed by another editor. Validation points to the file, line and suggested fix. **Show as it will run** fills in a member's instructions; **Flow diagram** shows the team's flow and summary. For a ready-made flow to edit, start from the `fact_checked` example.
|
|
453
|
+
|
|
454
|
+
**Run…** accepts team defaults, a message, a file or a YAML task file. Rehearsal is selected by default for a team with no prior run; it uses placeholder words and random answers, needs no key and costs nothing. A model run uses your configured provider and its normal charges. The interface validates before starting, allows one active run at a time, streams the recorded events, and can stop the run cleanly. Key setup uses `~/.teamrunner/.env`; stored key values are never displayed.
|
|
455
|
+
|
|
456
|
+
**Runs** is the archive. Filter by team or status, search the message and output, then open a run:
|
|
457
|
+
|
|
458
|
+
- **Conversation:** member messages, decisions, tool calls, retries, tokens, models and cost, with a toggle for what each member could read.
|
|
459
|
+
- **Path:** the saved flow with taken arrows highlighted and transition counts. Older logs without a saved engine path show only observed completed-turn transitions, labelled accordingly.
|
|
460
|
+
- **Output:** the rendered `output.md`.
|
|
461
|
+
- **Record:** inputs, numbered member versions, recorded settings and files. Version numbers come from the available run history.
|
|
462
|
+
|
|
463
|
+
Select two runs to compare their outputs, cost, time, paths and member versions. Exact member source diffs are available for new logs that record the source and its fingerprint; older logs show their recorded prompts, which may include substituted inputs.
|
|
464
|
+
|
|
465
|
+
**Re-run with these inputs…** uses the current team with the old run's input values and copied input files. It does not restore old member versions. **Edit a copy of the saved team…** appears only when a run contains `resolved/team.yaml`; it creates an independent team with fresh IDs and lineage. Open the teams' parent folder to give that copy a place outside the original team. Ordinary runs do not yet save a complete resolved team, so their original files cannot be reconstructed from prompts alone.
|
|
466
|
+
|
|
467
|
+
Every run has **Open folder** and raw-file download links. The editor stays inside the folder you opened, and run records remain read-only. The server listens only on this computer at `127.0.0.1`, with a random access token. Editor, Markdown and diagram assets are bundled for offline use; model calls and online checks still need their usual services. The page adds no database or separate project format, and shows equivalent CLI commands alongside actions.
|
|
468
|
+
|
|
431
469
|
## What lands in a run folder
|
|
432
470
|
|
|
433
471
|
Every run is logged in a `runs/` folder at the top of the package you ran — even when you run just one subteam (**[v0.3]**). The folder is git-ignored, so sharing the package never shares its history.
|
|
@@ -448,12 +486,12 @@ runs/01JZXG7T3M9QKWP5RVB2C8HD6N/
|
|
|
448
486
|
|
|
449
487
|
| Event | What it records |
|
|
450
488
|
|---|---|
|
|
451
|
-
| `run_started` | the inputs, and every member exactly as it will run — model, settings, prompt |
|
|
489
|
+
| `run_started` | the inputs, and every member exactly as it will run — model, settings, prompt; from 0.0.4, also each member's source text, content fingerprints and the declared flow graph |
|
|
452
490
|
| `agent_message` | from, **to** (the *realized* topology), stage, attempt, **what it could read**, everything it said, typed outputs, the model actually used, tokens, cost, time |
|
|
453
491
|
| `route_decision` | who decided, which option, which arrow — the audit trail for "no hidden if-statements" |
|
|
454
492
|
| `tool_call` | who asked, which toolkeeper, the toolkeeper's yes or no, tool, arguments, the full result (for a search, how many results came back, unless `keep_results: true`) |
|
|
455
493
|
| `state_write` **[v0.3]** | item, old → new, writer |
|
|
456
|
-
| `check`, `retry`, `warning`, `error`, `run_finished` | the pre-run check, retries, warnings, failures, and the totals |
|
|
494
|
+
| `check`, `retry`, `warning`, `error`, `run_finished` | the pre-run check, retries, warnings, failures, and the totals; from 0.0.4, `run_finished` also saves the path taken, for arrow counts |
|
|
457
495
|
|
|
458
496
|
A field never changes its meaning between versions — later versions only add — so an analysis script you write today keeps working.
|
|
459
497
|
|
|
@@ -532,9 +570,10 @@ The recasting campaign is the point, not decoration: each one is run *before* th
|
|
|
532
570
|
|---|---|
|
|
533
571
|
| **Design** | Spec drafted; paper recasts written as pure text ✅ |
|
|
534
572
|
| **0.1 · The org package** ← now: in preview as 0.0.x | A folder that gets a job done: validation with friendly errors, its own runner, the event log, toolkeepers and four built-in tools, rehearsals, `run`, `runs`, `explain`, `new`, the authoring skill ✅ |
|
|
573
|
+
| **Before 0.2 · Local browser interface** | In 0.0.4 ✅ `teamrunner ui` / `gui`: edit, validate, rehearse or run, browse recorded conversations and paths, compare two runs |
|
|
535
574
|
| **0.2 · Experiments** | Experiments outside the package (`base:`), factorial designs, seeded replications, manifests, response cache, the answer-key metric, `teamrunner experiment`, export; `promote` / `export`. First real result: leader vs. flat |
|
|
536
575
|
| **0.3 · Richer organizations** | Managers and rules-table chairs, `whiteboard.yaml` (private information, sealed and conditional visibility, addressed messages), subteams, parallel work, announcers; the recasts run for real, and the Invisible Orchestrators replication |
|
|
537
|
-
| **0.4+ · Later** | LLM-as-judge metrics,
|
|
576
|
+
| **0.4+ · Later** | LLM-as-judge metrics, communication-network statistics and visualization, template library of canonical org structures, human-in-the-loop, automated improvement, running packages in production |
|
|
538
577
|
|
|
539
578
|
Human-in-the-loop nodes (`kind: human` — the runner pauses and asks a person) are for mixed human–agent studies.
|
|
540
579
|
|
|
@@ -562,7 +601,7 @@ Inside a package, these names are reserved. **Every one of them is optional exce
|
|
|
562
601
|
| `chair.md` | How a protocol team is run: the script, as the file's body, or a `kind: rules` table **[v0.3]** | Everyone may talk to everyone; members act in alphabetical filename order, one turn each |
|
|
563
602
|
| `manager.md` **[v0.3]** | How a managed team is run: an LLM that decides who acts next; only it may use input placeholders. A team has at most one of `chair.md` and `manager.md` | — |
|
|
564
603
|
| `whiteboard.yaml` **[v0.3]** | Who sees what: declared items with `read_by` / `written_by`, and who can read what each member says (`said`) | Everyone reads everything said; built-in items only (e.g. `stage`) |
|
|
565
|
-
| `tools/` | Reserved for a package's own tools, which come after 0.1. In 0.1 toolkeepers use the built-ins: `web_search_brave`, `read_page`, `read_file`, `run_code` | Built-ins only |
|
|
604
|
+
| `tools/` | Reserved for a package's own tools, which come after 0.1. In 0.1 toolkeepers use the built-ins: `web_search_brave`, `read_page`, `read_file`, `run_code`, `read_email`, `save_draft` | Built-ins only |
|
|
566
605
|
| `models/` | Model profiles: a model and its settings in one `.yaml`, named by agents as `model: careful.yaml` or by `team.yaml` as `default_model:` | Agents name provider models directly |
|
|
567
606
|
| `runs/` | Where every run of this package is logged, whichever team you started; git-ignored | Created on the first run |
|
|
568
607
|
| *subfolder with `team.yaml`* **[v0.3]** | A subteam — a node in the parent flow, with its own whiteboard; the parent hears only its report (`report_from:`) | — |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "teamrunner"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "The experimentation platform for agent organizations — run multi-agent orchestrations and factorial experiments entirely from text files."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later AND MIT-0"
|
|
@@ -11,7 +11,7 @@ https://github.com/teamrunner/teamrunner.
|
|
|
11
11
|
|
|
12
12
|
import os as _os
|
|
13
13
|
|
|
14
|
-
__version__ = "0.0.
|
|
14
|
+
__version__ = "0.0.4"
|
|
15
15
|
|
|
16
16
|
# LiteLLM fetches its price table from the internet on import unless told not to. TeamRunner
|
|
17
17
|
# uses the table that came with the installed LiteLLM, so a run's prices depend only on what
|
|
@@ -153,6 +153,8 @@ I search the web. Send me a query and I return the top five results.
|
|
|
153
153
|
| `read_page` | a link → the page's text, PDFs too | `domains`, `max_chars`, `types: [pdf]` |
|
|
154
154
|
| `read_file` | a file the run was given with `--file` → its text | `max_chars`, `types: [pdf]` |
|
|
155
155
|
| `run_code` | Python → its output and errors, in a sandbox | `network: true` (off by default), `paths: [folder]` to read — the code finds it at `/package/<folder>` |
|
|
156
|
+
| `read_email` | an email's id → its sender, recipients, date, subject and text, from a folder of `.eml` files in the package | `mailbox: <folder>` (`mailbox` if not written), `max_chars` |
|
|
157
|
+
| `save_draft` | an email's id and a reply → the reply saved in the run's `work/outbox/`, never sent; it answers only the email's sender | `mailbox: <folder>`, `max_chars`, `reply_only: false` (lets a draft name any address) |
|
|
156
158
|
|
|
157
159
|
Every tool also understands `calls_per_turn` (5 if not written),
|
|
158
160
|
`calls_per_run` and `timeout` (seconds). Any other limit is an error.
|
|
@@ -298,6 +298,97 @@ async def run_code(args: dict, guardrails: dict, ctx: Context) -> Answer:
|
|
|
298
298
|
service=f"pyodide {sandbox.PYODIDE} (Python {ran.python}), deno {version('deno')}")
|
|
299
299
|
|
|
300
300
|
|
|
301
|
+
# --- read_email and save_draft: a mailbox folder of .eml files --------------------------
|
|
302
|
+
|
|
303
|
+
EMAIL_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,99}")
|
|
304
|
+
DEFAULT_MAILBOX = "mailbox"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def mailbox_folder(guardrails: dict, ctx: Context) -> Path:
|
|
308
|
+
"""The mailbox this toolkeeper reads: a folder of .eml files, written relative to the package."""
|
|
309
|
+
written = str(guardrails.get("mailbox", DEFAULT_MAILBOX))
|
|
310
|
+
folder = Path(written).expanduser()
|
|
311
|
+
if not folder.is_absolute() and ctx.package_top is not None:
|
|
312
|
+
folder = ctx.package_top / folder
|
|
313
|
+
if not folder.is_dir():
|
|
314
|
+
raise Refused(f"the mailbox {written} that this toolkeeper reads does not exist")
|
|
315
|
+
return folder
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def find_email(asked: str, guardrails: dict, ctx: Context):
|
|
319
|
+
"""One email in the mailbox, by its id (its file name without .eml)."""
|
|
320
|
+
from email import policy
|
|
321
|
+
from email.parser import BytesParser
|
|
322
|
+
asked = asked.strip().removesuffix(".eml")
|
|
323
|
+
if not EMAIL_ID.fullmatch(asked):
|
|
324
|
+
raise Refused("an email is asked for by its id, such as m001")
|
|
325
|
+
path = mailbox_folder(guardrails, ctx) / f"{asked}.eml"
|
|
326
|
+
if not path.is_file():
|
|
327
|
+
raise Refused(f"there is no email {asked} in the mailbox")
|
|
328
|
+
return asked, BytesParser(policy=policy.default).parsebytes(path.read_bytes())
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def email_text(message) -> str:
|
|
332
|
+
"""The headers a reader needs, the body as plain text, and the names of any attachments."""
|
|
333
|
+
lines = [f"{h}: {message[h]}" for h in ("From", "To", "Cc", "Date", "Subject") if message[h]]
|
|
334
|
+
part = message.get_body(preferencelist=("plain", "html"))
|
|
335
|
+
body = "" if part is None else part.get_content()
|
|
336
|
+
if part is not None and part.get_content_type() == "text/html":
|
|
337
|
+
body = html_text(body)
|
|
338
|
+
attached = [p.get_filename() or p.get_content_type() for p in message.iter_attachments()]
|
|
339
|
+
if attached:
|
|
340
|
+
lines.append("Attachments: " + ", ".join(attached))
|
|
341
|
+
return "\n".join(lines) + "\n\n" + body.strip()
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
async def read_email(args: dict, guardrails: dict, ctx: Context) -> Answer:
|
|
345
|
+
_, message = find_email(str(args.get("id", "")), guardrails, ctx)
|
|
346
|
+
return Answer(cut(email_text(message), guardrails), service="mailbox folder")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
async def save_draft(args: dict, guardrails: dict, ctx: Context) -> Answer:
|
|
350
|
+
"""A reply saved to the run's work/outbox/, never sent. By default it goes to the
|
|
351
|
+
email's sender and no one else (`reply_only`)."""
|
|
352
|
+
from email.message import EmailMessage
|
|
353
|
+
from email.utils import getaddresses
|
|
354
|
+
asked, original = find_email(str(args.get("reply_to", "")), guardrails, ctx)
|
|
355
|
+
body = str(args.get("body", "")).strip()
|
|
356
|
+
if not body:
|
|
357
|
+
raise Refused("the draft was empty")
|
|
358
|
+
limit = guardrails.get("max_chars", DEFAULT_MAX_CHARS)
|
|
359
|
+
if len(body) > limit:
|
|
360
|
+
raise Refused(f"the draft is {len(body)} characters; this outbox takes at most {limit}")
|
|
361
|
+
sender = original.get("Reply-To") or original.get("From") or ""
|
|
362
|
+
sender_address = next((a for _, a in getaddresses([sender]) if a), "")
|
|
363
|
+
wanted = str(args.get("to") or "").strip()
|
|
364
|
+
wanted_addresses = [a for _, a in getaddresses([wanted]) if a]
|
|
365
|
+
if wanted and guardrails.get("reply_only", True):
|
|
366
|
+
if [a.lower() for a in wanted_addresses] != [sender_address.lower()]:
|
|
367
|
+
raise Refused("this outbox only answers the sender of the email "
|
|
368
|
+
f"({sender_address}); it does not write to other addresses")
|
|
369
|
+
to = wanted if wanted and not guardrails.get("reply_only", True) else sender
|
|
370
|
+
if not to:
|
|
371
|
+
raise Refused(f"email {asked} has no sender to answer")
|
|
372
|
+
outbox = ctx.work_dir / "outbox"
|
|
373
|
+
path = outbox / f"{asked}.eml"
|
|
374
|
+
if path.exists():
|
|
375
|
+
raise Refused(f"a draft answering {asked} is already in the outbox")
|
|
376
|
+
draft = EmailMessage()
|
|
377
|
+
draft["To"] = to
|
|
378
|
+
if original["To"]:
|
|
379
|
+
draft["From"] = original["To"]
|
|
380
|
+
subject = str(original.get("Subject") or "")
|
|
381
|
+
draft["Subject"] = subject if subject.lower().startswith("re:") else f"Re: {subject}".strip()
|
|
382
|
+
if original["Message-ID"]:
|
|
383
|
+
draft["In-Reply-To"] = original["Message-ID"]
|
|
384
|
+
draft["X-TeamRunner"] = "draft, not sent"
|
|
385
|
+
draft.set_content(body + "\n", cte="8bit") # readable as it is, in any text editor
|
|
386
|
+
outbox.mkdir(parents=True, exist_ok=True)
|
|
387
|
+
path.write_bytes(bytes(draft))
|
|
388
|
+
return Answer(f"Saved a draft to {to} in the outbox as outbox/{asked}.eml. It has not been sent.",
|
|
389
|
+
service="outbox folder")
|
|
390
|
+
|
|
391
|
+
|
|
301
392
|
TOOLS: dict[str, Tool] = {
|
|
302
393
|
"web_search_brave": Tool("web_search_brave", "Searches the web: a query in, titles, links and snippets out.",
|
|
303
394
|
{"type": "object", "properties": {"query": {"type": "string"}},
|
|
@@ -312,6 +403,18 @@ TOOLS: dict[str, Tool] = {
|
|
|
312
403
|
"errors out. Files it writes in the current folder are kept for the rest of the run.",
|
|
313
404
|
{"type": "object", "properties": {"code": {"type": "string"}},
|
|
314
405
|
"required": ["code"], "additionalProperties": False}, run_code),
|
|
406
|
+
"read_email": Tool("read_email", "Reads one email from the mailbox: its id in, its sender, "
|
|
407
|
+
"recipients, date, subject and text out.",
|
|
408
|
+
{"type": "object", "properties": {"id": {"type": "string"}},
|
|
409
|
+
"required": ["id"], "additionalProperties": False}, read_email),
|
|
410
|
+
"save_draft": Tool("save_draft", "Saves a reply to an email in the outbox, where a person reads it "
|
|
411
|
+
"before anything is sent: the id of the email it answers and the reply's text in. "
|
|
412
|
+
"Nothing is sent.",
|
|
413
|
+
{"type": "object", "properties": {
|
|
414
|
+
"reply_to": {"type": "string", "description": "the id of the email this answers"},
|
|
415
|
+
"body": {"type": "string", "description": "the reply's text"},
|
|
416
|
+
"to": {"type": "string", "description": "who it goes to; leave out to answer the sender"}},
|
|
417
|
+
"required": ["reply_to", "body"], "additionalProperties": False}, save_draft),
|
|
315
418
|
}
|
|
316
419
|
NOT_BUILT: dict[str, str] = {} # tool -> why this version cannot run it yet
|
|
317
420
|
TOOL_KEYS = {"web_search_brave": BRAVE_KEY}
|
|
@@ -23,6 +23,15 @@ from .keys import NO_KEY, PROVIDER_KEYS, SET_UP_ELSEWHERE, Keys
|
|
|
23
23
|
Lister = Callable[[str | None, dict], "set[str] | None"]
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def online_check(report):
|
|
27
|
+
"""The explicit CLI/browser online check, without printing or prompting."""
|
|
28
|
+
from .build import load_team
|
|
29
|
+
from .keys import extra_settings, find_keys
|
|
30
|
+
team, _ = load_team(report.package.team.folder)
|
|
31
|
+
keys = find_keys(needed_keys(team), team.package_top, None)
|
|
32
|
+
return run_check(team, keys, LISTERS, extra_settings(), litellm_data, litellm_params), keys
|
|
33
|
+
|
|
34
|
+
|
|
26
35
|
@dataclass
|
|
27
36
|
class Problem:
|
|
28
37
|
what: str # the model or key
|
|
@@ -213,8 +222,12 @@ def _probe(model: str, keys: Keys, listers: dict[str, Lister], settings: dict, l
|
|
|
213
222
|
available = lists[provider]
|
|
214
223
|
if isinstance(available, Exception):
|
|
215
224
|
e = available
|
|
225
|
+
reason = str(e)
|
|
226
|
+
for secret in keys.values.values():
|
|
227
|
+
if secret:
|
|
228
|
+
reason = reason.replace(secret, "[key hidden]")
|
|
216
229
|
return Probe(row, lambda who: Problem(
|
|
217
|
-
model, who, f"could not reach {provider} to check it: {
|
|
230
|
+
model, who, f"could not reach {provider} to check it: {reason}",
|
|
218
231
|
"check your connection" + (", and that Ollama is running" if provider in NO_KEY else
|
|
219
232
|
f", and that {key_name} is right")),
|
|
220
233
|
f"{provider} could not be reached")
|
|
@@ -52,13 +52,9 @@ def print_problems(report: Report, out: Style) -> None:
|
|
|
52
52
|
def _online_check(report, out, quiet: bool):
|
|
53
53
|
"""validate --online (D100): the same key and model check as `run`, and no run.
|
|
54
54
|
It never asks: a key found only in the shell is used for the check, not saved."""
|
|
55
|
-
from .
|
|
56
|
-
from .checks import LISTERS, litellm_data, litellm_params, needed_keys, run_check
|
|
55
|
+
from .checks import online_check
|
|
57
56
|
from .console import Console
|
|
58
|
-
|
|
59
|
-
team, _ = load_team(report.package.team.folder)
|
|
60
|
-
keys = find_keys(needed_keys(team), team.package_top, None)
|
|
61
|
-
check = run_check(team, keys, LISTERS, extra_settings(), litellm_data, litellm_params)
|
|
57
|
+
check, keys = online_check(report)
|
|
62
58
|
if not quiet:
|
|
63
59
|
Console(False, sys.stdout).check(check, keys)
|
|
64
60
|
return check
|
|
@@ -356,6 +352,11 @@ def cmd_skill(args) -> int:
|
|
|
356
352
|
return 1 if failed else 0
|
|
357
353
|
|
|
358
354
|
|
|
355
|
+
def cmd_ui(args) -> int:
|
|
356
|
+
from .ui.server import serve
|
|
357
|
+
return serve(Path(args.folder))
|
|
358
|
+
|
|
359
|
+
|
|
359
360
|
def main(argv=None) -> int:
|
|
360
361
|
for stream in (sys.stdout, sys.stderr): # Windows sends piped output in its old code page, without ✓ or ≈
|
|
361
362
|
if hasattr(stream, "reconfigure") and (stream.encoding or "").lower().replace("-", "") != "utf8":
|
|
@@ -365,6 +366,10 @@ def main(argv=None) -> int:
|
|
|
365
366
|
parser.add_argument("--version", action="version", version=f"teamrunner {__version__}")
|
|
366
367
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
367
368
|
|
|
369
|
+
ui = commands.add_parser("ui", aliases=["gui"], help="open this folder in the local browser interface")
|
|
370
|
+
ui.add_argument("folder", nargs="?", default=".", help="the folder to open (default: this folder)")
|
|
371
|
+
ui.set_defaults(func=cmd_ui)
|
|
372
|
+
|
|
368
373
|
v = commands.add_parser("validate", help="check a team's files before running it")
|
|
369
374
|
v.add_argument("team", nargs="?", default=".", help="the team folder (default: this folder)")
|
|
370
375
|
v.add_argument("-v", "--verbose", action="store_true",
|
|
@@ -164,7 +164,7 @@ class Console:
|
|
|
164
164
|
request = " ".join(str(first).split())
|
|
165
165
|
request = request if len(request) <= 50 else request[:49] + "…"
|
|
166
166
|
verb = {"web_search_brave": "search", "read_page": "read", "read_file": "read",
|
|
167
|
-
"run_code": "run"}.get(e["tool"], e["tool"])
|
|
167
|
+
"run_code": "run", "read_email": "read", "save_draft": "draft"}.get(e["tool"], e["tool"])
|
|
168
168
|
if e["access"] == "allowed" and e.get("result") is not None:
|
|
169
169
|
allowance = e.get("allowance") or {}
|
|
170
170
|
mark = p.green(f"✓ {allowance.get('used')} of {allowance.get('of')}")
|