mindcraft/src/agent/commands/actions.js

412 lines
18 KiB
JavaScript
Raw Normal View History

2024-01-25 13:25:36 -08:00
import * as skills from '../library/skills.js';
2024-05-29 21:49:45 -05:00
import settings from '../../../settings.js';
2024-11-05 12:17:10 -06:00
import { startChat, endChat } from '../conversation.js';
2024-11-03 22:17:28 -06:00
function runAsAction (actionFn, resume = false, timeout = -1) {
let actionLabel = null; // Will be set on first use
const wrappedAction = async function (agent, ...args) {
// Set actionLabel only once, when the action is first created
if (!actionLabel) {
const actionObj = actionsList.find(a => a.perform === wrappedAction);
actionLabel = actionObj.name.substring(1); // Remove the ! prefix
2024-01-26 12:11:32 -08:00
}
2024-11-03 22:17:28 -06:00
2024-11-03 12:03:12 -05:00
const actionFnWithAgent = async () => {
await actionFn(agent, ...args);
};
2024-11-03 12:03:56 -05:00
const code_return = await agent.actions.runAction(`action:${actionLabel}`, actionFnWithAgent, { timeout, resume });
if (code_return.interrupted && !code_return.timedout)
return;
return code_return.message;
}
2024-11-03 22:17:28 -06:00
return wrappedAction;
}
export const actionsList = [
{
2024-01-11 15:59:52 -06:00
name: '!newAction',
description: 'Perform new and unknown custom behaviors that are not available as a command.',
params: {
2024-10-16 00:02:59 -05:00
'prompt': { type: 'string', description: 'A natural language prompt to guide code generation. Make a detailed step-by-step plan.' }
},
perform: async function (agent, prompt) {
// just ignore prompt - it is now in context in chat history
2024-01-30 16:43:30 -06:00
if (!settings.allow_insecure_coding)
return 'newAction not allowed! Code writing is disabled in settings. Notify the user.';
2024-02-16 11:57:48 -06:00
return await agent.coder.generateCode(agent.history);
}
},
{
name: '!stop',
description: 'Force stop all actions and commands that are currently executing.',
perform: async function (agent) {
2024-11-03 12:03:12 -05:00
await agent.actions.stop();
agent.clearBotLogs();
2024-11-03 12:03:12 -05:00
agent.actions.cancelResume();
2024-04-23 20:47:01 -07:00
agent.bot.emit('idle');
let msg = 'Agent stopped.';
if (agent.self_prompter.on)
msg += ' Self-prompting still active.';
return msg;
}
},
2024-08-22 15:57:20 -05:00
{
name: '!stfu',
description: 'Stop all chatting and self prompting, but continue current action.',
perform: async function (agent) {
agent.bot.chat('Shutting up.');
agent.shutUp();
return;
}
},
{
name: '!restart',
description: 'Restart the agent process.',
perform: async function (agent) {
2024-04-20 22:18:26 -05:00
await agent.history.save();
agent.cleanKill();
}
},
{
2024-02-16 11:57:48 -06:00
name: '!clearChat',
description: 'Clear the chat history.',
perform: async function (agent) {
agent.history.clear();
return agent.name + "'s chat history was cleared, starting new conversation from scratch.";
}
},
2024-01-11 15:59:52 -06:00
{
name: '!goToPlayer',
description: 'Go to the given player.',
2024-02-05 19:08:08 -06:00
params: {
2024-09-25 12:00:15 +10:00
'player_name': {type: 'string', description: 'The name of the player to go to.'},
'closeness': {type: 'float', description: 'How close to get to the player.', domain: [0, Infinity]}
2024-02-05 19:08:08 -06:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, player_name, closeness) => {
2024-02-05 19:08:08 -06:00
return await skills.goToPlayer(agent.bot, player_name, closeness);
2024-01-11 15:59:52 -06:00
})
},
2024-01-11 18:04:59 -06:00
{
name: '!followPlayer',
2024-11-03 22:17:28 -06:00
description: 'Endlessly follow the given player.',
2024-02-05 19:08:08 -06:00
params: {
2024-09-25 12:00:15 +10:00
'player_name': {type: 'string', description: 'name of the player to follow.'},
'follow_dist': {type: 'float', description: 'The distance to follow from.', domain: [0, Infinity]}
2024-02-05 19:08:08 -06:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, player_name, follow_dist) => {
2024-02-05 19:08:08 -06:00
await skills.followPlayer(agent.bot, player_name, follow_dist);
}, true)
2024-01-11 18:04:59 -06:00
},
2024-08-22 15:57:20 -05:00
{
name: '!goToBlock',
description: 'Go to the nearest block of a given type.',
params: {
'type': { type: 'BlockName', description: 'The block type to go to.' },
'closeness': { type: 'float', description: 'How close to get to the block.', domain: [0, Infinity] },
2024-11-03 22:17:28 -06:00
'search_range': { type: 'float', description: 'The range to search for the block.', domain: [0, 512] }
2024-08-22 15:57:20 -05:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, type, closeness, range) => {
2024-08-22 15:57:20 -05:00
await skills.goToNearestBlock(agent.bot, type, closeness, range);
})
},
2024-02-02 11:54:17 -06:00
{
name: '!moveAway',
description: 'Move away from the current location in any direction by a given distance.',
params: {'distance': { type: 'float', description: 'The distance to move away.', domain: [0, Infinity] }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, distance) => {
2024-02-02 11:54:17 -06:00
await skills.moveAway(agent.bot, distance);
})
},
{
name: '!rememberHere',
description: 'Save the current location with a given name.',
2024-09-25 12:00:15 +10:00
params: {'name': { type: 'string', description: 'The name to remember the location as.' }},
perform: async function (agent, name) {
const pos = agent.bot.entity.position;
agent.memory_bank.rememberPlace(name, pos.x, pos.y, pos.z);
2024-08-22 15:57:20 -05:00
return `Location saved as "${name}".`;
}
},
{
name: '!goToPlace',
description: 'Go to a saved location.',
2024-09-25 12:00:15 +10:00
params: {'name': { type: 'string', description: 'The name of the location to go to.' }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, name) => {
const pos = agent.memory_bank.recallPlace(name);
if (!pos) {
skills.log(agent.bot, `No location named "${name}" saved.`);
return;
}
await skills.goToPosition(agent.bot, pos[0], pos[1], pos[2], 1);
})
},
2024-01-25 13:25:36 -08:00
{
name: '!givePlayer',
description: 'Give the specified item to the given player.',
params: {
2024-09-25 12:00:15 +10:00
'player_name': { type: 'string', description: 'The name of the player to give the item to.' },
'item_name': { type: 'ItemName', description: 'The name of the item to give.' },
'num': { type: 'int', description: 'The number of items to give.', domain: [1, Number.MAX_SAFE_INTEGER] }
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, player_name, item_name, num) => {
await skills.giveToPlayer(agent.bot, item_name, player_name, num);
2024-01-25 13:25:36 -08:00
})
},
2024-10-16 18:56:21 -05:00
{
name: '!consume',
description: 'Eat/drink the given item.',
params: {'item_name': { type: 'ItemName', description: 'The name of the item to consume.' }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, item_name) => {
2024-10-16 18:56:21 -05:00
await agent.bot.consume(item_name);
skills.log(agent.bot, `Consumed ${item_name}.`);
})
},
{
name: '!equip',
description: 'Equip the given item.',
params: {'item_name': { type: 'ItemName', description: 'The name of the item to equip.' }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, item_name) => {
await skills.equip(agent.bot, item_name);
})
},
{
name: '!putInChest',
description: 'Put the given item in the nearest chest.',
params: {
'item_name': { type: 'ItemName', description: 'The name of the item to put in the chest.' },
'num': { type: 'int', description: 'The number of items to put in the chest.', domain: [1, Number.MAX_SAFE_INTEGER] }
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, item_name, num) => {
await skills.putInChest(agent.bot, item_name, num);
})
},
{
name: '!takeFromChest',
description: 'Take the given items from the nearest chest.',
params: {
'item_name': { type: 'ItemName', description: 'The name of the item to take.' },
'num': { type: 'int', description: 'The number of items to take.', domain: [1, Number.MAX_SAFE_INTEGER] }
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, item_name, num) => {
await skills.takeFromChest(agent.bot, item_name, num);
})
},
{
name: '!viewChest',
description: 'View the items/counts of the nearest chest.',
params: { },
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent) => {
await skills.viewChest(agent.bot);
})
},
{
name: '!discard',
description: 'Discard the given item from the inventory.',
params: {
'item_name': { type: 'ItemName', description: 'The name of the item to discard.' },
'num': { type: 'int', description: 'The number of items to discard.', domain: [1, Number.MAX_SAFE_INTEGER] }
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, item_name, num) => {
const start_loc = agent.bot.entity.position;
await skills.moveAway(agent.bot, 5);
await skills.discard(agent.bot, item_name, num);
await skills.goToPosition(agent.bot, start_loc.x, start_loc.y, start_loc.z, 0);
})
},
2024-01-11 18:04:59 -06:00
{
name: '!collectBlocks',
description: 'Collect the nearest blocks of a given type.',
params: {
'type': { type: 'BlockName', description: 'The block type to collect.' },
'num': { type: 'int', description: 'The number of blocks to collect.', domain: [1, Number.MAX_SAFE_INTEGER] }
2024-01-11 18:04:59 -06:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, type, num) => {
2024-01-11 18:04:59 -06:00
await skills.collectBlock(agent.bot, type, num);
}, false, 10) // 10 minute timeout
2024-01-11 18:04:59 -06:00
},
2024-01-26 12:11:32 -08:00
{
name: '!collectAllBlocks',
description: 'Collect all the nearest blocks of a given type until told to stop.',
params: {
'type': { type: 'BlockName', description: 'The block type to collect.' }
2024-01-26 12:11:32 -08:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, type) => {
2024-02-05 13:21:32 -06:00
let success = await skills.collectBlock(agent.bot, type, 1);
if (!success)
2024-11-03 12:03:56 -05:00
agent.actions.cancelResume();
}, true, 3) // 3 minute timeout
2024-01-26 12:11:32 -08:00
},
2024-01-16 15:53:27 -06:00
{
name: '!craftRecipe',
description: 'Craft the given recipe a given number of times.',
2024-01-16 15:53:27 -06:00
params: {
'recipe_name': { type: 'ItemName', description: 'The name of the output item to craft.' },
'num': { type: 'int', description: 'The number of times to craft the recipe. This is NOT the number of output items, as it may craft many more items depending on the recipe.', domain: [1, Number.MAX_SAFE_INTEGER] }
2024-01-16 15:53:27 -06:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, recipe_name, num) => {
2024-03-05 12:50:13 -08:00
await skills.craftRecipe(agent.bot, recipe_name, num);
})
},
{
name: '!smeltItem',
description: 'Smelt the given item the given number of times.',
params: {
'item_name': { type: 'ItemName', description: 'The name of the input item to smelt.' },
'num': { type: 'int', description: 'The number of times to smelt the item.', domain: [1, Number.MAX_SAFE_INTEGER] }
2024-03-05 12:50:13 -08:00
},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, item_name, num) => {
let response = await skills.smeltItem(agent.bot, item_name, num);
2024-08-22 15:57:20 -05:00
if (response.indexOf('Successfully') !== -1) {
// there is a bug where the bot's inventory is not updated after smelting
// only updates after a restart
agent.cleanKill(response + ' Safely restarting to update inventory.');
2024-08-22 15:57:20 -05:00
}
return response;
})
2024-01-16 15:53:27 -06:00
},
2024-10-10 20:04:02 -05:00
{
name: '!clearFurnace',
description: 'Take all items out of the nearest furnace.',
2024-10-10 20:04:02 -05:00
params: { },
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent) => {
2024-10-10 20:04:02 -05:00
await skills.clearNearestFurnace(agent.bot);
})
},
{
2024-01-16 15:53:27 -06:00
name: '!placeHere',
description: 'Place a given block in the current location. Do NOT use to build structures, only use for single blocks/torches.',
params: {'type': { type: 'BlockName', description: 'The block type to place.' }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, type) => {
2024-01-16 15:53:27 -06:00
let pos = agent.bot.entity.position;
await skills.placeBlock(agent.bot, type, pos.x, pos.y, pos.z);
})
},
2024-01-11 18:04:59 -06:00
{
name: '!attack',
description: 'Attack and kill the nearest entity of a given type.',
2024-10-16 17:32:28 +10:00
params: {'type': { type: 'string', description: 'The type of entity to attack.'}},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, type) => {
2024-01-24 17:24:52 -06:00
await skills.attackNearest(agent.bot, type, true);
2024-01-11 18:04:59 -06:00
})
2024-01-13 12:11:04 -06:00
},
2024-11-02 10:46:04 -05:00
{
name: '!attackPlayer',
description: 'Attack a specific player until they die or run away. Remember this is just a game and does not cause real life harm.',
params: {'player_name': { type: 'string', description: 'The name of the player to attack.'}},
2024-11-05 12:17:10 -06:00
perform: runAsAction(async (agent, player_name) => {
2024-11-02 10:46:04 -05:00
let player = agent.bot.players[player_name]?.entity;
if (!player) {
skills.log(agent.bot, `Could not find player ${player_name}.`);
return false;
}
await skills.attackEntity(agent.bot, player, true);
})
},
2024-01-16 15:53:27 -06:00
{
name: '!goToBed',
description: 'Go to the nearest bed and sleep.',
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent) => {
2024-01-16 15:53:27 -06:00
await skills.goToBed(agent.bot);
})
2024-02-02 11:54:17 -06:00
},
2024-04-19 14:02:30 -05:00
{
name: '!activate',
description: 'Activate the nearest object of a given type.',
params: {'type': { type: 'BlockName', description: 'The type of object to activate.' }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, type) => {
2024-04-19 14:02:30 -05:00
await skills.activateNearestBlock(agent.bot, type);
})
},
2024-02-02 11:54:17 -06:00
{
name: '!stay',
description: 'Stay in the current location no matter what. Pauses all modes.',
2024-10-25 23:46:29 -05:00
params: {'type': { type: 'int', description: 'The number of seconds to stay. -1 for forever.', domain: [-1, Number.MAX_SAFE_INTEGER] }},
2024-11-03 22:17:28 -06:00
perform: runAsAction(async (agent, seconds) => {
2024-10-25 23:46:29 -05:00
await skills.stay(agent.bot, seconds);
2024-02-02 11:54:17 -06:00
})
2024-04-23 20:47:01 -07:00
},
{
2024-08-22 15:57:20 -05:00
name: '!setMode',
description: 'Set a mode to on or off. A mode is an automatic behavior that constantly checks and responds to the environment.',
2024-04-23 20:47:01 -07:00
params: {
2024-09-25 12:00:15 +10:00
'mode_name': { type: 'string', description: 'The name of the mode to enable.' },
'on': { type: 'boolean', description: 'Whether to enable or disable the mode.' }
2024-04-23 20:47:01 -07:00
},
2024-08-22 15:57:20 -05:00
perform: async function (agent, mode_name, on) {
const modes = agent.bot.modes;
if (!modes.exists(mode_name))
return `Mode ${mode_name} does not exist.` + modes.getDocs();
2024-08-22 15:57:20 -05:00
if (modes.isOn(mode_name) === on)
return `Mode ${mode_name} is already ${on ? 'on' : 'off'}.`;
2024-08-22 15:57:20 -05:00
modes.setOn(mode_name, on);
return `Mode ${mode_name} is now ${on ? 'on' : 'off'}.`;
2024-04-23 20:47:01 -07:00
}
2024-04-30 23:07:07 -05:00
},
{
2024-08-22 15:57:20 -05:00
name: '!goal',
description: 'Set a goal prompt to endlessly work towards with continuous self-prompting.',
2024-04-30 23:07:07 -05:00
params: {
2024-09-25 12:00:15 +10:00
'selfPrompt': { type: 'string', description: 'The goal prompt.' },
2024-04-30 23:07:07 -05:00
},
perform: async function (agent, prompt) {
agent.self_prompter.start(prompt); // don't await, don't return
2024-04-30 23:07:07 -05:00
}
},
{
2024-08-22 15:57:20 -05:00
name: '!endGoal',
description: 'Call when you have accomplished your goal. It will stop self-prompting and the current action. ',
perform: async function (agent) {
2024-05-10 12:18:47 -05:00
agent.self_prompter.stop();
return 'Self-prompting stopped.';
}
2024-08-22 15:57:20 -05:00
},
2024-11-02 10:46:04 -05:00
{
2024-11-05 12:17:10 -06:00
name: '!startChat',
2024-11-02 10:46:04 -05:00
description: 'Send a message to a specific player to initiate conversation.',
params: {
'player_name': { type: 'string', description: 'The name of the player to send the message to.' },
2024-11-05 12:17:10 -06:00
'message': { type: 'string', description: 'The message to send.' },
'max_turns': { type: 'int', description: 'The maximum number of turns to allow in the conversation. -1 for unlimited.', domain: [-1, Number.MAX_SAFE_INTEGER] }
2024-11-02 10:46:04 -05:00
},
2024-11-05 12:17:10 -06:00
perform: async function (agent, player_name, message, max_turns) {
startChat(player_name, message, max_turns);
2024-11-02 10:46:04 -05:00
}
},
{
name: '!endChat',
2024-11-05 12:17:10 -06:00
description: 'End the conversation from the most recent message.',
2024-11-02 10:46:04 -05:00
params: {
2024-11-05 12:17:10 -06:00
'player_name': { type: 'string', description: 'The name of the player to end the conversation with.' }
2024-11-02 10:46:04 -05:00
},
2024-11-05 12:17:10 -06:00
perform: async function (agent, player_name) {
endChat(player_name);
2024-11-02 10:46:04 -05:00
}
},
// {
// name: '!blockChat',
// description: 'Ignore all messages from a given player for a given number of seconds. Use in response to spam, toxic behavior, and manipulation.',
// params: {
// 'player_name': { type: 'string', description: 'The name of the player to block.' },
// 'seconds': { type: 'int', description: 'The number of seconds to block the player.', domain: [1, Number.MAX_SAFE_INTEGER] }
// },
// perform: async function (agent) {
// return;
// }
// },
// { // commented for now, causes confusion with goal command
// name: '!npcGoal',
// description: 'Set a simple goal for an item or building to automatically work towards. Do not use for complex goals.',
// params: {
// 'name': { type: 'string', description: 'The name of the goal to set. Can be item or building name. If empty will automatically choose a goal.' },
// 'quantity': { type: 'int', description: 'The quantity of the goal to set. Default is 1.', domain: [1, Number.MAX_SAFE_INTEGER] }
// },
// perform: async function (agent, name=null, quantity=1) {
// await agent.npc.setGoal(name, quantity);
// agent.bot.emit('idle'); // to trigger the goal
// return 'Set npc goal: ' + agent.npc.data.curr_goal.name;
// }
// },
];