Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
50003cb
harness: System One joins as the fifteenth base, a decision loop over…
richard-epsilla Sep 20, 2026
b58e125
entrypoint: the systemone installer takes an alternative source and l…
richard-epsilla Sep 20, 2026
ef42850
gateway: a continuation belongs to its session's harness
richard-epsilla Sep 20, 2026
10eac7a
gateway: a custom endpoint's models are listed only on backends a cus…
richard-epsilla Sep 20, 2026
40697ae
systemone takes no skills: none offered, none mounted, and the consol…
richard-epsilla Sep 20, 2026
7eaeba4
systemone: pin the harness at 0.2.0
richard-epsilla Sep 20, 2026
06bd4a6
console: the System One base shows 0.2.0
richard-epsilla Sep 20, 2026
5fed672
systemone: the base gets a browser (0.2.1)
richard-epsilla Sep 20, 2026
cdcd738
fix(entrypoint): Chromium's system libraries are installed per contai…
richard-epsilla Sep 20, 2026
68be604
fix(gateway): the System One base declares no built-in tools
richard-epsilla Sep 20, 2026
8b48cf4
feat(gateway): a kit launch carries the kit's step and time budgets
richard-epsilla Sep 20, 2026
df5d2d1
systemone: pin the harness at 0.3.0 (real-time environments)
richard-epsilla Sep 20, 2026
4267210
systemone: pin the harness at 0.3.1
richard-epsilla Sep 20, 2026
08b20ab
ci: the runner job installs the harness without its MCP extra
richard-epsilla Sep 20, 2026
9037758
systemone: the harness's own mark, as a PNG like the other base logos
richard-epsilla Sep 20, 2026
53cbfb2
ci: run the checks on #225
richard-epsilla Sep 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ jobs:
cache-dependency-path: runner/requirements.txt
# httpx: the runner suite drives the app through FastAPI's TestClient, which needs it. The
# runner itself does not, so it stays out of requirements.txt and lives with pytest here.
- run: pip install -r runner/requirements.txt pytest pytest-asyncio httpx
# The System One Harness is on the runner's venv in the image (docker/entrypoint.sh pins the tag);
# the driver's tests import it, and skip without it, so the suite installs the same pin.
- run: pip install -r runner/requirements.txt pytest pytest-asyncio httpx "systemone-harness @ git+https://github.com/HarnessRouter/SystemOneHarness@v0.3.1"
- run: python -m pytest runner/tests -q

conformance:
Expand Down
65 changes: 64 additions & 1 deletion docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export HOSTNAME=0.0.0.0
TOOLS="$DATA_DIR/agent-tools"
export PATH="$TOOLS/bin:$PATH"
export NODE_PATH="$TOOLS/lib/node_modules"
export HR_BACKENDS="${HR_BACKENDS:-claude,codex,hermes,pi,dsh,opencode,qwen,gemini,cline,omp,goose,kimi,aider,openhands}"
export HR_BACKENDS="${HR_BACKENDS:-claude,codex,hermes,pi,dsh,opencode,qwen,gemini,cline,omp,goose,kimi,aider,openhands,systemone}"

wanted() { [[ ",$HR_BACKENDS," == *",$1,"* ]]; }
# The executable IS the definition of "installed" — an installer that exits 0 without producing
Expand All @@ -199,6 +199,7 @@ backend_bin() {
kimi) echo "$TOOLS/bin/kimi" ;;
aider) echo "$TOOLS/aider-venv/bin/aider" ;;
openhands) echo "$TOOLS/openhands-venv/bin/python" ;;
systemone) echo "$TOOLS/systemone-venv/bin/python" ;;
esac
}

