UNPKG

naisys

Version:

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

139 lines 6.36 kB
import chalk from "chalk"; import { isElevated } from "../services/runtime/shellPlatform.js"; import { getSharedReadline } from "../utils/input/sharedReadline.js"; import { isTimedWait } from "./commandRegistry.js"; export function createPromptBuilder({ globalConfig }, { agentConfig }, shellWrapper, contextManager, costTracker, output, inputMode, platformConfig, promptNotification, localUserId) { async function getPrompt(wait) { const promptSuffix = isElevated() ? platformConfig.adminPromptSuffix : platformConfig.promptSuffix; const tokenMax = agentConfig().tokenMax; const usedTokens = contextManager.getTokenCount(); const tokenSuffix = ` [Tokens: ${usedTokens}/${tokenMax}]`; let pause = ""; if (inputMode.isDebug()) { if (isTimedWait(wait)) { pause += ` [Wait: ${wait.seconds}s]`; } if (agentConfig().wakeOnMessage) { pause += " [WakeOnMsg]"; } } const now = new Date(); const timestamp = now.toLocaleDateString("en-US", { month: "short", day: "2-digit" }) + " " + now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false, }); const budgetLeft = costTracker.getBudgetLeft(); const budgetSuffix = budgetLeft !== null ? ` [Budget: $${budgetLeft.toFixed(2)}]` : ""; return `[${timestamp}] ${await getUserHostPathPrompt()}${tokenSuffix}${budgetSuffix}${pause}${promptSuffix} `; } async function getUserHostPathPrompt() { const currentPath = await shellWrapper.getCurrentPath(); return `${getUserHostPrompt()}${platformConfig.promptDivider}${currentPath}`; } function getUserHostPrompt() { return `${agentConfig().username}@${globalConfig().hostname}`; } function getInput(commandPrompt, wait, onTimerCancelled) { if (wait.kind === "none") { return Promise.resolve(""); } return new Promise((resolve) => { const questionController = new AbortController(); let timeout; let notificationInterval; let timeoutCancelled = false; let unsubscribeInput; function clearTimers(keepNotifications = false) { timeoutCancelled = true; if (unsubscribeInput) { unsubscribeInput(); unsubscribeInput = undefined; } clearTimeout(timeout); if (!keepNotifications) { clearInterval(notificationInterval); } } /** * Using a shared readline interface singleton to avoid conflicts when multiple agents are running. * Only one agent should be active on the console at a time (controlled by output.isWriteEnabled). */ const readlineInterface = output.isConsoleEnabled() ? getSharedReadline() : undefined; /** Cancels auto-wait timers. Keeps the notification interval alive so * wake:"always" notifications (e.g. shutdown) can still break through. */ const cancelWaitingForUserInput = (questionAborted) => { if (timeoutCancelled) { return; } clearTimers(true); if (questionAborted) { return; } // Else timeout interrupted by user input onTimerCancelled?.(); // Update the prompt to remove timeout information so the user // doesn't think the timeout is still active if (readlineInterface) { const newPrompt = commandPrompt.replace(/\s*\[Wait: \d+s\]/, ""); readlineInterface.setPrompt(chalk.greenBright(newPrompt)); readlineInterface._refreshLine(); } }; if (readlineInterface) { readlineInterface.question(chalk.greenBright(commandPrompt), { signal: questionController.signal }, (answer) => { clearTimers(); readlineInterface.pause(); resolve(answer); }); // If user presses any key, cancel auto-continue timers and/or wake on msg const onStdinData = () => cancelWaitingForUserInput(false); process.stdin.on("data", onStdinData); unsubscribeInput = () => process.stdin.removeListener("data", onStdinData); } else { // Agent not in focus - just output to buffer and wait for events // Don't actively cycle with timeouts as stdout writes interfere with readline output.comment(commandPrompt + "<agent not in focus>"); } function abortQuestion() { clearTimers(); questionController.abort(); try { readlineInterface?.pause(); } catch { // On Windows, the readline interface may already be closed after abort } resolve(""); } // Timed waits auto-continue after the timeout. Indefinite waits rely on // manual input and/or wake notifications. if (isTimedWait(wait)) { timeout = setTimeout(abortQuestion, wait.seconds * 1000); } // Poll for prompt notifications that should wake/interrupt. // After timers are cancelled (user started typing), pass false for // wakeOnMessage so only wake:"always" notifications (e.g. shutdown) // can still break through. notificationInterval = setInterval(() => { if (promptNotification.hasPending(localUserId, timeoutCancelled ? false : agentConfig().wakeOnMessage)) { abortQuestion(); } }, 250); }); } return { getPrompt, getUserHostPathPrompt, getUserHostPrompt, getInput, }; } //# sourceMappingURL=promptBuilder.js.map