loopy-loop 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- loopy_loop-0.5.0/CHANGELOG.md +206 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/Makefile +1 -1
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/PKG-INFO +56 -16
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/README.md +54 -15
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/designs/improvement-proposals.md +77 -36
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/docs/http-contract.md +32 -1
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/docs/session-layout.md +37 -2
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/pyproject.toml +2 -1
- loopy_loop-0.5.0/skills/loopy-loop/SKILL.md +575 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/cli.py +221 -14
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/config.py +57 -0
- loopy_loop-0.5.0/src/loopy_loop/coordinator_app.py +1472 -0
- loopy_loop-0.5.0/src/loopy_loop/events.py +83 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/harness_runner.py +75 -10
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/models.py +94 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/recovery.py +2 -3
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/sessions.py +28 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_reviewer/prompt.txt +4 -2
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_runner/prompt.txt +14 -4
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/inner/prompt.txt +3 -1
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/outer/prompt.txt +15 -4
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/dispatcher/prompt.txt +12 -1
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/planner/prompt.txt +8 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/loopy_loop_config.yaml +7 -4
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/worker.py +131 -11
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_cli.py +105 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_coordinator_app.py +7 -0
- loopy_loop-0.5.0/src/tests/test_events_and_usage.py +622 -0
- loopy_loop-0.5.0/src/tests/test_failure_taxonomy.py +488 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_idempotent_finished.py +3 -0
- loopy_loop-0.5.0/src/tests/test_session_stack_recovery.py +824 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_worker.py +80 -3
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/uv.lock +17 -1
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/cli-reference/page.mdx +1 -1
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/http-contract/page.mdx +12 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/session-layout/page.mdx +9 -0
- loopy_loop-0.3.0/CHANGELOG.md +0 -88
- loopy_loop-0.3.0/skills/loopy-loop/SKILL.md +0 -379
- loopy_loop-0.3.0/src/loopy_loop/coordinator_app.py +0 -900
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.eval-banana/config.toml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.github/workflows/ci.yml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.github/workflows/docs-deploy.yml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.github/workflows/release.yml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.gitignore +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.team-harness/config.toml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.team-harness/coordinator_system_message.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.team-harness/worker_footer.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/.team-harness/worker_suffix.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/AGENTS.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/CLAUDE.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/LICENSE +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/analysis/codex_parallel_analysis.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/analysis/loopy-loop-state-and-roadmap.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/analysis/synthesis.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/decisions.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/designs/documentation-site.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/design/designs/success-semantics-and-evaluation.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/docs/images/logo.png +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/skills-lock.json +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/__init__.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/scheduler.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/state_store.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.gitignore +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_reviewer/config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_runner/config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/inner/config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/outer/config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/loopy_loop_config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/inner_outer_eval/loopy_loop_goal.txt +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/.gitignore +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/dispatcher/config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/planner/config.yaml +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/templates/pm_planner_dispatcher/loopy_loop_goal.txt +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/loopy_loop/worker_identity.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/__init__.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/conftest.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_api_base_normalization.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_config.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_examples.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_fresh_run_archive.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_goal_check_gate.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_goal_hash_derivation.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_harness_runner.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_must_follow_success.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_scheduler.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_sessions.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_state_store.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/src/tests/test_worker_liveness_recovery.py +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/.gitignore +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/README.md +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/mdx-components.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/next.config.ts +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/package-lock.json +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/package.json +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/postcss.config.mjs +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/public/.nojekyll +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/public/CNAME +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/child-sessions/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/concepts/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/configuration/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/evaluation/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/getting-started/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/layout.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/success-and-control/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/troubleshooting/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/docs/workflows/page.mdx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/globals.css +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/layout.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/app/page.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/components/docs/DocsPageNavigation.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/components/docs/DocsSidebar.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/components/docs/DocsTableOfContents.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/components/docs/MobileDocsSidebar.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/components/search/SearchCommand.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/components/site/SiteHeader.tsx +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/lib/docs/navigation.ts +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/lib/search/pagefind.ts +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/src/lib/utils.ts +0 -0
- {loopy_loop-0.3.0 → loopy_loop-0.5.0}/website/tsconfig.json +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- **events.jsonl is now written (P1.1).** The coordinator appends one
|
|
6
|
+
versioned JSON event per significant transition — `session_started`,
|
|
7
|
+
`task_dispatched`, `task_finished` (with tokens/duration/failure_kind),
|
|
8
|
+
`iteration_abandoned`, `goal_check`, `child_started`, `child_finished`,
|
|
9
|
+
`session_stopped` — to each session's `events.jsonl` after the producing
|
|
10
|
+
state mutation commits. Delivery is best-effort by design: a crash in the
|
|
11
|
+
commit-to-append window drops the event while the durable truth
|
|
12
|
+
(`state.json` history/ledger) survives; readers key on `event_id` and
|
|
13
|
+
tolerate gaps and a torn final line. New `loopy events [--follow]
|
|
14
|
+
[--json]` tails the deepest active session's stream and follows the
|
|
15
|
+
active session as it changes.
|
|
16
|
+
- **Usage/cost ledger + `max_cost_usd` (P1.1).** The worker reads
|
|
17
|
+
coordinator-model token usage from team-harness's `run.json` and reports
|
|
18
|
+
it (plus wall-clock duration) on `/finished`; the coordinator keeps a
|
|
19
|
+
durable per-session ledger in `state.json` and records a finalized
|
|
20
|
+
child's totals on its `children.json` record. With the new optional
|
|
21
|
+
`model_prices` (USD per 1M tokens, coordinator-side), `loopy status`
|
|
22
|
+
shows estimated cost and the new optional `max_cost_usd` budget stops
|
|
23
|
+
the loop with `stop_reason="max_cost_usd"`. Cost explicitly covers the
|
|
24
|
+
harness coordinator model only — agent-CLI subprocess spend is not
|
|
25
|
+
measurable and is never pretended into the number.
|
|
26
|
+
- **`loopy status` shows the session stack and `--watch`.** While a child
|
|
27
|
+
session runs, `status` now walks the durable parent→child pointers and
|
|
28
|
+
shows the live child (previously it showed only the suspended parent);
|
|
29
|
+
`--watch` re-renders every 2 seconds.
|
|
30
|
+
|
|
31
|
+
- **Failure taxonomy + per-workflow failure cap (P2.3).** Failed iterations
|
|
32
|
+
now record a `failure_kind` — `transient` (provider said retry;
|
|
33
|
+
team-harness's own retries were exhausted), `deterministic` (auth/config
|
|
34
|
+
errors retries cannot fix), `crash` (worker died mid-iteration), or
|
|
35
|
+
`unknown` — on `result.json`, `/finished`, and session history. A new
|
|
36
|
+
coordinator-side `workflow_consecutive_failures_cap` (default 5) stops the
|
|
37
|
+
loop with `stop_reason="workflow_failure_cap"` when the same workflow fails
|
|
38
|
+
that many iterations in a row (crash-abandoned iterations included; any
|
|
39
|
+
success resets the workflow's counter) instead of burning the remaining
|
|
40
|
+
`max_turns` on a wedged workflow. The cap is not part of the wire config
|
|
41
|
+
snapshot, so released workers are unaffected.
|
|
42
|
+
- **Agent Skill rewritten for the current API (P1.3).** `skills/loopy-loop/SKILL.md`
|
|
43
|
+
still described the removed pre-0.2.0 surface (top-level `.loopy_loop/state.json`,
|
|
44
|
+
polling multi-worker model, inline `goal`, `.loopy_loop/workflows/<id>/` layout) and
|
|
45
|
+
would have taught agents to generate broken setups. It now documents the 0.4.0
|
|
46
|
+
reality: `goal_file`, workflow sets, all three templates, the single
|
|
47
|
+
identity-verified worker, child sessions, crash recovery, and resume semantics —
|
|
48
|
+
validated against clean `loopy init` runs of every template. Remaining plural
|
|
49
|
+
"workers" wording in the README corrected to the single-worker model.
|
|
50
|
+
- **eval-banana is now a hard dependency.** The recommended
|
|
51
|
+
`inner_outer_eval` template (and the PM template's child sessions) shell
|
|
52
|
+
out to the `eval-banana` CLI; previously it had to be installed
|
|
53
|
+
separately, so a fresh install could dispatch eval workflows against a
|
|
54
|
+
missing tool. It now installs with loopy-loop — no extra, no preflight.
|
|
55
|
+
- **Worker appends its bundled-CLI scripts directory to `PATH`.** Harness
|
|
56
|
+
agents inherit the worker's environment, but under `uv tool install` /
|
|
57
|
+
`pipx` only loopy-loop's own entry points are exposed on `PATH`, so
|
|
58
|
+
dependency CLIs such as `eval-banana` were invisible to agents. The
|
|
59
|
+
directory is derived from where eval-banana's script was actually
|
|
60
|
+
installed (its package record; `sysconfig` only as fallback — the default
|
|
61
|
+
scheme is wrong for e.g. `pip install --user` under a system
|
|
62
|
+
interpreter). It is appended, never prepended, and existing `PATH`
|
|
63
|
+
entries are preserved verbatim: the target repo's own `python`/tooling
|
|
64
|
+
and any eval-banana already on `PATH` keep winning.
|
|
65
|
+
|
|
66
|
+
## 0.4.0 (breaking)
|
|
67
|
+
|
|
68
|
+
**Breaking API interaction — completions must echo the dispatched
|
|
69
|
+
`attempt_id`.** Every dispatched task now carries an `attempt_id` that the
|
|
70
|
+
worker must echo on `/finished`; a completion without the live task's attempt
|
|
71
|
+
is treated as stale (this is what fences superseded work). A 0.3.0 worker
|
|
72
|
+
therefore cannot complete tasks against a 0.4.0 coordinator — upgrade workers
|
|
73
|
+
and coordinator together (they normally ship in the same install).
|
|
74
|
+
|
|
75
|
+
- **Durable session-stack recovery (P0.1).** While a child session runs, the
|
|
76
|
+
parent's `state.json` records `active_child_session_id`; on `--resume` the
|
|
77
|
+
coordinator walks the pointer chain to the deepest live session instead of
|
|
78
|
+
silently reopening the parent and orphaning the running child. Terminal
|
|
79
|
+
children found at startup are finalized (children.json completed, pointer
|
|
80
|
+
cleared) and their parent resumed. Every interrupted-dispatch crash window
|
|
81
|
+
reconciles deterministically: dangling pointers are cleared, a fully
|
|
82
|
+
created child whose parent commit never landed is adopted, and leftover
|
|
83
|
+
request files never dispatch twice (children.json records the originating
|
|
84
|
+
`request_file`). Invalid child requests are terminally rejected
|
|
85
|
+
(`*.json.rejected`) instead of being re-read forever.
|
|
86
|
+
- **Attempt ids.** Every dispatched task carries a unique `attempt_id`
|
|
87
|
+
(also on the wire in `TaskResponse`, echoed in `FinishedRequest`); a late
|
|
88
|
+
`/finished` from a superseded attempt of the same coordinates is treated as
|
|
89
|
+
stale rather than recorded as the current result.
|
|
90
|
+
- Iteration artifacts (`result.json`, `result_text.txt`, `prompt.txt`,
|
|
91
|
+
`harness_run_id.txt`, `pending_finished_request.json`), `children.json`,
|
|
92
|
+
and `salvage.json` are all written atomically (unique temp + rename) — a
|
|
93
|
+
crash can never leave a truncated recovery artifact.
|
|
94
|
+
- Internal: the three duplicated dispatch blocks in the coordinator collapsed
|
|
95
|
+
into one `_advance()` step (stop checks → child dispatch → next workflow →
|
|
96
|
+
stamped task), so they can no longer drift apart. `_advance()` also enforces
|
|
97
|
+
the suspended-parent invariant: a parent with a live child can never acquire
|
|
98
|
+
its own task (a duplicate `/finished` retry gets the child's live task
|
|
99
|
+
instead), and a coordinator-level transition lock serializes cross-store
|
|
100
|
+
handoffs.
|
|
101
|
+
- Review hardening (adversarial Codex review of the above): request-file
|
|
102
|
+
tombstones apply only to RUNNING child records (a completed child's
|
|
103
|
+
filename is reusable for new work); the children.json record lands BEFORE
|
|
104
|
+
the child state so an interrupted dispatch is always discoverable
|
|
105
|
+
(`failed_dispatch` + exactly-once redispatch); startup reconciles every
|
|
106
|
+
running-projected record (terminal children finalize even without a
|
|
107
|
+
pointer); the first child task carries an attempt id; attempt checks are
|
|
108
|
+
strict whenever the live task has one (including `result.json` provenance —
|
|
109
|
+
a stale artifact can no longer complete a new attempt); semantically
|
|
110
|
+
unusable child requests (unknown workflow set, no eligible workflow) are
|
|
111
|
+
terminally rejected instead of wedging every completion; packaged prompts
|
|
112
|
+
instruct atomic control/goal_check publication; the crash model (process
|
|
113
|
+
crash, no fsync) is documented.
|
|
114
|
+
- **The `pm_planner_dispatcher` template is executable from a clean init
|
|
115
|
+
(P0.4).** `loopy init --template pm_planner_dispatcher` now also ships the
|
|
116
|
+
`inner_outer_eval` child workflow set its dispatcher spawns — previously a
|
|
117
|
+
clean init could not execute a single child session. The child set is
|
|
118
|
+
sourced from the `inner_outer_eval` template itself, so the two copies can
|
|
119
|
+
never drift apart.
|
|
120
|
+
|
|
121
|
+
## 0.3.0 (breaking)
|
|
122
|
+
|
|
123
|
+
**Breaking API change — `/register` requires the worker's process identity.**
|
|
124
|
+
A register without a `worker` object is rejected with HTTP 400. Pre-0.3
|
|
125
|
+
workers cannot register against a 0.3 coordinator; upgrade workers and
|
|
126
|
+
coordinator together (they normally ship in the same install).
|
|
127
|
+
|
|
128
|
+
- **Worker liveness verification (D7).** The worker sends its process
|
|
129
|
+
identity (hostname + pid + a pid-reuse-proof start-time token) with
|
|
130
|
+
`/register` and `/finished`; the coordinator stamps it onto the dispatched
|
|
131
|
+
task. A `/register` while the recorded worker is *verifiably still alive*
|
|
132
|
+
returns HTTP 409 instead of abandoning live work — closing the
|
|
133
|
+
duplicate-work window. Unverifiable identities (remote hosts, or a
|
|
134
|
+
team-harness without process identity) keep the pre-existing
|
|
135
|
+
assume-abandoned recovery behavior. Because every dispatched task now has
|
|
136
|
+
a recorded owner, a stale `/finished` is replayed **only to that owner**
|
|
137
|
+
(anyone else gets HTTP 409) — a task persisted by a pre-identity version
|
|
138
|
+
keeps the legacy replay for that one resume.
|
|
139
|
+
- **Orphaned-agent recovery (P2.5 / TH-D5 consumer side).** When a worker is
|
|
140
|
+
confirmed dead with nothing recoverable, the coordinator applies
|
|
141
|
+
`recovery_policy` (new coordinator-side config, NOT part of the wire
|
|
142
|
+
snapshot; default `drain`, one shared `recovery_drain_timeout_s` deadline,
|
|
143
|
+
default 600s) to agent processes the dead worker's harness run left behind,
|
|
144
|
+
via team-harness's process reaper: drained agents finish and their repo
|
|
145
|
+
edits survive; a `salvage.json` in the interrupted iteration directory
|
|
146
|
+
records what was handled; the history entry is `abandoned_after_<policy>`
|
|
147
|
+
when anything settled. Recovery runs OUTSIDE the state lock (`loopy
|
|
148
|
+
status`/`stop` stay usable while draining), validates that each discovered
|
|
149
|
+
run record actually belongs to the iteration, is same-host-only (a worker
|
|
150
|
+
identity from another hostname skips reaping), and refuses to dispatch
|
|
151
|
+
replacement work (HTTP 409) when any orphan may still be running or when
|
|
152
|
+
team-harness's parent-liveness guard reports the run's owner alive.
|
|
153
|
+
Requires team-harness with the process reaper (> 0.2.10); older versions
|
|
154
|
+
skip orphan recovery gracefully.
|
|
155
|
+
- A stale `/finished` from a **different identified worker** now gets HTTP 409
|
|
156
|
+
instead of a second copy of the live task; unknown identities keep the
|
|
157
|
+
pre-existing stale-retry behavior. State-lock contention surfaces as a clean
|
|
158
|
+
HTTP 503 (and friendly CLI errors) instead of raw tracebacks.
|
|
159
|
+
- The bundled worker uses an unbounded read timeout on `/register` only
|
|
160
|
+
(recovery can legitimately block registration up to the drain deadline),
|
|
161
|
+
keeps the bounded timeout on `/finished`, and exits with code 3 on a 409.
|
|
162
|
+
|
|
163
|
+
## 0.2.1
|
|
164
|
+
|
|
165
|
+
- Improve README onboarding, install, initialization, configuration, and logging docs.
|
|
166
|
+
- Update package and skill descriptions to avoid unclear repository-scope jargon.
|
|
167
|
+
|
|
168
|
+
## 0.2.0 (breaking)
|
|
169
|
+
|
|
170
|
+
**Breaking API change — drop leases, polling, and worker identity.**
|
|
171
|
+
|
|
172
|
+
The three-endpoint API (`POST /workers/register`, `POST /workers/{worker_id}/next`,
|
|
173
|
+
`POST /workers/{worker_id}/finished`) is replaced by a two-endpoint ping-pong API:
|
|
174
|
+
|
|
175
|
+
- `POST /register` — returns `TaskResponse` directly (`action: "run"` or `"stop"`).
|
|
176
|
+
- `POST /finished` — returns `TaskResponse` for the next task or stop signal.
|
|
177
|
+
|
|
178
|
+
The `"wait"` action is gone. The worker loop is now a simple `while task.action == "run"`
|
|
179
|
+
loop with no polling.
|
|
180
|
+
|
|
181
|
+
**Removed models and fields:**
|
|
182
|
+
- `WorkerState`, `ActiveAssignment`, `RegisterWorkerResponse`, `NextActionResponse` removed.
|
|
183
|
+
- `assignment_id` removed from `FinishedRequest` and `HistoryEntry`.
|
|
184
|
+
- `worker_id` removed from `HistoryEntry`.
|
|
185
|
+
- `workers` dict and `active_assignment` removed from `LoopState`.
|
|
186
|
+
- Constants `DEFAULT_LEASE_SECONDS`, `DEFAULT_POLL_INTERVAL_SECONDS`,
|
|
187
|
+
`DEFAULT_FINISHED_RETRY_ATTEMPTS`, `DEFAULT_FINISHED_RETRY_BACKOFF_SECONDS`,
|
|
188
|
+
`WAIT_ACTION` removed.
|
|
189
|
+
|
|
190
|
+
**Added:**
|
|
191
|
+
- `CurrentTask` model replaces `ActiveAssignment` (no `worker_id`, no lease).
|
|
192
|
+
- `TaskResponse` model returned by both `/register` and `/finished`.
|
|
193
|
+
- `LoopState.current_task: CurrentTask | None` replaces `active_assignment`.
|
|
194
|
+
|
|
195
|
+
**Crash recovery:** If a worker crashes mid-run, the next `/register` call detects the
|
|
196
|
+
orphaned `current_task`, records it in history as `error="abandoned"`, and dispatches
|
|
197
|
+
fresh work.
|
|
198
|
+
|
|
199
|
+
**Migration note:** Existing `state.json` files containing `active_assignment` or `workers`
|
|
200
|
+
fields will have those fields silently dropped on first read (Pydantic ignores unknown
|
|
201
|
+
fields). Any in-flight run at upgrade time will lose its `active_assignment` record without
|
|
202
|
+
a history entry. Drain all workers before deploying this version.
|
|
203
|
+
|
|
204
|
+
## 0.1.0
|
|
205
|
+
|
|
206
|
+
- Initial release.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopy-loop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Run long-running AI agent workflows inside your repository.
|
|
5
5
|
Project-URL: Homepage, https://github.com/writeitai/loopy-loop
|
|
6
6
|
Project-URL: Repository, https://github.com/writeitai/loopy-loop
|
|
@@ -23,6 +23,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
23
23
|
Classifier: Typing :: Typed
|
|
24
24
|
Requires-Python: >=3.12
|
|
25
25
|
Requires-Dist: click>=8.1
|
|
26
|
+
Requires-Dist: eval-banana>=0.3.1
|
|
26
27
|
Requires-Dist: fastapi>=0.115.0
|
|
27
28
|
Requires-Dist: filelock>=3.16
|
|
28
29
|
Requires-Dist: httpx>=0.28
|
|
@@ -58,12 +59,13 @@ and PRs.
|
|
|
58
59
|
|
|
59
60
|
Under the hood, loopy-loop runs a small FastAPI coordinator and a single
|
|
60
61
|
worker. The coordinator owns the loop state and chooses the next workflow. The
|
|
61
|
-
|
|
62
|
+
worker runs assignments through
|
|
62
63
|
[`team-harness`](https://github.com/writeitai/team-harness), which can delegate
|
|
63
64
|
to agent CLIs such as Codex, Claude Code, and Gemini. The packaged
|
|
64
65
|
`inner_outer_eval` template also uses
|
|
65
66
|
[`eval-banana`](https://github.com/writeitai/eval-banana) conventions for
|
|
66
|
-
session-scoped evaluation checks
|
|
67
|
+
session-scoped evaluation checks; eval-banana installs automatically as a
|
|
68
|
+
loopy-loop dependency.
|
|
67
69
|
|
|
68
70
|
## Install
|
|
69
71
|
|
|
@@ -118,7 +120,8 @@ This is the recommended starting template. It creates:
|
|
|
118
120
|
- a `.gitignore` entry for `.loopy_loop/sessions/`
|
|
119
121
|
|
|
120
122
|
`loopy init` is idempotent. It creates missing files and leaves existing files
|
|
121
|
-
alone
|
|
123
|
+
alone — except `.gitignore`, which is updated in place to ensure the sessions
|
|
124
|
+
ignore rule.
|
|
122
125
|
|
|
123
126
|
## Write the Goal
|
|
124
127
|
|
|
@@ -244,6 +247,7 @@ goal_file: loopy_loop_goal.txt
|
|
|
244
247
|
workflow_set: inner_outer_eval
|
|
245
248
|
max_turns: 160
|
|
246
249
|
goal_check_consecutive_failures_cap: 3
|
|
250
|
+
# workflow_consecutive_failures_cap: 5
|
|
247
251
|
team_harness_provider: "codex"
|
|
248
252
|
team_harness_model: "gpt-5.5"
|
|
249
253
|
team_harness_agents:
|
|
@@ -278,7 +282,21 @@ Important rules:
|
|
|
278
282
|
processes a dead worker left running: drain lets them finish (one shared
|
|
279
283
|
bounded deadline) before the iteration is re-run; reap kills them
|
|
280
284
|
immediately. These are coordinator-side settings and are not part of the
|
|
281
|
-
config snapshot sent to
|
|
285
|
+
config snapshot sent to the worker.
|
|
286
|
+
- `workflow_consecutive_failures_cap` (default 5) is a per-workflow circuit
|
|
287
|
+
breaker: that many consecutive failed iterations of the same workflow stop
|
|
288
|
+
the loop with `stop_reason="workflow_failure_cap"` instead of retrying a
|
|
289
|
+
wedged workflow until `max_turns`. Any success of the workflow resets its
|
|
290
|
+
counter. Coordinator-side only; not part of the config snapshot sent to
|
|
291
|
+
the worker.
|
|
292
|
+
- `model_prices` (optional, coordinator-side only) sets USD prices per 1M
|
|
293
|
+
tokens for the harness coordinator model (`prompt_usd_per_1m`,
|
|
294
|
+
`completion_usd_per_1m`); with prices set, `loopy status` derives an
|
|
295
|
+
estimated cost from the token ledger. `max_cost_usd` (optional, requires
|
|
296
|
+
`model_prices`) stops the loop with `stop_reason="max_cost_usd"` once the
|
|
297
|
+
session tree's estimated cost reaches the budget. Cost covers the harness
|
|
298
|
+
COORDINATOR model only — agent-CLI subprocesses (codex, claude, gemini)
|
|
299
|
+
bill through their own accounts and are not measurable here.
|
|
282
300
|
|
|
283
301
|
Workflow config lives beside each workflow prompt:
|
|
284
302
|
|
|
@@ -332,8 +350,12 @@ Important session files:
|
|
|
332
350
|
|
|
333
351
|
- `goal.md`: the exact goal text copied into the session.
|
|
334
352
|
- `session.json`: session metadata.
|
|
335
|
-
- `state.json`: coordinator-owned dispatch state
|
|
336
|
-
|
|
353
|
+
- `state.json`: coordinator-owned dispatch state, including the session's
|
|
354
|
+
token/duration usage ledger.
|
|
355
|
+
- `events.jsonl`: append-only event stream — one versioned JSON line per
|
|
356
|
+
significant transition (`session_started`, `task_dispatched`,
|
|
357
|
+
`task_finished`, `iteration_abandoned`, `goal_check`, `child_started`,
|
|
358
|
+
`child_finished`, `session_stopped`). Tail it with `loopy events --follow`.
|
|
337
359
|
- `control.json`: workflow-owned stop switch.
|
|
338
360
|
- `updates_from_user.md`: human-writable inbox for changes after the session
|
|
339
361
|
starts.
|
|
@@ -419,7 +441,13 @@ To stop because the loop cannot continue:
|
|
|
419
441
|
A valid `goal_check.json` does not stop the loop by itself. It is evidence.
|
|
420
442
|
Stopping is controlled by session `control.json`. If goal-check output is
|
|
421
443
|
missing or invalid repeatedly, the coordinator stops with
|
|
422
|
-
`stop_reason="goal_check_broken"` after the configured failure cap.
|
|
444
|
+
`stop_reason="goal_check_broken"` after the configured failure cap. Similarly,
|
|
445
|
+
consecutive failed iterations of any single workflow stop the loop with
|
|
446
|
+
`stop_reason="workflow_failure_cap"` after `workflow_consecutive_failures_cap`.
|
|
447
|
+
Failed iterations record a `failure_kind` in history — `transient` (provider
|
|
448
|
+
said retry; team-harness's own retries were exhausted), `deterministic`
|
|
449
|
+
(auth/config errors retries cannot fix), `crash` (worker died mid-iteration),
|
|
450
|
+
or `unknown` — so a stopped run is legible without reading harness logs.
|
|
423
451
|
|
|
424
452
|
## Workflow Sets and Child Sessions
|
|
425
453
|
|
|
@@ -431,8 +459,11 @@ Workflow sets are mandatory. Even a single-loop repo uses:
|
|
|
431
459
|
|
|
432
460
|
The older `.loopy_loop/workflows/...` layout is not loaded.
|
|
433
461
|
|
|
434
|
-
A workflow can request one sequential child session by
|
|
435
|
-
the active session's
|
|
462
|
+
A top-level session's workflow can request one sequential child session by
|
|
463
|
+
writing a uniquely named `*.json` file under the active session's
|
|
464
|
+
`child_requests/` directory (only `*.json` filenames are scanned; publish via
|
|
465
|
+
a non-`.json` temp name plus rename — invalid requests are renamed to
|
|
466
|
+
`*.json.rejected` and skipped):
|
|
436
467
|
|
|
437
468
|
```json
|
|
438
469
|
{
|
|
@@ -484,7 +515,8 @@ loopy init [--template default|inner_outer_eval|pm_planner_dispatcher]
|
|
|
484
515
|
Scaffolds loopy-loop files. The default template creates only the reserved
|
|
485
516
|
`goal_check` workflow. `inner_outer_eval` creates the recommended outer/inner/eval
|
|
486
517
|
workflow set. `pm_planner_dispatcher` creates planner/dispatcher workflows for
|
|
487
|
-
child-session orchestration
|
|
518
|
+
child-session orchestration — and also ships the `inner_outer_eval` child set
|
|
519
|
+
its dispatcher spawns, so a clean init is executable end to end.
|
|
488
520
|
|
|
489
521
|
```bash
|
|
490
522
|
loopy coordinator --host 0.0.0.0 --port 8080 [--resume] [--workflow-set NAME] [--goal-file PATH]
|
|
@@ -500,16 +532,24 @@ loopy worker --coordinator http://127.0.0.1:8080
|
|
|
500
532
|
Runs a blocking worker until the coordinator returns `action: "stop"`.
|
|
501
533
|
|
|
502
534
|
```bash
|
|
503
|
-
loopy status
|
|
535
|
+
loopy status # session stack, usage totals, estimated cost
|
|
536
|
+
loopy status --watch # re-render every 2 seconds
|
|
537
|
+
loopy events # the active session's event stream
|
|
538
|
+
loopy events --follow # tail it live (--json for raw lines)
|
|
504
539
|
loopy stop
|
|
505
540
|
```
|
|
506
541
|
|
|
507
|
-
`status` prints the latest session state
|
|
508
|
-
the
|
|
542
|
+
`status` prints the latest session state — the whole session stack while a
|
|
543
|
+
child runs (the live child is shown under its suspended parent), each
|
|
544
|
+
session's subtree token usage, and (with `model_prices` configured) estimated
|
|
545
|
+
cost. `stop` still flags the latest **top-level** session only: a running
|
|
546
|
+
child does not see the flag, and the stop takes effect once the child reaches
|
|
547
|
+
a terminal state and the parent resumes.
|
|
509
548
|
|
|
510
549
|
## Related Projects
|
|
511
550
|
|
|
512
551
|
- [`team-harness`](https://github.com/writeitai/team-harness): the model and
|
|
513
|
-
agent-CLI orchestration layer used by loopy-loop
|
|
552
|
+
agent-CLI orchestration layer used by the loopy-loop worker.
|
|
514
553
|
- [`eval-banana`](https://github.com/writeitai/eval-banana): a lightweight YAML
|
|
515
|
-
evaluation framework used by the packaged eval workflows.
|
|
554
|
+
evaluation framework used by the packaged eval workflows. Installed
|
|
555
|
+
automatically as a loopy-loop dependency.
|
|
@@ -18,12 +18,13 @@ and PRs.
|
|
|
18
18
|
|
|
19
19
|
Under the hood, loopy-loop runs a small FastAPI coordinator and a single
|
|
20
20
|
worker. The coordinator owns the loop state and chooses the next workflow. The
|
|
21
|
-
|
|
21
|
+
worker runs assignments through
|
|
22
22
|
[`team-harness`](https://github.com/writeitai/team-harness), which can delegate
|
|
23
23
|
to agent CLIs such as Codex, Claude Code, and Gemini. The packaged
|
|
24
24
|
`inner_outer_eval` template also uses
|
|
25
25
|
[`eval-banana`](https://github.com/writeitai/eval-banana) conventions for
|
|
26
|
-
session-scoped evaluation checks
|
|
26
|
+
session-scoped evaluation checks; eval-banana installs automatically as a
|
|
27
|
+
loopy-loop dependency.
|
|
27
28
|
|
|
28
29
|
## Install
|
|
29
30
|
|
|
@@ -78,7 +79,8 @@ This is the recommended starting template. It creates:
|
|
|
78
79
|
- a `.gitignore` entry for `.loopy_loop/sessions/`
|
|
79
80
|
|
|
80
81
|
`loopy init` is idempotent. It creates missing files and leaves existing files
|
|
81
|
-
alone
|
|
82
|
+
alone — except `.gitignore`, which is updated in place to ensure the sessions
|
|
83
|
+
ignore rule.
|
|
82
84
|
|
|
83
85
|
## Write the Goal
|
|
84
86
|
|
|
@@ -204,6 +206,7 @@ goal_file: loopy_loop_goal.txt
|
|
|
204
206
|
workflow_set: inner_outer_eval
|
|
205
207
|
max_turns: 160
|
|
206
208
|
goal_check_consecutive_failures_cap: 3
|
|
209
|
+
# workflow_consecutive_failures_cap: 5
|
|
207
210
|
team_harness_provider: "codex"
|
|
208
211
|
team_harness_model: "gpt-5.5"
|
|
209
212
|
team_harness_agents:
|
|
@@ -238,7 +241,21 @@ Important rules:
|
|
|
238
241
|
processes a dead worker left running: drain lets them finish (one shared
|
|
239
242
|
bounded deadline) before the iteration is re-run; reap kills them
|
|
240
243
|
immediately. These are coordinator-side settings and are not part of the
|
|
241
|
-
config snapshot sent to
|
|
244
|
+
config snapshot sent to the worker.
|
|
245
|
+
- `workflow_consecutive_failures_cap` (default 5) is a per-workflow circuit
|
|
246
|
+
breaker: that many consecutive failed iterations of the same workflow stop
|
|
247
|
+
the loop with `stop_reason="workflow_failure_cap"` instead of retrying a
|
|
248
|
+
wedged workflow until `max_turns`. Any success of the workflow resets its
|
|
249
|
+
counter. Coordinator-side only; not part of the config snapshot sent to
|
|
250
|
+
the worker.
|
|
251
|
+
- `model_prices` (optional, coordinator-side only) sets USD prices per 1M
|
|
252
|
+
tokens for the harness coordinator model (`prompt_usd_per_1m`,
|
|
253
|
+
`completion_usd_per_1m`); with prices set, `loopy status` derives an
|
|
254
|
+
estimated cost from the token ledger. `max_cost_usd` (optional, requires
|
|
255
|
+
`model_prices`) stops the loop with `stop_reason="max_cost_usd"` once the
|
|
256
|
+
session tree's estimated cost reaches the budget. Cost covers the harness
|
|
257
|
+
COORDINATOR model only — agent-CLI subprocesses (codex, claude, gemini)
|
|
258
|
+
bill through their own accounts and are not measurable here.
|
|
242
259
|
|
|
243
260
|
Workflow config lives beside each workflow prompt:
|
|
244
261
|
|
|
@@ -292,8 +309,12 @@ Important session files:
|
|
|
292
309
|
|
|
293
310
|
- `goal.md`: the exact goal text copied into the session.
|
|
294
311
|
- `session.json`: session metadata.
|
|
295
|
-
- `state.json`: coordinator-owned dispatch state
|
|
296
|
-
|
|
312
|
+
- `state.json`: coordinator-owned dispatch state, including the session's
|
|
313
|
+
token/duration usage ledger.
|
|
314
|
+
- `events.jsonl`: append-only event stream — one versioned JSON line per
|
|
315
|
+
significant transition (`session_started`, `task_dispatched`,
|
|
316
|
+
`task_finished`, `iteration_abandoned`, `goal_check`, `child_started`,
|
|
317
|
+
`child_finished`, `session_stopped`). Tail it with `loopy events --follow`.
|
|
297
318
|
- `control.json`: workflow-owned stop switch.
|
|
298
319
|
- `updates_from_user.md`: human-writable inbox for changes after the session
|
|
299
320
|
starts.
|
|
@@ -379,7 +400,13 @@ To stop because the loop cannot continue:
|
|
|
379
400
|
A valid `goal_check.json` does not stop the loop by itself. It is evidence.
|
|
380
401
|
Stopping is controlled by session `control.json`. If goal-check output is
|
|
381
402
|
missing or invalid repeatedly, the coordinator stops with
|
|
382
|
-
`stop_reason="goal_check_broken"` after the configured failure cap.
|
|
403
|
+
`stop_reason="goal_check_broken"` after the configured failure cap. Similarly,
|
|
404
|
+
consecutive failed iterations of any single workflow stop the loop with
|
|
405
|
+
`stop_reason="workflow_failure_cap"` after `workflow_consecutive_failures_cap`.
|
|
406
|
+
Failed iterations record a `failure_kind` in history — `transient` (provider
|
|
407
|
+
said retry; team-harness's own retries were exhausted), `deterministic`
|
|
408
|
+
(auth/config errors retries cannot fix), `crash` (worker died mid-iteration),
|
|
409
|
+
or `unknown` — so a stopped run is legible without reading harness logs.
|
|
383
410
|
|
|
384
411
|
## Workflow Sets and Child Sessions
|
|
385
412
|
|
|
@@ -391,8 +418,11 @@ Workflow sets are mandatory. Even a single-loop repo uses:
|
|
|
391
418
|
|
|
392
419
|
The older `.loopy_loop/workflows/...` layout is not loaded.
|
|
393
420
|
|
|
394
|
-
A workflow can request one sequential child session by
|
|
395
|
-
the active session's
|
|
421
|
+
A top-level session's workflow can request one sequential child session by
|
|
422
|
+
writing a uniquely named `*.json` file under the active session's
|
|
423
|
+
`child_requests/` directory (only `*.json` filenames are scanned; publish via
|
|
424
|
+
a non-`.json` temp name plus rename — invalid requests are renamed to
|
|
425
|
+
`*.json.rejected` and skipped):
|
|
396
426
|
|
|
397
427
|
```json
|
|
398
428
|
{
|
|
@@ -444,7 +474,8 @@ loopy init [--template default|inner_outer_eval|pm_planner_dispatcher]
|
|
|
444
474
|
Scaffolds loopy-loop files. The default template creates only the reserved
|
|
445
475
|
`goal_check` workflow. `inner_outer_eval` creates the recommended outer/inner/eval
|
|
446
476
|
workflow set. `pm_planner_dispatcher` creates planner/dispatcher workflows for
|
|
447
|
-
child-session orchestration
|
|
477
|
+
child-session orchestration — and also ships the `inner_outer_eval` child set
|
|
478
|
+
its dispatcher spawns, so a clean init is executable end to end.
|
|
448
479
|
|
|
449
480
|
```bash
|
|
450
481
|
loopy coordinator --host 0.0.0.0 --port 8080 [--resume] [--workflow-set NAME] [--goal-file PATH]
|
|
@@ -460,16 +491,24 @@ loopy worker --coordinator http://127.0.0.1:8080
|
|
|
460
491
|
Runs a blocking worker until the coordinator returns `action: "stop"`.
|
|
461
492
|
|
|
462
493
|
```bash
|
|
463
|
-
loopy status
|
|
494
|
+
loopy status # session stack, usage totals, estimated cost
|
|
495
|
+
loopy status --watch # re-render every 2 seconds
|
|
496
|
+
loopy events # the active session's event stream
|
|
497
|
+
loopy events --follow # tail it live (--json for raw lines)
|
|
464
498
|
loopy stop
|
|
465
499
|
```
|
|
466
500
|
|
|
467
|
-
`status` prints the latest session state
|
|
468
|
-
the
|
|
501
|
+
`status` prints the latest session state — the whole session stack while a
|
|
502
|
+
child runs (the live child is shown under its suspended parent), each
|
|
503
|
+
session's subtree token usage, and (with `model_prices` configured) estimated
|
|
504
|
+
cost. `stop` still flags the latest **top-level** session only: a running
|
|
505
|
+
child does not see the flag, and the stop takes effect once the child reaches
|
|
506
|
+
a terminal state and the parent resumes.
|
|
469
507
|
|
|
470
508
|
## Related Projects
|
|
471
509
|
|
|
472
510
|
- [`team-harness`](https://github.com/writeitai/team-harness): the model and
|
|
473
|
-
agent-CLI orchestration layer used by loopy-loop
|
|
511
|
+
agent-CLI orchestration layer used by the loopy-loop worker.
|
|
474
512
|
- [`eval-banana`](https://github.com/writeitai/eval-banana): a lightweight YAML
|
|
475
|
-
evaluation framework used by the packaged eval workflows.
|
|
513
|
+
evaluation framework used by the packaged eval workflows. Installed
|
|
514
|
+
automatically as a loopy-loop dependency.
|