agent-relay-plugin 0.1.0 → 0.1.6
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.
- package/.claude-plugin/plugin.json +1 -1
- package/hooks/hooks.json +0 -11
- package/hooks/poll-inbox.sh +19 -1
- package/hooks/{session-start.sh → relay-monitor.sh} +50 -27
- package/hooks/session-end.sh +11 -19
- package/monitors/monitors.json +7 -0
- package/package.json +4 -3
package/hooks/hooks.json
CHANGED
package/hooks/poll-inbox.sh
CHANGED
|
@@ -12,14 +12,26 @@ if [ -z "$RELAY_URL" ] || [ -z "$AGENT_ID" ]; then
|
|
|
12
12
|
exit 1
|
|
13
13
|
fi
|
|
14
14
|
|
|
15
|
+
# PID file so session-start.sh can find and kill us on /clear or /compact.
|
|
16
|
+
pid_file="/tmp/agent-relay-poll-${AGENT_ID}.pid"
|
|
17
|
+
cleanup() { rm -f "$pid_file"; }
|
|
18
|
+
trap cleanup EXIT
|
|
19
|
+
echo $$ > "$pid_file"
|
|
20
|
+
|
|
15
21
|
# Bootstrap cursor at current max so we only see messages arriving after session start.
|
|
16
22
|
since_id=$(curl -s "${RELAY_URL}/api/messages/cursor" 2>/dev/null | jq -r '.latestId // 0' 2>/dev/null || echo 0)
|
|
17
23
|
|
|
18
24
|
fail_streak=0
|
|
25
|
+
marked_ready=false
|
|
19
26
|
while true; do
|
|
20
27
|
hb_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
|
|
21
28
|
"${RELAY_URL}/api/agents/${AGENT_ID}/heartbeat" 2>/dev/null)
|
|
22
29
|
|
|
30
|
+
# Agent was deleted or doesn't exist — we're orphaned, exit.
|
|
31
|
+
if [ "$hb_code" = "404" ]; then
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
23
35
|
msgs=$(curl -s --fail "${RELAY_URL}/api/messages?for=${AGENT_ID}&sinceId=${since_id}&unread=true" 2>/dev/null)
|
|
24
36
|
poll_rc=$?
|
|
25
37
|
|
|
@@ -39,9 +51,15 @@ while true; do
|
|
|
39
51
|
fi
|
|
40
52
|
fail_streak=0
|
|
41
53
|
|
|
54
|
+
if [ "$marked_ready" = "false" ]; then
|
|
55
|
+
curl -s -o /dev/null -X PATCH "${RELAY_URL}/api/agents/${AGENT_ID}/ready" \
|
|
56
|
+
-H 'Content-Type: application/json' -d '{"ready":true}' 2>/dev/null
|
|
57
|
+
marked_ready=true
|
|
58
|
+
fi
|
|
59
|
+
|
|
42
60
|
count=$(echo "$msgs" | jq 'length' 2>/dev/null || echo 0)
|
|
43
61
|
if [ "$count" -gt 0 ] 2>/dev/null && [ "$count" != "0" ]; then
|
|
44
|
-
echo "$msgs" | jq -r '.[] | "\(.from) → \(.to) | \(.subject // "(no subject)"): \(.body)"'
|
|
62
|
+
echo "$msgs" | jq -r '.[] | if .type == "system" then "⚠ SYSTEM [msg:\(.id)]: \(.body)" else "[msg:\(.id)] \(.from) → \(.to) | \(.subject // "(no subject)"): \(.body)" end'
|
|
45
63
|
since_id=$(echo "$msgs" | jq '[.[].id] | max')
|
|
46
64
|
echo "$msgs" | jq -r '.[].id' | while read -r mid; do
|
|
47
65
|
curl -s -X PATCH "${RELAY_URL}/api/messages/${mid}" \
|
|
@@ -1,33 +1,53 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Agent Relay
|
|
3
|
-
# Registers this
|
|
2
|
+
# Agent Relay — self-contained native monitor
|
|
3
|
+
# Registers this session as an agent, outputs messaging context, then polls inbox.
|
|
4
|
+
# Runs automatically via monitors.json — no user interaction required.
|
|
5
|
+
# PPID = Claude Code process, stable across /clear and /compact.
|
|
4
6
|
|
|
5
7
|
RELAY_URL="${AGENT_RELAY_URL:-http://localhost:4850}"
|
|
6
|
-
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "$0")")}"
|
|
7
|
-
|
|
8
|
-
input=$(cat)
|
|
9
|
-
|
|
10
|
-
session_id=$(echo "$input" | jq -r '.session_id // empty')
|
|
11
|
-
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
|
12
|
-
model=$(echo "$input" | jq -r '.model // empty')
|
|
13
|
-
source=$(echo "$input" | jq -r '.source // empty')
|
|
14
8
|
|
|
9
|
+
# --- Compute agent identity ---
|
|
15
10
|
machine=$(hostname)
|
|
16
|
-
project=$(basename "${
|
|
17
|
-
|
|
11
|
+
project=$(basename "${PWD:-unknown}")
|
|
12
|
+
|
|
13
|
+
if [ -n "$CLAUDE_RIG_NAME" ]; then
|
|
14
|
+
rig="$CLAUDE_RIG_NAME"
|
|
15
|
+
elif [ -n "$CLAUDE_CONFIG_DIR" ]; then
|
|
16
|
+
rig=$(basename "$CLAUDE_CONFIG_DIR")
|
|
17
|
+
else
|
|
18
|
+
rig="default"
|
|
19
|
+
fi
|
|
18
20
|
|
|
19
21
|
if command -v shasum >/dev/null 2>&1; then
|
|
20
|
-
|
|
22
|
+
short_pid=$(printf '%s' "$PPID" | shasum -a 1 | head -c 6)
|
|
21
23
|
else
|
|
22
|
-
|
|
24
|
+
short_pid=$(printf '%s' "$PPID" | md5sum | head -c 6)
|
|
25
|
+
fi
|
|
26
|
+
agent_id="${machine}-${rig}-${project}-${short_pid}"
|
|
27
|
+
|
|
28
|
+
# --- Clean up previous agent from this instance ---
|
|
29
|
+
instance_state="/tmp/agent-relay-instance-${PPID}.state"
|
|
30
|
+
if [ -f "$instance_state" ]; then
|
|
31
|
+
old_agent_id=$(head -1 "$instance_state" 2>/dev/null)
|
|
32
|
+
if [ -n "$old_agent_id" ] && [ "$old_agent_id" != "$agent_id" ]; then
|
|
33
|
+
old_pid_file="/tmp/agent-relay-poll-${old_agent_id}.pid"
|
|
34
|
+
if [ -f "$old_pid_file" ]; then
|
|
35
|
+
old_pid=$(cat "$old_pid_file" 2>/dev/null)
|
|
36
|
+
if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then
|
|
37
|
+
kill "$old_pid" 2>/dev/null
|
|
38
|
+
fi
|
|
39
|
+
rm -f "$old_pid_file"
|
|
40
|
+
fi
|
|
41
|
+
curl -s -o /dev/null -X PATCH "${RELAY_URL}/api/agents/${old_agent_id}/status" \
|
|
42
|
+
-H 'Content-Type: application/json' -d '{"status":"offline"}' 2>/dev/null
|
|
43
|
+
fi
|
|
23
44
|
fi
|
|
24
|
-
agent_id
|
|
45
|
+
printf '%s\n' "$agent_id" > "$instance_state"
|
|
25
46
|
|
|
26
|
-
#
|
|
47
|
+
# --- Register agent ---
|
|
27
48
|
caps_csv="${AGENT_RELAY_CAPS:-chat}"
|
|
28
49
|
caps_json=$(echo "$caps_csv" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$"; ""))')
|
|
29
50
|
|
|
30
|
-
# Register agent card
|
|
31
51
|
reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agents" \
|
|
32
52
|
-H 'Content-Type: application/json' \
|
|
33
53
|
-d "$(jq -n \
|
|
@@ -37,18 +57,15 @@ reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agen
|
|
|
37
57
|
--arg rig "$rig" \
|
|
38
58
|
--argjson tags "$(jq -n --arg r "$rig" --arg p "$project" '[$r, $p]')" \
|
|
39
59
|
--argjson caps "$caps_json" \
|
|
40
|
-
--argjson meta "$(jq -n --arg
|
|
60
|
+
--argjson meta "$(jq -n --arg cwd "$PWD" '{cwd: $cwd}')" \
|
|
41
61
|
'{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta}'
|
|
42
62
|
)" 2>/dev/null)
|
|
63
|
+
|
|
43
64
|
if [ "$reg_code" != "201" ] && [ "$reg_code" != "200" ]; then
|
|
44
65
|
echo "warn: agent-relay register failed (HTTP ${reg_code:-none}) — ${RELAY_URL}" >&2
|
|
45
66
|
fi
|
|
46
67
|
|
|
47
|
-
#
|
|
48
|
-
unread=$(curl -s "${RELAY_URL}/api/messages?for=${agent_id}&unread=true" 2>/dev/null)
|
|
49
|
-
unread_count=$(echo "$unread" | jq 'length' 2>/dev/null || echo "0")
|
|
50
|
-
|
|
51
|
-
# Build context output
|
|
68
|
+
# --- Output context (first stdout = notification injected into conversation) ---
|
|
52
69
|
cat <<CONTEXT
|
|
53
70
|
Agent Relay active. Your agent ID: ${agent_id}
|
|
54
71
|
Relay URL: ${RELAY_URL}
|
|
@@ -66,6 +83,7 @@ To rename yourself (set a short, human-friendly label so the user can refer to y
|
|
|
66
83
|
To reply to a message (threading):
|
|
67
84
|
curl -s -X POST ${RELAY_URL}/api/messages -H 'Content-Type: application/json' -d '{"from":"${agent_id}","to":"TARGET","body":"MESSAGE","replyTo":MSG_ID}'
|
|
68
85
|
Always use replyTo when responding to a specific message — it creates a thread.
|
|
86
|
+
The inbox monitor prefixes each message with [msg:ID] — parse the ID from there.
|
|
69
87
|
|
|
70
88
|
To send a claimable task (only one agent can claim it):
|
|
71
89
|
curl -s -X POST ${RELAY_URL}/api/messages -H 'Content-Type: application/json' -d '{"from":"${agent_id}","to":"cap:review","body":"TASK","claimable":true}'
|
|
@@ -89,14 +107,19 @@ To find agents by capability:
|
|
|
89
107
|
To list online agents:
|
|
90
108
|
curl -s '${RELAY_URL}/api/agents?status=online'
|
|
91
109
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
System messages:
|
|
111
|
+
Messages prefixed with "⚠ SYSTEM" are relay-level instructions (e.g. plugin updates, restart requests).
|
|
112
|
+
When you see one, follow the instruction immediately — for example, restart the inbox monitor if asked.
|
|
95
113
|
CONTEXT
|
|
96
114
|
|
|
97
|
-
#
|
|
115
|
+
# --- Check for unread messages ---
|
|
116
|
+
unread=$(curl -s "${RELAY_URL}/api/messages?for=${agent_id}&unread=true" 2>/dev/null)
|
|
117
|
+
unread_count=$(echo "$unread" | jq 'length' 2>/dev/null || echo "0")
|
|
98
118
|
if [ "$unread_count" -gt 0 ] 2>/dev/null && [ "$unread_count" != "0" ]; then
|
|
99
119
|
echo ""
|
|
100
120
|
echo "${unread_count} unread message(s):"
|
|
101
121
|
echo "$unread" | jq -r '.[] | " [\(.from)] \(.subject // "(no subject)"): \(.body | if length > 120 then .[:120] + "..." else . end)"'
|
|
102
122
|
fi
|
|
123
|
+
|
|
124
|
+
# --- Start poll loop ---
|
|
125
|
+
exec "$(dirname "$0")/poll-inbox.sh" "$RELAY_URL" "$agent_id" 10
|
package/hooks/session-end.sh
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Agent Relay plugin — SessionEnd hook
|
|
3
|
-
# Marks this session's agent as offline.
|
|
3
|
+
# Marks this session's agent as offline using state file written by relay-monitor.
|
|
4
4
|
|
|
5
5
|
RELAY_URL="${AGENT_RELAY_URL:-http://localhost:4850}"
|
|
6
|
+
instance_state="/tmp/agent-relay-instance-${PPID}.state"
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
short_sid=$(printf '%s' "$session_id" | shasum -a 1 | head -c 6)
|
|
17
|
-
else
|
|
18
|
-
short_sid=$(printf '%s' "$session_id" | md5sum | head -c 6)
|
|
8
|
+
if [ -f "$instance_state" ]; then
|
|
9
|
+
agent_id=$(head -1 "$instance_state" 2>/dev/null)
|
|
10
|
+
if [ -n "$agent_id" ]; then
|
|
11
|
+
curl -s -X PATCH "${RELAY_URL}/api/agents/${agent_id}/status" \
|
|
12
|
+
-H 'Content-Type: application/json' \
|
|
13
|
+
-d '{"status":"offline"}' \
|
|
14
|
+
> /dev/null 2>&1 &
|
|
15
|
+
fi
|
|
16
|
+
rm -f "$instance_state"
|
|
19
17
|
fi
|
|
20
|
-
agent_id="${machine}-${rig}-${project}-${short_sid}"
|
|
21
|
-
|
|
22
|
-
curl -s -X PATCH "${RELAY_URL}/api/agents/${agent_id}/status" \
|
|
23
|
-
-H 'Content-Type: application/json' \
|
|
24
|
-
-d '{"status":"offline"}' \
|
|
25
|
-
> /dev/null 2>&1 &
|
|
26
18
|
|
|
27
19
|
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Claude Code plugin for Agent Relay — auto-registers sessions as agents and enables inter-agent messaging",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/edimuj/agent-relay.git",
|
|
11
|
-
"directory": "
|
|
11
|
+
"directory": "claude"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
".claude-plugin/",
|
|
15
|
-
"hooks/"
|
|
15
|
+
"hooks/",
|
|
16
|
+
"monitors/"
|
|
16
17
|
],
|
|
17
18
|
"keywords": [
|
|
18
19
|
"claude-code",
|