import { runAgent } from './plugins.js'; import type { ModelMessage, IAgentRunResult } from './plugins.js'; import type { IChatSessionOptions, IChatCallbacks, IChatUsage } from './smartchat.interfaces.js'; /** * ChatSession manages in-memory conversation state and wraps `runAgent()`. * Stateless by design — consumers are responsible for persistence. */ export class ChatSession { private messages: ModelMessage[] = []; private usage: IChatUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0, turns: 0, }; private busy = false; constructor( private options: IChatSessionOptions, private callbacks: IChatCallbacks = {}, ) {} /** * Send a user message, run the agent, and return the result. * Updates internal message history automatically. */ async send(userMessage: string): Promise { if (this.busy) { throw new Error('ChatSession is busy — wait for the current turn to complete.'); } this.busy = true; try { const result = await runAgent({ model: this.options.model, system: this.options.system, prompt: userMessage, tools: this.options.tools, maxSteps: this.options.maxSteps ?? 20, messages: this.messages, onToken: this.callbacks.onToken, onToolCall: this.callbacks.onToolCall, }); // Update conversation history this.messages = result.messages; // Accumulate usage this.usage.inputTokens += result.usage.inputTokens; this.usage.outputTokens += result.usage.outputTokens; this.usage.totalTokens += result.usage.totalTokens; this.usage.turns++; this.callbacks.onTurnComplete?.(result); return result; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); this.callbacks.onError?.(err); throw err; } finally { this.busy = false; } } /** Whether a turn is currently in progress */ isBusy(): boolean { return this.busy; } /** Get current conversation history */ getMessages(): ModelMessage[] { return [...this.messages]; } /** Replace messages (e.g. for restoring a saved session) */ setMessages(messages: ModelMessage[]): void { this.messages = [...messages]; } /** Clear conversation history and reset usage */ clear(): void { this.messages = []; this.usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0, turns: 0 }; } /** Get accumulated token usage across all turns */ getUsage(): IChatUsage { return { ...this.usage }; } /** Update session options (model, system prompt, tools, etc.) */ updateOptions(options: Partial): void { Object.assign(this.options, options); } /** Update callbacks */ updateCallbacks(callbacks: Partial): void { Object.assign(this.callbacks, callbacks); } }