UNPKG

naisys

Version:

NAISYS - Autonomous AI agent runner with built-in context management and cost tracking

139 lines (137 loc) 7.87 kB
/** * Generates the system message for prefixing the context sent to the LLM * * Broken into three parts: * 1. The agent prompt from the agent config * 2. A summary of situation, 'you are a psuedo-user in a command shell' * 3. The MTOD for the shell itself as if the agent just logged in * * We start with lots of conditional logic to build up the system message. * Once exported the system message is essentially cached */ import { browserCmd, chatCmd, commentCmd, genImgCmd, listenCmd, lookCmd, lynxCmd, mailCmd, ptyCmd, sessionCmd, subagentCmd, usersCmd, webSearchCmd, workspaceCmd, } from "../command/commandDefs.js"; import { getPlatformConfig } from "../services/runtime/shellPlatform.js"; export function createSystemMessage({ globalConfig }, { agentConfig }, modelService) { const platformConfig = getPlatformConfig(); let genImgStr = ""; if (agentConfig().imageModel) { genImgStr = `\n ${genImgCmd.name} ${genImgCmd.usage}: ${genImgCmd.description}`; } let lookStr = ""; const shellModel = modelService.getLlmModel(agentConfig().shellModel); if (shellModel.supportsVision) { lookStr = `\n ${lookCmd.name} ${lookCmd.usage}: ${lookCmd.description}`; } let listenStr = ""; if (shellModel.supportsHearing) { listenStr = `\n ${listenCmd.name} ${listenCmd.usage}: ${listenCmd.description}`; } let mailStr = ""; if (globalConfig().mailServiceEnabled && agentConfig().mailEnabled) { mailStr += `\n ${usersCmd.name}: ${usersCmd.description}`; mailStr += `\n ${mailCmd.name}: ${mailCmd.description}`; } let chatStr = ""; if (agentConfig().chatEnabled) { const chatSubs = chatCmd.subcommands; chatStr = `\n ${chatCmd.name}: ${chatCmd.description}, e.g. \`${chatCmd.name} ${chatSubs.send.usage}\``; } let lynxStr = ""; if (agentConfig().webEnabled) { lynxStr = `\n ${lynxCmd.name}: ${lynxCmd.description}`; } let browserStr = ""; if (agentConfig().browserEnabled) { browserStr = `\n ${browserCmd.name}: ${browserCmd.description}`; } let webSearchStr = ""; const webSearchEnabled = agentConfig().webEnabled || agentConfig().browserEnabled; const hasGoogleSearchEnvVars = !!globalConfig().variableMap["GOOGLE_API_KEY"] && !!globalConfig().googleSearchEngineId; if (webSearchEnabled && hasGoogleSearchEnvVars) { webSearchStr = `\n ${webSearchCmd.name} ${webSearchCmd.usage}: ${webSearchCmd.description}`; } let workspaceStr = ""; if (agentConfig().workspacesEnabled) { workspaceStr = `\n ${workspaceCmd.name}: ${workspaceCmd.description}`; } let ptyStr = ""; if (platformConfig.platform === "linux") { ptyStr = `\n ${ptyCmd.name} ${ptyCmd.usage}: ${ptyCmd.description}`; } let supervisorApiStr = ""; if (agentConfig().supervisorApiHints) { supervisorApiStr = ` Supervisor API: Manage NAISYS agents, hosts, variables, models, users, etc., via the root API: curl -sS -H "Authorization: Bearer $NAISYS_API_KEY" "$NAISYS_API_URL_BASE/supervisor/api/" The response includes links to other subsystems (e.g. ERP) when those plugins are loaded. Need a permission you don't have? Ask the admin to grant it via the Users menu.`; } // ns-session guidance lives in the token-notes section below — listing // these in the command list as well led to the LLM treating them as just // another option rather than the right move when idle / near token limit. const sessionSubs = sessionCmd.subcommands; let tokenNote = ""; if (globalConfig().compactSessionEnabled) { tokenNote += `\n Call \`${sessionCmd.name} ${sessionSubs.compact.usage}\` before the token limit is hit so you can continue your work without interruption.`; } tokenNote += `\n When there is nothing more to do, call \`${sessionCmd.name} ${sessionSubs.wait.usage}\`.`; if (agentConfig().completeSessionEnabled) { tokenNote += `\n Or, after reporting your results call \`${sessionCmd.name} ${sessionSubs.complete.usage}\` to end the session.`; if (agentConfig().wakeOnMessage) { tokenNote += `\n The session will auto-wake and start again on new messages or events. Prefer completing the session to excessive waiting.`; } } if (agentConfig().multipleCommandsEnabled) { tokenNote += "\n Be careful running multiple commands on a single prompt, and never assume the output of commands. Better to run one command at a time if you're not sure."; } else { tokenNote += "\n Only run one command at a time, evaluate the output, then run the next command. Don't overload the same line with multiple commands."; } const subagentStr = `\n ${subagentCmd.name}: ${subagentCmd.description}`; // ns-* commands and shell commands take different code paths, so mixing them // in one response is error-prone. Chaining ns-* with ns-* stays in the // registered-command path and is fine when multipleCommands is on. const naisysChainNote = agentConfig().multipleCommandsEnabled ? "(multiple ns-* commands can be chained on separate lines as long as they come before any standard shell commands)" : "(cannot be used with other commands on the same prompt)"; // Fill out the templates in the agent prompt and stick it to the front of the system message // A lot of the stipulations in here are to prevent common LLM mistakes // Like we can't jump between standard and special commands in a single prompt, which the LLM will try to do if not warned let agentPrompt = agentConfig().agentPrompt; agentPrompt = agentConfig().resolveConfigVars(agentPrompt); // Build up the final system message const systemMessage = `${agentPrompt.trim()} This is a command line interface presenting you with the next command prompt. *** Your response will literally be piped into a command shell, so you must use valid commands. Make sure to read the command line rules in the MOTD carefully. Don't put commands in \`\`\` blocks. Do not preempt or hallucinate the output of commands. The system will provide the output of commands for you. For example when you run 'cat' or 'ls', don't write what you think the output will be. Let the system do that. The system will provide responses and next command prompt. Don't output your own command prompt. Be careful when writing files through the command prompt with cat. Make sure to close and escape quotes properly. Don't blindly overwrite existing files without reading them first. NAISYS ${globalConfig().packageVersion} Shell Welcome back ${agentConfig().username}! MOTD: Date: ${new Date().toLocaleString()} ${platformConfig.displayName} Commands: Standard ${platformConfig.shellName} commands are available${platformConfig.platform === "windows" ? ` PowerShell has aliases for common commands: ls, cat, pwd, cd, mkdir, rm, cp, mv Read files with Get-Content. Write files with Set-Content -Path "file" -Value "content"` : ` vi and nano are not supported Read files with cat. Write files with \`cat > filename << 'EOF'\` A heredoc (\`<<EOF\`) claims stdin, so piping into a command that also uses one silently discards the piped data. Route one of the inputs through a tempfile instead.`} Do not input notes after the prompt. Only valid commands. NAISYS Commands: ${naisysChainNote}${mailStr}${chatStr}${subagentStr}${lynxStr}${browserStr}${webSearchStr}${genImgStr}${lookStr}${listenStr}${workspaceStr}${ptyStr} ${commentCmd.name} ${commentCmd.usage}: ${commentCmd.description}${supervisorApiStr} Tokens: The console log can only hold a certain number of tokens that is specified in the prompt.${tokenNote}`; return systemMessage; } //# sourceMappingURL=systemMessage.js.map