Memory HooksManager

Standalone, opt-in utility for running .praisonai/hooks.json scripts you invoke yourself

memory.HooksManager runs pre/post scripts from .praisonai/hooks.json when you call .execute() — it does not fire automatically inside an Agent.

Diagram

memory.HooksManager

📥 Your code calls\n.execute(event)

🔧 Load\n.praisonai/hooks.json

⚙️ Run hook\nscript

✅ HookResult\nblocked / stdout

Two Hook Systems

PraisonAI has two hook systems with the same short name. Pick the right one.

SystemImportAuto-fires around agent tool/LLM calls?Purpose
Live agent hooksfrom praisonaiagents.hooks import HookRegistry, HookEvent, add_hook✅ Yes — wired into Agent via hooks=Security gates, redaction, observability around the agent loop
Standalone memory.HooksManagerfrom praisonaiagents.memory import HooksManager, create_hooks_manager❌ No — you call .execute() yourself.praisonai/hooks.json-driven scripts; backs the praisonai hooks CLI
Diagram

Yes

No — my own scripts

What do you need?

Auto-fire around\nagent tool/LLM calls?

praisonaiagents.hooks\nHookRegistry + Agent(hooks=…)

memory.HooksManager\n.praisonai/hooks.json + .execute()

Quick Start

  1. Create the manager

    Use the correct import from praisonaiagents.memory:

    python
    from praisonaiagents.memory import HooksManager
    
    hooks = HooksManager(workspace_path=".")
  2. Call .execute() yourself

    Nothing calls this for you — invoke it from your own code:

    python
    result = hooks.execute("pre_write_code", context={"file_path": "main.py"})
    
    if result.blocked:
        raise RuntimeError(result.stderr or "Blocked by hook")

How It Works

The “event” is triggered by whoever calls .execute() — not by the Agent lifecycle.

Diagram
.praisonai/hooks.jsonHooksManagerYour Tool / Script.praisonai/hooks.jsonHooksManagerYour Tool / Scriptexecute(“pre_write_code”, context)load matching hookrun script (subprocess)HookResult(blocked, stdout, stderr)
StepWhat happens
You call .execute()The only trigger — the Agent never calls this
Config loadsReads .praisonai/hooks.json (lazy, on first use)
Script runsExecutes the mapped command with context env vars
Result returnsHookResult tells you if the operation was blocked

Hook Events

All ten events from the HookEvent type in praisonaiagents/memory/hooks.py:

EventWhen you’d call it
pre_read_codeBefore reading a file
post_read_codeAfter reading a file
pre_write_codeBefore writing to a file
post_write_codeAfter writing to a file
pre_run_commandBefore running a terminal command
post_run_commandAfter running a terminal command
pre_user_promptBefore processing a user prompt
post_user_promptAfter processing a user prompt
pre_mcp_tool_useBefore an MCP tool call
post_mcp_tool_useAfter an MCP tool call

HookResult

execute() returns a HookResult dataclass:

FieldTypeDefaultDescription
successbool—Whether all hooks ran without error
exit_codeint0Exit code of the last script hook
stdoutstr""Combined script stdout
stderrstr""Combined script stderr
blockedboolFalseIf True, block the operation
modified_input`str \None`NoneReplacement input, if a hook emitted one

HookConfig

Each configured hook loads into a HookConfig:

FieldTypeDefaultDescription
eventHookEvent—The event this hook responds to
commandstr—Script/command to run
timeoutint30Per-hook timeout in seconds
enabledboolTrueSkip the hook when False
block_on_failureboolFalseSet blocked=True when the script fails
pass_inputboolTruePass context as PRAISON_HOOK_* env vars

Configuration File

Configure scripts in .praisonai/hooks.json:

json
{
  "enabled": true,
  "timeout": 30,
  "hooks": {
    "pre_write_code": "./scripts/lint.sh",
    "post_write_code": "./scripts/format.sh",
    "pre_run_command": "./scripts/validate.sh"
  }
}

Generate one programmatically with create_config:

python
from praisonaiagents.memory import HooksManager

hooks = HooksManager(workspace_path=".")
hooks.create_config(
    hooks={"post_write_code": "./scripts/format.sh"},
    timeout=30,
    enabled=True,
)

The praisonai hooks list, praisonai hooks stats, and praisonai hooks init CLI commands read this same file — see CLI Hooks.


Common Patterns

Gate a write inside your own tool

The way to make HooksManager “gate” an operation is to call .execute() inside your tool — the Agent framework will not do it for you.

python
from praisonaiagents import Agent
from praisonaiagents.memory import HooksManager

hooks = HooksManager(workspace_path=".")

def safe_write(file_path: str, content: str) -> str:
    """Write a file, gated by pre_write_code hooks."""
    result = hooks.execute("pre_write_code", context={"file_path": file_path})
    if result.blocked:
        return f"Blocked: {result.stderr}"
    with open(file_path, "w") as f:
        f.write(content)
    return f"Wrote {file_path}"

agent = Agent(
    name="Writer",
    instructions="Write files using the safe_write tool.",
    tools=[safe_write],
)

agent.start("Create main.py with a hello world function")

Register a Python callable

python
from praisonaiagents.memory import HooksManager

hooks = HooksManager(workspace_path=".")
hooks.register("pre_write_code", lambda ctx: print(f"Writing {ctx['file_path']}"))

hooks.execute("pre_write_code", context={"file_path": "main.py"})

Best Practices

Use the live hooks package for automatic gating

If you want a hook to fire automatically before every tool call, use praisonaiagents.hooks with Agent(hooks=registry). memory.HooksManager never runs on its own.

Call .execute() at the exact point you want to gate

Put the .execute() call inside the tool or wrapper that performs the operation, so the hook runs at the right moment with the right context.

Keep hook scripts fast

Scripts run inline via subprocess with a per-hook timeout (default 30s). Slow scripts delay your own code.

Handle blocked results explicitly

Check result.blocked and stop the operation yourself — nothing downstream enforces it.


Related