flywheel-relay 0.2.5__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.
- flywheel_relay-0.2.5/CHANGELOG.md +120 -0
- flywheel_relay-0.2.5/LICENSE +110 -0
- flywheel_relay-0.2.5/MANIFEST.in +4 -0
- flywheel_relay-0.2.5/PKG-INFO +338 -0
- flywheel_relay-0.2.5/README.md +319 -0
- flywheel_relay-0.2.5/docs/ACCOUNTABILITY.md +71 -0
- flywheel_relay-0.2.5/docs/BENCHMARKS.md +63 -0
- flywheel_relay-0.2.5/docs/GITHUB-ONLY-INSTALL.md +56 -0
- flywheel_relay-0.2.5/docs/MCP-PARITY-DESIGN-20260908.md +18 -0
- flywheel_relay-0.2.5/docs/RELEASE-0.2.0-CANDIDATE.md +65 -0
- flywheel_relay-0.2.5/docs/REMOTE-SETUP.md +159 -0
- flywheel_relay-0.2.5/docs/art/accountability-lane.svg +19 -0
- flywheel_relay-0.2.5/docs/art/clause-ladder.svg +17 -0
- flywheel_relay-0.2.5/docs/art/endpoint-ladder.svg +19 -0
- flywheel_relay-0.2.5/docs/art/relay-header.svg +1 -0
- flywheel_relay-0.2.5/docs/art/relay.art.json +198 -0
- flywheel_relay-0.2.5/pyproject.toml +33 -0
- flywheel_relay-0.2.5/setup.cfg +4 -0
- flywheel_relay-0.2.5/src/flywheel_relay.egg-info/PKG-INFO +338 -0
- flywheel_relay-0.2.5/src/flywheel_relay.egg-info/SOURCES.txt +97 -0
- flywheel_relay-0.2.5/src/flywheel_relay.egg-info/dependency_links.txt +1 -0
- flywheel_relay-0.2.5/src/flywheel_relay.egg-info/entry_points.txt +2 -0
- flywheel_relay-0.2.5/src/flywheel_relay.egg-info/requires.txt +3 -0
- flywheel_relay-0.2.5/src/flywheel_relay.egg-info/top_level.txt +1 -0
- flywheel_relay-0.2.5/src/relay/__init__.py +29 -0
- flywheel_relay-0.2.5/src/relay/__main__.py +4 -0
- flywheel_relay-0.2.5/src/relay/approvals.py +57 -0
- flywheel_relay-0.2.5/src/relay/architect.py +32 -0
- flywheel_relay-0.2.5/src/relay/async_runs.py +328 -0
- flywheel_relay-0.2.5/src/relay/bisect.py +110 -0
- flywheel_relay-0.2.5/src/relay/cert.py +142 -0
- flywheel_relay-0.2.5/src/relay/claim_grounding.py +135 -0
- flywheel_relay-0.2.5/src/relay/compaction.py +221 -0
- flywheel_relay-0.2.5/src/relay/contract.py +146 -0
- flywheel_relay-0.2.5/src/relay/conventions.py +43 -0
- flywheel_relay-0.2.5/src/relay/edit_plan.py +108 -0
- flywheel_relay-0.2.5/src/relay/endpoints.py +267 -0
- flywheel_relay-0.2.5/src/relay/hashline.py +60 -0
- flywheel_relay-0.2.5/src/relay/injection_probe.py +101 -0
- flywheel_relay-0.2.5/src/relay/integrity.py +156 -0
- flywheel_relay-0.2.5/src/relay/intent_audit.py +149 -0
- flywheel_relay-0.2.5/src/relay/local_agent.py +313 -0
- flywheel_relay-0.2.5/src/relay/local_agent_cli.py +513 -0
- flywheel_relay-0.2.5/src/relay/local_git.py +70 -0
- flywheel_relay-0.2.5/src/relay/local_loop.py +296 -0
- flywheel_relay-0.2.5/src/relay/local_mcp.py +363 -0
- flywheel_relay-0.2.5/src/relay/local_repomap.py +219 -0
- flywheel_relay-0.2.5/src/relay/local_review_agent.py +93 -0
- flywheel_relay-0.2.5/src/relay/local_session.py +95 -0
- flywheel_relay-0.2.5/src/relay/local_tools.py +300 -0
- flywheel_relay-0.2.5/src/relay/messages_api.py +144 -0
- flywheel_relay-0.2.5/src/relay/oauth.py +267 -0
- flywheel_relay-0.2.5/src/relay/remote_cli.py +40 -0
- flywheel_relay-0.2.5/src/relay/remote_mcp.py +275 -0
- flywheel_relay-0.2.5/src/relay/remote_oauth.py +182 -0
- flywheel_relay-0.2.5/src/relay/remote_state.py +121 -0
- flywheel_relay-0.2.5/src/relay/review.py +168 -0
- flywheel_relay-0.2.5/src/relay/run_view.py +207 -0
- flywheel_relay-0.2.5/src/relay/session_store.py +61 -0
- flywheel_relay-0.2.5/src/relay/tools_prompt.py +34 -0
- flywheel_relay-0.2.5/src/relay/udiff.py +80 -0
- flywheel_relay-0.2.5/src/relay/verified_bon.py +111 -0
- flywheel_relay-0.2.5/src/relay/watch.py +73 -0
- flywheel_relay-0.2.5/tests/test_ambient_repo_map.py +142 -0
- flywheel_relay-0.2.5/tests/test_apply_diff.py +101 -0
- flywheel_relay-0.2.5/tests/test_approvals.py +135 -0
- flywheel_relay-0.2.5/tests/test_architect.py +240 -0
- flywheel_relay-0.2.5/tests/test_async_runs.py +403 -0
- flywheel_relay-0.2.5/tests/test_bisect.py +88 -0
- flywheel_relay-0.2.5/tests/test_cert.py +179 -0
- flywheel_relay-0.2.5/tests/test_claim_grounding.py +77 -0
- flywheel_relay-0.2.5/tests/test_compaction.py +118 -0
- flywheel_relay-0.2.5/tests/test_conventions.py +43 -0
- flywheel_relay-0.2.5/tests/test_edit_plan.py +175 -0
- flywheel_relay-0.2.5/tests/test_endpoints.py +169 -0
- flywheel_relay-0.2.5/tests/test_hashline_edits.py +184 -0
- flywheel_relay-0.2.5/tests/test_injection_probe.py +59 -0
- flywheel_relay-0.2.5/tests/test_integrity.py +45 -0
- flywheel_relay-0.2.5/tests/test_intent_audit.py +85 -0
- flywheel_relay-0.2.5/tests/test_local_agent.py +232 -0
- flywheel_relay-0.2.5/tests/test_local_agentic.py +505 -0
- flywheel_relay-0.2.5/tests/test_local_git.py +89 -0
- flywheel_relay-0.2.5/tests/test_local_mcp.py +680 -0
- flywheel_relay-0.2.5/tests/test_local_repomap.py +100 -0
- flywheel_relay-0.2.5/tests/test_local_review_agent.py +75 -0
- flywheel_relay-0.2.5/tests/test_messages_api.py +57 -0
- flywheel_relay-0.2.5/tests/test_oauth.py +205 -0
- flywheel_relay-0.2.5/tests/test_parallel_calls.py +66 -0
- flywheel_relay-0.2.5/tests/test_release_metadata.py +241 -0
- flywheel_relay-0.2.5/tests/test_remote_mcp.py +231 -0
- flywheel_relay-0.2.5/tests/test_remote_oauth_http.py +182 -0
- flywheel_relay-0.2.5/tests/test_remote_state.py +187 -0
- flywheel_relay-0.2.5/tests/test_repo_art.py +174 -0
- flywheel_relay-0.2.5/tests/test_review.py +80 -0
- flywheel_relay-0.2.5/tests/test_run_view.py +114 -0
- flywheel_relay-0.2.5/tests/test_session_store.py +86 -0
- flywheel_relay-0.2.5/tests/test_verified_bon.py +68 -0
- flywheel_relay-0.2.5/tests/test_watch.py +125 -0
- flywheel_relay-0.2.5/tools/check_release_metadata.py +185 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.5, 2026-09-22
|
|
4
|
+
|
|
5
|
+
Relay now publishes to PyPI as `flywheel-relay`. The install command changes, so
|
|
6
|
+
this is a release rather than a metadata edit.
|
|
7
|
+
|
|
8
|
+
Source version metadata is not release availability proof; release availability
|
|
9
|
+
is established only by the accepted Git tag, uploaded GitHub Release assets, and
|
|
10
|
+
matching hash readback. Do not publish or recommend the bare PyPI name
|
|
11
|
+
`relay-agent`; that public namespace belongs to an unrelated project and is not
|
|
12
|
+
the HarperZ9 Relay distribution.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- The distribution name is `flywheel-relay`. `pip install flywheel-relay` is the
|
|
17
|
+
documented path, and releases carry PEP 740 attestations recording which
|
|
18
|
+
workflow built the bytes. The import name, the module layout and the `relay`
|
|
19
|
+
console script are unchanged.
|
|
20
|
+
- The hash-verified GitHub route is kept and still supported, for anyone who
|
|
21
|
+
would rather check the bytes than trust an index. See
|
|
22
|
+
`docs/GITHUB-ONLY-INSTALL.md`, updated for the new asset names.
|
|
23
|
+
- `tools/check_release_metadata.py` compares whitespace-normalized text, so a
|
|
24
|
+
phrase split across a wrapped line no longer fails a guard that no change in
|
|
25
|
+
wording had broken.
|
|
26
|
+
|
|
27
|
+
### Note
|
|
28
|
+
|
|
29
|
+
0.2.4 was never released. A stray `v0.2.4` tag points at a commit that is not on
|
|
30
|
+
main and whose own `pyproject.toml` reads 0.1.0, so this release skips that
|
|
31
|
+
number rather than reusing it.
|
|
32
|
+
|
|
33
|
+
## 0.2.3, 2026-09-17
|
|
34
|
+
|
|
35
|
+
Status: GitHub-only patch release. Source version metadata is not release
|
|
36
|
+
availability proof; release availability is established only by the accepted Git
|
|
37
|
+
tag, uploaded GitHub Release assets, and matching hash readback. Do not publish
|
|
38
|
+
or recommend the bare PyPI name `relay-agent`; that public namespace is not the
|
|
39
|
+
HarperZ9 Relay distribution.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Architect mode can run an attributed planning pass before a plain single-run
|
|
44
|
+
agent execution, then pass that proposal to the implementer as context.
|
|
45
|
+
- `--architect` refuses watch, MCP, probe, view, verify, bisect, health,
|
|
46
|
+
best-of, and other modes until those paths have explicit planner semantics.
|
|
47
|
+
|
|
48
|
+
### Distribution boundary
|
|
49
|
+
|
|
50
|
+
- Source version metadata, changelog text, and built local artifacts are not
|
|
51
|
+
release availability proof. The release is established by the accepted Git tag,
|
|
52
|
+
uploaded GitHub Release assets, and matching hash readback.
|
|
53
|
+
- Install from a pinned HarperZ9 GitHub commit or from a hash-verified GitHub
|
|
54
|
+
Release wheel. Missing checksum entries or hash mismatches stop before
|
|
55
|
+
`pip install`.
|
|
56
|
+
|
|
57
|
+
## 0.2.2, 2026-09-13
|
|
58
|
+
|
|
59
|
+
Status: GitHub-only patch release. Do not publish or recommend the bare PyPI
|
|
60
|
+
name `relay-agent`; that public namespace is not the HarperZ9 Relay
|
|
61
|
+
distribution.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Background MCP/agent runs with `RELAY_RUN_ROOT` now durably checkpoint
|
|
66
|
+
witnessed partial ledgers while they are still running, so a server restart can
|
|
67
|
+
reload observed progress as an `interrupted` partial run instead of losing it.
|
|
68
|
+
- Final result persistence and running checkpoint writes are serialized so a
|
|
69
|
+
final `done` record is not replaced by an older running checkpoint.
|
|
70
|
+
|
|
71
|
+
### Limits
|
|
72
|
+
|
|
73
|
+
- A partial checkpoint is observed progress only. It is not a completed result,
|
|
74
|
+
rollback guarantee, or acceptance verdict; `local_agent_result` returns `done`
|
|
75
|
+
only after the final result record persists.
|
|
76
|
+
|
|
77
|
+
## 0.2.1, 2026-09-10
|
|
78
|
+
|
|
79
|
+
Status: GitHub-only patch release. Do not publish or recommend the bare PyPI name
|
|
80
|
+
`relay-agent`; that public namespace is not the HarperZ9 Relay distribution.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- MCP stdio now returns JSON-RPC parse/invalid-request/invalid-params errors
|
|
85
|
+
for malformed input, invalid request ids, and non-object `tools/call` params
|
|
86
|
+
without echoing rejected input or stopping the server.
|
|
87
|
+
|
|
88
|
+
## 0.2.0, 2026-09-08
|
|
89
|
+
|
|
90
|
+
Status: GitHub-only release with hash-verified GitHub Release artifacts
|
|
91
|
+
uploaded by the release owner.
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- `local_agent_run` and `local_agent_start` accept the existing CLI route and
|
|
96
|
+
acceptance dials: `backend`, `model`, `max_tokens`, `check`, `test_cmd`, and
|
|
97
|
+
`compact_budget`.
|
|
98
|
+
- Blocking and background MCP runs return a `relay.mcp-run-request/v1` binding
|
|
99
|
+
with admitted backend/model/root/effective gate choices, request gate intent,
|
|
100
|
+
and hashes of goal/check/test command text.
|
|
101
|
+
- Background run status/result/list preserve the request binding across process
|
|
102
|
+
restarts when `RELAY_RUN_ROOT` is configured.
|
|
103
|
+
- MCP results report the last witnessed assistant route with receipt id,
|
|
104
|
+
`model_ref`, and ledger sequence when available.
|
|
105
|
+
|
|
106
|
+
### Fixed
|
|
107
|
+
|
|
108
|
+
- `DONE` background results are not exposed before the final done/result/finished
|
|
109
|
+
record is durable.
|
|
110
|
+
- MCP scalar validation for run/start authority fields rejects string integers,
|
|
111
|
+
floats, booleans in integer fields, non-string route fields, and negative
|
|
112
|
+
limits.
|
|
113
|
+
- `local_agent_runs.limit` now rejects coercive or negative values.
|
|
114
|
+
|
|
115
|
+
### Distribution boundary
|
|
116
|
+
|
|
117
|
+
- This candidate is GitHub-only. Do not publish or recommend the bare PyPI name
|
|
118
|
+
`relay-agent`; that public namespace is not the HarperZ9 Relay distribution.
|
|
119
|
+
- Install from a pinned HarperZ9 GitHub commit or from a hash-verified GitHub
|
|
120
|
+
Release wheel after the release owner publishes one.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, MIT Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-MIT
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Zain Dana Harper
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the MIT license that is effective on the second anniversary of the date we make
|
|
91
|
+
the Software available. On or after that date, you may use the Software under
|
|
92
|
+
the MIT license, in which case the following will apply:
|
|
93
|
+
|
|
94
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
95
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
96
|
+
the Software without restriction, including without limitation the rights to
|
|
97
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
98
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
99
|
+
so, subject to the following conditions:
|
|
100
|
+
|
|
101
|
+
The above copyright notice and this permission notice shall be included in all
|
|
102
|
+
copies or substantial portions of the Software.
|
|
103
|
+
|
|
104
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
105
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
106
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
107
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
108
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
109
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
110
|
+
SOFTWARE.
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flywheel-relay
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: A zero-dependency, accountable coding agent that runs on any model endpoint: local (served 14B/32B or Ollama) or online (codex/claude/gemini/deepseek via your own keys, subscription CLI, or gateway), with automatic failover, a gated tool loop, and a hash-chained re-verifiable session ledger.
|
|
5
|
+
Author: Zain Dana Harper
|
|
6
|
+
License-Expression: LicenseRef-FSL-1.1-MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/HarperZ9/relay
|
|
8
|
+
Keywords: coding-agent,local-llm,ollama,llm-router,failover,mcp,offline,agent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Provides-Extra: test
|
|
17
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
<p align="center"><img src="docs/art/relay-header.svg" alt="relay: accountable coding agent. Every run leaves a certificate a stranger can check offline." width="100%"></p>
|
|
21
|
+
|
|
22
|
+
# relay
|
|
23
|
+
|
|
24
|
+
**A zero-dependency, accountable coding agent that runs on any model endpoint.**
|
|
25
|
+
Local models when you're offline, your subscription or API when you need more,
|
|
26
|
+
automatic failover across all of them, and every run is a re-verifiable,
|
|
27
|
+
git-anchored trajectory. Stdlib only.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
python -m pip install flywheel-relay
|
|
31
|
+
|
|
32
|
+
relay --health --online # which model tiers are live?
|
|
33
|
+
relay "explain this function" --file app.py
|
|
34
|
+
relay --agent "fix the off-by-one in paginate()" --root . --allow-write --auto-commit
|
|
35
|
+
relay --mcp # serve the agent to any MCP client
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Relay publishes to PyPI as `flywheel-relay`, with PEP 740 attestations recording
|
|
39
|
+
which workflow built the bytes. The bare name `relay-agent` belongs to an
|
|
40
|
+
unrelated project and is not this distribution.
|
|
41
|
+
|
|
42
|
+
If you would rather verify the bytes yourself than trust the index, the
|
|
43
|
+
hash-verified path still works and is still supported: a pinned HarperZ9 GitHub
|
|
44
|
+
commit or a GitHub Release wheel, where a missing checksum entry or a hash
|
|
45
|
+
mismatch stops before `pip install`. See
|
|
46
|
+
[`docs/GITHUB-ONLY-INSTALL.md`](docs/GITHUB-ONLY-INSTALL.md).
|
|
47
|
+
|
|
48
|
+
## Reaches every endpoint (with your own credentials)
|
|
49
|
+
|
|
50
|
+
<p align="center"><img src="docs/art/endpoint-ladder.svg" alt="The endpoint ladder from prompt to cloud, with rungs tried in order and free tiers first." width="100%"></p>
|
|
51
|
+
|
|
52
|
+
One ladder, tried in order, failing over on exhaustion or error, free/private
|
|
53
|
+
tiers first so you only spend metered tokens when you have to:
|
|
54
|
+
|
|
55
|
+
| Tier | Reached by |
|
|
56
|
+
|---|---|
|
|
57
|
+
| **local** | a served 14B/32B (`serve.py`) → Ollama (largest pulled model) |
|
|
58
|
+
| **plan / max** | the official CLI (`claude`, `codex`) using your subscription auth |
|
|
59
|
+
| **api** | `codex` / `claude` / `glm` / `gemini` / `deepseek` public APIs + `<PROVIDER>_API_KEY` |
|
|
60
|
+
| **provider** | a gateway (OpenRouter, ...) via `<PROVIDER>_PROVIDER_BASE_URL` |
|
|
61
|
+
| **cloud** | a cloud OpenAI-compatible endpoint via `<PROVIDER>_CLOUD_BASE_URL` + `_CLOUD_KEY` |
|
|
62
|
+
|
|
63
|
+
Legitimate by construction: keys come from the environment, subscriptions from
|
|
64
|
+
your own authenticated CLI, gateways from a base URL you set. Nothing is forged,
|
|
65
|
+
no cover identity is minted, no session token is harvested, no billing is evaded.
|
|
66
|
+
A missing credential just drops that tier from the ladder.
|
|
67
|
+
|
|
68
|
+
One rule inside that is worth stating, because it is the difference between a
|
|
69
|
+
gateway and a leak. A gateway rung points at an arbitrary base URL that you set,
|
|
70
|
+
so it may use only its own dedicated `<PROVIDER>_PROVIDER_KEY`. It never falls
|
|
71
|
+
back to that provider's official API key, because replaying your real credential
|
|
72
|
+
to a third-party URL is exactly the failure the rung exists to avoid. With no
|
|
73
|
+
provider key set, the gateway is called unauthenticated and the official secret
|
|
74
|
+
stays where it is. A rung whose credential is absent is never added to the ladder
|
|
75
|
+
in the first place, so a missing key is a shorter ladder rather than an error at
|
|
76
|
+
call time.
|
|
77
|
+
|
|
78
|
+
## An actual coding agent, not a chat box
|
|
79
|
+
|
|
80
|
+
`--agent` runs a permission-checked tool loop the model drives:
|
|
81
|
+
|
|
82
|
+
- **`repo_map`**: a compact code outline (Python via `ast`; JS/TS/Go/Rust/Java/
|
|
83
|
+
C#/Swift/PHP/Ruby via patterns) so the model finds the right file.
|
|
84
|
+
- **`edit_file`**: precise search/replace where the target must match exactly
|
|
85
|
+
once, so an ambiguous edit is refused, not guessed.
|
|
86
|
+
- **`edit_lines`**: hash-anchored edits. A `read_file` with `"hashed": true`
|
|
87
|
+
returns every line as `<8hex>|<line>`, and the model edits by that anchor
|
|
88
|
+
instead of by repeating the line. It is compact, and an anchor computed against
|
|
89
|
+
a stale view will not match, so a mismatched edit fails closed rather than
|
|
90
|
+
landing on the wrong line.
|
|
91
|
+
- **`edit_plan`**: a coordinated multi-file change applied as one all-or-nothing
|
|
92
|
+
checkpoint. Every hash-anchored op is resolved first; if any anchor is stale,
|
|
93
|
+
ambiguous, or overlaps another op, nothing is written. Each op carries a receipt
|
|
94
|
+
(its resolved line, that line's pre-image, and the anchor) so a stranger can
|
|
95
|
+
recompute the anchor and confirm the edit landed exactly where the plan said.
|
|
96
|
+
- **`apply_diff`**: applies a unified diff to one file, fail-closed. A hunk whose
|
|
97
|
+
context does not match the current file exactly is refused with nothing written,
|
|
98
|
+
so a model that emits diffs gets the same no-silent-misapply guarantee. Unlike a
|
|
99
|
+
fuzzy applier, drift is a refusal, not a wrong-place edit.
|
|
100
|
+
- **`read_file` / `list_dir`**: confined to `--root`. `read_file` takes an
|
|
101
|
+
optional `"hashed": true` for the anchored view above.
|
|
102
|
+
- **`write_file`**: off by default; enabled with `--allow-write`; confined to `--root`.
|
|
103
|
+
- **`run`**: off by default; enabled with `--allow-exec`. A shell can write, so
|
|
104
|
+
`--allow-exec` implies write, and unlike the file tools `run` is not confined to
|
|
105
|
+
`--root` (it sets only the working directory). A denylist refuses a few literal
|
|
106
|
+
destructive spellings: a guardrail against a small model wrecking the tree, not
|
|
107
|
+
a security boundary.
|
|
108
|
+
|
|
109
|
+
Two opt-in loop features, both witnessed:
|
|
110
|
+
|
|
111
|
+
- **`--interactive`**: prompt for approval before every mutating call. Each decision
|
|
112
|
+
is a hash-chained ledger entry bound to the call's exact bytes, so the `.rvc` can
|
|
113
|
+
prove a human gated the step and the approved bytes match the executed bytes. Off
|
|
114
|
+
by default, and a headless run is byte-identical to one without it.
|
|
115
|
+
- **`--compact-budget N`**: once the prompt passes `N` tokens, fold older turns into
|
|
116
|
+
one summary so the loop keeps running in any context window, pinning the task
|
|
117
|
+
anchor and the policy text. Every fold records the folded-span and summary hashes
|
|
118
|
+
on the ledger, and the untruncated trajectory stays there, so shrinking the prompt
|
|
119
|
+
never loses the record.
|
|
120
|
+
|
|
121
|
+
## Watch mode: a marker comment, in any editor
|
|
122
|
+
|
|
123
|
+
No editor plugin, so it works the same in vim, Notepad, or a hex editor: drop a
|
|
124
|
+
comment with the marker anywhere in the tree and relay picks it up.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
relay --watch --root . --allow-write # polls for "RELAY:" comments; Ctrl-C to stop
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
def add(a, b):
|
|
132
|
+
return a - b # RELAY: this should add, not subtract
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Each marker becomes its own agent goal with its own witnessed ledger, through the
|
|
136
|
+
exact same gated tool loop as any other run. The model is told to remove the
|
|
137
|
+
marker itself via `edit_file` once it has acted, so even a change you triggered by
|
|
138
|
+
typing a comment, not a prompt, is never a bypass of the ledger. `--watch-marker`
|
|
139
|
+
changes the trigger string; `--watch-interval` the poll period.
|
|
140
|
+
|
|
141
|
+
## Project conventions, once
|
|
142
|
+
|
|
143
|
+
Drop an `AGENTS.md` or `CONVENTIONS.md` at your project root and every `--agent`
|
|
144
|
+
/ `--watch` run folds it into the system prompt automatically (verbatim, never
|
|
145
|
+
summarized, length-bounded so an oversized file degrades instead of blowing a
|
|
146
|
+
small model's context). `--no-conventions` opts out.
|
|
147
|
+
|
|
148
|
+
## Ambient repo context
|
|
149
|
+
|
|
150
|
+
`--agent`/`--watch` fold a bounded repo map into the system prompt automatically
|
|
151
|
+
(`--root`, stopped at 20 files and capped at 4096 UTF-8 bytes so it never grows
|
|
152
|
+
unbounded on a large tree): the model starts with the codebase's shape instead
|
|
153
|
+
of spending its first turn calling `repo_map` to ask for it. It can still call
|
|
154
|
+
`repo_map` itself for more detail or a subdirectory; this is a head start, not a
|
|
155
|
+
replacement. `--no-repo-map` opts out.
|
|
156
|
+
|
|
157
|
+
This closes a real, verified gap in *what context the model has* (Copilot's
|
|
158
|
+
agent mode does this too). It is not a claim about the small local model's
|
|
159
|
+
tool-use reliability, which is a separate, already-known limitation (see
|
|
160
|
+
Architect mode below). Live runs during development showed high run-to-run
|
|
161
|
+
variance in whether the model actually calls `edit_file` at all, on identical
|
|
162
|
+
input, with and without the ambient map. That variance predates this change and
|
|
163
|
+
is not attributed to it here.
|
|
164
|
+
|
|
165
|
+
## Architect mode: plan with one model, implement with another
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
relay --agent "add rate limiting to fetch()" --root . --allow-write \
|
|
169
|
+
--architect claude-plan --online --check "pytest -q"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A planning turn runs first on the backend you name. It can be any tier Relay
|
|
173
|
+
already reaches: local, subscription, API, gateway, or cloud. Relay folds that
|
|
174
|
+
plan into the implementer's goal as an attributed proposal. The implementing
|
|
175
|
+
agent still gets the current project context, reads the real code, and may
|
|
176
|
+
adapt or ignore the plan if the code points to a better path. Bare
|
|
177
|
+
`--architect` uses the first healthy backend. Architect mode is currently
|
|
178
|
+
limited to plain single-run `--agent`; Relay refuses `--architect` with
|
|
179
|
+
non-agent modes, watch/MCP/probe/view/verify/bisect/health commands, and
|
|
180
|
+
`--best-of` until those paths have explicit planner semantics.
|
|
181
|
+
|
|
182
|
+
## The wedge: a provable run
|
|
183
|
+
|
|
184
|
+
<p align="center"><img src="docs/art/accountability-lane.svg" alt="Eight stages from goal to certificate, ending in allow, refuted, or unverifiable." width="100%"></p>
|
|
185
|
+
|
|
186
|
+
Every turn, tool call, and result is appended to a **hash-chained session
|
|
187
|
+
ledger**. A saved run is tamper-evident: reload it and `verify()` re-derives the
|
|
188
|
+
chain (a broken chain is refused, not loaded). With `--auto-commit`, relay stages
|
|
189
|
+
only the files the ledger recorded as edits and carries the checkpoint in the
|
|
190
|
+
message, so the commit binds the witnessed edit set; unrelated or shell-written
|
|
191
|
+
working-tree changes are left out, never attributed to the run. Each model turn
|
|
192
|
+
also carries a content-addressed receipt whose id a stranger can re-derive from
|
|
193
|
+
the saved record. No other coding agent gives you a run you can *prove*, not just
|
|
194
|
+
read.
|
|
195
|
+
|
|
196
|
+
## Prove it works, not just that it ran
|
|
197
|
+
|
|
198
|
+
A witnessed trajectory proves *what* the agent did. It does not prove the edits are
|
|
199
|
+
*correct*: a model can finish confidently and leave a broken tree. Pass `--check`
|
|
200
|
+
and relay closes that gap: after the agent finishes, it runs your acceptance command
|
|
201
|
+
once, witnesses the result on the ledger, and **accepts** the run only if it passes.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
relay --agent "fix the failing test in paginate()" --root . --allow-write \
|
|
205
|
+
--check "pytest -q" --auto-commit
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The check carries *your* authority, not the model's: it runs outside the tool
|
|
209
|
+
permission boundary and is never a call the model can emit or steer. A failed check means the run is not
|
|
210
|
+
accepted, `--auto-commit` is skipped (a broken tree is never committed on your
|
|
211
|
+
behalf), and the exit code is non-zero, so `--agent --check` works as a CI check over the
|
|
212
|
+
agent's own work. `accepted` = a provable trajectory whose acceptance check held.
|
|
213
|
+
|
|
214
|
+
And the pass has to be *earned*. A rule-based reward-hacking guard reads the
|
|
215
|
+
witnessed edit set: if the agent made the check green by editing the test that grades
|
|
216
|
+
it, or by injecting a `pytest.skip` / `sys.exit`, the pass is flagged UNTRUSTED and
|
|
217
|
+
the run is not accepted. A gamed green is never committed. The flags ship with the
|
|
218
|
+
run under their own hash, re-checkable; the guard is non-learned and only ever turns
|
|
219
|
+
an accept into a refusal, never the reverse.
|
|
220
|
+
|
|
221
|
+
## Prove the boundary holds (prompt-injection robustness)
|
|
222
|
+
|
|
223
|
+
Third-party data an agent reads (a file, a webpage, a tool result) can carry an
|
|
224
|
+
instruction that tries to make it exfiltrate, overwrite, or escape. relay's defense
|
|
225
|
+
is the boundary: tool output is data, never a command, and writes and exec are off by
|
|
226
|
+
default. `relay --probe-injection` measures that defense. It runs a fixed,
|
|
227
|
+
inspectable corpus of injection scenarios through the permission-checked executor, assuming the
|
|
228
|
+
worst case that the model was fully fooled and emitted exactly the smuggled call,
|
|
229
|
+
and reports **containment** with a re-derivable receipt. It exits non-zero if any
|
|
230
|
+
scenario is not contained, so it works as a CI check.
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
relay --probe-injection # safe default: every injection contained
|
|
234
|
+
relay --probe-injection --allow-exec # honest: an open shell is a superset capability
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
It generates no attacks (the corpus is readable data) and it can fail, so it is a
|
|
238
|
+
real measurement, not a reassurance. Harden the defender, measure it, feed the
|
|
239
|
+
failures back.
|
|
240
|
+
|
|
241
|
+
## A run a reviewer can read
|
|
242
|
+
|
|
243
|
+
Every `--agent` run also ships a **reviewability projection** derived purely from the
|
|
244
|
+
witnessed ledger, in the terms a senior reviewer checks first: which files were
|
|
245
|
+
`edited_unread` (changed without ever being read), which edits no passing check
|
|
246
|
+
covered (`unverified_edits`), the failed-call scars, and a `reviewability` score over
|
|
247
|
+
read-before-write, verified, and clean-call ratios. Alongside it, a `risk` table tiers
|
|
248
|
+
each edit by mechanical signals (lines, nesting depth, branching, duplicate lines);
|
|
249
|
+
a high-tier edit **demands** a stronger receipt. These are facts, never generated
|
|
250
|
+
prose, so a surface can enforce them. Expert reviewers get the middle of the run, not
|
|
251
|
+
just its ending.
|
|
252
|
+
|
|
253
|
+
## The proof toolkit: five ways to check a run
|
|
254
|
+
|
|
255
|
+
The witnessed ledger is the substrate for five checks a stranger can run offline.
|
|
256
|
+
The full capability matrix and the honest nulls are in
|
|
257
|
+
[docs/ACCOUNTABILITY.md](docs/ACCOUNTABILITY.md); the benchmark posture is in
|
|
258
|
+
[docs/BENCHMARKS.md](docs/BENCHMARKS.md).
|
|
259
|
+
|
|
260
|
+
- **See it.** `relay --view run.jsonl` draws the run as a hash-chained timeline.
|
|
261
|
+
Flip one byte in the saved run and exactly one edge snaps red, verdict REFUTED.
|
|
262
|
+
- **Certify it.** `--cert run.rvc` writes a few-KB proof-carrying certificate;
|
|
263
|
+
`python verify_cert.py run.rvc` re-derives ALLOW / UNVERIFIABLE / REFUTED offline,
|
|
264
|
+
no model and no re-execution, with zero dependencies.
|
|
265
|
+
- **Select by proof.** `--best-of 8 --check "pytest -q"` runs the goal eight times
|
|
266
|
+
and keeps the verified winner. A run that passed by editing the grader ranks below
|
|
267
|
+
an honest run that scored higher.
|
|
268
|
+
- **Localize a regression.** `--bisect run.jsonl --root <clean> --check "pytest -q"`
|
|
269
|
+
replays the witnessed edit set and names the first edit that broke the tests.
|
|
270
|
+
- **Ground the summary.** relay checks the final answer against the ledger: a summary
|
|
271
|
+
that claims the tests pass over a failed check is REFUTED, even with an intact chain.
|
|
272
|
+
|
|
273
|
+
Those three verdicts are ordered, and the order is the point. `verify_cert.py`
|
|
274
|
+
returns REFUTED first, UNVERIFIABLE next, and reaches ALLOW only when nothing
|
|
275
|
+
earlier fired. A confirmed contradiction therefore outranks an inability to check,
|
|
276
|
+
and both outrank acceptance, so a clause the verifier cannot re-derive can never
|
|
277
|
+
be rounded up to a pass. The exit code follows: zero on ALLOW, non-zero on either
|
|
278
|
+
of the other two, which is what makes it usable as a check in someone else's CI.
|
|
279
|
+
|
|
280
|
+
<p align="center"><img src="docs/art/clause-ladder.svg" alt="The eight clause types a relay acceptance contract may carry, one to a row, with whether the standalone verifier that ships inside every certificate can re-derive each one. chain_intact re-derives before any clause is read, so a flipped byte is refuted whether or not the contract asked. check_not_gamed, no_claimed_history, no_edit and steps_approved re-derive from the file alone: the ledger carries the edits, the reasoning and the approvals they read. tests_pass, reviewability and claim_grounded need the in-tree verifier, because they read per-turn receipts, a diff-level reviewability pass and a syntax-level scan that the vendored file does not carry. Those three report unverifiable rather than assuming they hold." width="100%"></p>
|
|
281
|
+
|
|
282
|
+
Which clause sits in which row is not a matter of taste. Five of the eight
|
|
283
|
+
re-derive from the certificate alone, because the ledger inside it carries the
|
|
284
|
+
edits, the reasoning and the approvals those clauses read. The other three want
|
|
285
|
+
the per-turn receipts, the diff-level reviewability pass, or the syntax-level
|
|
286
|
+
scan for a reward hack, and none of those travel in the file. The vendored
|
|
287
|
+
verifier names them unverifiable and stops there.
|
|
288
|
+
|
|
289
|
+
## Use from an agent (MCP)
|
|
290
|
+
|
|
291
|
+
`relay --mcp` is a zero-dep stdio MCP server exposing `local_agent_health`,
|
|
292
|
+
`local_agent_chat`, `local_agent_run`, and the background `local_agent_start` /
|
|
293
|
+
`local_agent_status` / `local_agent_result` loop. Point Claude Code (or any MCP
|
|
294
|
+
client) at it to use relay as a fallback tier, e.g. keep working on local models
|
|
295
|
+
when a hosted quota runs out.
|
|
296
|
+
|
|
297
|
+
The MCP run tools accept the same bounded routing and acceptance dials as the
|
|
298
|
+
local CLI agent path: `backend`, `model`, `max_tokens`, `check`, `test_cmd`, and
|
|
299
|
+
`compact_budget`, in addition to `goal`, `root`, `allow_write`, `allow_exec`,
|
|
300
|
+
`max_steps`, and `online`. Results carry a request binding with the admitted
|
|
301
|
+
effective backend/model/gate choices, including that exec implies write, and
|
|
302
|
+
hashes of the goal/check commands. Results also include the last witnessed
|
|
303
|
+
assistant backend/model receipt when a run reaches the agent loop.
|
|
304
|
+
|
|
305
|
+
For background runs, set `RELAY_RUN_ROOT` to make progress durable across a
|
|
306
|
+
server restart. `local_agent_start` snapshots the run record when it starts and
|
|
307
|
+
the agent loop asks for a durable checkpoint after witnessed progress, so a
|
|
308
|
+
fresh server can reload the partial ledger and report `interrupted` instead of
|
|
309
|
+
silently losing the entries. A partial checkpoint is only bytes-on-disk evidence
|
|
310
|
+
for observed progress; it is not a completed result, a rollback guarantee, or an
|
|
311
|
+
acceptance verdict. `local_agent_result` reports `done` only after the final
|
|
312
|
+
result record is persisted.
|
|
313
|
+
|
|
314
|
+
## Library
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
from relay import LocalAgent, available_backends, build_endpoints, run_agent
|
|
318
|
+
|
|
319
|
+
agent = LocalAgent(backends=available_backends() + build_endpoints()) # local + online
|
|
320
|
+
print(agent.send("hi")["content"][0]["text"])
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## License
|
|
324
|
+
|
|
325
|
+
Relay is fair-source: open to read, run, and build on, with commercial use reserved so the project can fund its own development. See [LICENSE](LICENSE).
|
|
326
|
+
|
|
327
|
+
## What this believes
|
|
328
|
+
|
|
329
|
+
This tool is one part of a family that holds a single belief steady across
|
|
330
|
+
every surface: knowledge open to anyone who can attain the means; acceptance
|
|
331
|
+
decided by external checks, never reputation; every result re-runnable;
|
|
332
|
+
honest nulls first-class; ownership earned by comprehension; learning woven
|
|
333
|
+
into the work. The full text lives in [CREDO.md](CREDO.md).
|
|
334
|
+
The long form of this belief: [The Unbundling](https://github.com/HarperZ9/flywheel/blob/fix/release-model-identity/docs/essays/2026-07-13-the-unbundling.md).
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
**[Zentropy Labs](https://github.com/ZentropyLabs-ai)** · order out of entropy. An independent lab building evidence-first tools that leave a re-checkable artifact behind. Built by Zain Dana Harper in Seattle. The full workbench is at [Project Telos](https://harperz9.github.io).
|