UNPKG

@measey/mycoder-agent

Version:

Agent module for mycoder - an AI-powered software development assistant

197 lines 8.67 kB
import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; import { sleep } from '../../utils/sleep.js'; import { ShellStatus } from './ShellTracker.js'; // Define NodeJS signals as an enum export var NodeSignals; (function (NodeSignals) { NodeSignals["SIGABRT"] = "SIGABRT"; NodeSignals["SIGALRM"] = "SIGALRM"; NodeSignals["SIGBUS"] = "SIGBUS"; NodeSignals["SIGCHLD"] = "SIGCHLD"; NodeSignals["SIGCONT"] = "SIGCONT"; NodeSignals["SIGFPE"] = "SIGFPE"; NodeSignals["SIGHUP"] = "SIGHUP"; NodeSignals["SIGILL"] = "SIGILL"; NodeSignals["SIGINT"] = "SIGINT"; NodeSignals["SIGIO"] = "SIGIO"; NodeSignals["SIGIOT"] = "SIGIOT"; NodeSignals["SIGKILL"] = "SIGKILL"; NodeSignals["SIGPIPE"] = "SIGPIPE"; NodeSignals["SIGPOLL"] = "SIGPOLL"; NodeSignals["SIGPROF"] = "SIGPROF"; NodeSignals["SIGPWR"] = "SIGPWR"; NodeSignals["SIGQUIT"] = "SIGQUIT"; NodeSignals["SIGSEGV"] = "SIGSEGV"; NodeSignals["SIGSTKFLT"] = "SIGSTKFLT"; NodeSignals["SIGSTOP"] = "SIGSTOP"; NodeSignals["SIGSYS"] = "SIGSYS"; NodeSignals["SIGTERM"] = "SIGTERM"; NodeSignals["SIGTRAP"] = "SIGTRAP"; NodeSignals["SIGTSTP"] = "SIGTSTP"; NodeSignals["SIGTTIN"] = "SIGTTIN"; NodeSignals["SIGTTOU"] = "SIGTTOU"; NodeSignals["SIGUNUSED"] = "SIGUNUSED"; NodeSignals["SIGURG"] = "SIGURG"; NodeSignals["SIGUSR1"] = "SIGUSR1"; NodeSignals["SIGUSR2"] = "SIGUSR2"; NodeSignals["SIGVTALRM"] = "SIGVTALRM"; NodeSignals["SIGWINCH"] = "SIGWINCH"; NodeSignals["SIGXCPU"] = "SIGXCPU"; NodeSignals["SIGXFSZ"] = "SIGXFSZ"; })(NodeSignals || (NodeSignals = {})); const parameterSchema = z.object({ shellId: z.string().describe('The ID returned by shellStart'), stdin: z.string().optional().describe('Input to send to process'), signal: z .nativeEnum(NodeSignals) .optional() .describe('Signal to send to the process (e.g., SIGTERM, SIGINT)'), description: z .string() .describe('The reason for this shell interaction (max 80 chars)'), showStdIn: z .boolean() .optional() .describe('Whether to show the input to the user, or keep the output clean (default: false or value from shellStart)'), showStdout: z .boolean() .optional() .describe('Whether to show output to the user, or keep the output clean (default: false or value from shellStart)'), }); const returnSchema = z .object({ stdout: z.string(), stderr: z.string(), completed: z.boolean(), error: z.string().optional(), signaled: z.boolean().optional(), }) .describe('Process interaction results including stdout, stderr, and completion status'); export const shellMessageTool = { name: 'shellMessage', description: 'Interacts with a running shell process, sending input and receiving output', logPrefix: '💻', parameters: parameterSchema, parametersJsonSchema: zodToJsonSchema(parameterSchema), returns: returnSchema, returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ({ shellId, stdin, signal, showStdIn, showStdout }, { logger, shellTracker }) => { logger.debug(`Interacting with shell process ${shellId}${stdin ? ' with input' : ''}${signal ? ` with signal ${signal}` : ''}`); try { const processState = shellTracker.processStates.get(shellId); if (!processState) { throw new Error(`No process found with ID ${shellId}`); } // Send signal if provided if (signal) { try { processState.process.kill(signal); // Mark as signaled regardless of process status processState.state.signaled = true; } catch (error) { // If the process is already terminated, we'll just mark it as signaled anyway processState.state.signaled = true; // Update shell tracker if signal failed shellTracker.updateShellStatus(shellId, ShellStatus.ERROR, { error: `Failed to send signal ${signal}: ${String(error)}`, signalAttempted: signal, }); logger.debug(`Failed to send signal ${signal}: ${String(error)}, but marking as signaled anyway`); } // Update shell tracker with signal information if (signal === 'SIGTERM' || signal === 'SIGKILL' || signal === 'SIGINT') { shellTracker.updateShellStatus(shellId, ShellStatus.TERMINATED, { signal, terminatedByUser: true, }); } else { shellTracker.updateShellStatus(shellId, ShellStatus.RUNNING, { signal, signaled: true, }); } } // Send input if provided if (stdin) { if (!processState.process.stdin?.writable) { throw new Error('Process stdin is not available'); } // Determine whether to show stdin (prefer explicit parameter, fall back to process state) const shouldShowStdIn = showStdIn !== undefined ? showStdIn : processState.showStdIn; if (shouldShowStdIn) { logger.log(`[${shellId}] stdin: ${stdin}`); } // No special handling for 'cat' command - let the actual process handle the echo processState.process.stdin.write(`${stdin}\n`); // For interactive processes like 'cat', we need to give them time to process // and echo back the input before clearing the buffer await sleep(300); } // Wait a brief moment for output to be processed await sleep(100); // Get accumulated output const stdout = processState.stdout.join(''); const stderr = processState.stderr.join(''); // Clear the buffers processState.stdout = []; processState.stderr = []; logger.debug('Interaction completed successfully'); // Determine whether to show stdout (prefer explicit parameter, fall back to process state) const shouldShowStdout = showStdout !== undefined ? showStdout : processState.showStdout; if (stdout) { logger.debug(`stdout: ${stdout.trim()}`); if (shouldShowStdout) { logger.log(`[${shellId}] stdout: ${stdout.trim()}`); } } if (stderr) { logger.debug(`stderr: ${stderr.trim()}`); if (shouldShowStdout) { logger.log(`[${shellId}] stderr: ${stderr.trim()}`); } } return { stdout: stdout.trim(), stderr: stderr.trim(), completed: processState.state.completed, signaled: processState.state.signaled, }; } catch (error) { if (error instanceof Error) { logger.debug(`Process interaction failed: ${error.message}`); return { stdout: '', stderr: '', completed: false, error: error.message, }; } const errorMessage = String(error); logger.error(`Unknown error during process interaction: ${errorMessage}`); return { stdout: '', stderr: '', completed: false, error: `Unknown error occurred: ${errorMessage}`, }; } }, logParameters: (input, { logger, shellTracker }) => { const processState = shellTracker.processStates.get(input.shellId); const showStdIn = input.showStdIn !== undefined ? input.showStdIn : processState?.showStdIn || false; const showStdout = input.showStdout !== undefined ? input.showStdout : processState?.showStdout || false; logger.log(`Interacting with shell command "${processState ? processState.command : '<unknown shellId>'}", ${input.description} (showStdIn: ${showStdIn}, showStdout: ${showStdout})`); }, logReturns: () => { }, }; //# sourceMappingURL=shellMessage.js.map