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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "agent-relay",
3
3
  "description": "Client connector for Agent Relay — auto-registers Claude Code sessions as agents and enables inter-agent messaging via a lightweight HTTP message bus",
4
- "version": "0.1.0"
4
+ "version": "0.1.6"
5
5
  }
package/hooks/hooks.json CHANGED
@@ -1,16 +1,5 @@
1
1
  {
2
2
  "hooks": {
3
- "SessionStart": [
4
- {
5
- "hooks": [
6
- {
7
- "type": "command",
8
- "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh\"",
9
- "timeout": 15
10
- }
11
- ]
12
- }
13
- ],
14
3
  "SessionEnd": [
15
4
  {
16
5
  "hooks": [
@@ -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 plugin SessionStart hook
3
- # Registers this Claude Code session as an agent and injects messaging context.
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 "${cwd:-unknown}")
17
- rig="${CLAUDE_RIG_NAME:-default}"
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
- short_sid=$(printf '%s' "$session_id" | shasum -a 1 | head -c 6)
22
+ short_pid=$(printf '%s' "$PPID" | shasum -a 1 | head -c 6)
21
23
  else
22
- short_sid=$(printf '%s' "$session_id" | md5sum | head -c 6)
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="${machine}-${rig}-${project}-${short_sid}"
45
+ printf '%s\n' "$agent_id" > "$instance_state"
25
46
 
26
- # Capabilities: AGENT_RELAY_CAPS="chat,review,edit" (comma-separated) → JSON array
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 sid "$session_id" --arg model "$model" --arg src "$source" --arg cwd "$cwd" '{sessionId: $sid, model: $model, source: $src, cwd: $cwd}')" \
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
- # Check for unread messages
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
- IMPORTANT: Start a persistent Monitor NOW to listen for incoming messages:
93
- Monitor({ command: "${PLUGIN_ROOT}/hooks/poll-inbox.sh ${RELAY_URL} ${agent_id} 10", description: "Agent Relay inbox for ${agent_id}", persistent: true })
94
- You MUST start this Monitor immediately at session start. Do not wait for the user to ask.
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
- # Append unread messages if any
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
@@ -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
- input=$(cat)
8
- cwd=$(echo "$input" | jq -r '.cwd // empty')
9
- session_id=$(echo "$input" | jq -r '.session_id // empty')
10
-
11
- machine=$(hostname)
12
- project=$(basename "${cwd:-unknown}")
13
- rig="${CLAUDE_RIG_NAME:-default}"
14
-
15
- if command -v shasum >/dev/null 2>&1; then
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
@@ -0,0 +1,7 @@
1
+ [
2
+ {
3
+ "name": "agent-relay-inbox",
4
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/relay-monitor.sh\"",
5
+ "description": "Agent Relay inbox"
6
+ }
7
+ ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-plugin",
3
- "version": "0.1.0",
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": "plugin"
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",