loopy-loop 0.2.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.
- loopy_loop-0.2.0/.eval-banana/config.toml +57 -0
- loopy_loop-0.2.0/.github/workflows/ci.yml +39 -0
- loopy_loop-0.2.0/.github/workflows/release.yml +68 -0
- loopy_loop-0.2.0/.gitignore +17 -0
- loopy_loop-0.2.0/.team-harness/config.toml +205 -0
- loopy_loop-0.2.0/.team-harness/coordinator_system_message.md +195 -0
- loopy_loop-0.2.0/.team-harness/worker_footer.md +8 -0
- loopy_loop-0.2.0/.team-harness/worker_suffix.md +0 -0
- loopy_loop-0.2.0/CHANGELOG.md +41 -0
- loopy_loop-0.2.0/LICENSE +201 -0
- loopy_loop-0.2.0/Makefile +27 -0
- loopy_loop-0.2.0/PKG-INFO +408 -0
- loopy_loop-0.2.0/README.md +368 -0
- loopy_loop-0.2.0/docs/http-contract.md +106 -0
- loopy_loop-0.2.0/docs/session-layout.md +217 -0
- loopy_loop-0.2.0/pyproject.toml +131 -0
- loopy_loop-0.2.0/skills/loopy-loop/SKILL.md +379 -0
- loopy_loop-0.2.0/src/loopy_loop/__init__.py +1 -0
- loopy_loop-0.2.0/src/loopy_loop/cli.py +293 -0
- loopy_loop-0.2.0/src/loopy_loop/config.py +491 -0
- loopy_loop-0.2.0/src/loopy_loop/coordinator_app.py +706 -0
- loopy_loop-0.2.0/src/loopy_loop/harness_runner.py +195 -0
- loopy_loop-0.2.0/src/loopy_loop/models.py +163 -0
- loopy_loop-0.2.0/src/loopy_loop/scheduler.py +232 -0
- loopy_loop-0.2.0/src/loopy_loop/sessions.py +313 -0
- loopy_loop-0.2.0/src/loopy_loop/state_store.py +110 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.gitignore +4 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_reviewer/config.yaml +11 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_reviewer/prompt.txt +97 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_runner/config.yaml +12 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_runner/prompt.txt +86 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/inner/config.yaml +8 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/inner/prompt.txt +223 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/outer/config.yaml +8 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/outer/prompt.txt +333 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/loopy_loop_config.yaml +45 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/inner_outer_eval/loopy_loop_goal.txt +5 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/.gitignore +1 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/dispatcher/config.yaml +8 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/dispatcher/prompt.txt +78 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/planner/config.yaml +9 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/planner/prompt.txt +99 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/loopy_loop_config.yaml +24 -0
- loopy_loop-0.2.0/src/loopy_loop/templates/pm_planner_dispatcher/loopy_loop_goal.txt +1 -0
- loopy_loop-0.2.0/src/loopy_loop/worker.py +265 -0
- loopy_loop-0.2.0/src/tests/__init__.py +1 -0
- loopy_loop-0.2.0/src/tests/conftest.py +194 -0
- loopy_loop-0.2.0/src/tests/test_api_base_normalization.py +15 -0
- loopy_loop-0.2.0/src/tests/test_cli.py +240 -0
- loopy_loop-0.2.0/src/tests/test_config.py +381 -0
- loopy_loop-0.2.0/src/tests/test_coordinator_app.py +1046 -0
- loopy_loop-0.2.0/src/tests/test_examples.py +42 -0
- loopy_loop-0.2.0/src/tests/test_fresh_run_archive.py +26 -0
- loopy_loop-0.2.0/src/tests/test_goal_check_gate.py +51 -0
- loopy_loop-0.2.0/src/tests/test_goal_hash_derivation.py +13 -0
- loopy_loop-0.2.0/src/tests/test_harness_runner.py +399 -0
- loopy_loop-0.2.0/src/tests/test_idempotent_finished.py +153 -0
- loopy_loop-0.2.0/src/tests/test_must_follow_success.py +82 -0
- loopy_loop-0.2.0/src/tests/test_scheduler.py +470 -0
- loopy_loop-0.2.0/src/tests/test_sessions.py +147 -0
- loopy_loop-0.2.0/src/tests/test_state_store.py +72 -0
- loopy_loop-0.2.0/src/tests/test_worker.py +436 -0
- loopy_loop-0.2.0/uv.lock +868 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Project-level eval-banana configuration.
|
|
2
|
+
|
|
3
|
+
[core]
|
|
4
|
+
# Directory where run artifacts (report.json, report.md, per-check output) are
|
|
5
|
+
# written. Relative paths resolve from the project root.
|
|
6
|
+
# Env: EVAL_BANANA_OUTPUT_DIR
|
|
7
|
+
output_dir = ".eval-banana/results"
|
|
8
|
+
|
|
9
|
+
# Minimum pass ratio (0.0-1.0) for `run_passed` to be true. 1.0 means every
|
|
10
|
+
# check must pass. Set lower (e.g. 0.8) to tolerate some failing checks.
|
|
11
|
+
# Env: EVAL_BANANA_PASS_THRESHOLD
|
|
12
|
+
pass_threshold = 1.0
|
|
13
|
+
|
|
14
|
+
# Maximum characters of each target file sent to harness_judge checks. Content
|
|
15
|
+
# beyond this limit is truncated with a [TRUNCATED] marker.
|
|
16
|
+
# Set to 0 (the default) to disable truncation entirely — the full file
|
|
17
|
+
# content is sent to the harness agent regardless of size.
|
|
18
|
+
# Env: EVAL_BANANA_LLM_MAX_INPUT_CHARS
|
|
19
|
+
llm_max_input_chars = 0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
[harness]
|
|
23
|
+
# AI coding agent used by harness_judge checks. One of:
|
|
24
|
+
# claude, codex, gemini, openhands, opencode, pi
|
|
25
|
+
# Env: EVAL_BANANA_HARNESS_AGENT
|
|
26
|
+
agent = "codex"
|
|
27
|
+
|
|
28
|
+
# Override the agent's default model. Format is agent-specific.
|
|
29
|
+
# Env: EVAL_BANANA_HARNESS_MODEL
|
|
30
|
+
model = "gpt-5.5"
|
|
31
|
+
|
|
32
|
+
# Agent-specific reasoning-effort level. Common values: "low", "medium", "high".
|
|
33
|
+
# Not all agents honor this. Env: EVAL_BANANA_HARNESS_REASONING_EFFORT
|
|
34
|
+
reasoning_effort = "high"
|
|
35
|
+
|
|
36
|
+
# Extra environment variables injected into the harness subprocess.
|
|
37
|
+
# [harness.env]
|
|
38
|
+
# MY_VAR = "value"
|
|
39
|
+
|
|
40
|
+
# --- Built-in agent flags (applied automatically) ---
|
|
41
|
+
# These flags are passed to the agent subprocess by default.
|
|
42
|
+
# To override, uncomment the relevant [agents.*] section below.
|
|
43
|
+
#
|
|
44
|
+
# [agents.codex]
|
|
45
|
+
# shared_flags = ["--dangerously-bypass-approvals-and-sandbox", "--skip-git-repo-check"]
|
|
46
|
+
#
|
|
47
|
+
# [agents.gemini]
|
|
48
|
+
# shared_flags = ["--approval-mode", "yolo", "--output-format", "stream-json"]
|
|
49
|
+
#
|
|
50
|
+
# [agents.claude]
|
|
51
|
+
# shared_flags = ["-p", "--dangerously-skip-permissions", "--output-format", "stream-json", "--verbose"]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
[discovery]
|
|
55
|
+
# Directories skipped when auto-discovering eval_checks/ folders. Override this
|
|
56
|
+
# if your project uses a non-standard layout.
|
|
57
|
+
exclude_dirs = [".git", ".hg", ".svn", ".venv", "venv", "node_modules", "__pycache__", "dist", "build"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main", "dev"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main", "dev"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
|
|
23
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
24
|
+
run: uv python install ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync --extra dev --python ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Ruff lint
|
|
30
|
+
run: uv run ruff check src/
|
|
31
|
+
|
|
32
|
+
- name: Ruff format check
|
|
33
|
+
run: uv run ruff format --check src/
|
|
34
|
+
|
|
35
|
+
- name: Pyright type check
|
|
36
|
+
run: uv run pyright src/ --pythonversion ${{ matrix.python-version }}
|
|
37
|
+
|
|
38
|
+
- name: Run tests
|
|
39
|
+
run: uv run pytest src/tests/ -v --tb=short
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*.*.*"]
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: release-${{ github.ref }}
|
|
9
|
+
cancel-in-progress: false
|
|
10
|
+
|
|
11
|
+
env:
|
|
12
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
22
|
+
|
|
23
|
+
- name: Build sdist and wheel
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Check distribution metadata
|
|
27
|
+
run: uv run --with twine twine check --strict dist/*
|
|
28
|
+
|
|
29
|
+
- name: Upload dist artifacts
|
|
30
|
+
uses: actions/upload-artifact@v7
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment: pypi
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
steps:
|
|
42
|
+
- name: Download dist artifacts
|
|
43
|
+
uses: actions/download-artifact@v8
|
|
44
|
+
with:
|
|
45
|
+
name: dist
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
- name: Publish to PyPI
|
|
49
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
|
+
|
|
51
|
+
github-release:
|
|
52
|
+
needs: publish
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
permissions:
|
|
55
|
+
contents: write
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v6
|
|
58
|
+
|
|
59
|
+
- name: Download dist artifacts
|
|
60
|
+
uses: actions/download-artifact@v8
|
|
61
|
+
with:
|
|
62
|
+
name: dist
|
|
63
|
+
path: dist/
|
|
64
|
+
|
|
65
|
+
- name: Create GitHub Release
|
|
66
|
+
env:
|
|
67
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
68
|
+
run: gh release create "${GITHUB_REF_NAME}" dist/* --generate-notes --verify-tag
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
.idea/
|
|
5
|
+
|
|
6
|
+
.loopy_loop/sessions/
|
|
7
|
+
.loopy_loop/state.json
|
|
8
|
+
.loopy_loop/state.json.lock
|
|
9
|
+
.loopy_loop/state.json.archive_*.json
|
|
10
|
+
|
|
11
|
+
.mcp.json
|
|
12
|
+
.agents/
|
|
13
|
+
.claude/
|
|
14
|
+
_claude_prompts/
|
|
15
|
+
_feature_planning/
|
|
16
|
+
_outputs/
|
|
17
|
+
_additional_context/
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Project-level team-harness config.
|
|
2
|
+
# Values here override ~/.team-harness/config.toml.
|
|
3
|
+
# Lists replace, they do not extend, the global value.
|
|
4
|
+
# Do not store API keys here; prefer environment variables.
|
|
5
|
+
|
|
6
|
+
[coordinator]
|
|
7
|
+
# Coordinator backend: "openai_compat" or "codex" (experimental).
|
|
8
|
+
provider = "codex"
|
|
9
|
+
|
|
10
|
+
# Model name passed to the coordinator API.
|
|
11
|
+
model = "gpt-5.5"
|
|
12
|
+
|
|
13
|
+
# Base URL for the coordinator API.
|
|
14
|
+
api_base = "https://openrouter.ai/api/v1"
|
|
15
|
+
|
|
16
|
+
# API key — prefer OPENROUTER_API_KEY or OPENAI_API_KEY env var instead.
|
|
17
|
+
# api_key = ""
|
|
18
|
+
|
|
19
|
+
# Coordinator system-message, worker suffix, and worker footer files.
|
|
20
|
+
coordinator_system_message_file = "coordinator_system_message.md"
|
|
21
|
+
worker_suffix_file = "worker_suffix.md"
|
|
22
|
+
worker_footer_file = "worker_footer.md"
|
|
23
|
+
|
|
24
|
+
# Extra text appended to the system prompt for every run.
|
|
25
|
+
system_prompt = ""
|
|
26
|
+
|
|
27
|
+
# Base directory for per-session coordinator/worker artifacts.
|
|
28
|
+
output_dir = "_outputs"
|
|
29
|
+
|
|
30
|
+
# Retry budget for transient API errors (429 / 5xx).
|
|
31
|
+
max_retries = 5
|
|
32
|
+
|
|
33
|
+
# Maximum nesting depth for recursive th-run agents.
|
|
34
|
+
max_depth = 3
|
|
35
|
+
|
|
36
|
+
# Override the model's context window size (tokens). Leave commented to auto-detect.
|
|
37
|
+
# context_limit = 128000
|
|
38
|
+
|
|
39
|
+
# Seconds to wait for running agents on /quit or Ctrl+C before force-killing.
|
|
40
|
+
shutdown_timeout_s = 10.0
|
|
41
|
+
|
|
42
|
+
# Minimum lifetime before kill_agent will refuse early termination.
|
|
43
|
+
min_agent_lifetime_before_kill_s = 600.0
|
|
44
|
+
|
|
45
|
+
# Restrict which agent types the coordinator can spawn. Leave commented to allow all.
|
|
46
|
+
# allowed_agents = ["codex", "gemini", "claude", "openhands", "opencode", "pi", "harness"]
|
|
47
|
+
|
|
48
|
+
# --- Experimental Codex subscription coordinator ---
|
|
49
|
+
# provider = "codex"
|
|
50
|
+
# model = "codex-mini-latest"
|
|
51
|
+
# codex_auth_path = ".team-harness/codex-auth.json"
|
|
52
|
+
|
|
53
|
+
# Worker agent invocations. Each agent is described as a structured
|
|
54
|
+
# command: a base `command` list, `shared_flags` that are always applied,
|
|
55
|
+
# `resume_flags` that are applied only when resuming a previous session,
|
|
56
|
+
# and a `session_capture` sub-table describing how the harness extracts
|
|
57
|
+
# the provider's session id from the worker's stream-json output.
|
|
58
|
+
#
|
|
59
|
+
# These blocks override the built-in defaults. Any field you omit falls
|
|
60
|
+
# back to the corresponding built-in default for that agent type (so, for
|
|
61
|
+
# example, a custom `[agents.codex]` that only sets `model_flag` keeps all
|
|
62
|
+
# other fields from the default codex template).
|
|
63
|
+
#
|
|
64
|
+
# Custom (non-built-in) agent types must set at least `command`.
|
|
65
|
+
|
|
66
|
+
# Codex worker. `--json` is required so the harness can parse the initial
|
|
67
|
+
# `thread.started` event and capture the session id for future resume.
|
|
68
|
+
# `default_model` is the model passed to codex when the coordinator does
|
|
69
|
+
# not override it via `spawn_agent(model="...")`.
|
|
70
|
+
# `reasoning_effort_flag` tells the harness how to pass the level; set
|
|
71
|
+
# `reasoning_effort` (commented) to actually enable it.
|
|
72
|
+
[agents.codex]
|
|
73
|
+
command = ["codex", "exec"]
|
|
74
|
+
shared_flags = [
|
|
75
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
76
|
+
"--skip-git-repo-check",
|
|
77
|
+
"--json",
|
|
78
|
+
]
|
|
79
|
+
resume_prefix = ["resume"]
|
|
80
|
+
resume_flags = ["{session_id}"]
|
|
81
|
+
model_flag = "--model"
|
|
82
|
+
default_model = "gpt-5.5"
|
|
83
|
+
reasoning_effort_flag = ["-c", "model_reasoning_effort={effort}"]
|
|
84
|
+
# reasoning_effort = "high" # uncomment to pin a level (low|medium|high|xhigh)
|
|
85
|
+
|
|
86
|
+
[agents.codex.session_capture]
|
|
87
|
+
strategy = "stream_json_event"
|
|
88
|
+
match = { type = "thread.started" }
|
|
89
|
+
field_path = ["thread_id"]
|
|
90
|
+
|
|
91
|
+
# --- OpenRouter recipe for Codex ---------------------------------------
|
|
92
|
+
# By default the codex worker talks to its native provider (the one codex
|
|
93
|
+
# is logged in to). To route codex through OpenRouter instead, export
|
|
94
|
+
# OPENROUTER_API_KEY in your shell and override just `shared_flags` and
|
|
95
|
+
# `default_model` on the [agents.codex] block above with the values
|
|
96
|
+
# below — everything else (command, resume_*, model_flag, session_capture,
|
|
97
|
+
# reasoning_effort_flag) stays as the default. Codex reads
|
|
98
|
+
# OPENROUTER_API_KEY directly via the env_key setting, so no
|
|
99
|
+
# provider_env is needed.
|
|
100
|
+
#
|
|
101
|
+
# shared_flags = [
|
|
102
|
+
# "--dangerously-bypass-approvals-and-sandbox",
|
|
103
|
+
# "--skip-git-repo-check",
|
|
104
|
+
# "--json",
|
|
105
|
+
# "-c", "model_provider=openrouter",
|
|
106
|
+
# "-c", 'model_providers.openrouter.name="openrouter"',
|
|
107
|
+
# "-c", 'model_providers.openrouter.base_url="https://openrouter.ai/api/v1"',
|
|
108
|
+
# "-c", 'model_providers.openrouter.env_key="OPENROUTER_API_KEY"',
|
|
109
|
+
# ]
|
|
110
|
+
# default_model = "openai/gpt-5.3-codex" # OpenRouter-flavoured model name
|
|
111
|
+
# -----------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Gemini worker. `stream-json` gives us a parseable session id in the
|
|
116
|
+
# initial `init` event.
|
|
117
|
+
[agents.gemini]
|
|
118
|
+
command = ["gemini"]
|
|
119
|
+
shared_flags = ["--approval-mode", "yolo", "--output-format", "stream-json"]
|
|
120
|
+
resume_flags = ["--resume", "{session_id}"]
|
|
121
|
+
prompt_flag = "-p"
|
|
122
|
+
model_flag = "--model"
|
|
123
|
+
|
|
124
|
+
[agents.gemini.session_capture]
|
|
125
|
+
strategy = "stream_json_event"
|
|
126
|
+
match = { type = "init" }
|
|
127
|
+
field_path = ["session_id"]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# Claude Code worker. `--verbose` is mandatory when `-p` and
|
|
132
|
+
# `--output-format stream-json` are combined (Claude CLI requirement).
|
|
133
|
+
# Claude reads its model from several env vars, not just --model. We
|
|
134
|
+
# set the three "main model" vars together (ANTHROPIC_MODEL plus the
|
|
135
|
+
# opus/sonnet alias resolvers) so overriding the model is deterministic
|
|
136
|
+
# across all of Claude Code's internal code paths. We deliberately do
|
|
137
|
+
# NOT set ANTHROPIC_DEFAULT_HAIKU_MODEL / ANTHROPIC_SMALL_FAST_MODEL /
|
|
138
|
+
# CLAUDE_CODE_SUBAGENT_MODEL so cheap auxiliary helpers stay cheap.
|
|
139
|
+
# `default_model` is left unset by default — configure per-project.
|
|
140
|
+
[agents.claude]
|
|
141
|
+
command = ["claude"]
|
|
142
|
+
shared_flags = [
|
|
143
|
+
"-p",
|
|
144
|
+
"--dangerously-skip-permissions",
|
|
145
|
+
"--output-format", "stream-json",
|
|
146
|
+
"--verbose",
|
|
147
|
+
]
|
|
148
|
+
resume_flags = ["--resume", "{session_id}"]
|
|
149
|
+
model_flag = "--model"
|
|
150
|
+
model_env_vars = [
|
|
151
|
+
"ANTHROPIC_MODEL",
|
|
152
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
153
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
154
|
+
]
|
|
155
|
+
reasoning_effort_flag = ["--effort", "{effort}"]
|
|
156
|
+
# default_model = "claude-sonnet-4-6" # uncomment to pin a default
|
|
157
|
+
# reasoning_effort = "high" # values: low|medium|high|max
|
|
158
|
+
|
|
159
|
+
[agents.claude.session_capture]
|
|
160
|
+
strategy = "stream_json_event"
|
|
161
|
+
match = { type = "system", subtype = "init" }
|
|
162
|
+
field_path = ["session_id"]
|
|
163
|
+
|
|
164
|
+
# --- OpenRouter recipe for Claude Code ---------------------------------
|
|
165
|
+
# By default the claude worker talks to native Anthropic (or whichever
|
|
166
|
+
# provider your existing ANTHROPIC_* shell env points at). To route
|
|
167
|
+
# claude through OpenRouter instead, export OPENROUTER_API_KEY in your
|
|
168
|
+
# shell and uncomment the provider_env block below. The
|
|
169
|
+
# `{env:OPENROUTER_API_KEY}` placeholder is resolved at spawn time from
|
|
170
|
+
# the parent environment so no secret lives in this file.
|
|
171
|
+
# `ANTHROPIC_API_KEY` MUST be set to the empty string so Claude Code
|
|
172
|
+
# does not short-circuit to native Anthropic auth. Also uncomment
|
|
173
|
+
# `default_model` above and point it at an OpenRouter-flavoured model
|
|
174
|
+
# name like "anthropic/claude-opus-4.6".
|
|
175
|
+
#
|
|
176
|
+
# [agents.claude.provider_env]
|
|
177
|
+
# ANTHROPIC_BASE_URL = "https://openrouter.ai/api"
|
|
178
|
+
# ANTHROPIC_AUTH_TOKEN = "{env:OPENROUTER_API_KEY}"
|
|
179
|
+
# ANTHROPIC_API_KEY = ""
|
|
180
|
+
# -----------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
# OpenHands worker. `--override-with-envs` is required or `LLM_MODEL`
|
|
183
|
+
# is ignored by OpenHands-CLI. `session_capture` is intentionally
|
|
184
|
+
# omitted: `--json` emits multi-line pretty JSON blocks delimited by
|
|
185
|
+
# `--JSON Event--`, which the harness cannot parse as stream-json today.
|
|
186
|
+
# That also means resume is not wired up for this worker.
|
|
187
|
+
[agents.openhands]
|
|
188
|
+
command = ["openhands"]
|
|
189
|
+
shared_flags = ["--headless", "--json", "--override-with-envs"]
|
|
190
|
+
prompt_flag = "-t"
|
|
191
|
+
model_env_vars = ["LLM_MODEL"]
|
|
192
|
+
|
|
193
|
+
[agents.opencode]
|
|
194
|
+
command = ["opencode"]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
[agents.pi]
|
|
199
|
+
command = ["pi", "--print", "--no-session"]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
[agents.harness]
|
|
204
|
+
command = ["th", "run"]
|
|
205
|
+
model_flag = "--model"
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
You are the coordinator for team-harness.
|
|
2
|
+
|
|
3
|
+
You are not a coding agent. You do not personally implement the task. Your role
|
|
4
|
+
is to understand the work, delegate it to worker agents, monitor progress,
|
|
5
|
+
intervene when needed, and synthesize the final answer for the user.
|
|
6
|
+
|
|
7
|
+
Your operating style is pragmatic, clear, direct, patient, and rigorous.
|
|
8
|
+
|
|
9
|
+
Values:
|
|
10
|
+
- Clarity: make plans, prompts, risks, and conclusions explicit.
|
|
11
|
+
- Pragmatism: choose the simplest orchestration strategy that will actually
|
|
12
|
+
work.
|
|
13
|
+
- Rigor: challenge weak assumptions, detect gaps, and verify claims before
|
|
14
|
+
reporting them.
|
|
15
|
+
- Patience: wait for real evidence from agents and tools; never fabricate
|
|
16
|
+
progress or results.
|
|
17
|
+
|
|
18
|
+
Core responsibilities:
|
|
19
|
+
1. Understand the user request and identify the real deliverable.
|
|
20
|
+
2. Break the work into concrete units with clear ownership.
|
|
21
|
+
3. Spawn workers with self-contained prompts.
|
|
22
|
+
4. Monitor workers until they finish, fail, or clearly stall.
|
|
23
|
+
5. Read worker outputs and logs before making conclusions.
|
|
24
|
+
6. Synthesize the final answer truthfully and concisely.
|
|
25
|
+
|
|
26
|
+
Coordinator boundaries:
|
|
27
|
+
- You are an orchestrator, not an implementer.
|
|
28
|
+
- Do not do work that you already delegated to a worker.
|
|
29
|
+
- Do not silently replace missing worker results with your own guesses.
|
|
30
|
+
- Do not ask workers to "figure everything out" without context, scope, and
|
|
31
|
+
success criteria.
|
|
32
|
+
- Delegate execution. Keep understanding, planning, prioritization, and
|
|
33
|
+
cross-worker synthesis with yourself.
|
|
34
|
+
|
|
35
|
+
Task tracking:
|
|
36
|
+
- Use the todo tools aggressively.
|
|
37
|
+
- Create or refresh the todo list at the start of each run.
|
|
38
|
+
- Keep tasks concrete and outcome-oriented.
|
|
39
|
+
- Mark progress as you spawn agents, receive results, revise the plan, and
|
|
40
|
+
complete synthesis.
|
|
41
|
+
- Prefer exactly one `in_progress` todo at a time unless the tool semantics
|
|
42
|
+
require otherwise.
|
|
43
|
+
- If the plan changes, update the todo list immediately instead of letting it
|
|
44
|
+
drift.
|
|
45
|
+
|
|
46
|
+
Delegation rules:
|
|
47
|
+
- Every worker prompt must be self-contained.
|
|
48
|
+
- Include the task, relevant context, constraints, expected output, and working
|
|
49
|
+
directory.
|
|
50
|
+
- Tell the worker exactly what you want back: files, analysis, verification, or
|
|
51
|
+
a recommendation.
|
|
52
|
+
- Assign distinct ownership so workers do not duplicate each other.
|
|
53
|
+
- Parallelize only independent work. Keep dependent work serial.
|
|
54
|
+
- If a worker needs repository context, give enough context in the prompt; do
|
|
55
|
+
not offload your own planning confusion.
|
|
56
|
+
- If a worker should write artifacts, tell it exactly where to write them
|
|
57
|
+
inside the session output directory.
|
|
58
|
+
|
|
59
|
+
Good worker prompt pattern:
|
|
60
|
+
- objective
|
|
61
|
+
- relevant context
|
|
62
|
+
- constraints and non-goals
|
|
63
|
+
- exact cwd
|
|
64
|
+
- exact output path or directory
|
|
65
|
+
- definition of done
|
|
66
|
+
|
|
67
|
+
Bad worker prompt pattern:
|
|
68
|
+
- "Look around and do whatever seems right."
|
|
69
|
+
|
|
70
|
+
Patience Protocol (STRICT — violations cause real harm):
|
|
71
|
+
|
|
72
|
+
1. `timed_out=true` from `wait_for_any` or `wait_for_agents` DOES NOT mean the
|
|
73
|
+
agent is stuck. It means "the agent is still running, your timeout elapsed."
|
|
74
|
+
This is the expected, normal outcome for the first several minutes of any
|
|
75
|
+
non-trivial task. Re-enter `wait_for_any` with a longer timeout.
|
|
76
|
+
|
|
77
|
+
2. Typical durations you should expect:
|
|
78
|
+
- codex planning task: 20–45 minutes
|
|
79
|
+
- gemini research task: 15–30 minutes
|
|
80
|
+
- claude review task: 5–15 minutes
|
|
81
|
+
If your wait-timeout is shorter than the typical duration, the first few
|
|
82
|
+
wake-ups will ALL be timeouts. That is normal.
|
|
83
|
+
|
|
84
|
+
3. HARD FLOOR: do NOT call `kill_agent` on any worker whose `elapsed_seconds`
|
|
85
|
+
is less than {min_agent_lifetime_before_kill_s} (the configured minimum
|
|
86
|
+
lifetime). The tool itself will refuse premature kills.
|
|
87
|
+
|
|
88
|
+
4. STDERR GROWTH RULE: if `stderr_bytes_delta_since_last_check > 0` on the
|
|
89
|
+
most recent `wait_for_any` response, the agent is actively producing work.
|
|
90
|
+
DO NOT kill it. Re-enter `wait_for_any`. This rule overrides all other
|
|
91
|
+
timers.
|
|
92
|
+
|
|
93
|
+
5. Before you EVER call `kill_agent`, you MUST have called `read_agent_output`
|
|
94
|
+
at least once in this run and looked at the stderr tail. If the wait
|
|
95
|
+
snapshot's `advisory` says HEALTHY, treat `kill_agent` as forbidden.
|
|
96
|
+
|
|
97
|
+
6. Respawn prohibition: never respawn the same worker with essentially the
|
|
98
|
+
same prompt without first documenting in your own reasoning exactly why
|
|
99
|
+
the previous attempt's trajectory was wrong. "It was slow" is not a
|
|
100
|
+
reason. "It did not emit output for 5 minutes" is not a reason.
|
|
101
|
+
|
|
102
|
+
7. When there is nothing useful to do locally while waiting, the correct
|
|
103
|
+
action is `wait_for_any` with a longer timeout, NOT `read_agent_output`
|
|
104
|
+
in a tight loop. Polling is not patience.
|
|
105
|
+
|
|
106
|
+
Recommended monitoring loop:
|
|
107
|
+
1. Spawn the agents that can run now.
|
|
108
|
+
2. Update todos.
|
|
109
|
+
3. Call `wait_for_any(agent_ids, timeout=60-120)`.
|
|
110
|
+
4. On wake-up, inspect the `running[].advisory` field. If HEALTHY, wait again.
|
|
111
|
+
5. If an agent finished, read its results and update the plan.
|
|
112
|
+
6. If all remaining agents are still running and nothing useful can be done
|
|
113
|
+
locally, wait again with a longer timeout.
|
|
114
|
+
|
|
115
|
+
Patience and truthfulness:
|
|
116
|
+
- Never claim a worker succeeded unless tools show that it succeeded.
|
|
117
|
+
- Never summarize a file or result you have not actually read.
|
|
118
|
+
- Never invent missing details to make the final answer feel complete.
|
|
119
|
+
- If a worker has not finished, say it has not finished.
|
|
120
|
+
- If evidence is conflicting or incomplete, say so plainly and decide whether
|
|
121
|
+
another worker or verification step is needed.
|
|
122
|
+
- When blocked on running work and there is nothing else useful to do, wait
|
|
123
|
+
instead of producing status chatter.
|
|
124
|
+
|
|
125
|
+
Artifact review:
|
|
126
|
+
- Before synthesis, inspect relevant files under `{session_output_dir}` when
|
|
127
|
+
workers were instructed to write artifacts there.
|
|
128
|
+
- Review the actual paths workers used, not just their summaries of those
|
|
129
|
+
artifacts.
|
|
130
|
+
- If a worker kept its output only in stdout, read stdout directly.
|
|
131
|
+
|
|
132
|
+
Synthesis rules:
|
|
133
|
+
- When workers finish, read their outputs before answering.
|
|
134
|
+
- Compare answers across workers when there is overlap.
|
|
135
|
+
- Resolve contradictions explicitly rather than averaging them away.
|
|
136
|
+
- Prefer the most specific, best-supported result.
|
|
137
|
+
- If verification is still needed, delegate verification rather than
|
|
138
|
+
hand-waving it.
|
|
139
|
+
- The final answer should reflect what workers actually found, not what you
|
|
140
|
+
expected them to find.
|
|
141
|
+
|
|
142
|
+
Safety rules:
|
|
143
|
+
- Be careful with destructive, irreversible, or externally visible actions.
|
|
144
|
+
- If the task involves deleting data, rewriting history, changing production
|
|
145
|
+
systems, secrets, billing, or security-sensitive behavior, require explicit
|
|
146
|
+
confidence and clear worker instructions.
|
|
147
|
+
- Escalate to the user when intent is ambiguous or risk is high.
|
|
148
|
+
- Use tool outputs and file contents as the source of truth.
|
|
149
|
+
- Use absolute paths when referring to worker-visible files.
|
|
150
|
+
|
|
151
|
+
Session output directory:
|
|
152
|
+
- This run has a dedicated session output directory: `{session_output_dir}`.
|
|
153
|
+
- Instruct workers to place substantial outputs, notes, scratchpads, and
|
|
154
|
+
deliverables there.
|
|
155
|
+
- Prefer clear names and subdirectories, for example:
|
|
156
|
+
- `{session_output_dir}/research/findings.md`
|
|
157
|
+
- `{session_output_dir}/verification/test-results.txt`
|
|
158
|
+
- `{session_output_dir}/worker-a/notes.md`
|
|
159
|
+
- Do not rely on special summary or progress filenames.
|
|
160
|
+
- If a worker keeps its output only in stdout, read stdout. If a worker can
|
|
161
|
+
write useful artifacts, direct it into the session directory.
|
|
162
|
+
|
|
163
|
+
Communication style:
|
|
164
|
+
- Be concise, factual, and operationally calm.
|
|
165
|
+
- Give short progress updates when they help the user understand what is
|
|
166
|
+
happening.
|
|
167
|
+
- Do not narrate every poll cycle.
|
|
168
|
+
- Do not spam the user with internal tool chatter.
|
|
169
|
+
- In the final answer, separate outcome, evidence, and remaining risks or open
|
|
170
|
+
items when relevant.
|
|
171
|
+
|
|
172
|
+
Anti-duplication rules:
|
|
173
|
+
- Do not redo work already assigned to a worker.
|
|
174
|
+
- Do not spawn multiple workers on the same task unless you intentionally want
|
|
175
|
+
a cross-check.
|
|
176
|
+
- Do not read large parts of the repo yourself just to duplicate an active
|
|
177
|
+
worker's assignment.
|
|
178
|
+
- Keep your own effort focused on planning, monitoring, intervention, and
|
|
179
|
+
synthesis.
|
|
180
|
+
|
|
181
|
+
Completion standard:
|
|
182
|
+
- Do not stop when you have a plausible answer.
|
|
183
|
+
- Stop when the user request has been handled end-to-end, all necessary workers
|
|
184
|
+
have finished or been accounted for, and the final answer is supported by
|
|
185
|
+
actual evidence.
|
|
186
|
+
|
|
187
|
+
Runtime context for this run:
|
|
188
|
+
- Available agent types: {allowed_agent_types}
|
|
189
|
+
- Working directory: {cwd}
|
|
190
|
+
- Current UTC time: {current_utc_time}
|
|
191
|
+
- Session output directory: {session_output_dir}
|
|
192
|
+
|
|
193
|
+
If additional skills are available for this run, they will be listed below.
|
|
194
|
+
If the user or config provides extra system instructions, follow them unless
|
|
195
|
+
they conflict with the rules above.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
IMPORTANT — output requirements:
|
|
3
|
+
- Write substantial artifacts to files when useful instead of relying only on
|
|
4
|
+
stdout.
|
|
5
|
+
- Session output directory: {session_output_dir}
|
|
6
|
+
Place outputs, notes, and scratchpads there.
|
|
7
|
+
- Report blockers, errors, and any final result clearly in your response.
|
|
8
|
+
---
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 (breaking)
|
|
4
|
+
|
|
5
|
+
**Breaking API change — drop leases, polling, and worker identity.**
|
|
6
|
+
|
|
7
|
+
The three-endpoint API (`POST /workers/register`, `POST /workers/{worker_id}/next`,
|
|
8
|
+
`POST /workers/{worker_id}/finished`) is replaced by a two-endpoint ping-pong API:
|
|
9
|
+
|
|
10
|
+
- `POST /register` — returns `TaskResponse` directly (`action: "run"` or `"stop"`).
|
|
11
|
+
- `POST /finished` — returns `TaskResponse` for the next task or stop signal.
|
|
12
|
+
|
|
13
|
+
The `"wait"` action is gone. The worker loop is now a simple `while task.action == "run"`
|
|
14
|
+
loop with no polling.
|
|
15
|
+
|
|
16
|
+
**Removed models and fields:**
|
|
17
|
+
- `WorkerState`, `ActiveAssignment`, `RegisterWorkerResponse`, `NextActionResponse` removed.
|
|
18
|
+
- `assignment_id` removed from `FinishedRequest` and `HistoryEntry`.
|
|
19
|
+
- `worker_id` removed from `HistoryEntry`.
|
|
20
|
+
- `workers` dict and `active_assignment` removed from `LoopState`.
|
|
21
|
+
- Constants `DEFAULT_LEASE_SECONDS`, `DEFAULT_POLL_INTERVAL_SECONDS`,
|
|
22
|
+
`DEFAULT_FINISHED_RETRY_ATTEMPTS`, `DEFAULT_FINISHED_RETRY_BACKOFF_SECONDS`,
|
|
23
|
+
`WAIT_ACTION` removed.
|
|
24
|
+
|
|
25
|
+
**Added:**
|
|
26
|
+
- `CurrentTask` model replaces `ActiveAssignment` (no `worker_id`, no lease).
|
|
27
|
+
- `TaskResponse` model returned by both `/register` and `/finished`.
|
|
28
|
+
- `LoopState.current_task: CurrentTask | None` replaces `active_assignment`.
|
|
29
|
+
|
|
30
|
+
**Crash recovery:** If a worker crashes mid-run, the next `/register` call detects the
|
|
31
|
+
orphaned `current_task`, records it in history as `error="abandoned"`, and dispatches
|
|
32
|
+
fresh work.
|
|
33
|
+
|
|
34
|
+
**Migration note:** Existing `state.json` files containing `active_assignment` or `workers`
|
|
35
|
+
fields will have those fields silently dropped on first read (Pydantic ignores unknown
|
|
36
|
+
fields). Any in-flight run at upgrade time will lose its `active_assignment` record without
|
|
37
|
+
a history entry. Drain all workers before deploying this version.
|
|
38
|
+
|
|
39
|
+
## 0.1.0
|
|
40
|
+
|
|
41
|
+
- Initial release.
|