Expand Down Expand Up @@ -341,6 +342,55 @@ KIMI_PIN="${HR_KIMI_VERSION:-2.0.0}"; KIMI_PIN="${KIMI_PIN#v}"
#
# Own venv, the dsh/hermes precedent: it pins litellm, fastmcp, pydantic and a browser stack, and
# must not share the runner's interpreter.
# The System One Harness (github.com/HarnessRouter/SystemOneHarness, Apache-2.0): a loop over a
# decision model, pinned by git tag. Its own venv on the data volume like openhands and aider: three
# small dependencies (httpx, pyyaml, the MCP SDK), installed once and rebuilt when the pin moves.
# The executable is the venv's python, which the runner hands runner/systemone_driver.py.
SYSTEMONE_PIN="${HR_SYSTEMONE_VERSION:-0.3.1}"; SYSTEMONE_PIN="${SYSTEMONE_PIN#v}"
# HR_SYSTEMONE_SPEC overrides where pip takes the package from (a mirror, a fork, a local tree
# copied into a derived image); the version proven below is the pin either way. The browser extra
# brings Browser Use, and with it the page and game environments; the Chromium they drive is
# installed beside the venv on the data volume (Playwright's, world-readable), because the image
# ships no browser and a session process cannot read root's cache.
SYSTEMONE_SPEC="${HR_SYSTEMONE_SPEC:-systemone-harness[browser] @ git+https://github.com/HarnessRouter/SystemOneHarness@v${SYSTEMONE_PIN}}"
export PLAYWRIGHT_BROWSERS_PATH="$TOOLS/ms-playwright"
# The libraries that Chromium links against live in the container, not on the volume the browser
# is on, so a container recreated over a volume that already holds the browser has the binary and
# none of its libraries (measured: libatk, libatspi, libXcomposite "not found", and every launch
# died before CDP came up). They are installed per container, keyed on a marker in the container's
# own filesystem: on the first install, and on every start that finds the venv already there.
CHROMIUM_LIBS_MARK=/var/lib/harnessrouter/chromium-libs
chromium_libs() {
[ -f "$CHROMIUM_LIBS_MARK" ] && return 0
echo "[harnessrouter] installing the system libraries Chromium links against (this container)…"
"$TOOLS/systemone-venv/bin/playwright" install-deps chromium >/dev/null 2>&1 || return 1
mkdir -p "$(dirname "$CHROMIUM_LIBS_MARK")" && : > "$CHROMIUM_LIBS_MARK"
}
install_systemone() {
# A failed install leaves NO venv behind: the executable is the definition of installed, and a
# venv whose pip step failed reported the base as available on a box where it could not run.
"${HR_SYSTEMONE_BASE_PYTHON:-python3}" -m venv "$TOOLS/systemone-venv" || { rm -rf "$TOOLS/systemone-venv"; return 1; }
"$TOOLS/systemone-venv/bin/pip" install -q --disable-pip-version-check "$SYSTEMONE_SPEC" playwright \
|| { rm -rf "$TOOLS/systemone-venv"; return 1; }
if ! ls "$PLAYWRIGHT_BROWSERS_PATH"/chromium-*/chrome-linux*/chrome >/dev/null 2>&1; then
echo "[harnessrouter] installing a Chromium for the System One base (Playwright's) under $PLAYWRIGHT_BROWSERS_PATH …"
"$TOOLS/systemone-venv/bin/playwright" install chromium \
|| { rm -rf "$TOOLS/systemone-venv"; return 1; }
chmod -R a+rX "$PLAYWRIGHT_BROWSERS_PATH" 2>/dev/null || true
fi
chromium_libs || { rm -rf "$TOOLS/systemone-venv"; return 1; }
# Prove the loop imports and the version is the pin, the way every other installer here proves
# its executable: an install that cannot import is a base that dies on its first turn.
"$TOOLS/systemone-venv/bin/python" -c '
import sys
import systemone_harness, systemone_harness.envs.mcp, systemone_harness.envs.browser # noqa: F401 - the loop, the MCP adapter, the browser
import browser_use # noqa: F401 - the browser extra
have = systemone_harness.__version__
if have != sys.argv[1]:
print(f"systemone-harness {have} installed, {sys.argv[1]} pinned", file=sys.stderr); sys.exit(1)
' "$SYSTEMONE_PIN" || { rm -rf "$TOOLS/systemone-venv"; return 1; }
}

