heaven_base.hooks.default_hooks

Default hooks for Heaven agents — skill injection via hook system.

 1"""Default hooks for Heaven agents — skill injection via hook system."""
 2from ..baseheavenagent import HookPoint, HookContext, HookRegistry
 3
 4
 5def make_skill_description_hook(skillset_name: str, agent_id: str = None):
 6    """BEFORE_SYSTEM_PROMPT hook: injects agent-scoped skill descriptions into system prompt.
 7
 8    Uses SkillManager with agent_id for per-agent equipped state.
 9    Equips the persona's skillset on first call, then reads from agent-scoped equipped list.
10    """
11    _initialized = [False]
12
13    def _inject_skill_descriptions(ctx: HookContext):
14        try:
15            import re
16            from skill_manager.core import SkillManager
17            manager = SkillManager(agent_id=agent_id)
18
19            # First call: equip the persona's skillset into agent-scoped state
20            if not _initialized[0] and skillset_name:
21                manager.equip_skillset(skillset_name)
22                _initialized[0] = True
23
24            equipped = manager.list_equipped()
25            if not equipped:
26                return
27
28            skill_block = "\n\n<EQUIPPED_SKILLS>\n"
29            for s in equipped:
30                desc = s.get("description", "")
31                if desc:
32                    skill_block += f"- {s['name']}: {desc}\n"
33            skill_block += "</EQUIPPED_SKILLS>"
34
35            # Strip any previous injection, then append
36            current = ctx.data.get("system_prompt", ctx.prompt or "")
37            current = re.sub(r'\n*<EQUIPPED_SKILLS>.*?</EQUIPPED_SKILLS>', '', current, flags=re.DOTALL)
38            ctx.data["system_prompt"] = current + skill_block
39        except Exception:
40            pass  # Skill injection is best-effort, never block agent
41    return _inject_skill_descriptions
42
43
44def make_skill_identity_hook(agent_name: str):
45    """BEFORE_TOOL_CALL hook: injects agent identity into sancrev treeshell skill calls.
46
47    When agent calls sancrev treeshell with skill-related actions (equip, list_equipped, etc.),
48    this hook injects the agent_name so skillmanager tracks per-agent state.
49    """
50    SKILL_ACTIONS = {"equip", "unequip", "list_equipped", "get_equipped_content", "unequip_all"}
51
52    def _inject_identity(ctx: HookContext):
53        tool_name = ctx.tool_name or ""
54        tool_args = ctx.tool_args or {}
55        # Only intercept sancrev treeshell calls
56        if "treeshell" not in tool_name.lower() and "sancrev" not in tool_name.lower():
57            return
58        command = tool_args.get("command", "")
59        # Check if this is a skill-related action
60        for action in SKILL_ACTIONS:
61            if action in command:
62                # Inject agent identity if not already present
63                if "agent_id" not in command:
64                    ctx.data["injected_identity"] = agent_name
65                break
66    return _inject_identity
67
68
69def register_skill_hooks(registry: HookRegistry, agent_name: str, skillset_name: str = None):
70    """Register both default skill hooks on a HookRegistry.
71
72    Args:
73        registry: The HookRegistry to register hooks on
74        agent_name: Agent name for identity injection and agent-scoped skill state
75        skillset_name: Optional skillset name for description injection
76    """
77    if skillset_name:
78        registry.register(HookPoint.BEFORE_SYSTEM_PROMPT, make_skill_description_hook(skillset_name, agent_id=agent_name))
79    registry.register(HookPoint.BEFORE_TOOL_CALL, make_skill_identity_hook(agent_name))
def make_skill_description_hook(skillset_name: str, agent_id: str = None):
 6def make_skill_description_hook(skillset_name: str, agent_id: str = None):
 7    """BEFORE_SYSTEM_PROMPT hook: injects agent-scoped skill descriptions into system prompt.
 8
 9    Uses SkillManager with agent_id for per-agent equipped state.
10    Equips the persona's skillset on first call, then reads from agent-scoped equipped list.
11    """
12    _initialized = [False]
13
14    def _inject_skill_descriptions(ctx: HookContext):
15        try:
16            import re
17            from skill_manager.core import SkillManager
18            manager = SkillManager(agent_id=agent_id)
19
20            # First call: equip the persona's skillset into agent-scoped state
21            if not _initialized[0] and skillset_name:
22                manager.equip_skillset(skillset_name)
23                _initialized[0] = True
24
25            equipped = manager.list_equipped()
26            if not equipped:
27                return
28
29            skill_block = "\n\n<EQUIPPED_SKILLS>\n"
30            for s in equipped:
31                desc = s.get("description", "")
32                if desc:
33                    skill_block += f"- {s['name']}: {desc}\n"
34            skill_block += "</EQUIPPED_SKILLS>"
35
36            # Strip any previous injection, then append
37            current = ctx.data.get("system_prompt", ctx.prompt or "")
38            current = re.sub(r'\n*<EQUIPPED_SKILLS>.*?</EQUIPPED_SKILLS>', '', current, flags=re.DOTALL)
39            ctx.data["system_prompt"] = current + skill_block
40        except Exception:
41            pass  # Skill injection is best-effort, never block agent
42    return _inject_skill_descriptions

