naisys
Version:
NAISYS - Autonomous AI agent runner with built-in context management and cost tracking
139 lines • 6.36 kB
JavaScript
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