Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion docs/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ RECIPE=ahe
```

Supported values are `aevolve`, `ahe`, `ahe_codex`, `gepa`, `hill_climb`,
`hill_climb_codex`, `hyperagents`, and `hyperagents_codex`. Codex-capable
`hill_climb_codex`, `hyperagents`, `hyperagents_codex`, and
`hyperagents_dsh` (see its
[recipe README](https://github.com/simple-agent-lab/RSIHub/blob/main/recipes/hyperagents_dsh/README.md) for the extra dsh SDK
setup it needs). Codex-capable
profiles may use `CODEX_AUTH_JSON_PATH=/absolute/path/to/auth.json` instead of
an API key. Use `WORKSPACE`, `TASKS`, `GENERATIONS`, `ENV_FILE`, or
`EVOLVE_ASSET_DIR` to override launcher defaults. See the
Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ recipe YAML
```

The framework ships `aevolve`, `ahe`, `ahe_codex`, `gepa`, `gepa_local`,
`hill_climb`, `hill_climb_codex`, `hyperagents`, `hyperagents_codex`, and
the full-benchmark profiles `hyperagents_tbench_full` and
`hyperagents_codex_tbench_full`.
`hill_climb`, `hill_climb_codex`, `hyperagents`, `hyperagents_codex`,
`hyperagents_dsh`, and the full-benchmark profiles `hyperagents_tbench_full`
and `hyperagents_codex_tbench_full`.
Development smoke recipes live under `tests/fixtures/recipes/` and are not part
of the public recipe inventory.

Expand Down
2 changes: 1 addition & 1 deletion library/PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ point to these entry files:
- analyze: `ahe`, `artifact_rubric`, `failure_patterns`, `gepa`,
`trace_browser`, `trajectory_only`
- mutate: `aevolve`, `ahe`, `gepa`, `hyperagents` (`runner`: `local` or `harbor`)
- validate: `hyperagents`, `minibatch_improvement`
- validate: `hyperagents`, `minibatch_improvement`, `node_check`
- novelty: `accept_all`, `diff_similarity`
- gate: `ahe_artifact_valid`, `hillclimb`, `parent_eligible`
- record: `gepa`, `hyperagents`, `jsonl`
Expand Down
2 changes: 1 addition & 1 deletion library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ library/
├─ analyze/ failure_patterns · trace_browser · trajectory_only · …
├─ mutate/ aevolve · ahe · gepa · hyperagents
│ └─ _support/ shared evidence loading
├─ validate/ hyperagents · minibatch_improvement
├─ validate/ hyperagents · minibatch_improvement · node_check
├─ novelty/ accept_all · diff_similarity
├─ gate/ hillclimb · parent_eligible · ahe_artifact_valid
├─ record/ gepa · hyperagents · jsonl
Expand Down
63 changes: 63 additions & 0 deletions library/validate/node_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Validate operator (script form): syntax guard for evolved dsh plugins.

