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
4 changes: 2 additions & 2 deletions hermit/agent/base/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,5 @@ def parse_claude(log: Path, model: str) -> tuple[str | None, list[Usage]]:


def parse_log(log: Path, backend: str, model: str) -> tuple[str | None, list[Usage]]:
"""Session handle and usage from the agent's JSON event stream: Codex's, else Claude Code's."""
return (parse_codex if backend == "codex" else parse_claude)(log, model)
"""Session handle and usage from the agent's JSON event stream: Codex's, Pi's, else Claude Code's."""
return {"codex": parse_codex, "pi": parse_pi}.get(backend, parse_claude)(log, model)
6 changes: 5 additions & 1 deletion hermit/agent/evolver/methods/ahe_adapter/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,11 @@ def trace_source(log: Path | None) -> tuple[Path, str] | None:
("the raw Codex event stream", "the raw Claude Code event stream"),
("the Codex event stream", "the Claude Code event stream"),
("`codex exec --json` event stream",
"`claude -p --output-format stream-json` event stream"))}
"`claude -p --output-format stream-json` event stream")),
"agent-pi.jsonl": (("agent-codex.jsonl", "agent-pi.jsonl"),
("the raw Codex event stream", "the raw Pi event stream"),
("the Codex event stream", "the Pi event stream"),
("`codex exec --json` event stream", "`pi --mode json` event stream"))}


def stream_of(logs) -> str:
Expand Down
6 changes: 5 additions & 1 deletion tests/core/test_pi_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ def test_system_instructions_are_appended_and_a_long_one_goes_in_a_file(fake):


def test_a_pi_stream_is_read_back_by_its_name_or_by_its_events(tmp_path):
"""The learning material keeps the acting agent's log: an Evolver reads Pi's steps from it like Codex's."""
"""The learning material keeps the acting agent's log: an Evolver reads Pi's steps from it like Codex's, and
records.parse_log reads its session and usage as Pi's (it read a Pi stream as Claude Code's: none of either)."""
named = tmp_path / "log"
named.mkdir()
shutil.copy(STREAMS / "success.jsonl", named / trajectory.PI_STREAM)
Expand All @@ -231,6 +232,9 @@ def test_a_pi_stream_is_read_back_by_its_name_or_by_its_events(tmp_path):
raw = (STREAMS / "failure.jsonl").read_text()
assert not base_cli.served("pi", raw) and base_cli.notices("pi", raw).startswith("503: ")
assert base_cli.served("pi", (STREAMS / "success.jsonl").read_text())
from hermit.agent.base.records import parse_log
session, [usage] = parse_log(STREAMS / "success.jsonl", "pi", "m")
assert session == SESSION and (usage.calls, usage.input_tokens, usage.output_tokens) == (4, 4000, 80)


def test_the_pi_acting_agent_deploys_the_adapter_and_trusts_its_project_for_the_session(fake):
Expand Down
16 changes: 16 additions & 0 deletions tests/methods/ahe/test_ahe_hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,22 @@ def test_the_evolvers_texts_name_the_acting_agents_stream(tmp_path):
assert "`claude -p --output-format stream-json` event stream" in question and "codex exec --json" not in question


def test_the_evolvers_texts_name_every_acting_agents_stream_it_reads():
"""A Pi acting agent's sessions keep their own stream (agent-pi.jsonl), which rollout_stream finds: the learners'
texts name it, not Codex's file, which is not there."""
from hermit.agent.base.trajectory import CODEX_STREAM, STREAMS
from hermit.agent.evolver.methods.ahe_adapter import debugger as D
methods = T.ROOT / "hermit" / "agent" / "evolver" / "methods" / "ahe_adapter"
texts = [(methods / name).read_text() for name in ("START.md", "DEBUGGER.md")]
for stream in set(STREAMS) - {CODEX_STREAM}:
for text in texts:
words = D.for_stream(text, stream)
assert "agent-codex.jsonl" not in words and "Codex event stream" not in words, stream
assert "codex exec --json" not in words, stream
assert stream in D.for_stream(texts[0], stream)
assert "`pi --mode json` event stream" in D.for_stream(texts[1], "agent-pi.jsonl")


# --- the real CLIs, each on a scripted local model ----------------------------------------------------------------
# A session of each host in a workspace the framework deployed, its model a script served on 127.0.0.1 (every proxy
# pointed at a closed port): which text reaches a request is what each channel delivers. Skipped without the CLI.
Expand Down
Loading