diff --git a/hooks/enforce-comms.py b/hooks/enforce-comms.py new file mode 100644 index 0000000..77e676e --- /dev/null +++ b/hooks/enforce-comms.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +""" +Enforce comms Delegation Hook - PreToolUse hook that blocks central from posting directly. + +This hook enforces the architectural pattern where central (the main agent) delegates +all public communications to the comms subagent. When central attempts to run +posting-related commands, this hook blocks them with a helpful redirect message. + +Exit codes: + 0 - Allow the action to proceed + 2 - Block the action and send stderr message to the agent +""" + +import sys +import json +import os + +# Agent IDs +CENTRAL_AGENT_ID = "agent-c770d1c8-510e-4414-be36-c9ebd95a7758" +COMMS_AGENT_ID = "agent-a856f614-7654-44ba-a35f-c817d477dded" + +# Patterns in Bash commands that indicate posting activity +POSTING_PATTERNS = [ + "tools.thread", + "tools.devlog", + "tools.responder send", + "tools.respond", + "agent.py post", + "tools.blog publish", + "tools.cognition write", + "create_post", +] + + +def main(): + """Process PreToolUse hook input.""" + try: + input_data = json.load(sys.stdin) + except Exception: + # If we can't parse input, allow the action + sys.exit(0) + + event_type = input_data.get("event_type") + tool_name = input_data.get("tool_name", "") + tool_input = input_data.get("tool_input", {}) + + # Only process PreToolUse events + if event_type != "PreToolUse": + sys.exit(0) + + # Only check Bash commands + if tool_name != "Bash": + sys.exit(0) + + # Check current agent + current_agent = os.environ.get("LETTA_AGENT_ID", "") + + # If this is comms or any other agent (not central), allow + if current_agent != CENTRAL_AGENT_ID: + sys.exit(0) + + # Get the command being run + command = tool_input.get("command", "") + + # Check if command matches any posting pattern + for pattern in POSTING_PATTERNS: + if pattern in command.lower(): + # Block the action with a helpful message + message = f"""BLOCKED: Direct posting not allowed for central agent. + +You attempted to run a posting command directly: + {command[:100]}{'...' if len(command) > 100 else ''} + +Per architectural rules, central must delegate all public communications to comms. + +To post content, use the comms subagent: + Task( + agent_id="{COMMS_AGENT_ID}", + subagent_type="general-purpose", + prompt="Post the following: " + ) + +If comms fails, message comms directly to debug/retry. Do NOT bypass by posting directly. +""" + print(message, file=sys.stderr) + sys.exit(2) + + # No posting pattern detected, allow the command + sys.exit(0) + + +if __name__ == "__main__": + main()