BEFORE_SYSTEM_PROMPT hook: injects agent-scoped skill descriptions into system prompt.

Uses SkillManager with agent_id for per-agent equipped state. Equips the persona's skillset on first call, then reads from agent-scoped equipped list.

def make_skill_identity_hook(agent_name: str):
45def make_skill_identity_hook(agent_name: str):
46    """BEFORE_TOOL_CALL hook: injects agent identity into sancrev treeshell skill calls.
47
48    When agent calls sancrev treeshell with skill-related actions (equip, list_equipped, etc.),
49    this hook injects the agent_name so skillmanager tracks per-agent state.
50    """
51    SKILL_ACTIONS = {"equip", "unequip", "list_equipped", "get_equipped_content", "unequip_all"}
52
53    def _inject_identity(ctx: HookContext):
54        tool_name = ctx.tool_name or ""
55        tool_args = ctx.tool_args or {}
56        # Only intercept sancrev treeshell calls
57        if "treeshell" not in tool_name.lower() and "sancrev" not in tool_name.lower():
58            return
59        command = tool_args.get("command", "")
60        # Check if this is a skill-related action
61        for action in SKILL_ACTIONS:
62            if action in command:
63                # Inject agent identity if not already present
64                if "agent_id" not in command:
65                    ctx.data["injected_identity"] = agent_name
66                break
67    return _inject_identity

BEFORE_TOOL_CALL hook: injects agent identity into sancrev treeshell skill calls.

When agent calls sancrev treeshell with skill-related actions (equip, list_equipped, etc.), this hook injects the agent_name so skillmanager tracks per-agent state.

def register_skill_hooks( registry: heaven_base.baseheavenagent.HookRegistry, agent_name: str, skillset_name: str = None):
70def register_skill_hooks(registry: HookRegistry, agent_name: str, skillset_name: str = None):
71    """Register both default skill hooks on a HookRegistry.
72
73    Args:
74        registry: The HookRegistry to register hooks on
75        agent_name: Agent name for identity injection and agent-scoped skill state
76        skillset_name: Optional skillset name for description injection
77    """
78    if skillset_name:
79        registry.register(HookPoint.BEFORE_SYSTEM_PROMPT, make_skill_description_hook(skillset_name, agent_id=agent_name))
80    registry.register(HookPoint.BEFORE_TOOL_CALL, make_skill_identity_hook(agent_name))

Register both default skill hooks on a HookRegistry.

Args: registry: The HookRegistry to register hooks on agent_name: Agent name for identity injection and agent-scoped skill state skillset_name: Optional skillset name for description injection