heal
Restore HP to an adjacent damaged allied unit using a healer unit. The healer must be READY or MOVED and becomes DONE after healing. Healing amount scales with the healer's magic stat. Returns amount healed and target's updated HP.
Instructions
Mutating. Heal an adjacent allied unit. Only units with the heal ability (typically Mages) can use this. healer_id is your healing unit (must be READY or MOVED); target_id is an adjacent allied unit that is damaged. Restores HP based on the healer's magic stat. After healing, the healer's status becomes DONE for this turn. Use get_legal_actions on the healer to see which allies are valid heal targets. Returns the amount healed and the target's updated HP.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| connection_id | Yes | ||
| healer_id | Yes | ||
| target_id | Yes |
Implementation Reference
- The `heal` tool handler function. Validates session/ownership, applies HealAction via engine rules, enriches errors, returns result with healer_status.
def heal(session: Session, viewer: Team, healer_id: str, target_id: str) -> dict: _require_active(session, viewer) _require_own_unit(session.state, healer_id, viewer) try: result = apply(session.state, HealAction(healer_id=healer_id, target_id=target_id)) except IllegalAction as e: raise ToolError(_enrich_heal_error(session.state, healer_id, target_id, e)) from e # Healer is DONE after healing. Status hint lets the model skip # re-deriving the rule. healer_after = session.state.units.get(healer_id) result["healer_status"] = ( healer_after.status.value if healer_after else "killed" ) _record_action(session, result) return result - Core `_apply_heal` engine rule. Validates can_heal, adjacency, ownership, self-heal, then applies heal amount and sets healer status to DONE.
def _apply_heal(state: GameState, healer: Unit, target_id: str) -> dict: if not healer.stats.can_heal: raise IllegalAction(f"{healer.id} cannot heal") if healer.status is UnitStatus.DONE: raise IllegalAction(f"{healer.id} has already acted this turn") target = state.units.get(target_id) if target is None or not target.alive: raise IllegalAction(f"target {target_id} not found (dead, nonexistent, or hidden by fog)") if target.owner is not healer.owner: raise IllegalAction("cannot heal enemy unit") if target.id == healer.id: raise IllegalAction("cannot self-heal") if healer.pos.manhattan(target.pos) != 1: raise IllegalAction("heal requires adjacent ally") heal_amt = min(healer.stats.heal_amount, target.stats.hp_max - target.hp) target.hp += heal_amt healer.status = UnitStatus.DONE return { "type": "heal", "unit_id": healer.id, "target_id": target.id, "heal_amount": heal_amt, } - src/silicon_pantheon/server/tools/__init__.py:158-169 (registration)Tool registration entry for 'heal' in TOOL_REGISTRY, mapping to `heal` function with input_schema (healer_id, target_id).
"heal": { "fn": heal, "description": "Heal an adjacent ally (Mage only). Unit becomes 'done'.", "input_schema": { "type": "object", "properties": { "healer_id": {"type": "string"}, "target_id": {"type": "string"}, }, "required": ["healer_id", "target_id"], }, }, - HealAction dataclass definition with healer_id and target_id fields.
class HealAction: healer_id: str target_id: str - `_enrich_heal_error` helper that provides actionable hints on heal failures (no can_heal, not adjacent, enemy target, self-heal).
def _enrich_heal_error( state: GameState, healer_id: str, target_id: str, e: IllegalAction ) -> str: """Hint on heal failures. The most frequent miss is picking a non-adjacent target -- name the adjacent wounded friendlies so the agent doesn't burn a get_state + distance calc to recover.""" msg = str(e) healer = state.units.get(healer_id) if healer is None: return msg if "cannot heal" in msg and "enemy" not in msg and "self" not in msg: # Class lacks can_heal. healers = [ u.id for u in state.units_of(healer.owner) if u.alive and u.stats.can_heal ] return ( f"{msg}. Your healers are: " f"[{', '.join(healers) or '(none -- no can_heal class fielded)'}]." ) if "requires adjacent ally" in msg: adjacent_wounded = [ u.id for u in state.units_of(healer.owner) if u.alive and u.id != healer.id and healer.pos.manhattan(u.pos) == 1 and u.hp < u.stats.hp_max ] return ( f"{msg}. Healer {healer_id} at ({healer.pos.x}," f"{healer.pos.y}); wounded friendly units adjacent right " f"now: [{', '.join(adjacent_wounded) or '(none)'}]." ) if "cannot heal enemy" in msg: return ( f"{msg}. Target {target_id} is on the opposing team. " f"Heal targets your own team only." ) if "cannot self-heal" in msg: return ( f"{msg}. Pick a wounded teammate at Manhattan distance 1." ) return msg