mindcraft 0.1.4-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/FAQ.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -0
  4. package/andy.json +6 -0
  5. package/bin/mindcraft.js +80 -0
  6. package/keys.example.json +19 -0
  7. package/main.js +80 -0
  8. package/package.json +78 -0
  9. package/patches/minecraft-data+3.97.0.patch +13 -0
  10. package/patches/mineflayer+4.33.0.patch +54 -0
  11. package/patches/mineflayer-pathfinder+2.4.5.patch +265 -0
  12. package/patches/mineflayer-pvp+1.3.2.patch +13 -0
  13. package/patches/prismarine-viewer+1.33.0.patch +13 -0
  14. package/patches/protodef+1.19.0.patch +15 -0
  15. package/profiles/andy-4-reasoning.json +14 -0
  16. package/profiles/andy-4.json +7 -0
  17. package/profiles/azure.json +19 -0
  18. package/profiles/claude.json +7 -0
  19. package/profiles/claude_thinker.json +15 -0
  20. package/profiles/deepseek.json +7 -0
  21. package/profiles/defaults/_default.json +256 -0
  22. package/profiles/defaults/assistant.json +14 -0
  23. package/profiles/defaults/creative.json +14 -0
  24. package/profiles/defaults/god_mode.json +14 -0
  25. package/profiles/defaults/survival.json +14 -0
  26. package/profiles/freeguy.json +7 -0
  27. package/profiles/gemini.json +9 -0
  28. package/profiles/gpt.json +12 -0
  29. package/profiles/grok.json +7 -0
  30. package/profiles/llama.json +10 -0
  31. package/profiles/mercury.json +9 -0
  32. package/profiles/mistral.json +5 -0
  33. package/profiles/qwen.json +17 -0
  34. package/profiles/tasks/construction_profile.json +42 -0
  35. package/profiles/tasks/cooking_profile.json +11 -0
  36. package/profiles/tasks/crafting_profile.json +71 -0
  37. package/profiles/vllm.json +10 -0
  38. package/settings.js +64 -0
  39. package/src/agent/action_manager.js +177 -0
  40. package/src/agent/agent.js +561 -0
  41. package/src/agent/coder.js +229 -0
  42. package/src/agent/commands/actions.js +504 -0
  43. package/src/agent/commands/index.js +259 -0
  44. package/src/agent/commands/queries.js +347 -0
  45. package/src/agent/connection_handler.js +96 -0
  46. package/src/agent/conversation.js +353 -0
  47. package/src/agent/history.js +122 -0
  48. package/src/agent/library/full_state.js +89 -0
  49. package/src/agent/library/index.js +23 -0
  50. package/src/agent/library/lockdown.js +32 -0
  51. package/src/agent/library/skill_library.js +93 -0
  52. package/src/agent/library/skills.js +2093 -0
  53. package/src/agent/library/world.js +431 -0
  54. package/src/agent/memory_bank.js +25 -0
  55. package/src/agent/mindserver_proxy.js +136 -0
  56. package/src/agent/modes.js +446 -0
  57. package/src/agent/npc/build_goal.js +80 -0
  58. package/src/agent/npc/construction/dirt_shelter.json +38 -0
  59. package/src/agent/npc/construction/large_house.json +230 -0
  60. package/src/agent/npc/construction/small_stone_house.json +42 -0
  61. package/src/agent/npc/construction/small_wood_house.json +42 -0
  62. package/src/agent/npc/controller.js +261 -0
  63. package/src/agent/npc/data.js +50 -0
  64. package/src/agent/npc/item_goal.js +355 -0
  65. package/src/agent/npc/utils.js +126 -0
  66. package/src/agent/self_prompter.js +146 -0
  67. package/src/agent/settings.js +7 -0
  68. package/src/agent/speak.js +150 -0
  69. package/src/agent/tasks/construction_tasks.js +1104 -0
  70. package/src/agent/tasks/cooking_tasks.js +358 -0
  71. package/src/agent/tasks/tasks.js +594 -0
  72. package/src/agent/templates/execTemplate.js +6 -0
  73. package/src/agent/templates/lintTemplate.js +10 -0
  74. package/src/agent/vision/browser_viewer.js +8 -0
  75. package/src/agent/vision/camera.js +78 -0
  76. package/src/agent/vision/vision_interpreter.js +82 -0
  77. package/src/mindcraft/index.js +28 -0
  78. package/src/mindcraft/mcserver.js +154 -0
  79. package/src/mindcraft/mindcraft.js +111 -0
  80. package/src/mindcraft/mindserver.js +328 -0
  81. package/src/mindcraft/public/index.html +1253 -0
  82. package/src/mindcraft/public/settings_spec.json +145 -0
  83. package/src/mindcraft/userconfig.js +72 -0
  84. package/src/mindcraft-py/example.py +27 -0
  85. package/src/mindcraft-py/init-mindcraft.js +24 -0
  86. package/src/mindcraft-py/mindcraft.py +99 -0
  87. package/src/models/_model_map.js +89 -0
  88. package/src/models/azure.js +32 -0
  89. package/src/models/cerebras.js +61 -0
  90. package/src/models/claude.js +87 -0
  91. package/src/models/deepseek.js +59 -0
  92. package/src/models/gemini.js +176 -0
  93. package/src/models/glhf.js +71 -0
  94. package/src/models/gpt.js +147 -0
  95. package/src/models/grok.js +82 -0
  96. package/src/models/groq.js +95 -0
  97. package/src/models/huggingface.js +86 -0
  98. package/src/models/hyperbolic.js +114 -0
  99. package/src/models/lmstudio.js +74 -0
  100. package/src/models/mercury.js +95 -0
  101. package/src/models/mistral.js +94 -0
  102. package/src/models/novita.js +71 -0
  103. package/src/models/ollama.js +115 -0
  104. package/src/models/openrouter.js +77 -0
  105. package/src/models/prompter.js +366 -0
  106. package/src/models/qwen.js +80 -0
  107. package/src/models/replicate.js +60 -0
  108. package/src/models/vllm.js +81 -0
  109. package/src/process/agent_process.js +84 -0
  110. package/src/process/init_agent.js +54 -0
  111. package/src/utils/examples.js +83 -0
  112. package/src/utils/keys.js +34 -0
  113. package/src/utils/math.js +13 -0
  114. package/src/utils/mcdata.js +572 -0
  115. package/src/utils/text.js +78 -0
  116. package/src/utils/translator.js +30 -0
