diff --git a/src/cli/tui.ts b/src/cli/tui.ts index 559daf5..063eea7 100644 --- a/src/cli/tui.ts +++ b/src/cli/tui.ts @@ -216,6 +216,7 @@ export function registerTuiCommand(program: Command): void { modelRouter, systemPrompt, model: config.models.default.model, + agent, onExit: cleanup, }); } else { @@ -259,6 +260,7 @@ export function registerTuiCommand(program: Command): void { modelRouter, systemPrompt, model: config.models.default.model, + agent, onExit: cleanup, }); return; diff --git a/src/frontends/tui/components/App.tsx b/src/frontends/tui/components/App.tsx index 6dfb02f..199162f 100644 --- a/src/frontends/tui/components/App.tsx +++ b/src/frontends/tui/components/App.tsx @@ -7,6 +7,7 @@ import { parseCommand, getHelpText, resolveModelAlias, getCommandCompletions } f import type { Message, ModelClient, TokenUsage } from '../../../models/types.js'; import type { ModelRouter } from '../../../models/router.js'; import type { ManagedSession } from '../../../session/index.js'; +import type { NativeAgent } from '../../../backends/native/agent.js'; export interface AppProps { session: ManagedSession; @@ -14,6 +15,7 @@ export interface AppProps { modelRouter?: ModelRouter; systemPrompt: string; model: string; + agent?: NativeAgent; onExit?: () => void; } @@ -23,6 +25,7 @@ export function App({ modelRouter, systemPrompt, model, + agent, onExit, }: AppProps): React.ReactElement { const { exit } = useApp(); @@ -156,19 +159,33 @@ export function App({ if (command.type !== 'message' || isStreaming) return; - // Add user message + // Add user message to UI (and session if no agent — agent adds it internally) const userMessage: Message = { role: 'user', content: command.content }; - const messageWithTimestamp = session.addMessage(userMessage); - setMessages(prev => [...prev, messageWithTimestamp]); + if (!agent) { + const messageWithTimestamp = session.addMessage(userMessage); + setMessages(prev => [...prev, messageWithTimestamp]); + } else { + setMessages(prev => [...prev, { ...userMessage, timestamp: Date.now() }]); + } setScrollOffset(0); // Auto-scroll to bottom - // Stream response + // Process response setIsStreaming(true); setStreamingContent(''); abortRef.current = false; try { - if (modelClient.chatStream) { + if (agent) { + // agent.process() handles session history internally + const response = await agent.process(command.content); + + const usage = agent.getUsage(); + setTokenUsage({ inputTokens: usage.inputTokens, outputTokens: usage.outputTokens }); + + // Sync UI with session history (agent already added messages to session) + setMessages(session.getHistory()); + } else if (modelClient.chatStream) { + // Fallback: direct streaming without tools let fullContent = ''; for await (const event of modelClient.chatStream({ @@ -199,7 +216,7 @@ export function App({ const assistantWithTimestamp = session.addMessage(assistantMessage); setMessages(prev => [...prev, assistantWithTimestamp]); } else { - // Fallback to non-streaming + // Fallback: non-streaming without tools const response = await modelClient.chat({ messages: session.getHistory(), system: systemPrompt, @@ -225,7 +242,7 @@ export function App({ setIsStreaming(false); setStreamingContent(''); } - }, [isStreaming, session, modelClient, modelRouter, systemPrompt, exit, onExit, messages.length, tokenUsage]); + }, [isStreaming, session, agent, modelClient, modelRouter, systemPrompt, exit, onExit, messages.length, tokenUsage]); return ( diff --git a/src/frontends/tui/fullscreen.ts b/src/frontends/tui/fullscreen.ts index a7703a3..0ffa108 100644 --- a/src/frontends/tui/fullscreen.ts +++ b/src/frontends/tui/fullscreen.ts @@ -4,6 +4,7 @@ import { App } from './components/index.js'; import type { ManagedSession } from '../../session/index.js'; import type { ModelClient } from '../../models/types.js'; import type { ModelRouter } from '../../models/router.js'; +import type { NativeAgent } from '../../backends/native/agent.js'; export interface FullscreenTuiConfig { session: ManagedSession; @@ -11,6 +12,7 @@ export interface FullscreenTuiConfig { modelRouter?: ModelRouter; systemPrompt: string; model: string; + agent?: NativeAgent; onExit?: () => void; } @@ -27,6 +29,7 @@ export async function startFullscreenTui(config: FullscreenTuiConfig): Promise