Rejects candidates whose ``*.mjs`` plugins fail ``node --check`` or whose
``profile.cordis.yml`` is not parseable YAML, saving a full evaluation on a
syntactically broken candidate. Runs in the workspace venv (PyYAML is
available); node is located via ``DSH_NODE_BIN`` or PATH — if node is absent
the check degrades to YAML-only (the guard is a cost saver, not a scorer).
"""

from __future__ import annotations

import os
import shutil
import subprocess
from pathlib import Path

import yaml

from evolve.frozen import sdk
from evolve.frozen.config import Config
from evolve.frozen.interfaces import ValidateOperator, ValidateResult

CONFIG = Config({})


class NodeCheckValidate(ValidateOperator):
# Contract: sdk._run_runtime_mode instantiates operator_cls() bare;
# config arrives through ctx.
def validate(self, checkout: Path, ctx) -> ValidateResult:
target = checkout / "target"
problems: list[str] = []

profile = target / "profile.cordis.yml"
if not profile.is_file():
return ValidateResult(accept=False, reason="target/profile.cordis.yml missing", artifacts=[])
try:
# The cordis dialect uses custom tags such as !!js: compose() checks
# syntax/structure without constructing tags; safe_load would reject
# legitimate profiles.
yaml.compose(profile.read_text())
except (OSError, UnicodeError, yaml.YAMLError) as error:
problems.append(f"profile.cordis.yml: {error}")

node = os.environ.get("DSH_NODE_BIN") or shutil.which("node")
if node:
for script in sorted(target.rglob("*.mjs")):
result = subprocess.run(
[node, "--check", str(script)],
capture_output=True,
text=True,
timeout=30,
)
if result.returncode != 0:
detail = (result.stderr or result.stdout).strip().splitlines()
problems.append(f"{script.relative_to(checkout)}: {detail[0] if detail else 'syntax error'}")

if problems:
return ValidateResult(accept=False, reason="; ".join(problems)[:800], artifacts=[])
return ValidateResult(accept=True, reason="profile yaml + node --check passed", artifacts=[])


if __name__ == "__main__":
sdk.main(NodeCheckValidate, config_schema=CONFIG)
1 change: 1 addition & 0 deletions recipes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ represents.
- [Hill Climb for Codex](hill_climb_codex/README.md)
- [HyperAgents](hyperagents/README.md)
- [HyperAgents for Codex](hyperagents_codex/README.md)
- [HyperAgents for DeepSeek Harness](hyperagents_dsh/README.md)
- [HyperAgents on full Terminal-Bench 2.0](hyperagents_tbench_full/README.md)
- [HyperAgents for Codex on full Terminal-Bench 2.0](hyperagents_codex_tbench_full/README.md)

Expand Down
50 changes: 50 additions & 0 deletions recipes/hyperagents_dsh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# HyperAgents for DeepSeek Harness (dsh)

This profile applies HyperAgents selection, trace browsing, and recording while
evolving the built-in [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
target: dsh's own agent profile (cordis composition + plugin sources + skills).
The mutate stage runs `runner: local` — a dsh self-modification session in the
child worktree reads the failure evidence and rewrites its own persona, plugins,
and skills. Validation uses the `node_check` operator (`node --check` on evolved
plugins plus a tag-tolerant YAML syntax check), so syntactically broken
candidates are rejected before a full evaluation.

## Runtime setup

The official dsh Python SDK is **not** installed from the unrelated
`deepseek-harness` PyPI package. After `evolve init`, add it to the workspace
runtime from a local [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)
clone (the documented `uv add` extension path in the generated `AGENTS.md`):

```bash
# Required: path to a deepseek-harness checkout
export DSH_HARNESS_REPO=/abs/path/to/deepseek-harness

uv add "$DSH_HARNESS_REPO/python/sdk"
uv add --editable "$DSH_HARNESS_REPO/python/sdk-runtime"
git add pyproject.toml uv.lock && git commit -m "workspace runtime: add dsh sdk"
```

`mutate_local.py` refuses to start if `deepseek_harness` is not importable in
the workspace `.venv`.

## Node and sandbox boundaries

- **Node ≥ 22.19** must be on `PATH` (or `DSH_NODE_BIN`).
`evaluator/prepare-runtime.sh` rejects older releases (including Node 22.0–22.18)
before every evaluation; `evaluator/doctor.json` requires `DSH_NODE_BIN` to be
an executable so doctor/preflight surfaces a missing runtime early.
- Mutation sessions use dsh `sandbox-policy` mode `workspace-write` rooted at the
candidate profile (`DSH_CWD` / `target/`). Do not widen this to
`danger-full-access` for the meta session.
- Task bash runs through `docker exec` into the Harbor task container. Candidate
plugins still load in the host-side dsh process for rollouts; treat that host
process as trusted evaluation infrastructure (further plugin isolation is a
follow-up).

The evaluator model is passed through to dsh and routed via `OPENAI_BASE_URL` /
`OPENAI_API_KEY` (mapped onto dsh's `DEEPSEEK_*`); the meta session's model
defaults to dsh's native default and can be overridden with `DSH_META_MODEL`.
Restricted-network hosts can set the optional `DSH_ASSETS_DIR` /
`DSH_CONTAINER_APT_MIRROR` / `DSH_CONTAINER_PIP_INDEX` /
`DSH_CONTAINER_PROXY` compensations described in `seeds/dsh/README.md`.
11 changes: 11 additions & 0 deletions recipes/hyperagents_dsh/evaluator/doctor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"backend": "docker",
"runtime": {
"prepare": "evaluator/prepare-runtime.sh",
"required_environment": {
"DSH_NODE_BIN": "executable"
},
"timeout_s": 120
},
"schema_version": 1
}
45 changes: 45 additions & 0 deletions recipes/hyperagents_dsh/evaluator/prepare-runtime.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh
# Host runtime hook. Engine contract: `sh prepare-runtime.sh <run_dir> <env_out>`
# under POSIX sh; KEY=VALUE lines written to $2 are injected into every trial
# as --ae (agent) and --ve (verifier). Diagnostics go to stderr.
set -eu

run_dir=${1:?run directory is required}
env_file=${2:?runtime environment output is required}

fail() { echo "prepare-runtime: $*" >&2; exit 1; }

# Node >= 22.19 (a dsh hard requirement; the dsh SDK spawns its Node runtime)
NODE_BIN="${DSH_NODE_BIN:-$(command -v node || true)}"
[ -x "$NODE_BIN" ] || fail "node not found (set DSH_NODE_BIN)"
NODE_VER="$("$NODE_BIN" --version | sed 's/^v//')"
NODE_MAJOR="${NODE_VER%%.*}"
NODE_REST="${NODE_VER#*.}"
NODE_MINOR="${NODE_REST%%.*}"
case "$NODE_MAJOR" in
''|*[!0-9]*) fail "unparseable node version: $NODE_VER" ;;
esac
case "$NODE_MINOR" in
''|*[!0-9]*) NODE_MINOR=0 ;;
esac
if [ "$NODE_MAJOR" -lt 22 ] || { [ "$NODE_MAJOR" -eq 22 ] && [ "$NODE_MINOR" -lt 19 ]; }; then
fail "node $NODE_VER too old (need >= 22.19)"
fi

# Optional restricted-network assets (uv/uvx binaries + portable python)
if [ -n "${DSH_ASSETS_DIR:-}" ]; then
for f in uv uvx py313.tar.gz; do
[ -f "$DSH_ASSETS_DIR/$f" ] || fail "asset missing: $DSH_ASSETS_DIR/$f"
done
fi

# Runtime facts injected into every trial
{
echo "DSH_NODE_BIN=$NODE_BIN"
[ -n "${DSH_ASSETS_DIR:-}" ] && echo "DSH_ASSETS_DIR=$DSH_ASSETS_DIR"
[ -n "${DSH_CONTAINER_APT_MIRROR:-}" ] && echo "DSH_CONTAINER_APT_MIRROR=$DSH_CONTAINER_APT_MIRROR"
[ -n "${DSH_CONTAINER_PIP_INDEX:-}" ] && echo "DSH_CONTAINER_PIP_INDEX=$DSH_CONTAINER_PIP_INDEX"
[ -n "${DSH_CONTAINER_PROXY:-}" ] && echo "DSH_CONTAINER_PROXY=$DSH_CONTAINER_PROXY"
[ -n "${DSH_CONTAINER_NO_PROXY:-}" ] && echo "DSH_CONTAINER_NO_PROXY=$DSH_CONTAINER_NO_PROXY"
true
} > "$env_file"
41 changes: 41 additions & 0 deletions recipes/hyperagents_dsh/evolve.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
experiment:
id: hyperagents_dsh
max_generations: 10
target_score: null
children_per_gen: 1
mode: driver
seed: 0
target:
seed: builtin-dsh
surface:
include:
- target/**
exclude:
- target/agent.py
- target/dsh_trajectory.py
- target/runners/**
operators:
select: {operator: score_child_prop, config: {seed: 0}}
rollout: {operator: parent_evaluation, timeout_s: 600, config: {field_limit: 2000}}
analyze: {operator: trace_browser, timeout_s: 600, config: {max_chars: 30000}}
mutate: {operator: hyperagents, timeout_s: 7200, config: {runner: local, command: "python3 target/runners/mutate_local.py", editable_roots: [target], expose_gate_data: false, max_retries: 1}}
validate: {operator: node_check, timeout_s: 300, config: {}}
gate: {operator: parent_eligible, config: {}}
record: {operator: hyperagents, config: {}}
timeout_s: 600
evaluator:
engine: harbor
model: openai/gpt-5.4-2026-03-05
dataset: terminal-bench-2-30-v1
agent: target.agent:DshAgent
task_scope: full
evaluation_split: train
sampling: static
tasks_per_round: 30
repetitions: 1
n_concurrent: 10
max_retries: 1
benchmark_timeout_is_zero: true
partial_floor: 0.8
agent_env:
DSH_TASK_TIMEOUT_SEC: "1800"
4 changes: 3 additions & 1 deletion scaffolds/workspace/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Any newly introduced Python package must be added to the workspace root
`pyproject.toml` with `uv add`, and the resulting `uv.lock` must be committed.
Harbor trials run from the locked workspace environment: never rely on a package
installed globally, injected through `PYTHONPATH`, or installed ad hoc during a
trial.
trial. Packages that are not on PyPI (for example the DeepSeek Harness SDK used
by `hyperagents_dsh`) must be added from a local clone path with `uv add
/path/to/clone/...`, never substituted with an unrelated PyPI name.

## Python runtime and uv cache

Expand Down
11 changes: 11 additions & 0 deletions seeds/dsh/EVOLUTION_LOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Evolution log

Design notes written by the mutation agent, one section per generation.
Read this before making changes: it records what was tried, what worked,
what regressed, and why.

## gen-0 (seed)

Baseline profile: generic engineer persona, one no-op wiring-proof plugin
(seed-probe), one baseline skill (task-execution). No task-specific
capabilities yet.
26 changes: 26 additions & 0 deletions seeds/dsh/PLAYBOOK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# PLAYBOOK — improvement methodology of this lineage

This file is owned by the self-improvement engineer (you, at mutation time).
It travels with your lineage: every future generation reads it FIRST.
Distill durable methodology here — not task trivia:

- What kinds of changes reliably help, and why.
- What was tried and failed (so descendants stop re-trying it).
- Open hypotheses worth testing next, ranked.
- How to spend a generation well when evidence is thin.

Keep it under 200 lines. Prune ruthlessly; stale advice is worse than none.

## Lessons

- ENVIRONMENT CONTRACT: the harness owns infrastructure. Environment patching
earns NO fitness — never evolve bootstrapping/offline-fallback/proxy
components.
- Capability is the only fitness source: reasoning, planning, execution
quality, verification rigor.
- Mechanism-level changes (tool-pipeline hooks, planning, subagents,
compaction, memory) beat persona text tweaks. Reach for prose last.

## Open hypotheses

(none yet)
51 changes: 51 additions & 0 deletions seeds/dsh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Built-in DeepSeek Harness (dsh) Target

This target runs [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
— a Node.js, plugin-composed agent harness — as the candidate. The evolvable
content is dsh's own agent profile; the harness pieces ship alongside it,
excluded from the mutable surface by the recipe.

Evolvable (the genome):

- `profile.cordis.yml` composes the model-visible layer against **sdk-minimal**
packages only: `system-prompt` (persona) + `persistent-bash`
(`@deepseek-ai/dsh-tool-bash-persistent`, `backendType: shell`) + local
`./plugins/*.mjs` mounts. Do not reference removed demo spine packages.
- `plugins/**` holds candidate-authored dsh plugins (`*.mjs`); `seed-probe.mjs`
is a no-op wiring proof.
- `skills/**` holds skill packages for future evolution notes; gen-0 does not
load skill plugins (they are not part of sdk-minimal).
- `PLAYBOOK.md` / `EVOLUTION_LOG.md` carry lineage methodology and per-generation
design notes for the mutation agent.

Harness-side (in `surface.exclude`, candidates cannot edit):

- `agent.py` — the Harbor candidate adapter. It runs host-side, spawns a dsh
session per trial through the dsh Python SDK, bridges the session's bash tool
into the task container via `docker exec`, and converts the session log into
`trajectory.json` for the analyze operators.
- `dsh_trajectory.py` — the session-log converter.
- `runners/` — SDK drivers, local mutate, and frozen cordis **patches**:
- `rollout.base.cordis.yml` — Harbor seams (docker-exec terminal-bash, pinned
model). No foreign `cordis-plugin-include`.
- `mutate.cordis.yml` — self-improvement persona on sdk-minimal packages.
- `candidate_overlay.py` — materializes `profile.cordis.yml` under the
per-trial `dsh_home` with absolute plugin paths so `@deepseek-ai/*` resolves
via `$DSH_HOME/profiles/node_modules` (include-from-`checkout/target` would
not).
Drivers construct `DeepSeekHarness` with `dsh_home` + `profile=sdk-minimal` +
`patches=(harbor, candidate_overlay)` (not the removed `session_root` /
`cordis` kwargs). `DSH_SESSION_ROOT` is the isolated harness home; session
JSONL lands under `$DSH_SESSION_ROOT/sessions/`.

Model routing follows the workspace's frozen identity: `OPENAI_BASE_URL` /
`OPENAI_API_KEY` are mapped onto dsh's `DEEPSEEK_BASE_URL` / `DEEPSEEK_API_KEY`.
The dsh Python SDK is not on PyPI under a trustworthy name for this harness —
add it from a deepseek-harness clone with `uv add` as documented in
`recipes/hyperagents_dsh/README.md`. Task containers are assumed to have
network access (the Terminal-Bench 2 graders assume it too); restricted-network
hosts can opt into compensations via `DSH_ASSETS_DIR` (preload `uv`/`uvx` and a
portable Python 3.13 for graders that cannot reach github),
`DSH_CONTAINER_APT_MIRROR`, `DSH_CONTAINER_PIP_INDEX`, and
`DSH_CONTAINER_PROXY` — none of them touch scoring. Never commit credentials
under `target/`.
Loading
Loading