diff --git a/services/minecraft/src/agents/action/adapter.ts b/services/minecraft/src/agents/action/adapter.ts index 60fdd245c..83cea2286 100644 --- a/services/minecraft/src/agents/action/adapter.ts +++ b/services/minecraft/src/agents/action/adapter.ts @@ -8,6 +8,7 @@ import { agent } from 'neuri' import { system, user } from 'neuri/openai' import { BaseLLMHandler } from '../../cognitive/conscious/handler' +import { ActionError } from '../../utils/errors' import { useLogger } from '../../utils/logger' import { generateActionSystemPrompt } from './system-prompt' import { actionsList } from './tools' @@ -25,7 +26,19 @@ export async function createActionNeuriAgent(mineflayer: Mineflayer): Promise "ensurePlanks" -> infinite loop. + // check if we can craft from raw materials using the recipe planner. if (itemName.includes('planks') || itemName === 'stick' || itemName === 'crafting_table') { - logger.log(`Recursion Guard: Skipping crafting table search for basic item: ${itemName}`) + logger.log(`Recursion Guard: Checking if we can craft ${itemName} from raw materials`) + + // Use the recipe planner to see if we can craft this item + const plan = planRecipe(mineflayer.bot, itemName, num) + + if (plan.status === 'unknown_item') { + throw new ActionError('UNKNOWN', `Unknown item: ${itemName}`) + } + + // If we can craft now (have all materials including intermediates), do it + if (plan.canCraftNow && plan.steps.length > 0) { + logger.log(`Recipe planner found craftable path with ${plan.steps.length} steps`) + + // Craft all intermediate steps first (in reverse order = base materials first) + for (const step of [...plan.steps].reverse()) { + if (step.action === 'craft') { + logger.log(`Auto-crafting intermediate: ${step.amount}x ${step.item}`) + // Use direct bot.craft for intermediates to avoid infinite recursion + const stepItemId = mcData.getItemId(step.item) + if (!stepItemId) { + throw new ActionError('UNKNOWN', `Unknown intermediate item: ${step.item}`) + } + const stepRecipes = mineflayer.bot.recipesFor(stepItemId, null, 1, null) + if (stepRecipes && stepRecipes.length > 0) { + const outputPerCraft = stepRecipes[0].result?.count ?? 1 + const craftCount = Math.ceil(step.amount / outputPerCraft) + await mineflayer.bot.craft(stepRecipes[0], craftCount) + logger.log(`Successfully crafted ${craftCount}x ${step.item}`) + } + } + } + + return true + } + + // Can't craft - provide helpful error message + if (Object.keys(plan.missing).length > 0) { + const missingList = Object.entries(plan.missing) + .map(([item, count]) => `${count}x ${item}`) + .join(', ') + throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing: ${missingList}`, { + item: itemName, + missing: plan.missing, + }) + } + throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing ingredients`, { item: itemName }) }