From 24bc8674fe8d1f27aa5a90e0847890784c24be16 Mon Sep 17 00:00:00 2001 From: Albert Zhao Date: Mon, 9 Nov 2026 02:53:54 -0500 Subject: [PATCH 1/5] Read a Pi acting agent's stream as Pi's in parse_log and the AHE Evolver's texts records.parse_log read every stream but Codex's as Claude Code's, so a Pi stream gave no session id and no usage; it now reads a pi stream with parse_pi. The AHE Evolver's texts named agent-codex.jsonl for a Pi acting agent, whose stream is agent-pi.jsonl; debugger.STREAM_TEXTS now has Pi's wording and command. Each test fails without its fix. Co-Authored-By: Claude Opus 5.5 --- hermit/agent/base/records.py | 4 ++-- .../evolver/methods/ahe_adapter/debugger.py | 6 +++++- tests/core/test_pi_agent.py | 6 +++++- tests/methods/ahe/test_ahe_hosts.py | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/hermit/agent/base/records.py b/hermit/agent/base/records.py index 225360d0..1159eea6 100644 --- a/hermit/agent/base/records.py +++ b/hermit/agent/base/records.py @@ -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) diff --git a/hermit/agent/evolver/methods/ahe_adapter/debugger.py b/hermit/agent/evolver/methods/ahe_adapter/debugger.py index 39ba9e9b..655a6bf0 100644 --- a/hermit/agent/evolver/methods/ahe_adapter/debugger.py +++ b/hermit/agent/evolver/methods/ahe_adapter/debugger.py @@ -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: diff --git a/tests/core/test_pi_agent.py b/tests/core/test_pi_agent.py index b2f38218..a6e82217 100644 --- a/tests/core/test_pi_agent.py +++ b/tests/core/test_pi_agent.py @@ -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) @@ -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): diff --git a/tests/methods/ahe/test_ahe_hosts.py b/tests/methods/ahe/test_ahe_hosts.py index 441b1805..6f1a7492 100644 --- a/tests/methods/ahe/test_ahe_hosts.py +++ b/tests/methods/ahe/test_ahe_hosts.py @@ -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. From 4fc88daa141d9e7eaa0bfaf9db99a9c3a92356a5 Mon Sep 17 00:00:00 2001 From: Albert Zhao Date: Tue, 10 Nov 2026 00:29:47 -0500 Subject: [PATCH 2/5] Name mini-swe-agent's event stream in the AHE Evolver's texts main added agent-mini-swe-agent.jsonl to the streams rollout_stream finds, and the learners' texts still named agent-codex.jsonl for it: this branch's test (every stream but Codex's) fails once main is merged. Co-Authored-By: Claude Opus 5.5 --- .../agent/evolver/methods/ahe_adapter/debugger.py | 7 ++++++- tests/methods/ahe/test_ahe_hosts.py | 15 ++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/hermit/agent/evolver/methods/ahe_adapter/debugger.py b/hermit/agent/evolver/methods/ahe_adapter/debugger.py index 655a6bf0..996233ee 100644 --- a/hermit/agent/evolver/methods/ahe_adapter/debugger.py +++ b/hermit/agent/evolver/methods/ahe_adapter/debugger.py @@ -185,7 +185,12 @@ def trace_source(log: Path | None) -> tuple[Path, str] | None: "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"))} + ("`codex exec --json` event stream", "`pi --mode json` event stream")), + "agent-mini-swe-agent.jsonl": (("agent-codex.jsonl", "agent-mini-swe-agent.jsonl"), + ("the raw Codex event stream", "the raw mini-swe-agent event stream"), + ("the Codex event stream", "the mini-swe-agent event stream"), + ("`codex exec --json` event stream", + "`python -m hermit.agent.base.mini_swe_agent_run` event stream"))} def stream_of(logs) -> str: diff --git a/tests/methods/ahe/test_ahe_hosts.py b/tests/methods/ahe/test_ahe_hosts.py index 6f1a7492..7d00d0e1 100644 --- a/tests/methods/ahe/test_ahe_hosts.py +++ b/tests/methods/ahe/test_ahe_hosts.py @@ -702,19 +702,24 @@ def test_the_evolvers_texts_name_the_acting_agents_stream(tmp_path): 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.""" + """A Pi or mini-swe-agent acting agent's sessions keep their own stream (agent-pi.jsonl, agent-mini-swe-agent.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 + from hermit.agent.evolver.methods.ahe_adapter import analysis as A, debugger as D + import test_ahe_analysis as AN methods = T.ROOT / "hermit" / "agent" / "evolver" / "methods" / "ahe_adapter" - texts = [(methods / name).read_text() for name in ("START.md", "DEBUGGER.md")] + stats = A.compute_stats([AN.rec("a", 1, **AN.PASS), AN.rec("b", 1, **AN.FAIL)]) + query = A.build_evolution_query({"learning_material": "lm", "state": "st", "adapter": "ad"}, stats, step=2) + texts = [(methods / name).read_text() for name in ("START.md", "DEBUGGER.md")] + [query] 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") + for stream, command in (("agent-pi.jsonl", "pi --mode json"), + ("agent-mini-swe-agent.jsonl", "python -m hermit.agent.base.mini_swe_agent_run")): + assert f"`{command}` event stream" in D.for_stream(texts[1], stream) # --- the real CLIs, each on a scripted local model ---------------------------------------------------------------- From 63a781f01b6fcc9e86d411af58adad42b9d5ea06 Mon Sep 17 00:00:00 2001 From: Albert Zhao Date: Tue, 10 Nov 2026 00:37:26 -0500 Subject: [PATCH 3/5] Name Pi's and mini-swe-agent's streams in what the AHE learner sees The comparison table in docs/methods/ahe_adapter.md listed only Codex's and Claude Code's. Co-Authored-By: Claude Opus 5.5 --- docs/methods/ahe_adapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/methods/ahe_adapter.md b/docs/methods/ahe_adapter.md index 07e7cff3..d8ff3dfd 100644 --- a/docs/methods/ahe_adapter.md +++ b/docs/methods/ahe_adapter.md @@ -154,7 +154,7 @@ this framework's own code. | Dimension | AHE (8b2a55d) | This Evolver | Why | |---|---|---|---| | When learning happens | Once per iteration, after k = 2 rollouts of every task; 10 iterations | The same: a step is every trial row, ten steps | Same | -| What the learner sees | The Debugger: each task's NexAU traces (with a per-turn timing breakdown for a timed-out rollout) and verifier output; the Evolve Agent: the overview, per-task reports, verdicts, history | The same, with the acting agent's event stream (Codex's or Claude Code's; whole, and one file per standard step) and the hook audit as the traces and the judge's feedback as the verifier output; no timing breakdown, since the stream has no timestamps | Framework: the host's trace format | +| What the learner sees | The Debugger: each task's NexAU traces (with a per-turn timing breakdown for a timed-out rollout) and verifier output; the Evolve Agent: the overview, per-task reports, verdicts, history | The same, with the acting agent's event stream (Codex's, Claude Code's, Pi's or mini-swe-agent's; whole, and one file per standard step) and the hook audit as the traces and the judge's feedback as the verifier output; no timing breakdown, since the stream has no timestamps | Framework: the host's trace format | | What is learned | NexAU's prompt, tool descriptions and implementations, middleware classes, skills, sub-agents, memory | The adapter's files (the table above), the same on either host; the host's loop and tools are fixed | Framework: the acting agent is fixed; the reason for the name | | The update | Edits plus a manifest of predictions, attributed at the next iteration; Algorithm 1 reverts HARMFUL changes, the released code leaves it to the agent | The same manifest and rules; `rollback: auto` (the paper) or `agent` (the code) | Decision: the paper by default | | Final harness | The best iteration by pass@1 | The best-ever commit, the harness its rollouts ran (Algorithm 1 line 15 names the harness after the next edit, which no rollout measured) | Decision | From 0de3891f11799e940346fb37948247b011fa27ba Mon Sep 17 00:00:00 2001 From: Albert Zhao Date: Tue, 10 Nov 2026 00:56:31 -0500 Subject: [PATCH 4/5] Claim the hook audit and the missing timings only where they hold Pi and mini-swe-agent run no hooks, and the timing breakdown comes from a Harbor trial's trajectory, not from whether a stream has timestamps (Pi's has). Found by the post-PR audit of this table row. Co-Authored-By: Claude Opus 5.5 --- docs/methods/ahe_adapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/methods/ahe_adapter.md b/docs/methods/ahe_adapter.md index d8ff3dfd..3c979020 100644 --- a/docs/methods/ahe_adapter.md +++ b/docs/methods/ahe_adapter.md @@ -154,7 +154,7 @@ this framework's own code. | Dimension | AHE (8b2a55d) | This Evolver | Why | |---|---|---|---| | When learning happens | Once per iteration, after k = 2 rollouts of every task; 10 iterations | The same: a step is every trial row, ten steps | Same | -| What the learner sees | The Debugger: each task's NexAU traces (with a per-turn timing breakdown for a timed-out rollout) and verifier output; the Evolve Agent: the overview, per-task reports, verdicts, history | The same, with the acting agent's event stream (Codex's, Claude Code's, Pi's or mini-swe-agent's; whole, and one file per standard step) and the hook audit as the traces and the judge's feedback as the verifier output; no timing breakdown, since the stream has no timestamps | Framework: the host's trace format | +| What the learner sees | The Debugger: each task's NexAU traces (with a per-turn timing breakdown for a timed-out rollout) and verifier output; the Evolve Agent: the overview, per-task reports, verdicts, history | The same, with the acting agent's event stream (Codex's, Claude Code's, Pi's or mini-swe-agent's; whole, and one file per standard step) and, on the hosts that run hooks (Codex, Claude Code), the hook audit as the traces, and the judge's feedback as the verifier output; no timing breakdown, which comes only from a Harbor trial's `agent/trajectory.json` | Framework: the host's trace format | | What is learned | NexAU's prompt, tool descriptions and implementations, middleware classes, skills, sub-agents, memory | The adapter's files (the table above), the same on either host; the host's loop and tools are fixed | Framework: the acting agent is fixed; the reason for the name | | The update | Edits plus a manifest of predictions, attributed at the next iteration; Algorithm 1 reverts HARMFUL changes, the released code leaves it to the agent | The same manifest and rules; `rollback: auto` (the paper) or `agent` (the code) | Decision: the paper by default | | Final harness | The best iteration by pass@1 | The best-ever commit, the harness its rollouts ran (Algorithm 1 line 15 names the harness after the next edit, which no rollout measured) | Decision | From ae01e780df9a7b2226cdf3059e7e4952aa043627 Mon Sep 17 00:00:00 2001 From: Albert Zhao Date: Tue, 10 Nov 2026 01:25:30 -0500 Subject: [PATCH 5/5] Keep this branch's lines apart from the DeepSeek Harness PR's Both PRs added to the end of STREAM_TEXTS and at one place in test_ahe_hosts.py, so whichever merged second conflicted. These entries now come before Claude Code's and this test before its neighbour. The table row no longer lists each stream, a list the DeepSeek Harness PR would have left short. Co-Authored-By: Claude Opus 5.5 --- docs/methods/ahe_adapter.md | 2 +- .../evolver/methods/ahe_adapter/debugger.py | 14 +++---- tests/methods/ahe/test_ahe_hosts.py | 42 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/methods/ahe_adapter.md b/docs/methods/ahe_adapter.md index 3c979020..f9069edf 100644 --- a/docs/methods/ahe_adapter.md +++ b/docs/methods/ahe_adapter.md @@ -154,7 +154,7 @@ this framework's own code. | Dimension | AHE (8b2a55d) | This Evolver | Why | |---|---|---|---| | When learning happens | Once per iteration, after k = 2 rollouts of every task; 10 iterations | The same: a step is every trial row, ten steps | Same | -| What the learner sees | The Debugger: each task's NexAU traces (with a per-turn timing breakdown for a timed-out rollout) and verifier output; the Evolve Agent: the overview, per-task reports, verdicts, history | The same, with the acting agent's event stream (Codex's, Claude Code's, Pi's or mini-swe-agent's; whole, and one file per standard step) and, on the hosts that run hooks (Codex, Claude Code), the hook audit as the traces, and the judge's feedback as the verifier output; no timing breakdown, which comes only from a Harbor trial's `agent/trajectory.json` | Framework: the host's trace format | +| What the learner sees | The Debugger: each task's NexAU traces (with a per-turn timing breakdown for a timed-out rollout) and verifier output; the Evolve Agent: the overview, per-task reports, verdicts, history | The same, with the acting agent's event stream (the one its CLI wrote; whole, and one file per standard step) and, on the hosts that run hooks (Codex, Claude Code), the hook audit as the traces, and the judge's feedback as the verifier output; no timing breakdown, which comes only from a Harbor trial's `agent/trajectory.json` | Framework: the host's trace format | | What is learned | NexAU's prompt, tool descriptions and implementations, middleware classes, skills, sub-agents, memory | The adapter's files (the table above), the same on either host; the host's loop and tools are fixed | Framework: the acting agent is fixed; the reason for the name | | The update | Edits plus a manifest of predictions, attributed at the next iteration; Algorithm 1 reverts HARMFUL changes, the released code leaves it to the agent | The same manifest and rules; `rollback: auto` (the paper) or `agent` (the code) | Decision: the paper by default | | Final harness | The best iteration by pass@1 | The best-ever commit, the harness its rollouts ran (Algorithm 1 line 15 names the harness after the next edit, which no rollout measured) | Decision | diff --git a/hermit/agent/evolver/methods/ahe_adapter/debugger.py b/hermit/agent/evolver/methods/ahe_adapter/debugger.py index 996233ee..24a93c05 100644 --- a/hermit/agent/evolver/methods/ahe_adapter/debugger.py +++ b/hermit/agent/evolver/methods/ahe_adapter/debugger.py @@ -177,12 +177,7 @@ def trace_source(log: Path | None) -> tuple[Path, str] | None: # The texts the learners read name the acting agent's event stream as the framework's Codex agent keeps it; with # another acting agent the same sentences name its own (the Debugger and the Evolve Agent run on the Evolver's CLI). -STREAM_TEXTS = {"agent-claude.jsonl": (("agent-codex.jsonl", "agent-claude.jsonl"), - ("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")), - "agent-pi.jsonl": (("agent-codex.jsonl", "agent-pi.jsonl"), +STREAM_TEXTS = {"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")), @@ -190,7 +185,12 @@ def trace_source(log: Path | None) -> tuple[Path, str] | None: ("the raw Codex event stream", "the raw mini-swe-agent event stream"), ("the Codex event stream", "the mini-swe-agent event stream"), ("`codex exec --json` event stream", - "`python -m hermit.agent.base.mini_swe_agent_run` event stream"))} + "`python -m hermit.agent.base.mini_swe_agent_run` event stream")), + "agent-claude.jsonl": (("agent-codex.jsonl", "agent-claude.jsonl"), + ("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"))} def stream_of(logs) -> str: diff --git a/tests/methods/ahe/test_ahe_hosts.py b/tests/methods/ahe/test_ahe_hosts.py index 7d00d0e1..8fe78859 100644 --- a/tests/methods/ahe/test_ahe_hosts.py +++ b/tests/methods/ahe/test_ahe_hosts.py @@ -668,6 +668,27 @@ def test_a_state_with_an_unencodable_character_is_saved(tmp_path, capsys, monkey # --- the Evolver's texts ----------------------------------------------------------------------------------------- +def test_the_evolvers_texts_name_every_acting_agents_stream_it_reads(): + """A Pi or mini-swe-agent acting agent's sessions keep their own stream (agent-pi.jsonl, agent-mini-swe-agent.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 analysis as A, debugger as D + import test_ahe_analysis as AN + methods = T.ROOT / "hermit" / "agent" / "evolver" / "methods" / "ahe_adapter" + stats = A.compute_stats([AN.rec("a", 1, **AN.PASS), AN.rec("b", 1, **AN.FAIL)]) + query = A.build_evolution_query({"learning_material": "lm", "state": "st", "adapter": "ad"}, stats, step=2) + texts = [(methods / name).read_text() for name in ("START.md", "DEBUGGER.md")] + [query] + 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) + for stream, command in (("agent-pi.jsonl", "pi --mode json"), + ("agent-mini-swe-agent.jsonl", "python -m hermit.agent.base.mini_swe_agent_run")): + assert f"`{command}` event stream" in D.for_stream(texts[1], stream) + + def test_the_evolvers_texts_name_the_acting_agents_stream(tmp_path): """START.md, the evolution query and the Debugger's question name Claude Code's stream when the rollouts are its sessions, and are the files' own for Codex's.""" @@ -701,27 +722,6 @@ 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 or mini-swe-agent acting agent's sessions keep their own stream (agent-pi.jsonl, agent-mini-swe-agent.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 analysis as A, debugger as D - import test_ahe_analysis as AN - methods = T.ROOT / "hermit" / "agent" / "evolver" / "methods" / "ahe_adapter" - stats = A.compute_stats([AN.rec("a", 1, **AN.PASS), AN.rec("b", 1, **AN.FAIL)]) - query = A.build_evolution_query({"learning_material": "lm", "state": "st", "adapter": "ad"}, stats, step=2) - texts = [(methods / name).read_text() for name in ("START.md", "DEBUGGER.md")] + [query] - 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) - for stream, command in (("agent-pi.jsonl", "pi --mode json"), - ("agent-mini-swe-agent.jsonl", "python -m hermit.agent.base.mini_swe_agent_run")): - assert f"`{command}` event stream" in D.for_stream(texts[1], stream) - - # --- 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.