agent-relay-plugin 0.4.10 → 0.4.12

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.4.10"
4
+ "version": "0.4.11"
5
5
  }
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ # Agent Relay plugin — approval gate for PreToolUse and PermissionDenied hooks.
3
+ # Enforces AGENT_RELAY_APPROVAL policy: open (default), guarded, read-only.
4
+
5
+ MODE="${AGENT_RELAY_APPROVAL:-open}"
6
+ [ "$MODE" = "open" ] && exit 0
7
+
8
+ input=$(cat)
9
+ event=$(printf '%s' "$input" | jq -r '.hook_event_name')
10
+
11
+ # --- PermissionDenied: allow retry so the model adapts ---
12
+ if [ "$event" = "PermissionDenied" ]; then
13
+ jq -n '{hookSpecificOutput:{hookEventName:"PermissionDenied",retry:true}}'
14
+ exit 0
15
+ fi
16
+
17
+ tool=$(printf '%s' "$input" | jq -r '.tool_name')
18
+
19
+ # --- Always allowed: read-only and orchestration tools ---
20
+ case "$tool" in
21
+ Read|Grep|Glob|WebFetch|WebSearch|Agent|Monitor|ToolSearch|AskUserQuestion|Skill|ScheduleWakeup|CronList|EnterPlanMode|ExitPlanMode)
22
+ exit 0 ;;
23
+ Task*|*McpResource*)
24
+ exit 0 ;;
25
+ mcp__*)
26
+ exit 0 ;;
27
+ esac
28
+
29
+ deny() {
30
+ jq -n --arg r "$1" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}'
31
+ exit 0
32
+ }
33
+
34
+ # --- read-only: deny mutation tools ---
35
+ if [ "$MODE" = "read-only" ]; then
36
+ case "$tool" in
37
+ Write|Edit|NotebookEdit|CronCreate|CronDelete|PushNotification|RemoteTrigger|EnterWorktree|ExitWorktree)
38
+ deny "${tool} blocked by read-only policy" ;;
39
+ esac
40
+ fi
41
+
42
+ # --- Bash command analysis ---
43
+ if [ "$tool" = "Bash" ]; then
44
+ cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // ""')
45
+
46
+ if [ "$MODE" = "guarded" ]; then
47
+ # Denylist: block destructive patterns anywhere in the command
48
+ if printf '%s' "$cmd" | grep -qE '(^|[;&|]\s*)\s*(rm|rmdir)\b'; then
49
+ deny "rm/rmdir blocked by guarded policy"
50
+ fi
51
+ if printf '%s' "$cmd" | grep -qE '(^|[;&|]\s*)\s*mv\b'; then
52
+ deny "mv blocked by guarded policy (use cp instead)"
53
+ fi
54
+ if printf '%s' "$cmd" | grep -qE '(^|[;&|]\s*)\s*(chmod|chown|chattr)\b'; then
55
+ deny "Permission change blocked by guarded policy"
56
+ fi
57
+ if printf '%s' "$cmd" | grep -qE '(^|[;&|]\s*)\s*(kill|pkill|killall)\b'; then
58
+ deny "Process signal blocked by guarded policy"
59
+ fi
60
+ if printf '%s' "$cmd" | grep -qE '(^|[;&|]\s*)\s*(dd|mkfs|truncate|shred)\b'; then
61
+ deny "Destructive command blocked by guarded policy"
62
+ fi
63
+ if printf '%s' "$cmd" | grep -qE '(^|[;&|]\s*)\s*sudo\b'; then
64
+ deny "sudo blocked by guarded policy"
65
+ fi
66
+ if printf '%s' "$cmd" | grep -qE 'git\s+(reset\s+--hard|push\s+.*(-f\b|--force)|clean\s+-[a-z]*f)'; then
67
+ deny "Destructive git operation blocked by guarded policy"
68
+ fi
69
+ exit 0
70
+ fi
71
+
72
+ if [ "$MODE" = "read-only" ]; then
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]*>/dev/null||g; s|[0-9]*>&[0-9]*||g')
75
+ if printf '%s' "$redirect_cleaned" | grep -qE '>>|[^a-zA-Z0-9_]>|^>'; then
76
+ deny "Output redirection blocked by read-only policy"
77
+ fi
78
+
79
+ # Allowlist: check first command word
80
+ first_word=$(printf '%s' "$cmd" | sed 's/^[[:space:]]*//' | awk '{print $1}')
81
+
82
+ case "$first_word" in
83
+ # Filesystem reads
84
+ ls|cat|head|tail|less|more|file|stat|tree|du|df|readlink|realpath|basename|dirname)
85
+ exit 0 ;;
86
+ # Text processing
87
+ wc|sort|uniq|diff|comm|tr|cut|paste|column|fmt|fold|tac|rev|nl|seq|xargs)
88
+ exit 0 ;;
89
+ # Search
90
+ find|fd|grep|egrep|fgrep|rg|ag|ast-grep)
91
+ exit 0 ;;
92
+ # Shell builtins / info
93
+ echo|printf|pwd|which|whoami|date|env|printenv|uname|id|hostname|type|true|false|test|\[)
94
+ exit 0 ;;
95
+ # System info
96
+ ps|top|htop|uptime|free|lsof|netstat|ss|ip|ifconfig)
97
+ exit 0 ;;
98
+ # Data processing
99
+ jq|yq|bc|expr|base64|xxd|hexdump|od|strings|md5sum|sha256sum|sha1sum|shasum)
100
+ exit 0 ;;
101
+ # Network reads
102
+ curl|wget|dig|nslookup|ping|traceroute|ssh)
103
+ exit 0 ;;
104
+ # Git — subcommand check
105
+ git)
106
+ git_sub=$(printf '%s' "$cmd" | sed 's/^[[:space:]]*git[[:space:]]*//' | awk '{print $1}')
107
+ case "$git_sub" in
108
+ status|log|diff|show|branch|remote|tag|rev-parse|config|shortlog|reflog|blame|describe|ls-files|ls-tree|cat-file|rev-list|for-each-ref|name-rev|merge-base|version)
109
+ exit 0 ;;
110
+ stash)
111
+ stash_action=$(printf '%s' "$cmd" | sed 's/.*stash[[:space:]]*//' | awk '{print $1}')
112
+ case "$stash_action" in
113
+ list|show|"") exit 0 ;;
114
+ *) deny "git stash ${stash_action} blocked by read-only policy" ;;
115
+ esac ;;
116
+ *)
117
+ deny "git ${git_sub} blocked by read-only policy" ;;
118
+ esac ;;
119
+ # Everything else denied
120
+ *)
121
+ deny "Command '${first_word}' not in read-only allowlist" ;;
122
+ esac
123
+ fi
124
+ fi
125
+
126
+ # Unknown tools: guarded → allow, read-only → deny
127
+ if [ "$MODE" = "read-only" ]; then
128
+ deny "${tool} not in read-only allowlist"
129
+ fi
130
+
131
+ exit 0
package/hooks/hooks.json CHANGED
@@ -1,5 +1,27 @@
1
1
  {
2
2
  "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/approval-gate.sh\"",
9
+ "timeout": 5
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PermissionDenied": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/approval-gate.sh\"",
20
+ "timeout": 5
21
+ }
22
+ ]
23
+ }
24
+ ],
3
25
  "UserPromptSubmit": [
4
26
  {
5
27
  "hooks": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-plugin",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
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": "AGPL-3.0-or-later",