Orchestrate, execute, and observe agent flows.
A flow is a coding agent driven in a loop. amflows is the three pieces that takes:
amflows.janusruns Claude Code, Codex and Kimi Code behind one interface, as agents that hand out sessions.amflows.exomythturns the trajectories they leave behind into a Chrome JSON trace.amflows.coganchorruns an agent on one machine and has it act on another.
exomyth stands alone, and so does coganchor; janus reads coganchor's settings, and only to say where an agent's work should land.
Python ≥ 3.12, and the coding agent CLIs you intend to drive (claude, codex, kimi) on your
PATH. coganchor additionally needs Linux x86-64 locally, and nothing but python3 on the target.
pip install git+https://github.com/humanfia/amflows.gitFrom source, with uv:
git clone git@github.com:humanfia/amflows.git
cd amflows
uv syncAn agent runs at a model and an effort; a session is one conversation with it. Which of the two a loop holds decides what the flow remembers.
from amflows.janus import ClaudeCodeAgent, ClaudeCodeAgentConfig
agent = ClaudeCodeAgent(ClaudeCodeAgentConfig(model="claude-opus-4-8", effort="high"))
agent.launch().run(
"Read TASK.md and get started."
) # a new session: nothing carries over
session = agent.launch()
session.run("Read TASK.md and get started.") # opens the session
session.run("continue") # resumes it, task still in contextCodexAgent and KimiCodeCLIAgent take the same calls. Both streams of a turn are passed through
as they arrive, and a turn that fails raises subprocess.CalledProcessError without opening the
session, so the next call retries it.
Two agents at one model and one effort are still two agents — an executor and the reviewer that judges it. Name them, and each reports the sessions it opened, which is what tells a trace apart:
config = ClaudeCodeAgentConfig(model="claude-opus-4-8", effort="high")
executor = ClaudeCodeAgent(config, name="executor")
reviewer = ClaudeCodeAgent(config, name="reviewer")
...
exomyth.collect(agents={a.id: a.opened for a in (executor, reviewer)})opened is the backend's id for every session the agent ever opened, including the ones a Ralph
loop dropped a turn later — ids, so a flow running for days remembers them in a list of strings.
Give a config an anchor and its agents work on another machine, without any other change to the
flow. The agent still runs here, so its credentials and its trajectory stay within reach:
from amflows.coganchor import AnchorConfig
config = ClaudeCodeAgentConfig(
model="claude-opus-4-8",
effort="high",
anchor=AnchorConfig(target="ssh://build-box", workspace="/srv/project"),
)Each turn is anchored on its own, so a loop of short turns reaches the target once per turn; a
target left listening on tcp:// makes that a socket rather than an ssh session to bootstrap.
examples/ has the flow loops from flowbench written this way: ralph_loop, goal,
flame_chase, stateful_ralph, continue_loop and rlar.
exomyth collect [<workspace>] [--session <session>[,<session>]...] [--output <output>] [--start <start>] [--end <end>]Collects the trajectories recorded for a workspace and writes .amflows/<datetime>.trace.json.
Load it in ui.perfetto.dev or chrome://tracing: sessions and
sub-agents become tracks, one slice per action, with prompt, reasoning, tool input and tool
output attached.
Each agent is one process. An agent is a configuration — a backend at a model at an effort —
together with every sub-agent it started, so a loop of one-shot sessions reads as one agent
rather than a hundred. A flow that drove the sessions itself knows better, and says so by passing
agents=, which is what tells two agents run at the same configuration apart.
exomyth collect # current workspace, all history
exomyth collect ~/myproject --start "3 days ago" # another workspace, recent history only
exomyth collect --session 0a1b2c3d,5f6e # two sessions, wherever they ranTrajectories are read from the backends' own home directories, named by CLAUDE_CONFIG_DIR,
CODEX_HOME and KIMI_CODE_HOME and falling back to ~/.claude, ~/.codex and ~/.kimi-code;
a missing one is skipped. amflows.exomyth.collect takes the same arguments plus agents,
returns the trace document, and writes a file only when output is given.
coganchor --target ssh://build-box claude
coganchor --target ssh://gpu-01 codex exec "run the test suite"The agent process stays here, keeping its credentials, its state directory and its link to its model provider. Everything it does — reading and writing files, running commands, reaching the network from them — happens on the target. It needs no plugin and no cooperation from the agent.
--target takes ssh://HOST, tcp://HOST:PORT or local[:DIR]; --workspace names the project
directory as it exists on the target; --check connects, reports what it found, and exits;
--shadow puts the local mirror somewhere other than the workspace path.
amflows.coganchor.connect runs that same session from Python, taking those settings as an
AnchorConfig and returning the agent's exit status:
from amflows.coganchor import AnchorConfig, connect
connect(
["claude", "--print"],
AnchorConfig(target="ssh://build-box", workspace="/srv/project"),
)amflows.coganchor.check is --check from Python: it returns what the target says about
itself, without running anything there.
Instead of reconnecting over ssh each time, a target can be left listening:
# on the target
coganchor serve --listen 0.0.0.0:7777 --export /srv/project --token "$SECRET"
# on this machine
COGANCHOR_TOKEN=$SECRET coganchor --target tcp://build-box:7777 --workspace /srv/project claudeA coganchor serve port is equivalent to a shell on that machine. An export bounds which files
a request may name; it does not confine the commands that request can run. Give --token a real
secret, and prefer ssh://, which needs no open port at all.
The agent flows in examples/ run their agents with permission prompts disabled, as flowbench does. Run them only in a workspace you are willing to have rewritten.
PRs accepted. Open an issue to discuss a substantial change first.
uvx ruff format && uvx ruff check && uv run pytest
uv run pytest --run-agents # also drives claude, codex and kimi for realIf you edit this README, please conform to the standard-readme specification.
Apache-2.0 © Zijian Zhang