mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-07-25 01:15:24 +02:00
154 lines
No EOL
5.6 KiB
JavaScript
154 lines
No EOL
5.6 KiB
JavaScript
export class ActionManager {
|
|
constructor(agent) {
|
|
this.agent = agent;
|
|
this.executing = false;
|
|
this.currentActionLabel = '';
|
|
this.currentActionFn = null;
|
|
this.timedout = false;
|
|
this.resume_func = null;
|
|
this.resume_name = '';
|
|
}
|
|
|
|
async resumeAction(actionFn, timeout) {
|
|
return this._executeResume(actionFn, timeout);
|
|
}
|
|
|
|
async runAction(actionLabel, actionFn, { timeout, resume = false } = {}) {
|
|
if (resume) {
|
|
return this._executeResume(actionLabel, actionFn, timeout);
|
|
} else {
|
|
return this._executeAction(actionLabel, actionFn, timeout);
|
|
}
|
|
}
|
|
|
|
async stop() {
|
|
if (!this.executing) return;
|
|
const timeout = setTimeout(() => {
|
|
this.agent.cleanKill('Code execution refused stop after 10 seconds. Killing process.');
|
|
}, 10000);
|
|
while (this.executing) {
|
|
this.agent.requestInterrupt();
|
|
console.log('waiting for code to finish executing...');
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
}
|
|
clearTimeout(timeout);
|
|
}
|
|
|
|
cancelResume() {
|
|
this.resume_func = null;
|
|
this.resume_name = null;
|
|
}
|
|
|
|
async _executeResume(actionLabel = null, actionFn = null, timeout = 10) {
|
|
const new_resume = actionFn != null;
|
|
if (new_resume) { // start new resume
|
|
this.resume_func = actionFn;
|
|
assert(actionLabel != null, 'actionLabel is required for new resume');
|
|
this.resume_name = actionLabel;
|
|
}
|
|
if (this.resume_func != null && (this.agent.isIdle() || new_resume) && (!this.agent.self_prompter.on || new_resume)) {
|
|
this.currentActionLabel = this.resume_name;
|
|
let res = await this._executeAction(this.resume_name, this.resume_func, timeout);
|
|
this.currentActionLabel = '';
|
|
return res;
|
|
} else {
|
|
return { success: false, message: null, interrupted: false, timedout: false };
|
|
}
|
|
}
|
|
|
|
async _executeAction(actionLabel, actionFn, timeout = 10) {
|
|
let TIMEOUT;
|
|
try {
|
|
console.log('executing code...\n');
|
|
|
|
// await current action to finish (executing=false), with 10 seconds timeout
|
|
// also tell agent.bot to stop various actions
|
|
if (this.executing) {
|
|
console.log(`action "${actionLabel}" trying to interrupt current action "${this.currentActionLabel}"`);
|
|
}
|
|
await this.stop();
|
|
|
|
// clear bot logs and reset interrupt code
|
|
this.agent.clearBotLogs();
|
|
|
|
this.executing = true;
|
|
this.currentActionLabel = actionLabel;
|
|
this.currentActionFn = actionFn;
|
|
|
|
// timeout in minutes
|
|
if (timeout > 0) {
|
|
TIMEOUT = this._startTimeout(timeout);
|
|
}
|
|
|
|
// start the action
|
|
await actionFn();
|
|
|
|
// mark action as finished + cleanup
|
|
this.executing = false;
|
|
this.currentActionLabel = '';
|
|
this.currentActionFn = null;
|
|
clearTimeout(TIMEOUT);
|
|
|
|
// get bot activity summary
|
|
let output = this._getBotOutputSummary();
|
|
let interrupted = this.agent.bot.interrupt_code;
|
|
let timedout = this.timedout;
|
|
this.agent.clearBotLogs();
|
|
|
|
// if not interrupted and not generating, emit idle event
|
|
if (!interrupted && !this.agent.coder.generating) {
|
|
this.agent.bot.emit('idle');
|
|
}
|
|
|
|
// return action status report
|
|
return { success: true, message: output, interrupted, timedout };
|
|
} catch (err) {
|
|
this.executing = false;
|
|
this.currentActionLabel = '';
|
|
this.currentActionFn = null;
|
|
clearTimeout(TIMEOUT);
|
|
this.cancelResume();
|
|
console.error("Code execution triggered catch:", err);
|
|
// Log the full stack trace
|
|
console.error(err.stack);
|
|
await this.stop();
|
|
|
|
let message = this._getBotOutputSummary() +
|
|
'!!Code threw exception!!\n' +
|
|
'Error: ' + err + '\n' +
|
|
'Stack trace:\n' + err.stack;
|
|
|
|
let interrupted = this.agent.bot.interrupt_code;
|
|
this.agent.clearBotLogs();
|
|
if (!interrupted && !this.agent.coder.generating) {
|
|
this.agent.bot.emit('idle');
|
|
}
|
|
return { success: false, message, interrupted, timedout: false };
|
|
}
|
|
}
|
|
|
|
_getBotOutputSummary() {
|
|
const { bot } = this.agent;
|
|
if (bot.interrupt_code && !this.timedout) return '';
|
|
let output = bot.output;
|
|
const MAX_OUT = 500;
|
|
if (output.length > MAX_OUT) {
|
|
output = `Code output is very long (${output.length} chars) and has been shortened.\n
|
|
First outputs:\n${output.substring(0, MAX_OUT / 2)}\n...skipping many lines.\nFinal outputs:\n ${output.substring(output.length - MAX_OUT / 2)}`;
|
|
}
|
|
else {
|
|
output = 'Code output:\n' + output;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
_startTimeout(TIMEOUT_MINS = 10) {
|
|
return setTimeout(async () => {
|
|
console.warn(`Code execution timed out after ${TIMEOUT_MINS} minutes. Attempting force stop.`);
|
|
this.timedout = true;
|
|
this.agent.history.add('system', `Code execution timed out after ${TIMEOUT_MINS} minutes. Attempting force stop.`);
|
|
await this.stop(); // last attempt to stop
|
|
}, TIMEOUT_MINS * 60 * 1000);
|
|
}
|
|
|
|
} |