@@ -0,0 +1,96 @@
1
+ import { sendOutputToServer } from './mindserver_proxy.js';
2
+
3
+ // Definitions of error types, keywords, and full human-readable messages.
4
+ const ERROR_DEFINITIONS = {
5
+ 'name_conflict': {
6
+ keywords: ['name_taken', 'duplicate_login', 'already connected', 'already logged in', 'username is already'],
7
+ msg: 'Name Conflict: The name is already in use or you are already logged in.',
8
+ isFatal: true
9
+ },
10
+ 'access_denied': {
11
+ keywords: ['whitelist', 'not white-listed', 'banned', 'suspended', 'verify'],
12
+ msg: 'Access Denied: You are not whitelisted or banned.',
13
+ isFatal: true
14
+ },
15
+ 'server_full': {
16
+ keywords: ['server is full', 'full server'],
17
+ msg: 'Connection Failed: The server is full.',
18
+ isFatal: false
19
+ },
20
+ 'version_mismatch': {
21
+ keywords: ['outdated', 'version', 'client'],
22
+ msg: 'Version Mismatch: Client and server versions do not match.',
23
+ isFatal: true
24
+ },
25
+ 'maintenance': {
26
+ keywords: ['maintenance', 'updating', 'closed', 'restarting'],
27
+ msg: 'Connection Failed: Server is under maintenance or restarting.',
28
+ isFatal: false
29
+ },
30
+ 'network_error': {
31
+ keywords: ['timeout', 'timed out', 'connection lost', 'reset', 'refused', 'keepalive'],
32
+ msg: 'Network Error: Connection timed out or was lost.',
33
+ isFatal: false
34
+ },
35
+ 'behavior': {
36
+ keywords: ['flying', 'spam', 'speed'],
37
+ msg: 'Kicked: Removed from server due to flying, spamming, or invalid movement.',
38
+ isFatal: true
39
+ }
40
+ };
41
+
42
+ // Helper to log messages to console (once) and MindServer.
43
+ export const log = (agentName, msg) => {
44
+ // Use console.error for visibility in terminal
45
+ console.error(msg);
46
+ try { sendOutputToServer(agentName || 'system', msg); } catch (_) {}
47
+ };
48
+
49
+ // Analyzes the kick reason and returns a full, human-readable sentence.
50
+ export function parseKickReason(reason) {
51
+ if (!reason) return { type: 'unknown', msg: 'Unknown reason (Empty)', isFatal: true };
52
+
53
+ const raw = (typeof reason === 'string' ? reason : JSON.stringify(reason)).toLowerCase();
54
+
55
+ // Search for keywords in definitions
56
+ for (const [type, def] of Object.entries(ERROR_DEFINITIONS)) {
57
+ if (def.keywords.some(k => raw.includes(k))) {
58
+ console.error(`Disconnected: ${raw}`);
59
+ return { type, msg: def.msg, isFatal: def.isFatal };
60
+ }
61
+ }
62
+
63
+ // Fallback: Extract text from JSON
64
+ let fallback = raw;
65
+ try {
66
+ const obj = typeof reason === 'string' ? JSON.parse(reason) : reason;
67
+ fallback = obj.translate || obj.text || (obj.value?.translate) || raw;
68
+ } catch (_) {}
69
+
70
+ return { type: 'other', msg: `Disconnected: ${fallback}`, isFatal: true };
71
+ }
72
+
73
+ // Centralized handler for disconnections.
74
+ export function handleDisconnection(agentName, reason) {
75
+ const { type, msg } = parseKickReason(reason);
76
+
77
+ // Format: [LoginGuard] Error Message
78
+ const finalMsg = `[LoginGuard] ${msg}`;
79
+
80
+ // Only call log once (it handles console printing)
81
+ log(agentName, finalMsg);
82
+
83
+ return { type, msg: finalMsg };
84
+ }
85
+
86
+ // Validates name format.
87
+ export function validateNameFormat(name) {
88
+ if (!name || !/^[a-zA-Z0-9_]{3,16}$/.test(name)) {
89
+ return {
90
+ success: false,
91
+ // Added [LoginGuard] prefix here for consistency
92
+ msg: `[LoginGuard] Invalid name '${name}'. Must be 3-16 alphanumeric/underscore characters.`
93
+ };
94
+ }
95
+ return { success: true };
96
+ }
@@ -0,0 +1,353 @@
1
+ import settings from './settings.js';
2
+ import { containsCommand } from './commands/index.js';
3
+ import { sendBotChatToServer } from './mindserver_proxy.js';
4
+
5
+ let agent;
6
+ let agent_names = [];
7
+ let agents_in_game = [];
8
+
9
+ class Conversation {
10
+ constructor(name) {
11
+ this.name = name;
12
+ this.active = false;
13
+ this.ignore_until_start = false;
14
+ this.blocked = false;
15
+ this.in_queue = [];
16
+ this.inMessageTimer = null;
17
+ }
18
+
19
+ reset() {
20
+ this.active = false;
21
+ this.ignore_until_start = false;
22
+ this.in_queue = [];
23
+ this.inMessageTimer = null;
24
+ }
25
+
26
+ end() {
27
+ this.active = false;
28
+ this.ignore_until_start = true;
29
+ this.inMessageTimer = null;
30
+ const full_message = _compileInMessages(this);
31
+ if (full_message.message.trim().length > 0)
32
+ agent.history.add(this.name, full_message.message);
33
+ // add the full queued messages to history, but don't respond
34
+
35
+ if (agent.last_sender === this.name)
36
+ agent.last_sender = null;
37
+ }
38
+
39
+ queue(message) {
40
+ this.in_queue.push(message);
41
+ }
42
+ }
43
+
44
+ const WAIT_TIME_START = 30000;
45
+ class ConversationManager {
46
+ constructor() {
47
+ this.convos = {};
48
+ this.activeConversation = null;
49
+ this.awaiting_response = false;
50
+ this.connection_timeout = null;
51
+ this.wait_time_limit = WAIT_TIME_START;
52
+ }
53
+
54
+ initAgent(a) {
55
+ agent = a;
56
+ }
57
+
58
+ _getConvo(name) {
59
+ if (!this.convos[name])
60
+ this.convos[name] = new Conversation(name);
61
+ return this.convos[name];
62
+ }
63
+
64
+ _startMonitor() {
65
+ clearInterval(this.connection_monitor);
66
+ let wait_time = 0;
67
+ let last_time = Date.now();
68
+ this.connection_monitor = setInterval(() => {
69
+ if (!this.activeConversation) {
70
+ this._stopMonitor();
71
+ return; // will clean itself up
72
+ }
73
+
74
+ let delta = Date.now() - last_time;
75
+ last_time = Date.now();
76
+ let convo_partner = this.activeConversation.name;
77
+
78
+ if (this.awaiting_response && agent.isIdle()) {
79
+ wait_time += delta;
80
+ if (wait_time > this.wait_time_limit) {
81
+ agent.handleMessage('system', `${convo_partner} hasn't responded in ${this.wait_time_limit/1000} seconds, respond with a message to them or your own action.`);
82
+ wait_time = 0;
83
+ this.wait_time_limit*=2;
84
+ }
85
+ }
86
+ else if (!this.awaiting_response){
87
+ this.wait_time_limit = WAIT_TIME_START;
88
+ wait_time = 0;
89
+ }
90
+
91
+ if (!this.otherAgentInGame(convo_partner) && !this.connection_timeout) {
92
+ this.connection_timeout = setTimeout(() => {
93
+ if (this.otherAgentInGame(convo_partner)){
94
+ this._clearMonitorTimeouts();
95
+ return;
96
+ }
97
+ if (!agent.self_prompter.isPaused()) {
98
+ this.endConversation(convo_partner);
99
+ agent.handleMessage('system', `${convo_partner} disconnected, conversation has ended.`);
100
+ }
101
+ else {
102
+ this.endConversation(convo_partner);
103
+ }
104
+ }, 10000);
105
+ }
106
+ }, 1000);
107
+ }
108
+
109
+ _stopMonitor() {
110
+ clearInterval(this.connection_monitor);
111
+ this.connection_monitor = null;
112
+ this._clearMonitorTimeouts();
113
+ }
114
+
115
+ _clearMonitorTimeouts() {
116
+ this.awaiting_response = false;
117
+ clearTimeout(this.connection_timeout);
118
+ this.connection_timeout = null;
119
+ }
120
+
121
+ async startConversation(send_to, message) {
122
+ const convo = this._getConvo(send_to);
123
+ convo.reset();
124
+
125
+ if (agent.self_prompter.isActive()) {
126
+ await agent.self_prompter.pause();
127
+ }
128
+ if (convo.active)
129
+ return;
130
+ convo.active = true;
131
+ this.activeConversation = convo;
132
+ this._startMonitor();
133
+ this.sendToBot(send_to, message, true, false);
134
+ }
135
+
136
+ startConversationFromOtherBot(name) {
137
+ const convo = this._getConvo(name);
138
+ convo.active = true;
139
+ this.activeConversation = convo;
140
+ this._startMonitor();
141
+ }
142
+
143
+ sendToBot(send_to, message, start=false, open_chat=true) {
144
+ if (!this.isOtherAgent(send_to)) {
145
+ console.warn(`${agent.name} tried to send bot message to non-bot ${send_to}`);
146
+ return;
147
+ }
148
+ const convo = this._getConvo(send_to);
149
+
150
+ if (settings.chat_bot_messages && open_chat)
151
+ agent.openChat(`(To ${send_to}) ${message}`);
152
+
153
+ if (convo.ignore_until_start)
154
+ return;
155
+ convo.active = true;
156
+
157
+ const end = message.includes('!endConversation');
158
+ const json = {
159
+ 'message': message,
160
+ start,
161
+ end,
162
+ };
163
+
164
+ this.awaiting_response = true;
165
+ sendBotChatToServer(send_to, json);
166
+ }
167
+
168
+ async receiveFromBot(sender, received) {
169
+ const convo = this._getConvo(sender);
170
+
171
+ if (convo.ignore_until_start && !received.start)
172
+ return;
173
+
174
+ // check if any convo is active besides the sender
175
+ if (this.inConversation() && !this.inConversation(sender)) {
176
+ this.sendToBot(sender, `I'm talking to someone else, try again later. !endConversation("${sender}")`, false, false);
177
+ this.endConversation(sender);
178
+ return;
179
+ }
180
+
181
+ if (received.start) {
182
+ convo.reset();
183
+ this.startConversationFromOtherBot(sender);
184
+ }
185
+
186
+ this._clearMonitorTimeouts();
187
+ convo.queue(received);
188
+
189
+ // responding to conversation takes priority over self prompting
190
+ if (agent.self_prompter.isActive()){
191
+ await agent.self_prompter.pause();
192
+ }
193
+
194
+ _scheduleProcessInMessage(sender, received, convo);
195
+ }
196
+
197
+ responseScheduledFor(sender) {
198
+ if (!this.isOtherAgent(sender) || !this.inConversation(sender))
199
+ return false;
200
+ const convo = this._getConvo(sender);
201
+ return !!convo.inMessageTimer;
202
+ }
203
+
204
+ isOtherAgent(name) {
205
+ return agent_names.some((n) => n === name);
206
+ }
207
+
208
+ otherAgentInGame(name) {
209
+ return agents_in_game.some((n) => n === name);
210
+ }
211
+
212
+ updateAgents(agents) {
213
+ agent_names = agents.map(a => a.name);
214
+ agents_in_game = agents.filter(a => a.in_game).map(a => a.name);
215
+ }
216
+
217
+ getInGameAgents() {
218
+ return agents_in_game;
219
+ }
220
+
221
+ inConversation(other_agent=null) {
222
+ if (other_agent)
223
+ return this.convos[other_agent]?.active;
224
+ return Object.values(this.convos).some(c => c.active);
225
+ }
226
+
227
+ endConversation(sender) {
228
+ if (this.convos[sender]) {
229
+ this.convos[sender].end();
230
+ if (this.activeConversation.name === sender) {
231
+ this._stopMonitor();
232
+ this.activeConversation = null;
233
+ if (agent.self_prompter.isPaused() && !this.inConversation()) {
234
+ _resumeSelfPrompter();
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ endAllConversations() {
241
+ for (const sender in this.convos) {
242
+ this.endConversation(sender);
243
+ }
244
+ if (agent.self_prompter.isPaused()) {
245
+ _resumeSelfPrompter();
246
+ }
247
+ }
248
+
249
+ forceEndCurrentConversation() {
250
+ if (this.activeConversation) {
251
+ let sender = this.activeConversation.name;
252
+ this.sendToBot(sender, '!endConversation("' + sender + '")', false, false);
253
+ this.endConversation(sender);
254
+ }
255
+ }
256
+ }
257
+
258
+ const convoManager = new ConversationManager();
259
+ export default convoManager;
260
+
261
+ /*
262
+ This function controls conversation flow by deciding when the bot responds.
263
+ The logic is as follows:
264
+ - If neither bot is busy, respond quickly with a small delay.
265
+ - If only the other bot is busy, respond with a long delay to allow it to finish short actions (ex check inventory)
266
+ - If I'm busy but other bot isn't, let LLM decide whether to respond
267
+ - If both bots are busy, don't respond until someone is done, excluding a few actions that allow fast responses
268
+ - New messages received during the delay will reset the delay following this logic, and be queued to respond in bulk
269
+ */
270
+ const talkOverActions = ['stay', 'followPlayer', 'mode:']; // all mode actions
271
+ const fastDelay = 200;
272
+ const longDelay = 5000;
273
+ async function _scheduleProcessInMessage(sender, received, convo) {
274
+ if (convo.inMessageTimer)
275
+ clearTimeout(convo.inMessageTimer);
276
+ let otherAgentBusy = containsCommand(received.message);
277
+
278
+ const scheduleResponse = (delay) => convo.inMessageTimer = setTimeout(() => _processInMessageQueue(sender), delay);
279
+
280
+ if (!agent.isIdle() && otherAgentBusy) {
281
+ // both are busy
282
+ let canTalkOver = talkOverActions.some(a => agent.actions.currentActionLabel.includes(a));
283
+ if (canTalkOver)
284
+ scheduleResponse(fastDelay)
285
+ // otherwise don't respond
286
+ }
287
+ else if (otherAgentBusy)
288
+ // other bot is busy but I'm not
289
+ scheduleResponse(longDelay);
290
+ else if (!agent.isIdle()) {
291
+ // I'm busy but other bot isn't
292
+ let canTalkOver = talkOverActions.some(a => agent.actions.currentActionLabel.includes(a));
293
+ if (canTalkOver) {
294
+ scheduleResponse(fastDelay);
295
+ }
296
+ else {
297
+ let shouldRespond = await agent.prompter.promptShouldRespondToBot(received.message);
298
+ console.log(`${agent.name} decided to ${shouldRespond?'respond':'not respond'} to ${sender}`);
299
+ if (shouldRespond)
300
+ scheduleResponse(fastDelay);
301
+ }
302
+ }
303
+ else {
304
+ // neither are busy
305
+ scheduleResponse(fastDelay);
306
+ }
307
+ }
308
+
309
+ function _processInMessageQueue(name) {
310
+ const convo = convoManager._getConvo(name);
311
+ _handleFullInMessage(name, _compileInMessages(convo));
312
+ }
313
+
314
+ function _compileInMessages(convo) {
315
+ let pack = {};
316
+ let full_message = '';
317
+ while (convo.in_queue.length > 0) {
318
+ pack = convo.in_queue.shift();
319
+ full_message += pack.message;
320
+ }
321
+ pack.message = full_message;
322
+ return pack;
323
+ }
324
+
325
+ function _handleFullInMessage(sender, received) {
326
+ console.log(`${agent.name} responding to "${received.message}" from ${sender}`);
327
+
328
+ const convo = convoManager._getConvo(sender);
329
+ convo.active = true;
330
+
331
+ let message = _tagMessage(received.message);
332
+ if (received.end) {
333
+ convoManager.endConversation(sender);
334
+ message = `Conversation with ${sender} ended with message: "${message}"`;
335
+ sender = 'system'; // bot will respond to system instead of the other bot
336
+ }
337
+ else if (received.start)
338
+ agent.shut_up = false;
339
+ convo.inMessageTimer = null;
340
+ agent.handleMessage(sender, message);
341
+ }
342
+
343
+
344
+ function _tagMessage(message) {
345
+ return "(FROM OTHER BOT)" + message;
346
+ }
347
+
348
+ async function _resumeSelfPrompter() {
349
+ await new Promise(resolve => setTimeout(resolve, 5000));
350
+ if (agent.self_prompter.isPaused() && !convoManager.inConversation()) {
351
+ agent.self_prompter.start();
352
+ }
353
+ }
@@ -0,0 +1,122 @@
1
+ import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'fs';
2
+ import { NPCData } from './npc/data.js';
3
+ import settings from './settings.js';
4
+
5
+
6
+ export class History {
7
+ constructor(agent) {
8
+ this.agent = agent;
9
+ this.name = agent.name;
10
+ this.bot_dir = `${settings.data_dir}/${this.name}`;
11
+ this.memory_fp = `${this.bot_dir}/memory.json`;
12
+ this.full_history_fp = undefined;
13
+
14
+ mkdirSync(`${this.bot_dir}/histories`, { recursive: true });
15
+
16
+ this.turns = [];
17
+
18
+ // Natural language memory as a summary of recent messages + previous memory
19
+ this.memory = '';
20
+
21
+ // Maximum number of messages to keep in context before saving chunk to memory
22
+ this.max_messages = settings.max_messages;
23
+
24
+ // Number of messages to remove from current history and save into memory
25
+ this.summary_chunk_size = 5;
26
+ // chunking reduces expensive calls to promptMemSaving and appendFullHistory
27
+ // and improves the quality of the memory summary
28
+ }
29
+
30
+ getHistory() { // expects an Examples object
31
+ return JSON.parse(JSON.stringify(this.turns));
32
+ }
33
+
34
+ async summarizeMemories(turns) {
35
+ console.log("Storing memories...");
36
+ this.memory = await this.agent.prompter.promptMemSaving(turns);
37
+
38
+ if (this.memory.length > 500) {
39
+ this.memory = this.memory.slice(0, 500);
40
+ this.memory += '...(Memory truncated to 500 chars. Compress it more next time)';
41
+ }
42
+
43
+ console.log("Memory updated to: ", this.memory);
44
+ }
45
+
46
+ async appendFullHistory(to_store) {
47
+ if (this.full_history_fp === undefined) {
48
+ const string_timestamp = new Date().toLocaleString().replace(/[/:]/g, '-').replace(/ /g, '').replace(/,/g, '_');
49
+ this.full_history_fp = `${this.bot_dir}/histories/${string_timestamp}.json`;
50
+ writeFileSync(this.full_history_fp, '[]', 'utf8');
51
+ }
52
+ try {
53
+ const data = readFileSync(this.full_history_fp, 'utf8');
54
+ let full_history = JSON.parse(data);
55
+ full_history.push(...to_store);
56
+ writeFileSync(this.full_history_fp, JSON.stringify(full_history, null, 4), 'utf8');
57
+ } catch (err) {
58
+ console.error(`Error reading ${this.name}'s full history file: ${err.message}`);
59
+ }
60
+ }
61
+
62
+ async add(name, content) {
63
+ let role = 'assistant';
64
+ if (name === 'system') {
65
+ role = 'system';
66
+ }
67
+ else if (name !== this.name) {
68
+ role = 'user';
69
+ content = `${name}: ${content}`;
70
+ }
71
+ this.turns.push({role, content});
72
+
73
+ if (this.turns.length >= this.max_messages) {
74
+ let chunk = this.turns.splice(0, this.summary_chunk_size);
75
+ while (this.turns.length > 0 && this.turns[0].role === 'assistant')
76
+ chunk.push(this.turns.shift()); // remove until turns starts with system/user message
77
+
78
+ await this.summarizeMemories(chunk);
79
+ await this.appendFullHistory(chunk);
80
+ }
81
+ }
82
+
83
+ async save() {
84
+ try {
85
+ const data = {
86
+ memory: this.memory,
87
+ turns: this.turns,
88
+ self_prompting_state: this.agent.self_prompter.state,
89
+ self_prompt: this.agent.self_prompter.isStopped() ? null : this.agent.self_prompter.prompt,
90
+ taskStart: this.agent.task.taskStartTime,
91
+ last_sender: this.agent.last_sender
92
+ };
93
+ writeFileSync(this.memory_fp, JSON.stringify(data, null, 2));
94
+ console.log('Saved memory to:', this.memory_fp);
95
+ } catch (error) {
96
+ console.error('Failed to save history:', error);
97
+ throw error;
98
+ }
99
+ }
100
+
101
+ load() {
102
+ try {
103
+ if (!existsSync(this.memory_fp)) {
104
+ console.log('No memory file found.');
105
+ return null;
106
+ }
107
+ const data = JSON.parse(readFileSync(this.memory_fp, 'utf8'));
108
+ this.memory = data.memory || '';
109
+ this.turns = data.turns || [];
110
+ console.log('Loaded memory:', this.memory);
111
+ return data;
112
+ } catch (error) {
113
+ console.error('Failed to load history:', error);
114
+ throw error;
115
+ }
116
+ }
117
+
118
+ clear() {
119
+ this.turns = [];
120
+ this.memory = '';
121
+ }
122
+ }
@@ -0,0 +1,89 @@
1
+ import {
2
+ getPosition,
3
+ getBiomeName,
4
+ getNearbyPlayerNames,
5
+ getInventoryCounts,
6
+ getNearbyEntityTypes,
7
+ getBlockAtPosition,
8
+ getFirstBlockAboveHead
9
+ } from "./world.js";
10
+ import convoManager from '../conversation.js';
11
+
12
+ export function getFullState(agent) {
13
+ const bot = agent.bot;
14
+
15
+ const pos = getPosition(bot);
16
+ const position = {
17
+ x: Number(pos.x.toFixed(2)),
18
+ y: Number(pos.y.toFixed(2)),
19
+ z: Number(pos.z.toFixed(2))
20
+ };
21
+
22
+ let weather = 'Clear';
23
+ if (bot.thunderState > 0) weather = 'Thunderstorm';
24
+ else if (bot.rainState > 0) weather = 'Rain';
25
+
26
+ let timeLabel = 'Night';
27
+ if (bot.time.timeOfDay < 6000) timeLabel = 'Morning';
28
+ else if (bot.time.timeOfDay < 12000) timeLabel = 'Afternoon';
29
+
30
+ const below = getBlockAtPosition(bot, 0, -1, 0).name;
31
+ const legs = getBlockAtPosition(bot, 0, 0, 0).name;
32
+ const head = getBlockAtPosition(bot, 0, 1, 0).name;
33
+
34
+ let players = getNearbyPlayerNames(bot);
35
+ let bots = convoManager.getInGameAgents().filter(b => b !== agent.name);
36
+ players = players.filter(p => !bots.includes(p));
37
+
38
+ const helmet = bot.inventory.slots[5];
39
+ const chestplate = bot.inventory.slots[6];
40
+ const leggings = bot.inventory.slots[7];
41
+ const boots = bot.inventory.slots[8];
42
+
43
+ const state = {
44
+ name: agent.name,
45
+ gameplay: {
46
+ position,
47
+ dimension: bot.game.dimension,
48
+ gamemode: bot.game.gameMode,
49
+ health: Math.round(bot.health),
50
+ hunger: Math.round(bot.food),
51
+ biome: getBiomeName(bot),
52
+ weather,
53
+ timeOfDay: bot.time.timeOfDay,
54
+ timeLabel
55
+ },
56
+ action: {
57
+ current: agent.isIdle() ? 'Idle' : agent.actions.currentActionLabel,
58
+ isIdle: agent.isIdle()
59
+ },
60
+ surroundings: {
61
+ below,
62
+ legs,
63
+ head,
64
+ firstBlockAboveHead: getFirstBlockAboveHead(bot, null, 32)
65
+ },
66
+ inventory: {
67
+ counts: getInventoryCounts(bot),
68
+ stacksUsed: bot.inventory.items().length,
69
+ totalSlots: bot.inventory.slots.length,
70
+ equipment: {
71
+ helmet: helmet ? helmet.name : null,
72
+ chestplate: chestplate ? chestplate.name : null,
73
+ leggings: leggings ? leggings.name : null,
74
+ boots: boots ? boots.name : null,
75
+ mainHand: bot.heldItem ? bot.heldItem.name : null
76
+ }
77
+ },
78
+ nearby: {
79
+ humanPlayers: players,
80
+ botPlayers: bots,
81
+ entityTypes: getNearbyEntityTypes(bot).filter(t => t !== 'player' && t !== 'item'),
82
+ },
83
+ modes: {
84
+ summary: bot.modes.getMiniDocs()
85
+ }
86
+ };
87
+
88
+ return state;
89
+ }
@@ -0,0 +1,23 @@
1
+ import * as skills from './skills.js';
2
+ import * as world from './world.js';
3
+
4
+
5
+ export function docHelper(functions, module_name) {
6
+ let docArray = [];
7
+ for (let skillFunc of functions) {
8
+ let str = skillFunc.toString();
9
+ if (str.includes('/**')) {
10
+ let docEntry = `${module_name}.${skillFunc.name}\n`;
11
+ docEntry += str.substring(str.indexOf('/**') + 3, str.indexOf('**/')).trim();
12
+ docArray.push(docEntry);
13
+ }
14
+ }
15
+ return docArray;
16
+ }
17
+
18
+ export function getSkillDocs() {
19
+ let docArray = [];
20
+ docArray = docArray.concat(docHelper(Object.values(skills), 'skills'));
21
+ docArray = docArray.concat(docHelper(Object.values(world), 'world'));
22
+ return docArray;
23
+ }