agent-relay-plugin 0.4.12 → 0.4.14
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/hooks/approval-gate.sh +1 -1
- package/hooks/poll-inbox.sh +88 -7
- package/hooks/relay-monitor.sh +9 -1
- package/hooks/session-end.sh +1 -0
- package/hooks/set-status.sh +2 -0
- package/package.json +1 -1
package/hooks/approval-gate.sh
CHANGED
|
@@ -71,7 +71,7 @@ if [ "$tool" = "Bash" ]; then
|
|
|
71
71
|
|
|
72
72
|
if [ "$MODE" = "read-only" ]; then
|
|
73
73
|
# Check for file-writing redirects (allow >/dev/null and fd redirects like 2>&1)
|
|
74
|
-
redirect_cleaned=$(printf '%s' "$cmd" | sed 's|[0-9]
|
|
74
|
+
redirect_cleaned=$(printf '%s' "$cmd" | sed 's|[0-9]*>[[:space:]]*/dev/null||g; s|[0-9]*>&[0-9]*||g')
|
|
75
75
|
if printf '%s' "$redirect_cleaned" | grep -qE '>>|[^a-zA-Z0-9_]>|^>'; then
|
|
76
76
|
deny "Output redirection blocked by read-only policy"
|
|
77
77
|
fi
|
package/hooks/poll-inbox.sh
CHANGED
|
@@ -18,12 +18,64 @@ fi
|
|
|
18
18
|
|
|
19
19
|
# PID file so session-start.sh can find and kill us on /clear or /compact.
|
|
20
20
|
pid_file="/tmp/agent-relay-poll-${AGENT_ID}.pid"
|
|
21
|
+
status_file="/tmp/agent-relay-status-${AGENT_ID}.state"
|
|
21
22
|
cleanup() { rm -f "$pid_file"; }
|
|
22
23
|
trap cleanup EXIT
|
|
23
24
|
echo $$ > "$pid_file"
|
|
24
25
|
|
|
26
|
+
fetch_cursor() {
|
|
27
|
+
local cursor
|
|
28
|
+
cursor=$(curl -s --fail "${auth_header_args[@]}" "${RELAY_URL}/api/messages/cursor" 2>/dev/null | jq -r '.latestId // empty' 2>/dev/null) || return 1
|
|
29
|
+
case "$cursor" in
|
|
30
|
+
''|*[!0-9]*) return 1 ;;
|
|
31
|
+
*) printf '%s\n' "$cursor" ;;
|
|
32
|
+
esac
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
current_status() {
|
|
36
|
+
local status
|
|
37
|
+
status=$(head -1 "$status_file" 2>/dev/null || true)
|
|
38
|
+
case "$status" in
|
|
39
|
+
online|idle|busy|offline) printf '%s\n' "$status" ;;
|
|
40
|
+
*) printf 'idle\n' ;;
|
|
41
|
+
esac
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
re_register() {
|
|
45
|
+
local machine rig project caps_csv caps_json approval reg_code
|
|
46
|
+
machine=$(hostname)
|
|
47
|
+
project=$(basename "${PWD:-unknown}")
|
|
48
|
+
if [ -n "$CLAUDE_RIG_NAME" ]; then rig="$CLAUDE_RIG_NAME"
|
|
49
|
+
elif [ -n "$CLAUDE_CONFIG_DIR" ]; then rig=$(basename "$CLAUDE_CONFIG_DIR")
|
|
50
|
+
else rig="default"; fi
|
|
51
|
+
caps_csv="${AGENT_RELAY_CAPS:-chat}"
|
|
52
|
+
caps_json=$(echo "$caps_csv" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$"; ""))')
|
|
53
|
+
approval="${AGENT_RELAY_APPROVAL:-open}"
|
|
54
|
+
|
|
55
|
+
reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agents" "${auth_header_args[@]}" \
|
|
56
|
+
-H 'Content-Type: application/json' \
|
|
57
|
+
-d "$(jq -n \
|
|
58
|
+
--arg id "$AGENT_ID" \
|
|
59
|
+
--arg name "$project ($rig @ $machine)" \
|
|
60
|
+
--arg machine "$machine" \
|
|
61
|
+
--arg rig "$rig" \
|
|
62
|
+
--argjson tags "$(jq -n --arg r "$rig" --arg p "$project" '[$r, $p]')" \
|
|
63
|
+
--argjson caps "$caps_json" \
|
|
64
|
+
--argjson meta "$(jq -n --arg cwd "$PWD" --arg approval "$approval" '{cwd: $cwd, approvalMode: $approval}')" \
|
|
65
|
+
'{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta}'
|
|
66
|
+
)" 2>/dev/null)
|
|
67
|
+
|
|
68
|
+
[ "$reg_code" = "200" ] || [ "$reg_code" = "201" ]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
set_status() {
|
|
72
|
+
printf '%s\n' "$1" > "$status_file"
|
|
73
|
+
curl -s -o /dev/null -X PATCH "${RELAY_URL}/api/agents/${AGENT_ID}/status" "${auth_header_args[@]}" \
|
|
74
|
+
-H 'Content-Type: application/json' -d "{\"status\":\"$1\"}" 2>/dev/null
|
|
75
|
+
}
|
|
76
|
+
|
|
25
77
|
# Bootstrap cursor at current max so we only see messages arriving after session start.
|
|
26
|
-
since_id=$(
|
|
78
|
+
since_id=$(fetch_cursor || echo 0)
|
|
27
79
|
|
|
28
80
|
fail_streak=0
|
|
29
81
|
marked_ready=false
|
|
@@ -31,15 +83,24 @@ while true; do
|
|
|
31
83
|
hb_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${auth_header_args[@]}" \
|
|
32
84
|
"${RELAY_URL}/api/agents/${AGENT_ID}/heartbeat" 2>/dev/null)
|
|
33
85
|
|
|
34
|
-
# Agent was
|
|
86
|
+
# Agent was pruned — re-register instead of exiting.
|
|
35
87
|
if [ "$hb_code" = "404" ]; then
|
|
36
|
-
|
|
88
|
+
if re_register; then
|
|
89
|
+
if since_id=$(fetch_cursor); then
|
|
90
|
+
marked_ready=false
|
|
91
|
+
hb_code="200"
|
|
92
|
+
else
|
|
93
|
+
hb_code="000"
|
|
94
|
+
fi
|
|
95
|
+
else
|
|
96
|
+
hb_code="000"
|
|
97
|
+
fi
|
|
37
98
|
fi
|
|
38
99
|
|
|
39
100
|
msgs=$(curl -s --fail "${auth_header_args[@]}" "${RELAY_URL}/api/messages?for=${AGENT_ID}&sinceId=${since_id}&unread=true" 2>/dev/null)
|
|
40
101
|
poll_rc=$?
|
|
41
102
|
|
|
42
|
-
if [ "$hb_code" != "200" ] || [ $poll_rc -ne 0 ]; then
|
|
103
|
+
if [ "$hb_code" != "200" ] && [ "$hb_code" != "404" ] || [ $poll_rc -ne 0 ]; then
|
|
43
104
|
fail_streak=$((fail_streak + 1))
|
|
44
105
|
case "$fail_streak" in
|
|
45
106
|
1) delay=2 ;;
|
|
@@ -53,23 +114,43 @@ while true; do
|
|
|
53
114
|
sleep "$delay"
|
|
54
115
|
continue
|
|
55
116
|
fi
|
|
117
|
+
|
|
118
|
+
# Recovery from failure streak — re-assert status so dashboard shows correct state.
|
|
119
|
+
if [ "$fail_streak" -gt 0 ]; then
|
|
120
|
+
set_status "$(current_status)"
|
|
121
|
+
fi
|
|
56
122
|
fail_streak=0
|
|
57
123
|
|
|
58
124
|
if [ "$marked_ready" = "false" ]; then
|
|
59
125
|
curl -s -o /dev/null -X PATCH "${RELAY_URL}/api/agents/${AGENT_ID}/ready" "${auth_header_args[@]}" \
|
|
60
126
|
-H 'Content-Type: application/json' -d '{"ready":true}' 2>/dev/null
|
|
127
|
+
set_status "idle"
|
|
61
128
|
marked_ready=true
|
|
62
129
|
fi
|
|
63
130
|
|
|
64
131
|
count=$(echo "$msgs" | jq 'length' 2>/dev/null || echo 0)
|
|
65
132
|
if [ "$count" -gt 0 ] 2>/dev/null && [ "$count" != "0" ]; then
|
|
66
|
-
echo "$msgs" | jq -
|
|
67
|
-
|
|
68
|
-
|
|
133
|
+
echo "$msgs" | jq -c '.[]' | while IFS= read -r msg; do
|
|
134
|
+
mid=$(printf '%s' "$msg" | jq -r '.id')
|
|
135
|
+
claimable=$(printf '%s' "$msg" | jq -r '.claimable // false')
|
|
136
|
+
|
|
137
|
+
if [ "$claimable" = "true" ]; then
|
|
138
|
+
claim_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
|
|
139
|
+
"${RELAY_URL}/api/messages/${mid}/claim" "${auth_header_args[@]}" \
|
|
140
|
+
-H 'Content-Type: application/json' \
|
|
141
|
+
-d "{\"agentId\":\"${AGENT_ID}\"}" 2>/dev/null)
|
|
142
|
+
if [ "$claim_code" != "200" ]; then
|
|
143
|
+
continue
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
printf '%s' "$msg" | jq -r 'if .type == "system" then "⚠ SYSTEM [msg:\(.id)]: \(.body)" else "[msg:\(.id)] \(.from) → \(.to) | \(.subject // "(no subject)"): \(.body)" end'
|
|
148
|
+
|
|
69
149
|
curl -s -X PATCH "${RELAY_URL}/api/messages/${mid}" "${auth_header_args[@]}" \
|
|
70
150
|
-H 'Content-Type: application/json' \
|
|
71
151
|
-d "{\"readBy\":\"${AGENT_ID}\"}" > /dev/null 2>&1 || true
|
|
72
152
|
done
|
|
153
|
+
since_id=$(echo "$msgs" | jq '[.[].id] | max')
|
|
73
154
|
fi
|
|
74
155
|
|
|
75
156
|
sleep "$INTERVAL"
|
package/hooks/relay-monitor.sh
CHANGED
|
@@ -53,6 +53,7 @@ printf '%s\n' "$agent_id" > "$instance_state"
|
|
|
53
53
|
# --- Register agent ---
|
|
54
54
|
caps_csv="${AGENT_RELAY_CAPS:-chat}"
|
|
55
55
|
caps_json=$(echo "$caps_csv" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$"; ""))')
|
|
56
|
+
approval="${AGENT_RELAY_APPROVAL:-open}"
|
|
56
57
|
|
|
57
58
|
reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agents" "${auth_header_args[@]}" \
|
|
58
59
|
-H 'Content-Type: application/json' \
|
|
@@ -63,7 +64,7 @@ reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agen
|
|
|
63
64
|
--arg rig "$rig" \
|
|
64
65
|
--argjson tags "$(jq -n --arg r "$rig" --arg p "$project" '[$r, $p]')" \
|
|
65
66
|
--argjson caps "$caps_json" \
|
|
66
|
-
--argjson meta "$(jq -n --arg cwd "$PWD" '{cwd: $cwd}')" \
|
|
67
|
+
--argjson meta "$(jq -n --arg cwd "$PWD" --arg approval "$approval" '{cwd: $cwd, approvalMode: $approval}')" \
|
|
67
68
|
'{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta}'
|
|
68
69
|
)" 2>/dev/null)
|
|
69
70
|
|
|
@@ -81,9 +82,16 @@ elif [ "$server_version" != "$PLUGIN_VERSION" ]; then
|
|
|
81
82
|
fi
|
|
82
83
|
|
|
83
84
|
# --- Output context (first stdout = notification injected into conversation) ---
|
|
85
|
+
approval_note=""
|
|
86
|
+
case "$approval" in
|
|
87
|
+
guarded) approval_note=" — destructive ops (rm, mv, chmod, kill, force-push) are blocked" ;;
|
|
88
|
+
read-only) approval_note=" — observe/analyze/report only, no file writes or mutation commands" ;;
|
|
89
|
+
esac
|
|
90
|
+
|
|
84
91
|
cat <<CONTEXT
|
|
85
92
|
Agent Relay active. Your agent ID: ${agent_id}
|
|
86
93
|
Relay URL: ${RELAY_URL} | Server: ${server_version:-unknown} | Plugin: ${PLUGIN_VERSION}
|
|
94
|
+
Approval mode: ${approval}${approval_note}
|
|
87
95
|
|
|
88
96
|
To send a message:
|
|
89
97
|
curl -s -X POST ${RELAY_URL}/api/messages${auth_header_example} -H 'Content-Type: application/json' -d '{"from":"${agent_id}","to":"TARGET","body":"MESSAGE"}'
|
package/hooks/session-end.sh
CHANGED
|
@@ -12,6 +12,7 @@ instance_state="/tmp/agent-relay-instance-${PPID}.state"
|
|
|
12
12
|
if [ -f "$instance_state" ]; then
|
|
13
13
|
agent_id=$(head -1 "$instance_state" 2>/dev/null)
|
|
14
14
|
if [ -n "$agent_id" ]; then
|
|
15
|
+
printf 'offline\n' > "/tmp/agent-relay-status-${agent_id}.state"
|
|
15
16
|
curl -s -X PATCH "${RELAY_URL}/api/agents/${agent_id}/status" "${auth_header_args[@]}" \
|
|
16
17
|
-H 'Content-Type: application/json' \
|
|
17
18
|
-d '{"status":"offline"}' \
|
package/hooks/set-status.sh
CHANGED
|
@@ -24,6 +24,8 @@ if [ -z "$agent_id" ]; then
|
|
|
24
24
|
exit 0
|
|
25
25
|
fi
|
|
26
26
|
|
|
27
|
+
printf '%s\n' "$status" > "/tmp/agent-relay-status-${agent_id}.state"
|
|
28
|
+
|
|
27
29
|
curl -s -o /dev/null -X PATCH "${RELAY_URL}/api/agents/${agent_id}/status" "${auth_header_args[@]}" \
|
|
28
30
|
-H 'Content-Type: application/json' \
|
|
29
31
|
-d "{\"status\":\"${status}\"}" \
|
package/package.json
CHANGED