Skip to content

Require on_mutation hook when class has read_only=False primitives #2

@rajkumar42

Description

@rajkumar42

Summary

If a PlanExecute subclass defines primitives with read_only=False (write/mutating operations), but does not provide an on_mutation hook in its config, instantiation should fail. This enforces security by default — write operations must have explicit authorization.

Problem

Currently, if you define a class with write primitives:

class MyAgent(PlanExecute):
    @primitive(read_only=False)
    def delete_user(self, user_id: str) -> bool:
        # Dangerous operation!
        return db.delete(user_id)

agent = MyAgent()  # ✅ Works, but no safety check!
agent.run("delete user 123")  # ❌ Executes silently without confirmation

The on_mutation hook exists but is optional. If not set, write operations execute without any prompt or confirmation.

Expected Behavior

# ❌ Should FAIL at instantiation — has write primitives but no hook
agent = MyAgent()

# ✅ Works — developer explicitly handles mutations
agent = MyAgent(config=PlanExecuteConfig(
    on_mutation=lambda info: None  # Allow all (explicit choice)
))

# ✅ Works — developer prompts for confirmation  
def confirm_mutation(info: PendingMutationInfo) -> str | None:
    if input(f"Allow {info.method_name}? [y/n]: ") == "y":
        return None  # Allow
    return "User rejected"  # Block

agent = MyAgent(config=PlanExecuteConfig(on_mutation=confirm_mutation))

Implementation Hints

  1. In PlanExecute.__init__, check if any primitives have read_only=False
  2. If yes, verify that config.on_mutation is not None
  3. If no hook is set, raise an error with a clear message explaining why
def __init__(self, ...):
    ...
    # Check for unprotected write primitives
    has_write_primitives = any(
        not getattr(m, "__primitive_read_only__", True)
        for _, m in self._get_primitive_methods()
    )
    if has_write_primitives and self.config.on_mutation is None:
        raise ValueError(
            "This agent has write primitives (read_only=False) but no on_mutation hook. "
            "You must provide an on_mutation callback to handle write operations safely. "
            "See docs for examples."
        )

Acceptance Criteria

  • Add validation in PlanExecute.__init__
  • Raise descriptive error when write primitives exist without hook
  • Add unit tests for both failure and success cases
  • Update docstrings with security guidance

Files to Look At

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions