relayforge 0.1.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.
- relayforge-0.1.0/.env.example +2 -0
- relayforge-0.1.0/.github/workflows/publish.yml +24 -0
- relayforge-0.1.0/.gitignore +13 -0
- relayforge-0.1.0/LICENSE +21 -0
- relayforge-0.1.0/PKG-INFO +145 -0
- relayforge-0.1.0/README.md +113 -0
- relayforge-0.1.0/docs/channels.md +115 -0
- relayforge-0.1.0/docs/completion-audit.md +59 -0
- relayforge-0.1.0/docs/design.md +57 -0
- relayforge-0.1.0/docs/idea-evolution.md +18 -0
- relayforge-0.1.0/docs/live-agreement.md +46 -0
- relayforge-0.1.0/docs/next-agent-brief.md +85 -0
- relayforge-0.1.0/docs/ownership-map.md +50 -0
- relayforge-0.1.0/docs/product-debt.md +17 -0
- relayforge-0.1.0/docs/providers.md +40 -0
- relayforge-0.1.0/docs/publishing.md +47 -0
- relayforge-0.1.0/docs/research/codex-thread-routing-2026-06-26.md +150 -0
- relayforge-0.1.0/docs/verification-matrix.md +24 -0
- relayforge-0.1.0/docs/worklog.md +140 -0
- relayforge-0.1.0/pyproject.toml +68 -0
- relayforge-0.1.0/relay.config.example.json +28 -0
- relayforge-0.1.0/relayforge_auto_guides.pth +1 -0
- relayforge-0.1.0/src/relayforge/__init__.py +5 -0
- relayforge-0.1.0/src/relayforge/_auto_guides.py +8 -0
- relayforge-0.1.0/src/relayforge/agent_guides.py +92 -0
- relayforge-0.1.0/src/relayforge/app.py +270 -0
- relayforge-0.1.0/src/relayforge/cli.py +280 -0
- relayforge-0.1.0/src/relayforge/cli_output.py +20 -0
- relayforge-0.1.0/src/relayforge/cli_parser.py +120 -0
- relayforge-0.1.0/src/relayforge/core/__init__.py +1 -0
- relayforge-0.1.0/src/relayforge/core/config.py +27 -0
- relayforge-0.1.0/src/relayforge/core/models.py +122 -0
- relayforge-0.1.0/src/relayforge/core/providers.py +8 -0
- relayforge-0.1.0/src/relayforge/core/router.py +84 -0
- relayforge-0.1.0/src/relayforge/core/targets.py +15 -0
- relayforge-0.1.0/src/relayforge/core/validation.py +67 -0
- relayforge-0.1.0/src/relayforge/providers/__init__.py +1 -0
- relayforge-0.1.0/src/relayforge/providers/discord/__init__.py +1 -0
- relayforge-0.1.0/src/relayforge/providers/discord/gateway.py +136 -0
- relayforge-0.1.0/src/relayforge/providers/discord/rest.py +75 -0
- relayforge-0.1.0/src/relayforge/py.typed +1 -0
- relayforge-0.1.0/src/relayforge/targets/__init__.py +1 -0
- relayforge-0.1.0/src/relayforge/targets/codex/__init__.py +1 -0
- relayforge-0.1.0/src/relayforge/targets/codex/jsonl.py +73 -0
- relayforge-0.1.0/src/relayforge/targets/codex_app_server/__init__.py +6 -0
- relayforge-0.1.0/src/relayforge/targets/codex_app_server/prompt.py +21 -0
- relayforge-0.1.0/src/relayforge/targets/codex_app_server/target.py +159 -0
- relayforge-0.1.0/src/relayforge/targets/codex_app_server/transport.py +160 -0
- relayforge-0.1.0/tests/test_agent_guides.py +104 -0
- relayforge-0.1.0/tests/test_app.py +604 -0
- relayforge-0.1.0/tests/test_boundaries.py +16 -0
- relayforge-0.1.0/tests/test_cli.py +717 -0
- relayforge-0.1.0/tests/test_cli_parser.py +92 -0
- relayforge-0.1.0/tests/test_codex_app_server_target.py +207 -0
- relayforge-0.1.0/tests/test_codex_jsonl.py +78 -0
- relayforge-0.1.0/tests/test_config.py +160 -0
- relayforge-0.1.0/tests/test_discord_gateway.py +127 -0
- relayforge-0.1.0/tests/test_discord_rest.py +56 -0
- relayforge-0.1.0/tests/test_packaging.py +110 -0
- relayforge-0.1.0/tests/test_router.py +258 -0
- relayforge-0.1.0/tests/test_validation.py +103 -0
- relayforge-0.1.0/uv.lock +882 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.11"
|
|
21
|
+
- uses: astral-sh/setup-uv@v5
|
|
22
|
+
- run: uv sync --extra dev
|
|
23
|
+
- run: uv run python -m build
|
|
24
|
+
- run: uv publish
|
relayforge-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rohan Sharma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: relayforge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Route chat channels to agent targets.
|
|
5
|
+
Project-URL: Homepage, https://github.com/AlmanacCode/relayforge
|
|
6
|
+
Project-URL: Issues, https://github.com/AlmanacCode/relayforge/issues
|
|
7
|
+
Author: Rohan Sharma
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,automation,codex,discord,relay
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Communications :: Chat
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: certifi>=2024.7.4
|
|
22
|
+
Requires-Dist: httpx<1,>=0.27
|
|
23
|
+
Requires-Dist: pydantic<3,>=2.8
|
|
24
|
+
Requires-Dist: websockets<15,>=13
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build<2,>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio<1,>=0.24; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest<9,>=8.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff<1,>=0.8; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine<7,>=5.1; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Relayforge
|
|
34
|
+
|
|
35
|
+
Relayforge routes chat channels to agent targets.
|
|
36
|
+
|
|
37
|
+
The first provider is Discord. The first targets are a Codex app-server binding
|
|
38
|
+
and a Codex JSONL fallback inbox. The package is Python and PyPI-ready, with
|
|
39
|
+
provider-neutral core types so Slack, WhatsApp, and other agent APIs can be
|
|
40
|
+
added later.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
After the package is published:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install relayforge
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The installed CLI command is `relayforge`.
|
|
51
|
+
|
|
52
|
+
Installed wheels add a small managed Relayforge block to:
|
|
53
|
+
|
|
54
|
+
- `~/.codex/AGENTS.md`
|
|
55
|
+
- `~/.claude/CLAUDE.md`
|
|
56
|
+
|
|
57
|
+
Set `RELAYFORGE_SKIP_AGENT_GUIDES=1` before starting Python with the installed
|
|
58
|
+
package on `sys.path` to skip this.
|
|
59
|
+
|
|
60
|
+
From this checkout:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python -m venv .venv
|
|
64
|
+
. .venv/bin/activate
|
|
65
|
+
pip install -e '.[dev]'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"channels": [
|
|
73
|
+
{
|
|
74
|
+
"name": "rohan-codex",
|
|
75
|
+
"provider": "discord",
|
|
76
|
+
"id": "1520170530148192466",
|
|
77
|
+
"owner": "rohan"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"bindings": [
|
|
81
|
+
{
|
|
82
|
+
"name": "rohan-almanac-main",
|
|
83
|
+
"source": "rohan-codex",
|
|
84
|
+
"target": "codex-app-server",
|
|
85
|
+
"codexThreadId": "replace-with-codex-thread-id",
|
|
86
|
+
"cwd": "/Users/rohan/Desktop/Projects/almanac",
|
|
87
|
+
"machine": "replace-with-hostname",
|
|
88
|
+
"transport": "codex app-server --config mcp_servers={} --listen stdio://"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"routes": [
|
|
92
|
+
{
|
|
93
|
+
"channel": "rohan-codex",
|
|
94
|
+
"target": "codex-jsonl",
|
|
95
|
+
"inboxPath": ".relay/rohan-codex-inbox.jsonl"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Each founder can use the same Discord bot token with a different named channel.
|
|
102
|
+
A binding delivers to that founder's Codex thread on the right machine. A route
|
|
103
|
+
keeps JSONL fallback delivery available when the direct target fails.
|
|
104
|
+
|
|
105
|
+
See [docs/channels.md](docs/channels.md) for the multi-founder pattern.
|
|
106
|
+
See [docs/research/codex-thread-routing-2026-06-26.md](docs/research/codex-thread-routing-2026-06-26.md)
|
|
107
|
+
for the Codex thread binding direction.
|
|
108
|
+
|
|
109
|
+
## Commands
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
export DISCORD_BOT_TOKEN=...
|
|
113
|
+
|
|
114
|
+
relayforge init --config relay.config.json
|
|
115
|
+
relayforge channel-set --name rohan-codex --provider discord --id 1520...
|
|
116
|
+
relayforge route-set --channel rohan-codex --target codex-jsonl --inbox-path .relay/rohan-codex-inbox.jsonl
|
|
117
|
+
relayforge binding-set --name rohan-almanac-main --source rohan-codex --target codex-app-server --codex-thread-id 019f... --cwd /Users/rohan/Desktop/Projects/almanac --transport "codex app-server --config mcp_servers={} --listen stdio://"
|
|
118
|
+
relayforge check --config relay.config.json
|
|
119
|
+
relayforge listen --config relay.config.json
|
|
120
|
+
relayforge listen --config relay.config.json --include-bots --max-messages 1 --timeout 20
|
|
121
|
+
relayforge check --config relay.config.json --json
|
|
122
|
+
relayforge channels --config relay.config.json
|
|
123
|
+
relayforge routes --config relay.config.json
|
|
124
|
+
relayforge bindings --config relay.config.json
|
|
125
|
+
relayforge codex-smoke --binding rohan-almanac-main --message "smoke test"
|
|
126
|
+
relayforge binding-send --binding rohan-almanac-main --message "reply in Discord"
|
|
127
|
+
relayforge send --channel rohan-codex --message "hello"
|
|
128
|
+
relayforge read --channel rohan-codex --limit 10
|
|
129
|
+
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --limit 10
|
|
130
|
+
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --cursor .relay/rohan.cursor.json --ack
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Raw Discord channel IDs work for `read` and `send` even before a config file
|
|
134
|
+
exists. Named channels require the config.
|
|
135
|
+
|
|
136
|
+
The `listen` command uses Discord Gateway events. That means Discord pushes new
|
|
137
|
+
messages to this process instead of the process polling the channel.
|
|
138
|
+
|
|
139
|
+
By default, Discord bot-authored messages are ignored to avoid loops. Use
|
|
140
|
+
`--include-bots` only for controlled smoke tests or deliberate bot-to-agent
|
|
141
|
+
routing.
|
|
142
|
+
|
|
143
|
+
`codex-smoke` sends a synthetic message through a configured Codex app-server
|
|
144
|
+
binding. Use it only with a rollout-backed Codex thread that is safe to receive
|
|
145
|
+
a test turn.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Relayforge
|
|
2
|
+
|
|
3
|
+
Relayforge routes chat channels to agent targets.
|
|
4
|
+
|
|
5
|
+
The first provider is Discord. The first targets are a Codex app-server binding
|
|
6
|
+
and a Codex JSONL fallback inbox. The package is Python and PyPI-ready, with
|
|
7
|
+
provider-neutral core types so Slack, WhatsApp, and other agent APIs can be
|
|
8
|
+
added later.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
After the package is published:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install relayforge
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The installed CLI command is `relayforge`.
|
|
19
|
+
|
|
20
|
+
Installed wheels add a small managed Relayforge block to:
|
|
21
|
+
|
|
22
|
+
- `~/.codex/AGENTS.md`
|
|
23
|
+
- `~/.claude/CLAUDE.md`
|
|
24
|
+
|
|
25
|
+
Set `RELAYFORGE_SKIP_AGENT_GUIDES=1` before starting Python with the installed
|
|
26
|
+
package on `sys.path` to skip this.
|
|
27
|
+
|
|
28
|
+
From this checkout:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python -m venv .venv
|
|
32
|
+
. .venv/bin/activate
|
|
33
|
+
pip install -e '.[dev]'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"channels": [
|
|
41
|
+
{
|
|
42
|
+
"name": "rohan-codex",
|
|
43
|
+
"provider": "discord",
|
|
44
|
+
"id": "1520170530148192466",
|
|
45
|
+
"owner": "rohan"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"bindings": [
|
|
49
|
+
{
|
|
50
|
+
"name": "rohan-almanac-main",
|
|
51
|
+
"source": "rohan-codex",
|
|
52
|
+
"target": "codex-app-server",
|
|
53
|
+
"codexThreadId": "replace-with-codex-thread-id",
|
|
54
|
+
"cwd": "/Users/rohan/Desktop/Projects/almanac",
|
|
55
|
+
"machine": "replace-with-hostname",
|
|
56
|
+
"transport": "codex app-server --config mcp_servers={} --listen stdio://"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"routes": [
|
|
60
|
+
{
|
|
61
|
+
"channel": "rohan-codex",
|
|
62
|
+
"target": "codex-jsonl",
|
|
63
|
+
"inboxPath": ".relay/rohan-codex-inbox.jsonl"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each founder can use the same Discord bot token with a different named channel.
|
|
70
|
+
A binding delivers to that founder's Codex thread on the right machine. A route
|
|
71
|
+
keeps JSONL fallback delivery available when the direct target fails.
|
|
72
|
+
|
|
73
|
+
See [docs/channels.md](docs/channels.md) for the multi-founder pattern.
|
|
74
|
+
See [docs/research/codex-thread-routing-2026-06-26.md](docs/research/codex-thread-routing-2026-06-26.md)
|
|
75
|
+
for the Codex thread binding direction.
|
|
76
|
+
|
|
77
|
+
## Commands
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export DISCORD_BOT_TOKEN=...
|
|
81
|
+
|
|
82
|
+
relayforge init --config relay.config.json
|
|
83
|
+
relayforge channel-set --name rohan-codex --provider discord --id 1520...
|
|
84
|
+
relayforge route-set --channel rohan-codex --target codex-jsonl --inbox-path .relay/rohan-codex-inbox.jsonl
|
|
85
|
+
relayforge binding-set --name rohan-almanac-main --source rohan-codex --target codex-app-server --codex-thread-id 019f... --cwd /Users/rohan/Desktop/Projects/almanac --transport "codex app-server --config mcp_servers={} --listen stdio://"
|
|
86
|
+
relayforge check --config relay.config.json
|
|
87
|
+
relayforge listen --config relay.config.json
|
|
88
|
+
relayforge listen --config relay.config.json --include-bots --max-messages 1 --timeout 20
|
|
89
|
+
relayforge check --config relay.config.json --json
|
|
90
|
+
relayforge channels --config relay.config.json
|
|
91
|
+
relayforge routes --config relay.config.json
|
|
92
|
+
relayforge bindings --config relay.config.json
|
|
93
|
+
relayforge codex-smoke --binding rohan-almanac-main --message "smoke test"
|
|
94
|
+
relayforge binding-send --binding rohan-almanac-main --message "reply in Discord"
|
|
95
|
+
relayforge send --channel rohan-codex --message "hello"
|
|
96
|
+
relayforge read --channel rohan-codex --limit 10
|
|
97
|
+
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --limit 10
|
|
98
|
+
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --cursor .relay/rohan.cursor.json --ack
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Raw Discord channel IDs work for `read` and `send` even before a config file
|
|
102
|
+
exists. Named channels require the config.
|
|
103
|
+
|
|
104
|
+
The `listen` command uses Discord Gateway events. That means Discord pushes new
|
|
105
|
+
messages to this process instead of the process polling the channel.
|
|
106
|
+
|
|
107
|
+
By default, Discord bot-authored messages are ignored to avoid loops. Use
|
|
108
|
+
`--include-bots` only for controlled smoke tests or deliberate bot-to-agent
|
|
109
|
+
routing.
|
|
110
|
+
|
|
111
|
+
`codex-smoke` sends a synthetic message through a configured Codex app-server
|
|
112
|
+
binding. Use it only with a rollout-backed Codex thread that is safe to receive
|
|
113
|
+
a test turn.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Channels
|
|
2
|
+
|
|
3
|
+
Channels are named routing endpoints. A channel belongs to a provider and has a
|
|
4
|
+
provider-specific ID.
|
|
5
|
+
|
|
6
|
+
The shared Discord bot token is not tied to one person. Each founder gets a
|
|
7
|
+
separate channel entry. A binding maps that channel to one machine-local Codex
|
|
8
|
+
thread; a route can remain as the JSONL fallback.
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"channels": [
|
|
13
|
+
{
|
|
14
|
+
"name": "rohan-codex",
|
|
15
|
+
"provider": "discord",
|
|
16
|
+
"id": "1520170530148192466",
|
|
17
|
+
"owner": "rohan"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "kush-codex",
|
|
21
|
+
"provider": "discord",
|
|
22
|
+
"id": "replace-with-kush-channel-id",
|
|
23
|
+
"owner": "kush"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"bindings": [
|
|
27
|
+
{
|
|
28
|
+
"name": "rohan-almanac-main",
|
|
29
|
+
"source": "rohan-codex",
|
|
30
|
+
"target": "codex-app-server",
|
|
31
|
+
"codexThreadId": "replace-with-codex-thread-id",
|
|
32
|
+
"cwd": "/Users/rohan/Desktop/Projects/almanac",
|
|
33
|
+
"machine": "replace-with-hostname"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"routes": [
|
|
37
|
+
{
|
|
38
|
+
"channel": "rohan-codex",
|
|
39
|
+
"target": "codex-jsonl",
|
|
40
|
+
"inboxPath": ".relay/rohan-codex-inbox.jsonl"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"channel": "kush-codex",
|
|
44
|
+
"target": "codex-jsonl",
|
|
45
|
+
"inboxPath": ".relay/kush-codex-inbox.jsonl"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use `relayforge channels --config relay.config.json` to inspect configured
|
|
52
|
+
channels. This command does not need Discord credentials because it only reads
|
|
53
|
+
local config.
|
|
54
|
+
|
|
55
|
+
Use `relayforge bindings --config relay.config.json` to inspect which channels
|
|
56
|
+
are bound to agent targets.
|
|
57
|
+
|
|
58
|
+
Use `relayforge init` once when no config exists yet:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
relayforge init --config relay.config.json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use `relayforge channel-set` to create or update a channel without
|
|
65
|
+
hand-editing JSON:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
relayforge channel-set \
|
|
69
|
+
--name rohan-codex \
|
|
70
|
+
--provider discord \
|
|
71
|
+
--id 1520170530148192466 \
|
|
72
|
+
--owner rohan
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Use `relayforge route-set` to create or update the JSONL fallback route:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
relayforge route-set \
|
|
79
|
+
--channel rohan-codex \
|
|
80
|
+
--target codex-jsonl \
|
|
81
|
+
--inbox-path .relay/rohan-codex-inbox.jsonl
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Use `relayforge binding-set` to create or update a binding:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
relayforge binding-set \
|
|
88
|
+
--name rohan-almanac-main \
|
|
89
|
+
--source rohan-codex \
|
|
90
|
+
--target codex-app-server \
|
|
91
|
+
--codex-thread-id 019f05b3-575b-7eb2-a5c3-ff91266b4815 \
|
|
92
|
+
--cwd /Users/rohan/Desktop/Projects/almanac
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use `relayforge codex-smoke --binding rohan-almanac-main` to verify that a
|
|
96
|
+
configured Codex app-server binding can resume its thread and start a test turn.
|
|
97
|
+
Run it only against a rollout-backed Codex thread that can safely receive a
|
|
98
|
+
smoke message.
|
|
99
|
+
|
|
100
|
+
Commands that talk to providers accept either a named channel or a raw provider
|
|
101
|
+
ID:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
relayforge read --channel rohan-codex
|
|
105
|
+
relayforge send --channel rohan-codex --message "hello"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Raw provider IDs do not require a config file. Named channels do.
|
|
109
|
+
|
|
110
|
+
Agents can send through a configured binding without knowing the provider
|
|
111
|
+
channel ID:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
relayforge binding-send --binding rohan-almanac-main --message "Done."
|
|
115
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Completion Audit
|
|
2
|
+
|
|
3
|
+
Date: 2026-06-27
|
|
4
|
+
|
|
5
|
+
This audit tracks the active goal: a clean, PR-ready Python package with a
|
|
6
|
+
Codex app-server target and binding-based Discord-to-Codex thread routing.
|
|
7
|
+
|
|
8
|
+
## Verdict
|
|
9
|
+
|
|
10
|
+
Complete for the current package goal. The package is locally verified, the core
|
|
11
|
+
product shape is implemented, and the direct Codex wakeup path has live
|
|
12
|
+
`turn/start` proof against an idle rollout-backed Codex thread fork.
|
|
13
|
+
|
|
14
|
+
## Requirement Audit
|
|
15
|
+
|
|
16
|
+
| Requirement | Current Evidence | Status |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| Python package prepared for PyPI | `pyproject.toml`, `src/relayforge`, `src/relayforge/py.typed`, console script, auto guide `.pth`, example config in sdist, local config excluded from sdist, `python -m build`, `twine check dist/*` | Proven locally |
|
|
19
|
+
| Clean, small package boundaries | `core/`, `providers/discord/`, `targets/codex/`, `targets/codex_app_server/`, boundary tests | Proven by structure and tests |
|
|
20
|
+
| Provider-neutral core | Core uses string provider/target names and does not import provider or target implementations | Proven by `tests/test_boundaries.py` |
|
|
21
|
+
| Discord provider | REST read/send and Gateway listener exist under `providers/discord/` | Proven by unit tests and prior live Discord read/send/Gateway smoke |
|
|
22
|
+
| Binding-based routing | `Binding`, machine scoping, provider thread scoping, binding precedence, and fallback routing are implemented | Proven by router tests |
|
|
23
|
+
| Configurable channels, routes, and bindings | `relayforge init`, `channel-set`, `route-set`, `binding-set`, `channels`, `routes`, `bindings`, `check`, and config save/load support setup without hand-editing JSON | Proven by app and CLI tests, including the full local setup flow |
|
|
24
|
+
| Agent outbound replies | `relayforge binding-send` sends through the configured binding source or provider thread, using the source channel provider's sender | Proven by tests; live bound send remains optional evidence |
|
|
25
|
+
| Codex JSONL fallback | JSONL target and cursor-backed inbox reads exist | Proven by unit tests |
|
|
26
|
+
| Codex app-server target | Target initializes app-server, resumes an existing thread, and can start a turn with a notification envelope | Proven by fake-transport tests, live resume-only smoke, and live `turn/start` smoke |
|
|
27
|
+
| Per-binding Codex transport | `Binding.transport` selects a separate app-server stdio command per transport string | Proven by unit tests |
|
|
28
|
+
| Raw default agent guides | Wheel install writes managed Relayforge guidance blocks to `~/.codex/AGENTS.md` and `~/.claude/CLAUDE.md` on Python startup | Proven by installed-wheel packaging tests, including opt-out |
|
|
29
|
+
| Steering docs current | Live agreement, ownership map, verification matrix, worklog, and next-agent brief describe the current system | Proven by direct inspection |
|
|
30
|
+
| Final live direct wakeup proof | Full `relayforge codex-smoke --binding <name>` against safe idle fork `019f069b-3ac6-7470-b502-4135061914c6` | Proven live |
|
|
31
|
+
|
|
32
|
+
## Live Evidence
|
|
33
|
+
|
|
34
|
+
The final smoke used a temporary config created through public CLI setup
|
|
35
|
+
commands:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
relayforge init --force --config "$tmp_config"
|
|
39
|
+
relayforge channel-set --config "$tmp_config" --name smoke-discord --provider discord --id smoke-channel
|
|
40
|
+
relayforge binding-set --config "$tmp_config" --name smoke-codex-fork --source smoke-discord --target codex-app-server --codex-thread-id 019f069b-3ac6-7470-b502-4135061914c6 --cwd /Users/rohan/Desktop/Projects/almanac
|
|
41
|
+
relayforge check --config "$tmp_config"
|
|
42
|
+
relayforge codex-smoke --config "$tmp_config" --binding smoke-codex-fork --message "Relayforge live Codex app-server smoke test. Please reply briefly that the smoke test reached this fork."
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The command returned:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{"threadId": "019f069b-3ac6-7470-b502-4135061914c6", "method": "turn/start"}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`codex_app.read_thread` then showed a new turn on the fork and the thread was
|
|
52
|
+
idle afterward.
|
|
53
|
+
|
|
54
|
+
## Optional Future Evidence
|
|
55
|
+
|
|
56
|
+
1. Run one live `relayforge binding-send --binding <name>` smoke against a safe
|
|
57
|
+
Discord channel/thread to prove bound outbound sends outside unit tests.
|
|
58
|
+
2. Run a live human-authored Discord Gateway message through the listener to
|
|
59
|
+
prove non-bot message routing.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Relayforge Design
|
|
2
|
+
|
|
3
|
+
Relayforge is a small message router. Chat providers produce inbound messages.
|
|
4
|
+
Agent targets receive normalized messages and may send outbound replies through
|
|
5
|
+
the same provider.
|
|
6
|
+
|
|
7
|
+
The initial implementation supports Discord Gateway events, a Codex JSONL
|
|
8
|
+
fallback inbox, and a Codex app-server target for existing Codex threads. JSONL
|
|
9
|
+
is deliberately simple because it works without a running Codex app-server.
|
|
10
|
+
|
|
11
|
+
Core provider and target names are strings. Concrete provider behavior lives
|
|
12
|
+
under `providers/`; concrete target behavior lives under `targets/`.
|
|
13
|
+
|
|
14
|
+
## Boundaries
|
|
15
|
+
|
|
16
|
+
- Providers know how to read and send messages on one platform.
|
|
17
|
+
- Targets know how to deliver a normalized message to an agent system.
|
|
18
|
+
- The router owns channel-to-target configuration and binding selection.
|
|
19
|
+
- The core types do not mention Discord, Slack, WhatsApp, or Codex internals.
|
|
20
|
+
|
|
21
|
+
## Message Flow
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
Discord Gateway MESSAGE_CREATE
|
|
25
|
+
-> Discord provider normalizes message
|
|
26
|
+
-> Router matches provider + channel id to a named channel
|
|
27
|
+
-> Router resolves the matching binding or fallback route
|
|
28
|
+
-> Codex app-server starts a turn with a Relayforge notification, or Codex
|
|
29
|
+
JSONL appends one event
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The Codex app-server prompt is an explicit notification envelope. It includes
|
|
33
|
+
the binding name, provider source, message ID, author, timestamp, and message
|
|
34
|
+
body. The prompt tells the agent that a new chat message arrived and that any
|
|
35
|
+
reply should go back through the relay binding.
|
|
36
|
+
|
|
37
|
+
If a binding target fails and a route exists for the same channel, the router
|
|
38
|
+
uses the route as fallback. If no fallback route exists, the direct target error
|
|
39
|
+
is preserved.
|
|
40
|
+
|
|
41
|
+
Outbound messages use the provider adapter directly:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
relayforge send --channel <id> --message "..."
|
|
45
|
+
-> Discord provider posts to the channel
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Bound outbound messages resolve through the binding first:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
relayforge binding-send --binding <name> --message "..."
|
|
52
|
+
-> Relay resolves the binding source channel or source thread
|
|
53
|
+
-> Discord provider posts to that destination
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Bindings can be updated through `relayforge binding-set`. This keeps setup
|
|
57
|
+
scriptable for agents while preserving JSON config as the source of truth.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Idea Evolution
|
|
2
|
+
|
|
3
|
+
## 2026-06-26
|
|
4
|
+
|
|
5
|
+
Old hypothesis: A TypeScript `relayforge` package is enough because Discord
|
|
6
|
+
support is easy through `discord.js`.
|
|
7
|
+
|
|
8
|
+
New hypothesis: The package should be Python and PyPI-ready because the desired
|
|
9
|
+
artifact is a reusable agent relay package, not a local Node script.
|
|
10
|
+
|
|
11
|
+
Evidence that forced the change: The user explicitly requested slow development,
|
|
12
|
+
good code, Python, and PyPI deployment, with channels as a first-class concern.
|
|
13
|
+
|
|
14
|
+
Code or product assumption affected: The initial TypeScript scaffold should be
|
|
15
|
+
replaced rather than ported in place.
|
|
16
|
+
|
|
17
|
+
Follow-up test: Build a Python package where Discord is isolated under
|
|
18
|
+
`providers/discord` and routing tests do not import Discord.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Live Agreement
|
|
2
|
+
|
|
3
|
+
Relayforge is a Python package for routing chat messages between human
|
|
4
|
+
channels and agent targets. It will be published to PyPI.
|
|
5
|
+
|
|
6
|
+
The product model is channel-first. A channel is a routable conversation endpoint
|
|
7
|
+
owned by a provider such as Discord, Slack, or WhatsApp. A target is an agent
|
|
8
|
+
delivery mechanism such as a Codex inbox file or direct Codex app-server
|
|
9
|
+
delivery.
|
|
10
|
+
|
|
11
|
+
## Current Contract
|
|
12
|
+
|
|
13
|
+
- Python is the implementation language.
|
|
14
|
+
- Packaging must be PyPI-ready.
|
|
15
|
+
- Discord is the first provider.
|
|
16
|
+
- Slack and WhatsApp are future providers, so core names must not mention
|
|
17
|
+
Discord unless the file is inside the Discord provider.
|
|
18
|
+
- Codex JSONL inbox is the fallback target because it works without a running
|
|
19
|
+
Codex app-server process.
|
|
20
|
+
- Codex app-server is the direct Codex target. It resumes a configured Codex
|
|
21
|
+
thread and starts a turn through JSON-RPC.
|
|
22
|
+
- Codex app-server bindings can override the stdio command with `transport`.
|
|
23
|
+
Different transport strings use separate initialized app-server processes.
|
|
24
|
+
- Channel IDs are configuration data. The bot token can be shared by many
|
|
25
|
+
channels.
|
|
26
|
+
- Channels are named objects. Routes refer to channel names, not raw provider
|
|
27
|
+
IDs, so a multi-founder setup remains readable.
|
|
28
|
+
- Bindings are machine-local delivery rules. A binding maps a named source
|
|
29
|
+
channel, and optionally a provider thread ID, to an agent target.
|
|
30
|
+
- Bindings are bidirectional at the product level. Inbound provider messages can
|
|
31
|
+
wake the bound agent thread, and the agent can explicitly send a message back
|
|
32
|
+
through the same binding.
|
|
33
|
+
- Binding replies use the source channel's provider sender. Discord is the first
|
|
34
|
+
registered sender, but the app service is not hardcoded to Discord-only
|
|
35
|
+
binding replies.
|
|
36
|
+
- Config setup is command-driven. Agents can create the config, upsert channels,
|
|
37
|
+
upsert JSONL fallback routes, and upsert bindings without hand-editing JSON.
|
|
38
|
+
- The relay must support both event-driven listening and one-shot read/send
|
|
39
|
+
commands.
|
|
40
|
+
|
|
41
|
+
## Non-Goals For The First Package Slice
|
|
42
|
+
|
|
43
|
+
- No hosted service.
|
|
44
|
+
- No database.
|
|
45
|
+
- No direct Codex Desktop UI automation.
|
|
46
|
+
- No hard dependency on Almanac.
|