Skip to content
Merged
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: 0 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,6 @@ ignore = [
# both modules say so where they reach for one -- `find` in one, `_own` in the other.
"src/hmz/runtime/flowing/finding.py" = ["PTH"]
"src/hmz/runtime/flowing/verses.py" = ["PTH"]
# A flow is a directory whose `__init__.py` holds its entry points, and that module's
# docstring is about them -- how the flow loops, what each agent is for, and the line
# that starts it. What a flow prints is what it says: the interface captures everything
# printed under it into the transcript, so a warning a flow has to give is a `print`.
"src/hmz/_legacy_flows/builtin/**" = ["D103", "T201"]
# The exceptions a flow catches are named for what happened -- `FlowNotFound`,
# `BudgetExceeded`, `TempCloneBusy` -- as the flow API's spec names them, rather than each
# given an `Error` suffix that would say only that they are exceptions.
Expand Down
6 changes: 2 additions & 4 deletions specs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def home() -> pathlib.Path: ...
the protocols a flow's agents, environments, sessions and context answer to, and the values
a flow writes or catches. The objects a flow is handed MUST be the runtime's, answering to
those protocols structurally. Everything humanize does *to* a flow MUST be `runtime/flowing`
instead.
instead. The flows humanize ships MUST be kept in `flows/builtin`, written against `flows`
like any other flow and importing nothing else.
- `cli`, `daemon` and `tui` MUST each be a way of reaching the runtime's one object rather
than a second copy of what it does. Anything two of them would otherwise each have written
MUST be written in `runtime` instead, so that a thing which can be done one way can be done
Expand All @@ -68,9 +69,6 @@ def home() -> pathlib.Path: ...
`Outworlder.new` to `runtime/flowing`, importing it inside the call and never at import.
`flows` MUST import nothing else of humanize, and MUST be checked to do so rather than taken
on trust.
- `_legacy_flows` is the previous flow API, kept whole until every way in has moved to
`flows`. It MAY go on naming `coganchor`, and pairing with `runtime/flowing`, as it did
under the old name; nothing new MUST import it, and it MUST go when nothing does.
- `cli` MUST reach `runtime` by name. `tui` MUST reach it through `daemon`, and `daemon` MUST
offer it. `sdk` MUST be named by no layer.
- `coganchor/serve` — the half that ships to a target of any architecture — MUST name the
Expand Down
38 changes: 22 additions & 16 deletions specs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ decides.

```shell
hmz [<command> [<args>...]] | hmz --version | hmz --help # no command: the terminal interface
hmz exec -f|--flow [<flowverse>/]<flow>[:<name>] -a|--agent <spec>[,<spec>...] [-a ...]
[-c|--config <path>] [--json] <task>
<spec> := [<name>=]<cli>[@<provider>]/<model>:<effort>
hmz exec -f|--flow <ref> [-a|--agents <agent>[,<agent>...]]... [-e|--envs <env>[,<env>...]]...
[-p|--params <key>=<value>[,...]]... [-b|--budget <limit>[,<limit>...]]...
[--resume] [--json] <task>
<ref> := [<flowverse>/]<flow>[:<name>] | <path> | git+<url>[@<rev>]#<flow>[:<name>]
<agent> := <role>=<cli>[@<provider>]/<model>:<effort>
<env> := <role>=<backend>@<provider>/<workdir>
<limit> := duration=<duration> | cost=<usd> | output_tokens=<count> | graceful=<bool>
hmz internal <command> [<args>...]
hmz internal anchor [<options>] <agent> [<args>...]
hmz internal anchor serve --export <virtual>[:<real>] [--export ...]
Expand Down Expand Up @@ -52,8 +56,7 @@ class Out: # a run written for a person, or as NDJSON for a program; a contex

class Shown: # the agents' own events, drawn as one run; a context manager
def __init__(self, out: Out) -> None: ...
def watches(self, agents: Iterable[AgentBase]) -> None: ...
def heard(
def heard( # handed to `Run.watch`, which every session of a run is watched through
self, agent: AgentBase, session: SessionBase | None, event: Event
) -> None: ...

Expand Down Expand Up @@ -89,23 +92,26 @@ def tools(argv: list[str]) -> int: ...
carrying objects alone -- and MUST NOT carry what only a terminal needed.
- MUST say nothing to a program that a line for a person would not: an account is its variable names
and never their values, a flowverse its URL with any secret taken out.
- `hmz exec` MUST take every `-a` on the line as one list of agents in the order written, however it
was broken up, and two agents of one spelling MUST be two agents.
- A `<spec>` MAY name the place it fills; naming MUST be all or nothing, and a name the flow does not
declare, one given twice, a place left unfilled, or naming places to a flow that declared a plain
tuple MUST each be a usage error before any agent has run -- as MUST a flow that is not there, has
no entry point, or drives a different number of agents than were given.
- MUST refuse a written-out agent -- `cli=`, `model=`, `effort=`, `provider=`, `service_tier=`,
`config.<key>=` -- and MUST refuse `permission=` and `web_search=` saying where they are said
instead.
- `hmz exec` MUST take every `-a`, `-e`, `-p` and `-b` on the line as one list apiece, however it
was broken up, and two roles given one spelling MUST be two agents.
- Every `<agent>` and `<env>` MUST name the role it fills. A role the flow does not declare, one
given twice, a required role left unfilled, a role the runtime fills -- an `Outworlder`, a
`LocalEnv` -- named at all, a param the flow does not take or cannot read, a spec that cannot be
read, an agent whose harness is not the one its role names or does not serve what its role asks,
and a line with no `-b` -- for every flow but `chat`, which runs under `Budget(cost=inf)` -- MUST
each be a usage error before any agent has started, as MUST a flow that is not there or will not
load, and `--resume` of a flow that cannot be picked up or has no run to pick up.
- `--resume` MUST pick up the newest run of that flow in this workspace that can be picked up;
without it every run MUST start from the top.
- MUST read `<cli>` from the front and `<effort>` from after the last colon so that a model's own
punctuation stays the model's, and MUST NOT restate here which backends exist.
- A run started from a command line MUST have nobody outside it: its outworlder is away.
- MUST draw a run from the agents' own event stream rather than from each backend's own progress and
MUST NOT show both, saying which agent is taking a turn and in which conversation, what it said,
what it ran, what it started, and what a turn cost -- in money as well as tokens, and tokens alone
for a model nobody prices -- with something going on moving while a terminal is reading.
- MUST say, without asking, when nothing will stop the run, when a cap cannot be read, and what a flow
declared that its agent could not be told.
- MUST say, without asking, when a cap cannot be read -- a cost cap over a model nobody prices -- and
MUST say a run its budget stopped in a line rather than as a failure.
- `hmz internal anchor` MUST load `coganchor` and nothing else of humanize, the door included.
- `hmz internal cred` MUST exit with the program's own status, MUST refuse a line naming nothing to
answer or no program to run, and MUST NOT fall back to running unsupervised.
Expand Down
Loading
Loading