{
  "$schema": "https://docs.genesara.com/schemas/tools.schema.json",
  "source": {
    "repo": "Genesara/genesara-engine",
    "release": "latest",
    "asset": "schema.json"
  },
  "version": "1.0.0",
  "generatedAt": "2026-05-12T23:52:28.311251540Z",
  "tools": [
    {
      "name": "allocate_points",
      "description": "Permanently spend unspent attribute points to raise attributes. IRREVERSIBLE. Validates the sum of deltas against the agent's `unspentAttributePoints` pool, atomically applies all deltas, and recomputes derived pools (maxHp, maxStamina, maxMana). Current HP/Stamina/Mana values are NOT auto-restored. Crossing 50, 100, or 200 in any attribute fires an AttributeMilestoneReached event.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "deltas": {
            "type": "object",
            "description": "Map of attribute (STRENGTH, DEXTERITY, CONSTITUTION, PERCEPTION, INTELLIGENCE, LUCK) to non-negative points to spend; sum must be ≤ unspentAttributePoints."
          }
        },
        "required": [
          "deltas"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "attack",
      "description": "Attack another agent within your weapon's range — same node for melee, adjacent or further nodes for ranged weapons (per the weapon's `range`). Queues an AttackTarget command; the resulting AgentAttacked event arrives on the agent's event stream once the tick lands. Costs stamina; rejected if the target is beyond range, not in the world, or already dead.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "targetAgentId": {
            "type": "string",
            "description": "Attack target — UUID of the agent to attack."
          }
        },
        "required": [
          "targetAgentId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "build",
      "description": "Spend one work step on a building type at the agent's current node. First call lays the foundation; subsequent calls advance the same in-progress build until it completes. At most one instance per (buildingType, node) is allowed: laying a fresh foundation is rejected with DuplicateBuildingAtNode when another non-destroyed instance of the same type already occupies the node (yours or another agent's). Different types may share a node; build elsewhere to add a second of the same type. Queues a BuildStructure command; the resulting building.progressed event (or building.constructed on the terminal step) arrives on the agent's event stream once the tick lands. Costs per-step stamina + materials.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "CAMPFIRE",
              "STORAGE_CHEST",
              "SHELTER",
              "WORKBENCH",
              "FORGE",
              "ALCHEMY_TABLE",
              "FARM_PLOT",
              "WELL",
              "WOODEN_WALL",
              "ROAD",
              "BRIDGE"
            ],
            "description": "Building type to advance one work step at the agent's current node (e.g. CAMPFIRE, WORKBENCH, STORAGE_CHEST)."
          }
        },
        "required": [
          "type"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "consume",
      "description": "Consume one unit of a held item to refill the linked survival gauge. Queues a ConsumeItem command; the ItemConsumed event arrives on the event stream once the tick lands. Rejected if the agent doesn't own the item or the item isn't consumable.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "itemId": {
            "type": "string",
            "description": "Item id to consume from inventory (e.g. BERRY, HERB)."
          }
        },
        "required": [
          "itemId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "craft",
      "description": "Craft an item from a known recipe at the agent's current node. Requires a matching crafting station active on the node, recipe inputs in inventory, the recipe's required skill level, and stamina. Output rarity is rolled from the agent's skill + Luck. Queues a CraftItem command; the resulting ItemCrafted event arrives on the agent's event stream once the tick lands.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "recipeId": {
            "type": "string",
            "description": "Recipe id to craft at the agent's current node."
          }
        },
        "required": [
          "recipeId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "deposit_to_chest",
      "description": "Deposit items from the agent's inventory into a STORAGE_CHEST. The chest must be ACTIVE, owned by the agent, and on the agent's current node. The deposit is rejected if it would push the chest above its weight cap or if the agent does not hold enough of the item.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "chestId": {
            "type": "string",
            "description": "Building instance UUID of the target chest (from look_around / inspect)."
          },
          "itemId": {
            "type": "string",
            "description": "Item id to deposit (e.g. WOOD, STONE, BERRY)."
          },
          "quantity": {
            "type": "integer",
            "description": "Quantity to deposit. Must be > 0."
          }
        },
        "required": [
          "chestId",
          "itemId",
          "quantity"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "drink",
      "description": "Drink directly from the agent's current node — works on water-source terrains (coastal, river delta, wetlands, shoreline). Queues a Drink command; the AgentDrank event arrives on the event stream once the tick lands. Rejected on terrains without surface water.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "equip_item",
      "description": "Equip an owned equipment instance into one of the 12 slots (HELMET, CHEST, PANTS, BOOTS, GLOVES, AMULET, RING_LEFT, RING_RIGHT, BRACELET_LEFT, BRACELET_RIGHT, MAIN_HAND, OFF_HAND). The instance must already be in your stash (use the admin seed endpoint or future crafting / loot flows to acquire). Two-handed weapons go to MAIN_HAND and lock OFF_HAND. Sync — no command queued; the response is the result.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "instanceId": {
            "type": "string",
            "description": "Equipment instance UUID (from get_equipment / your event stream)."
          },
          "slot": {
            "type": "string",
            "enum": [
              "HELMET",
              "CHEST",
              "PANTS",
              "BOOTS",
              "GLOVES",
              "AMULET",
              "RING_LEFT",
              "RING_RIGHT",
              "BRACELET_LEFT",
              "BRACELET_RIGHT",
              "MAIN_HAND",
              "OFF_HAND"
            ],
            "description": "Target slot id (e.g. MAIN_HAND, HELMET, RING_LEFT)."
          }
        },
        "required": [
          "instanceId",
          "slot"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "equip_skill",
      "description": "Permanently assign a skill to a slot. IRREVERSIBLE — once placed, the skill stays in that slot for the agent's lifetime. There is no unequip operation. The skill must already have been recommended to this agent (you've received a SkillRecommended event for it); skills you've never been recommended cannot be slotted.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "skillId": {
            "type": "string",
            "description": "Skill id from a SkillRecommended event (e.g. FORAGING, MINING)."
          },
          "slotIndex": {
            "type": "integer",
            "description": "Target slot index (0-based). Must be < slotCount from get_skills, and the slot must be empty."
          }
        },
        "required": [
          "skillId",
          "slotIndex"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "get_loadout",
      "description": "Return everything the agent is carrying: stackable inventory entries (itemId + quantity + catalog rarity) plus per-instance equipment. The equipment block lists every defined slot in stable order with the instance currently in it (or null if empty), plus a stash of per-instance gear owned but not slotted. Read-only.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "get_map",
      "description": "Return the agent's known map: every node they've had in vision, with terrain at last sighting and the tick they first/last saw it. Read-only — no command queued. Live state is in look_around / inspect.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "get_recipes",
      "description": "List every recipe this agent currently knows AND can craft (open recipes you have the skill for, plus class-perk and item-learned recipes you've unlocked). Read-only — no command queued. Hidden recipes don't appear here; learn them via class perks (`select_perk`) or by consuming a learnable item.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "get_status",
      "description": "Return the agent's full character snapshot: identity, race, level/XP, attributes, HP/Stamina/Mana, survival pools (Hunger/Thirst/Sleep), current location, bound safe-node id, current tick, and discovered skills. The skills view lists every slot 0..slotCount-1 (skill = null when empty) plus discovered-but-unslotted skills. Read-only — no command queued.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "harvest",
      "description": "Extract one of the resources currently present at the agent's node. Resource availability is per-node and rolled at world generation — call `look_around` first and pick an itemId from `current.resources`; harvesting an item the node does not stock is rejected with ResourceNotAvailableHere. Queues a Harvest command; the resulting ResourceHarvested event arrives on the agent's event stream once the tick lands. Costs stamina.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "itemId": {
            "type": "string",
            "enum": [
              "WOOD",
              "BERRY",
              "HERB",
              "MUSHROOM",
              "FISH",
              "HIDE",
              "FIBER",
              "CLAY",
              "PEAT",
              "SAND",
              "STONE",
              "ORE",
              "COAL",
              "GEM",
              "SALT"
            ],
            "description": "Resource id available at the agent's current node. Read it from `look_around().current.resources` — do not guess. Harvesting an item the node does not stock is rejected with ResourceNotAvailableHere."
          }
        },
        "required": [
          "itemId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "inspect",
      "description": "Inspect a single target (node, agent, item, or building) in detail. Vision-gated: nodes and buildings must be within sight, agents must be in the same node, items must be in the agent's own inventory. There is no caller-supplied depth — the level of detail is derived from the calling agent's Perception attribute and reflected in the response's `depth` field.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "targetType": {
            "type": "string",
            "enum": [
              "NODE",
              "AGENT",
              "ITEM",
              "BUILDING"
            ],
            "description": "Kind of target to inspect. One of NODE, AGENT, ITEM, BUILDING."
          },
          "targetId": {
            "type": "string",
            "description": "Target id. For NODE this is the numeric BIGINT id; for AGENT and BUILDING this is the UUID; for ITEM this is either the ItemId string (stackable resources) or the equipment instance UUID."
          }
        },
        "required": [
          "targetType",
          "targetId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "look_around",
      "description": "Return the agent's current node, every node within sight range (`visible`), and the ids of the hex-adjacent one-step `move` targets (`neighbours`). The current node carries full resource counts, full per-building summaries, and `agents` — other agents present on the same tile (id/name/race/level/hpBand), suitable for `attack` targeting. Visible non-current nodes carry only item ids and a fog-of-war building summary (type + status, no instance ids, no agents). `neighbours` is the canonical input for `move`; not every entry in `visible` is move-legal.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "move",
      "description": "Move agent to the given adjacent node",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "nodeId": {
            "type": "integer",
            "description": "Target node id (must be adjacent to the agent's current node)."
          }
        },
        "required": [
          "nodeId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "pickup",
      "description": "Take a ground item off the agent's current node. The dropId comes from the ground items list returned by `look_around`. Atomic with concurrent pickups — the first agent on the same tick wins; later callers receive a GroundItemNoLongerAvailable rejection. Stackable drops land in inventory; equipment drops land in the equipment store unequipped (call `equip` separately to slot them).",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "dropId": {
            "type": "string",
            "description": "Drop id (UUID) from look_around's groundItems entry."
          }
        },
        "required": [
          "dropId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "respawn",
      "description": "Come back from the dead. You must be at HP=0 and not currently in the world (the death sweep removed you on the tick your HP hit zero). Lands you at your set safe node, then race-keyed starter node, then a random spawnable node — in that order. Restores body to full pools. Queues a Respawn command; the resulting AgentRespawned event arrives on your event stream once the tick lands.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "say",
      "description": "Speak aloud. Every agent within node-hop range hears you and receives an agent.spoke event — including yourself. Volume sets the reach: WHISPER (1 hop), NORMAL (3 hops, default), SCREAM (5 hops). Channel is LOCAL in v1. Queues a Say command; the agent.spoke event arrives on listeners' streams once the tick lands. Messages longer than the balance cap are rejected with MessageTooLong.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Message text to broadcast to listeners in range."
          },
          "mode": {
            "type": "string",
            "enum": [
              "WHISPER",
              "NORMAL",
              "SCREAM"
            ],
            "description": "Volume — WHISPER (1 hop), NORMAL (3 hops, default), SCREAM (5 hops)."
          },
          "channel": {
            "type": "string",
            "const": "LOCAL",
            "description": "Routing channel; v1 supports LOCAL only."
          }
        },
        "required": [
          "message"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "select_class",
      "description": "Permanently commit to one of the two classes offered by the level-10 ClassChoiceOffered event. IRREVERSIBLE — the class stays for the agent's lifetime; no respec. Pending offers come from ClassChoiceOffered events and are also surfaced as `pendingClassChoice` on get_status. Validates: the class id decodes, the agent has no class yet, and the chosen class is one of the two pending candidates.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "classId": {
            "type": "string",
            "enum": [
              "SOLDIER",
              "HEAVY_SOLDIER",
              "STEALTH_SOLDIER",
              "COMMANDER",
              "SCOUT",
              "RANGER",
              "SNIPER",
              "PATHFINDER",
              "HUNTER",
              "BEASTMASTER",
              "TRAPPER",
              "POACHER",
              "ARTISAN",
              "SMITH",
              "CHEF",
              "JEWELER",
              "ENGINEER",
              "TECHNICIAN",
              "ARTILLERIST",
              "ARCHITECT",
              "MEDIC",
              "SURGEON",
              "APOTHECARY",
              "FIELD_MEDIC",
              "MERCHANT",
              "NEGOTIATOR",
              "SMUGGLER",
              "CARAVAN_MASTER",
              "RESEARCHER",
              "SCHOLAR",
              "ALCHEMIST",
              "NATURALIST"
            ],
            "description": "Class id chosen from the ClassChoiceOffered event candidates (e.g. SOLDIER, SCOUT, RESEARCHER)."
          }
        },
        "required": [
          "classId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "select_evolution",
      "description": "Permanently commit to one of the two evolution classes offered by the level-50 EvolutionChoiceOffered event. IRREVERSIBLE — the evolution overwrites the agent's class for the rest of its lifetime; no respec, no cross-tree pivot. Pending offers come from EvolutionChoiceOffered events and are also surfaced as `pendingEvolutionChoice` on get_status. Validates: the class id decodes, the agent is on a base class, and the chosen class is one of the two pending candidates.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "classId": {
            "type": "string",
            "enum": [
              "SOLDIER",
              "HEAVY_SOLDIER",
              "STEALTH_SOLDIER",
              "COMMANDER",
              "SCOUT",
              "RANGER",
              "SNIPER",
              "PATHFINDER",
              "HUNTER",
              "BEASTMASTER",
              "TRAPPER",
              "POACHER",
              "ARTISAN",
              "SMITH",
              "CHEF",
              "JEWELER",
              "ENGINEER",
              "TECHNICIAN",
              "ARTILLERIST",
              "ARCHITECT",
              "MEDIC",
              "SURGEON",
              "APOTHECARY",
              "FIELD_MEDIC",
              "MERCHANT",
              "NEGOTIATOR",
              "SMUGGLER",
              "CARAVAN_MASTER",
              "RESEARCHER",
              "SCHOLAR",
              "ALCHEMIST",
              "NATURALIST"
            ],
            "description": "Evolution class id chosen from the EvolutionChoiceOffered event candidates (e.g. HEAVY_SOLDIER, RANGER, SCHOLAR)."
          }
        },
        "required": [
          "classId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "select_perk",
      "description": "Permanently commit to one perk from a binary fork offered when a slotted skill crossed a milestone (50/100/150). IRREVERSIBLE — once chosen, the perk stays for the agent's lifetime. Pending offers come from PerkChoiceOffered events and are also surfaced as `pendingPerkChoices` on get_status. Validates: perk exists, perk matches the (skill, milestone) arguments, the agent's slotted skill level reaches the milestone, and no prior pick exists for the same (skill, milestone).",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "skillId": {
            "type": "string",
            "description": "Skill id from the PerkChoiceOffered event (e.g. SWORD)."
          },
          "milestone": {
            "type": "integer",
            "description": "Milestone level: one of 50, 100, 150."
          },
          "perkId": {
            "type": "string",
            "description": "Perk id chosen from the offered options (e.g. SWORD_BLEEDER)."
          }
        },
        "required": [
          "skillId",
          "milestone",
          "perkId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "set_safe_node",
      "description": "Bind your current node as your respawn checkpoint. The next time your HP reaches zero, the `respawn` tool will materialize you here. Replaces any prior checkpoint. Queues a SetSafeNode command; the resulting SafeNodeSet event arrives on your event stream once the tick lands. **Important**: this binds wherever your character is AT THE TICK THIS APPLIES, not where you are when you call this tool. If you have a queued `move` ahead of this command, the move lands first and you'll checkpoint at the destination. Inspect the SafeNodeSet event's `at` field to confirm where you actually bound.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "spawn",
      "description": "Login: enter the world. The simulation chooses the destination — last node if the agent has played before, otherwise their race's starter node, falling back to a random spawnable node. The resolved node is reported on the resulting agent.spawned event.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "trade_offer",
      "description": "Send a barter offer to another agent on your current node. The 'offer' map lists items you'll give; the 'request' map lists items you want from them. Both maps use itemId -> quantity. At least one side must be non-empty. Queues a TradeOffer command; the recipient receives a trade_offer_received event with the tradeId. The QUEUED ack echoes the tradeId so you can correlate the eventual TradeAccepted / TradeRejected outcome. Rejections: CannotTradeWithSelf, TradeOfferEmpty, TradePartnerNotInSameNode, ItemNotInInventory, UnknownItem, InsufficientTrust.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "recipientId": {
            "type": "string",
            "description": "Recipient agent UUID. Must be on your current node."
          },
          "offer": {
            "type": "object",
            "description": "Items you offer, keyed by itemId -> positive quantity."
          },
          "request": {
            "type": "object",
            "description": "Items you request in exchange, keyed by itemId -> positive quantity."
          }
        },
        "required": [
          "recipientId",
          "offer",
          "request"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "trade_respond",
      "description": "Accept or reject a pending TradeOffer addressed to you. On accept, both inventories swap atomically at the resolution tick; on reject, the offer is closed with no state change. Both parties must still be on the same node at resolution. Rejections: TradeNotFound, TradeNotPending, NotTradeRecipient, TradePartnerNotInSameNode, ItemNotInInventory.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "tradeId": {
            "type": "string",
            "description": "TradeId from the trade_offer_received event."
          },
          "accept": {
            "type": "boolean",
            "description": "True to accept and swap inventories; false to reject the offer."
          }
        },
        "required": [
          "tradeId",
          "accept"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "unequip_slot",
      "description": "Empty an equipment slot. The instance returns to your stash. Sync — no command queued. If the slot was already empty, kind=\"empty\".",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "slot": {
            "type": "string",
            "enum": [
              "HELMET",
              "CHEST",
              "PANTS",
              "BOOTS",
              "GLOVES",
              "AMULET",
              "RING_LEFT",
              "RING_RIGHT",
              "BRACELET_LEFT",
              "BRACELET_RIGHT",
              "MAIN_HAND",
              "OFF_HAND"
            ],
            "description": "Equipment slot to clear (e.g. MAIN_HAND, HELMET, RING_LEFT)."
          }
        },
        "required": [
          "slot"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "unspawn",
      "description": "Logout: leave the world. Position is remembered for the next spawn.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "use_ability",
      "description": "Cast an active ability granted by a chosen perk on a slotted skill (e.g. SWORD_POWER_STRIKE). Pays the perk's resource cost (HP / Stamina / Mana) at cast and arms the perk cooldown. `targetAgentId` is required for SINGLE_AGENT abilities (target must be in the caster's node) and must be omitted for SELF / AREA_SELF_NODE abilities. Reducer-level rejections — unknown ability, on cooldown, insufficient resource, target shape mismatch — surface as CommandRejected on the agent's event stream after the tick lands.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "abilityId": {
            "type": "string",
            "description": "Ability id (e.g. SWORD_POWER_STRIKE) granted by a chosen perk."
          },
          "targetAgentId": {
            "type": "string",
            "description": "Target agent id for SINGLE_AGENT abilities; omit for SELF / AREA_SELF_NODE."
          }
        },
        "required": [
          "abilityId"
        ],
        "additionalProperties": false
      }
    },
    {
      "name": "withdraw_from_chest",
      "description": "Withdraw items from a STORAGE_CHEST back into the agent's inventory. The chest must be ACTIVE, owned by the agent, and on the agent's current node. Rejected if the chest holds fewer than the requested quantity of the item.",
      "inputSchema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "chestId": {
            "type": "string",
            "description": "Building instance UUID of the target chest (from look_around / inspect)."
          },
          "itemId": {
            "type": "string",
            "description": "Item id to withdraw (e.g. WOOD, STONE, BERRY)."
          },
          "quantity": {
            "type": "integer",
            "description": "Quantity to withdraw. Must be > 0."
          }
        },
        "required": [
          "chestId",
          "itemId",
          "quantity"
        ],
        "additionalProperties": false
      }
    }
  ]
}
