relay-claude-channel 0.2.1

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.
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "relay",
3
+ "description": "Relay channel for Claude Code. Message your Claude Code session from your Relay agent conversation, with remote Allow/Deny permission relay. Run /relay:configure to set up.",
4
+ "version": "0.2.0",
5
+ "author": {
6
+ "name": "Relay"
7
+ },
8
+ "keywords": [
9
+ "relay",
10
+ "messaging",
11
+ "channel",
12
+ "mcp"
13
+ ],
14
+ "mcpServers": {
15
+ "relay": {
16
+ "command": "node",
17
+ "args": [
18
+ "${CLAUDE_PLUGIN_ROOT}/runtime/server.mjs"
19
+ ]
20
+ }
21
+ },
22
+ "channels": [
23
+ {
24
+ "server": "relay"
25
+ }
26
+ ]
27
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Companion Inc.
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.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # Relay channel for Claude Code
2
+
3
+ Message a running Claude Code session through your Relay agent conversation.
4
+ The plugin is a self-contained MCP stdio server for Claude Code's experimental
5
+ [channels contract](https://code.claude.com/docs/en/channels-reference).
6
+
7
+ - Owner-authenticated Relay messages enter the session as
8
+ `notifications/claude/channel` events.
9
+ - The `reply` tool sends a logical message back with retry-safe idempotency.
10
+ - Permission prompts can be reviewed and denied from Relay. Remote Allow is
11
+ available only when Claude supplies a complete, verifiable tool-input JSON;
12
+ otherwise approval remains local.
13
+
14
+ ## Requirements
15
+
16
+ - Node.js 20.11 or newer
17
+ - Claude Code with channel support (channels remain a research preview)
18
+ - A Relay agent and Agent Token
19
+ - No webhook enabled for that agent: Relay permits one event consumer, so
20
+ long-polling and webhook delivery are mutually exclusive
21
+
22
+ ## Install
23
+
24
+ Install the published CLI, pair once, and install its bundled local
25
+ marketplace:
26
+
27
+ ```sh
28
+ npm install -g @relaymessenger/cli
29
+ relaymessenger pair
30
+ relaymessenger install-claude
31
+ ```
32
+
33
+ `install-claude` strictly validates the bundled source, copies it to a stable
34
+ content-addressed directory under the paired account's private Relay runtime,
35
+ registers the local `relaymessenger-bundled` marketplace, and installs
36
+ `relay@relaymessenger-bundled`. It does not depend on this GitHub repository or on
37
+ the npm package remaining at its original install path. The installed plugin
38
+ already contains `runtime/server.mjs` with all runtime dependencies bundled;
39
+ do not locate a plugin cache or run `npm install` after installation.
40
+
41
+ ## Configure
42
+
43
+ The install command also configures the channel without exposing the token:
44
+
45
+ ```text
46
+ relaymessenger install-claude
47
+ ```
48
+
49
+ That command writes the paired token, API origin, and owner pin to the
50
+ platform-equivalent of:
51
+
52
+ ```text
53
+ ~/.claude/channels/relay/.env
54
+ ```
55
+
56
+ with owner-only permissions and these values:
57
+
58
+ ```dotenv
59
+ RELAY_AGENT_TOKEN=<your agent token>
60
+ RELAY_BASE_URL=https://api.relayapp.im
61
+ # Optional explicit owner pin. Otherwise GET /v1/agents/me must return one.
62
+ #RELAY_OWNER_USER_ID=usr_...
63
+ # Optional stable session namespace. Defaults to the Claude project directory.
64
+ #RELAY_CHANNEL_SESSION_ID=my-repository
65
+ # Explicit opt-in for a private agent only: trust the first user sender.
66
+ #RELAY_ALLOW_TOFU=1
67
+ ```
68
+
69
+ The file is mode 600/current-user-only. The command never prints the token and
70
+ refuses to overwrite a different existing channel identity. Run
71
+ `/relay:configure` for verification or for manual setup when relaymessenger is not
72
+ available and you already have a token through another secure route.
73
+
74
+ `RELAY_BASE_URL` must be an HTTPS origin with no path, query, fragment, or
75
+ embedded credentials. Plain HTTP is accepted only for `localhost`,
76
+ `127.0.0.1`, or `::1` development servers.
77
+
78
+ Verify without printing the token:
79
+
80
+ ```text
81
+ node <installed-plugin-directory>/runtime/server.mjs --check
82
+ ```
83
+
84
+ ## Run
85
+
86
+ Custom channels require Claude Code's development-channel flag during the
87
+ research preview:
88
+
89
+ ```text
90
+ claude --dangerously-load-development-channels plugin:relay@relaymessenger-bundled
91
+ ```
92
+
93
+ Use `server:relay` instead when registered as a bare MCP server.
94
+
95
+ Only one live Claude session may consume an agent's event stream. A second
96
+ session fails closed instead of stealing the long-poll consumer. Use a
97
+ different Relay agent when two sessions must receive messages concurrently.
98
+
99
+ ## Delivery and retry contract
100
+
101
+ Channel notifications are unacknowledged at the Claude transport layer. This
102
+ plugin therefore stages each inbound event in an atomic local ledger before
103
+ notifying Claude and includes `delivery_id="evt_..."` on the channel tag.
104
+ Claude calls the `acknowledge` tool after fully handling it. Until then, the
105
+ delivery is re-notified every 30 seconds and replayed after a channel restart.
106
+ This is **at-least-once**, not exactly-once, delivery.
107
+
108
+ A crash after an external side effect but before acknowledgement can replay the
109
+ request. Before repeating a deploy, deletion, payment, shell command, or other
110
+ non-idempotent action, reconcile whether it already succeeded. The bridge
111
+ cannot make arbitrary tools exactly-once.
112
+
113
+ Outbound `reply` calls require a `send_id`. Reuse the same `send_id`,
114
+ conversation, and text for an unknown-outcome retry; use a new `send_id` for an
115
+ intentional repeat. The mapping is persisted before the HTTP request, and
116
+ reusing an id with different content is rejected.
117
+
118
+ ## Permission safety and data boundary
119
+
120
+ Claude's channel permission notification exposes `input_preview`, documented
121
+ as JSON truncated at 200 characters. The plugin no longer truncates or folds
122
+ that field: it sends every character it receives and makes invisible controls
123
+ visible. When the value is shorter than the truncation boundary and parses as
124
+ complete JSON, the Relay card offers Allow and Deny. Otherwise it clearly says
125
+ the input may be incomplete, offers only Deny, and requires local-terminal
126
+ review to approve. This prevents a hidden destructive suffix from being
127
+ approved remotely.
128
+
129
+ Permission cards cross a data boundary: tool names, descriptions, shell
130
+ commands, local paths, and prefixes or complete contents supplied in the input
131
+ are uploaded to the configured Relay API and retained in Relay message history.
132
+ Do not enable permission relay for repositories or commands whose details must
133
+ not leave the machine. The Agent Token itself remains only in the local `.env`.
134
+
135
+ Only the agent owner's Relay user id can inject messages or verdicts. The owner
136
+ comes from `RELAY_OWNER_USER_ID` or `GET /v1/agents/me`; without one, startup
137
+ fails closed unless `RELAY_ALLOW_TOFU=1` was explicitly set. TOFU is suitable
138
+ only for an agent no one else can message.
139
+
140
+ ## Durable state
141
+
142
+ State lives below `~/.claude/channels/relay/state/`, namespaced first by the
143
+ canonical API origin and Relay agent id. The account directory contains:
144
+
145
+ - `consumer-state.json` for the shared cursor and TOFU owner pin
146
+ - `consumer-ledger.json` for unacknowledged deliveries and recent event ids
147
+
148
+ Those two files are account-scoped so a later Claude session inherits the
149
+ consumer position instead of replaying retained history. Each hashed session
150
+ subdirectory separately contains `routing.json` and `session-ledger.json` for
151
+ last-conversation routing, permission registrations, and logical outbound
152
+ sends. A new session cannot answer an old session's permission card.
153
+
154
+ Writes use owner-only files and atomic rename. If consumer cursor state or
155
+ either security-critical ledger is corrupt, startup fails closed and preserves
156
+ a block marker plus the quarantined file instead of resetting the cursor or
157
+ silently replaying old events. Session routing state may be quarantined and
158
+ reset because it does not guard event delivery or external side effects.
159
+
160
+ ## Development
161
+
162
+ ```text
163
+ npm ci
164
+ npm run check
165
+ npm test
166
+ npm run build
167
+ npm run pack:smoke
168
+ ```
169
+
170
+ `npm run build` produces the checked-in, self-contained
171
+ `runtime/server.mjs`. `npm pack` runs that build again through `prepack`. The
172
+ root release checks also run Claude Code's pinned `plugin validate --strict`
173
+ against both the plugin and marketplace before npm publication.
@@ -0,0 +1,71 @@
1
+ ---
2
+ description: Configure and verify the self-contained Relay channel
3
+ ---
4
+
5
+ Configure Relay without asking the user to paste or echo a secret in chat.
6
+
7
+ 1. Use the paired relaymessenger flow. Ask the user to run these locally if needed:
8
+
9
+ ```text
10
+ npm install -g @relaymessenger/cli
11
+ relaymessenger pair
12
+ relaymessenger install-claude
13
+ ```
14
+
15
+ `install-claude` installs the bundled local marketplace as
16
+ `relay@relaymessenger-bundled`, then copies the paired token, API origin, and
17
+ pinned owner into the channel `.env` with current-user-only permissions. It
18
+ never prints the token and refuses to overwrite a different configured
19
+ channel identity.
20
+
21
+ 2. Determine the user's channel directory using their platform conventions:
22
+ `~/.claude/channels/relay` on macOS/Linux or
23
+ `%USERPROFILE%\.claude\channels\relay` on Windows. Create it with access
24
+ restricted to the current user.
25
+
26
+ Verify that `.env` exists. Do not read or display its contents.
27
+
28
+ 3. If relaymessenger is unavailable and the user already obtained an Agent Token
29
+ through another secure route, they may create `.env` themselves with
30
+ current-user-only access:
31
+
32
+ ```dotenv
33
+ RELAY_AGENT_TOKEN=
34
+ RELAY_BASE_URL=https://api.relayapp.im
35
+ #RELAY_OWNER_USER_ID=usr_...
36
+ #RELAY_CHANNEL_SESSION_ID=my-repository
37
+ #RELAY_ALLOW_TOFU=1
38
+ ```
39
+
40
+ `RELAY_ALLOW_TOFU=1` is an explicit fallback only for an agent no one else
41
+ can message. Normally the owner comes from `GET /v1/agents/me`.
42
+
43
+ Never request, print, or place the token in a command argument.
44
+
45
+ 4. Do not run `npm install`. The installed plugin's
46
+ `runtime/server.mjs` already contains its runtime dependencies.
47
+
48
+ 5. After the user confirms the file is ready, run this from the installed
49
+ plugin directory using a platform-native path:
50
+
51
+ ```text
52
+ node runtime/server.mjs --check
53
+ ```
54
+
55
+ Report only success, agent id, or the sanitized error. Never display the
56
+ `.env` file or token. The check also rejects non-HTTPS remote origins.
57
+
58
+ 6. Explain how to start the research-preview channel:
59
+
60
+ ```text
61
+ claude --dangerously-load-development-channels plugin:relay@relaymessenger-bundled
62
+ ```
63
+
64
+ Use `server:relay` for a bare MCP registration. The agent must not have a
65
+ webhook enabled, and only one live Claude session can consume that agent.
66
+
67
+ 7. Ask the user to message the agent from Relay. Explain that messages are
68
+ delivered at least once until Claude acknowledges them. Permission cards
69
+ upload the displayed tool details to Relay history; an incomplete
70
+ 200-character Claude preview can be denied remotely but must be approved at
71
+ the local terminal.
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "relay-claude-channel",
3
+ "version": "0.2.1",
4
+ "type": "module",
5
+ "description": "Relay channel for Claude Code. Bridges a Relay agent conversation into a Claude Code session over the experimental claude/channel MCP contract.",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/relaymessenger/Relay-SDK.git",
13
+ "directory": "integrations/claude-code"
14
+ },
15
+ "engines": {
16
+ "node": ">=20.11"
17
+ },
18
+ "files": [
19
+ ".claude-plugin/",
20
+ "commands/",
21
+ "runtime/",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "build": "node scripts/build.mjs",
27
+ "check": "npm run typecheck",
28
+ "prepack": "npm run build",
29
+ "start": "node runtime/server.mjs",
30
+ "dev": "tsx server.ts",
31
+ "pack:smoke": "node scripts/pack-smoke.mjs",
32
+ "test": "node scripts/test.mjs",
33
+ "typecheck": "tsc --noEmit",
34
+ "verify": "npm run typecheck && npm test && npm run build"
35
+ },
36
+ "devDependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.30.0",
38
+ "@types/node": "^26.2.0",
39
+ "esbuild": "^0.28.2",
40
+ "tsx": "^4.23.11",
41
+ "typescript": "^7.0.2",
42
+ "zod": "4.4.3"
43
+ }
44
+ }