UNPKG

naisys

Version:

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

222 lines 13.3 kB
import { commentCmd } from "../command/commandDefs.js"; import { createCommandHandler } from "../command/commandHandler.js"; import { createCommandLoop } from "../command/commandLoop.js"; import { createCommandProtection } from "../command/commandProtection.js"; import { createCommandRegistry } from "../command/commandRegistry.js"; import { createDebugCommands } from "../command/debugCommand.js"; import { createPagedOutputBuffer } from "../command/pagedOutputBuffer.js"; import { createPromptBuilder } from "../command/promptBuilder.js"; import { createShellCommand } from "../command/shellCommand.js"; import { createShellWrapper } from "../command/shellWrapper.js"; import { createComputerService } from "../computer-use/computerService.js"; import { createDesktopService } from "../computer-use/desktop.js"; import { createGenImg } from "../features/media/genImg.js"; import { createListenService } from "../features/media/listen.js"; import { createLookService } from "../features/media/look.js"; import { createPtyService } from "../features/shell/pty.js"; import { createSessionService } from "../features/shell/session.js"; import { createSubagentService } from "../features/shell/subagent.js"; import { createWorkspacesFeature } from "../features/shell/workspaces.js"; import { createBrowserService } from "../features/web/browser.js"; import { createLynxService } from "../features/web/lynx.js"; import { createWebSearchService } from "../features/web/webSearch.js"; import { createCommandTools } from "../llm/commandTool.js"; import { createContextManager } from "../llm/contextManager.js"; import { createCostDisplayService } from "../llm/costDisplayService.js"; import { createCostTracker } from "../llm/costTracker.js"; import { createLLMService } from "../llm/llmService.js"; import { createSystemMessage } from "../llm/systemMessage.js"; import { createChatService } from "../mail/chat.js"; import { createMailService } from "../mail/mail.js"; import { createMailQueryService } from "../mail/mailQueryService.js"; import { createAttachmentService } from "../services/agent/attachmentService.js"; import { createLogService } from "../services/agent/logService.js"; import { createRunService } from "../services/agent/runService.js"; import { createStartupAttachmentService } from "../services/agent/startupAttachmentService.js"; import { createHubAttachmentClient } from "../services/hub/hubAttachmentClient.js"; import { createNaisysApiService } from "../services/hub/naisysApiService.js"; import { getPlatformConfig } from "../services/runtime/shellPlatform.js"; import { createCommandLoopState } from "../utils/commandLoopState.js"; import { createInputMode } from "../utils/input/inputMode.js"; import { createOutputService } from "../utils/output/output.js"; import { createAgentConfig } from "./agentConfig.js"; import { createUserDisplayService } from "./userDisplayService.js"; export async function createAgentRuntime(agentManager, localUserId, globalConfig, hubClient, hubCostBuffer, hubLogBuffer, hostService, userService, modelService, promptNotification, desktopClaimService, subagentContext, preallocated, runtimeApiKey, startupAttachments, restoreData) { // For subagents, strip the hub surface so hub-aware services take their // local-mode branch. The parent's hubClient is preserved as sessionHubClient // for the few subagent paths that genuinely need hub round-trips — run // session registration, $NAISYS_API_URL_BASE, and OpenAI Codex OAuth. const sessionHubClient = hubClient; if (subagentContext) { hubLogBuffer = wrapLogBufferForSubagent(hubLogBuffer, subagentContext); hubCostBuffer = wrapCostBufferForSubagent(hubCostBuffer, subagentContext); hubClient = undefined; } /* * Per-agent composition root. Keep this as linear hand-wiring rather than a * DI container: construction order is dependency order, which keeps cycles * visible and makes late-binding choices explicit. */ // Agent-local foundation: config, run identity, attachments, logs, output. const agentConfig = createAgentConfig(localUserId, globalConfig, userService); // Built before staging so the seed key is available to stage(). const naisysApiService = createNaisysApiService(runtimeApiKey); const hubAttachmentClient = createHubAttachmentClient(hubClient, naisysApiService); const startupAttachmentService = createStartupAttachmentService(hubAttachmentClient, hubClient, agentConfig); if (startupAttachments?.length) { await startupAttachmentService.stage(startupAttachments); } const runService = await createRunService(agentConfig, sessionHubClient, localUserId, subagentContext, preallocated); const attachmentService = createAttachmentService(hubClient, naisysApiService); const logService = createLogService(hubLogBuffer, runService, localUserId, attachmentService); const output = createOutputService(logService); // Shell surface and workspace context. apiUrlBase comes from sessionHubClient // so subagents (which null out `hubClient`) still get $NAISYS_API_URL_BASE // alongside the runtime API key the parent dispatched them with. const shellWrapper = createShellWrapper(globalConfig, agentConfig, output, naisysApiService, sessionHubClient?.getApiUrlBase()); const workspaces = createWorkspacesFeature(shellWrapper); // Loop state, prompt context, cost tracking, and model access. const inputMode = createInputMode(); const commandLoopState = createCommandLoopState(() => { // Immediate heartbeat so supervisors see state transitions within // roundtrip latency instead of waiting for the next interval tick agentManager.onHeartbeatNeeded?.(); }); const systemMessage = createSystemMessage(globalConfig, agentConfig, modelService); const tools = createCommandTools(agentConfig); const costTracker = createCostTracker(globalConfig, agentConfig, modelService, runService, hubClient, hubCostBuffer, localUserId, promptNotification, subagentContext?.parentCostTracker); const costDisplayService = createCostDisplayService(globalConfig, agentConfig, costTracker, modelService, output); const contextManager = createContextManager(agentConfig, modelService, workspaces, systemMessage, output, logService, inputMode); const computerService = await createComputerService(agentConfig); const llmService = createLLMService(globalConfig, agentConfig, costTracker, tools, modelService, computerService, sessionHubClient); // Agent-facing feature commands. const lookService = createLookService(agentConfig, modelService, contextManager, llmService, shellWrapper); const listenService = createListenService(agentConfig, modelService, contextManager, llmService, shellWrapper); const desktopService = createDesktopService(computerService, contextManager, output, agentConfig, modelService, shellWrapper, commandLoopState, inputMode, desktopClaimService); const genimg = createGenImg(globalConfig, agentConfig, costTracker, output, modelService.getImageModel); const userDisplayService = createUserDisplayService(userService, inputMode, localUserId); const mailQueryService = hubClient ? createMailQueryService(hubClient, localUserId) : null; const mailService = createMailService(hubClient, userService, mailQueryService, localUserId, promptNotification, attachmentService, shellWrapper, globalConfig, agentManager, runService); const chatService = createChatService(hubClient, userService, localUserId, promptNotification, attachmentService, shellWrapper, runService); const subagentService = createSubagentService(mailService, chatService, output, agentManager, inputMode, userService, localUserId, promptNotification, hubClient, globalConfig, agentConfig, runService, costTracker); // Per-agent paged-output buffer shared across shell, lynx, and browser so // `ns-more` always pages the most recent over-budget output for THIS agent. const pagedOutputBuffer = createPagedOutputBuffer(globalConfig); const lynxService = createLynxService(globalConfig, output, pagedOutputBuffer); const browserService = createBrowserService(globalConfig, agentConfig, contextManager, output, modelService, pagedOutputBuffer); const webSearchService = createWebSearchService(globalConfig, agentConfig, costTracker, lynxService); // Command dispatch and main loop. const platformConfig = getPlatformConfig(); const promptBuilder = createPromptBuilder(globalConfig, agentConfig, shellWrapper, contextManager, costTracker, output, inputMode, platformConfig, promptNotification, localUserId); const shellCommand = createShellCommand(shellWrapper, inputMode, pagedOutputBuffer); const ptyService = createPtyService(shellWrapper); const sessionService = createSessionService(globalConfig, agentConfig, shellCommand, output, contextManager, systemMessage, llmService, logService, hubAttachmentClient, inputMode, restoreData); const commandProtection = createCommandProtection(agentConfig, llmService, output, commandLoopState); const debugCommands = createDebugCommands(contextManager, output, inputMode, systemMessage, agentManager, localUserId); const commentCommand = { command: commentCmd, handleCommand: () => // Important - Hint the LLM to turn their thoughts into actions // ./bin/ns-comment shell script has the same message "Comment noted. Try running commands now to achieve your goal.", }; const commandRegistry = createCommandRegistry(inputMode, [ commentCommand, pagedOutputBuffer.moreCommand, ...shellWrapper.commands, lynxService, browserService, webSearchService, genimg, desktopService, lookService, listenService, subagentService, mailService, chatService, costDisplayService, sessionService, workspaces, ptyService, userDisplayService, ...debugCommands, agentConfig, ...(hubClient ? [hubClient, hostService] : []), ]); const commandHandler = createCommandHandler(globalConfig, agentConfig, commandProtection, promptBuilder, shellCommand, shellWrapper, commandRegistry, contextManager, output, inputMode, commandLoopState, sessionService); const commandLoop = createCommandLoop(globalConfig, agentConfig, commandHandler, promptBuilder, shellCommand, lynxService, contextManager, workspaces, llmService, systemMessage, output, logService, inputMode, runService, promptNotification, localUserId, mailService, chatService, hubClient, sessionService, modelService, desktopService, commandLoopState, startupAttachmentService, pagedOutputBuffer); const abortController = new AbortController(); const config = agentConfig.agentConfig(); return { agentUserId: localUserId, parentUserId: subagentContext?.parentUserId, agentUsername: config.username, agentTitle: config.title, shellModel: config.shellModel, getRunId: runService.getRunId, getSessionId: runService.getSessionId, isPaused: commandLoop.isPaused, setPaused: commandLoop.setPaused, getState: commandLoopState.getState, getTokenCount: contextManager.getTokenCount, output, subagentService, naisysApiService, runCommandLoop: async () => { try { return await commandLoop.run(abortController.signal); } catch (ex) { output.errorAndLog(`AGENT CRASHED: ${ex}`, ex); return `error: ${ex}`; } }, requestShutdown: (reason) => { abortController.abort(reason); }, completeShutdown: () => { commandLoop.cleanup(); costTracker.cleanup(); mailService.cleanup(); chatService.cleanup(); void browserService.cleanup(); desktopService.cleanup(); }, }; } // Re-stamp entries with the parent's identity tuple before forwarding to the // host buffer. sessionId rides through unchanged. flushFinal is a no-op: the // host owns the underlying buffer's lifecycle. function wrapLogBufferForSubagent(parent, ctx) { if (!parent) return undefined; return { pushEntry: (entry, resolveAttachment) => parent.pushEntry({ ...entry, userId: ctx.parentUserId, runId: ctx.parentRunId, subagentId: ctx.subagentId, }, resolveAttachment), flushFinal: () => Promise.resolve(), }; } function wrapCostBufferForSubagent(parent, ctx) { if (!parent) return undefined; // Budget callbacks are no-ops: subagent costs roll into the parent's pool // via the parent's userId on the wrapped entries. return { pushEntry: (entry) => parent.pushEntry({ ...entry, userId: ctx.parentUserId, runId: ctx.parentRunId, subagentId: ctx.subagentId, }), registerBudgetCallback: () => { }, unregisterBudgetCallback: () => { }, flushFinal: () => Promise.resolve(), }; } //# sourceMappingURL=agentRuntime.js.map