Metadata-Version: 2.4
Name: relayforge
Version: 0.1.1
Summary: Route chat channels to agent targets.
Project-URL: Homepage, https://github.com/AlmanacCode/relayforge
Project-URL: Issues, https://github.com/AlmanacCode/relayforge/issues
Author: Rohan Sharma
License: MIT
License-File: LICENSE
Keywords: agents,automation,codex,discord,relay
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: certifi>=2024.7.4
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.8
Requires-Dist: websockets<15,>=13
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: pytest-asyncio<1,>=0.24; extra == 'dev'
Requires-Dist: pytest<9,>=8.2; extra == 'dev'
Requires-Dist: ruff<1,>=0.8; extra == 'dev'
Requires-Dist: twine<7,>=5.1; extra == 'dev'
Description-Content-Type: text/markdown

# Relayforge

Relayforge routes chat channels to agent targets.

The first provider is Discord. The first targets are a Codex app-server binding
and a Codex JSONL fallback inbox. The package is Python and PyPI-ready, with
provider-neutral core types so Slack, WhatsApp, and other agent APIs can be
added later.

## Install

After the package is published:

```bash
pip install relayforge
```

The installed CLI command is `relayforge`.

Installed wheels add a small managed Relayforge block to:

- `~/.codex/AGENTS.md`
- `~/.claude/CLAUDE.md`

Set `RELAYFORGE_SKIP_AGENT_GUIDES=1` before starting Python with the installed
package on `sys.path` to skip this.

From this checkout:

```bash
python -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
```

## Configuration

```json
{
  "channels": [
    {
      "name": "rohan-codex",
      "provider": "discord",
      "id": "1520170530148192466",
      "owner": "rohan"
    }
  ],
  "bindings": [
    {
      "name": "rohan-almanac-main",
      "source": "rohan-codex",
      "target": "codex-app-server",
      "codexThreadId": "replace-with-codex-thread-id",
      "cwd": "/Users/rohan/Desktop/Projects/almanac",
      "machine": "replace-with-hostname",
      "transport": "codex app-server --config mcp_servers={} --listen stdio://"
    }
  ],
  "routes": [
    {
      "channel": "rohan-codex",
      "target": "codex-jsonl",
      "inboxPath": ".relay/rohan-codex-inbox.jsonl"
    }
  ]
}
```

Each founder can use the same Discord bot token with a different named channel.
A binding delivers to that founder's Codex thread on the right machine. A route
keeps JSONL fallback delivery available when the direct target fails.

See [docs/channels.md](docs/channels.md) for the multi-founder pattern.
See [docs/research/codex-thread-routing-2026-06-26.md](docs/research/codex-thread-routing-2026-06-26.md)
for the Codex thread binding direction.

## Commands

```bash
export DISCORD_BOT_TOKEN=...

relayforge init --config relay.config.json
relayforge channels add discord/rohan-codex --id 1520...
relayforge connect discord/rohan-codex --thread-id 019f... --fallback-inbox .relay/rohan-codex-inbox.jsonl
relayforge check --config relay.config.json
relayforge status --config relay.config.json
relayforge listen --config relay.config.json
relayforge reply "I am looking at it now."
relayforge send --channel rohan-codex --message "hello"
relayforge read --channel rohan-codex --limit 10
```

Raw Discord channel IDs work for `read` and `send` even before a config file
exists. Named channels require the config.

The `listen` command uses Discord Gateway events. That means Discord pushes new
messages to this process instead of the process polling the channel.

`connect` is the normal agent setup command. It creates a Codex app-server
binding from a provider channel such as `discord/rohan-codex` to one Codex
thread. When `listen` receives a message for that binding, Relayforge tries to
steer the active Codex turn first; if the thread is idle, it starts a new turn.

Use these commands when you need the lower-level surface:

```bash
relayforge check --config relay.config.json --json
relayforge channels --config relay.config.json
relayforge routes --config relay.config.json
relayforge bindings --config relay.config.json
relayforge channel-set --name rohan-codex --provider discord --id 1520...
relayforge route-set --channel rohan-codex --target codex-jsonl --inbox-path .relay/rohan-codex-inbox.jsonl
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://"
relayforge codex-smoke --binding rohan-almanac-main --message "smoke test"
relayforge binding-send --binding rohan-almanac-main --message "reply in Discord"
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --limit 10
relayforge inbox --path .relay/rohan-codex-inbox.jsonl --cursor .relay/rohan.cursor.json --ack
```

By default, Discord bot-authored messages are ignored to avoid loops. Use
`--include-bots` only for controlled smoke tests or deliberate bot-to-agent
routing.

`codex-smoke` sends a synthetic message through a configured Codex app-server
binding. Use it only with a rollout-backed Codex thread that is safe to receive
a test turn.