install_openhands() {
oh_py="${HR_OPENHANDS_BASE_PYTHON:-python3}"
"$oh_py" -m venv "$TOOLS/openhands-venv" || return 1
Expand Down Expand Up @@ -592,6 +642,19 @@ install_backends() {
try_install "OpenHands" install_openhands || true
fi

s1_have=""
if [ -x "$(backend_bin systemone)" ]; then
s1_have="$("$(backend_bin systemone)" -c 'import systemone_harness as s; print(s.__version__)' 2>/dev/null || true)"
fi
if wanted systemone && [ "$s1_have" != "$SYSTEMONE_PIN" ]; then
rm -rf "$TOOLS/systemone-venv"
echo "[harnessrouter] installing System One Harness $SYSTEMONE_PIN (Apache-2.0)…"
try_install "System One Harness" install_systemone || true
fi
if wanted systemone && [ -x "$TOOLS/systemone-venv/bin/playwright" ]; then
chromium_libs || echo "[harnessrouter] WARNING: Chromium's system libraries could not be installed; the System One base's browser environments will not start until they are (retried on the next start)"
fi

if wanted kimi && [ "$("$(backend_bin kimi)" --version 2>/dev/null | head -n 1)" != "$KIMI_PIN" ]; then
echo "[harnessrouter] installing Kimi Code CLI $KIMI_PIN (MIT, version-pinned)…"
try_install "Kimi Code CLI" install_kimi || true
Expand Down
5 changes: 5 additions & 0 deletions docs/harness-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ For every harness and every model its menu offers, one session runs five scenari
| Artifact | a file the task must produce exists in the turn record and is shown to the reader |
| Recycle | the sandbox is let go on purpose, then a follow-up still recalls the first message |

One base is not a coding agent. `systemone` runs a decision loop over a typed action space rather
than a CLI over a workspace, so the artifact scenario's prompt does not apply to it; it is verified by
its own loop (its package's tests, its UHP conformance and its benchmark) and by the first-turn,
follow-up, switch and recycle scenarios here. See docs/support-matrix-notes.md, "systemone".

## The rules that decide a row

A scenario that "completed" is not a pass on its own. Four rules turn a run into a verdict, and each
Expand Down
2 changes: 1 addition & 1 deletion docs/self-hosting-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ Backends are installed into your data volume rather than baked into the image, s
want is a run-time setting:

```bash
docker run -e HR_BACKENDS=claude,codex,hermes,pi,dsh,opencode,qwen,gemini,cline,omp,goose,kimi,aider,openhands ... # the default
docker run -e HR_BACKENDS=claude,codex,hermes,pi,dsh,opencode,qwen,gemini,cline,omp,goose,kimi,aider,openhands,systemone ... # the default
docker run -e HR_BACKENDS=opencode ... # lean
```

Expand Down
48 changes: 48 additions & 0 deletions docs/support-matrix-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -1266,3 +1266,51 @@ fourteen backends: the OpenHands venv installs in about a minute beside aider's.
of the turn survives the sweep. The agent doc reaches the model as context: asked, with no tool
allowed, for a secret word in the harness's instructions and the installed skills, it answered
both from the doc.

## systemone: the System One Harness as the fifteenth base (2026-09-19)

Not a coding CLI. The base runs the open-source System One Harness
(github.com/HarnessRouter/SystemOneHarness, Apache-2.0, pinned at v0.3.1 in `docker/entrypoint.sh`)
over TypeSafe's Jev, a decision model: it answers typed questions (a choice, a yes/no probability,
a score) with probabilities in one pass and writes no text. Every step is one request carrying
the next action as a choice over what the environment offers right now, every parameter of every
offered action, and a goal check; the harness gates the answer by the action's risk and executes.
The turn process is `runner/systemone_driver.py`; its events are claude's stream-json, so the
normaliser is the passthrough.

### Measured, 2026-09-19

- **Through the runner's own relay.** `_build_systemone` registers the route at the provider's API
ROOT (`https://openrouter.ai/api`), because OpenRouter serves decisions at `/api/alpha/decisions`
and a POST to `/api/v1/alpha/decisions` is a 404. The driver posts to `<relay>/v1/alpha/decisions`.
One live turn on the built-in order desk: six actions, `success`, 1.44 s wall; the relay's taps
read the served model `typesafe/jev-1.13-20260917` and usage 6304 in / 1410 out off the answer,
and the driver's own result event said the same. Rule 2 of harness-verification.md holds the
way it holds for cline and qwen: the base rides the relay.
- **The environment is the harness's MCP server** (the first one configured), its tools compiled to
actions at the start of each turn; a tool that needs free text is named in the trace as not
offered. With no server, the built-in order desk, so the base answers before anything is
configured. `disabledTools` withholds an action from the question itself (hard, by omission).
- **`incomplete` is a runner status now.** A loop that stops because the model asked for help or a
destructive action never cleared its confidence bar is neither a failure nor a step cap. The
driver's result carries `subtype: incomplete` and a `reason`; `_status_from_result` returns
`incomplete`, the poll body carries `reason`, and the gateway reports it as the task's
`incomplete_details.reason` without retrying another connection. Pinned by
`runner/tests/test_systemone_backend.py` and `gateway/tests/test_systemone_catalog.py`.
- **Continuation.** The desk's state and the loop's steps persist under
`.harness/systemone/<session>/` in the workspace; a resumed turn carries on from where the last
one stopped and the model sees the earlier steps as history (a two-step first turn followed by a
continuation redid none of its picks).
- **What the five matrix scenarios mean here.** First turn, follow-up, switch and recycle apply as
written; the artifact scenario's "create a file" prompt does not, because the desk produces one
artifact of its own (`manifest.json`, on ship). The base is verified by its own loop rather than
the coding prompts: the harness package's 35 tests, its UHP core conformance (40 of 40) and its
benchmark (15 of 15 goals on the live model) are the record, in that repository.

### The models

`jev-1.13` and `jev-latest`, OpenRouter only (`typesafe/jev-1.13`, `~typesafe/jev-latest`), added to
OpenRouter's vendor table after the shared copy so no other aggregator inherits an id it cannot
serve. No chat model is listed on this base: the loop asks typed questions a text model cannot
answer. TypeSafe's direct endpoint (`/v1/systemone`) is wired in the runner and not yet offered by
the gateway.
Loading
Loading