/** * MainAgent - Primary user-facing agent * * Implements DD §14.1 + state machine §20.1. * INV-1: no direct status writes (works via Scheduler/events). * INV-3: side effects only via ToolRegistry+PermissionEngine. * * @module packages/runtime/src/agents/main/MainAgent */ import type { SessionID, ProjectID } from '@aircoding/contracts' export type MainAgentState = | 'IDLE' | 'CLASSIFYING' | 'ANSWERING' | 'DELEGATING' | 'DIRECT_MODE' | 'SCHEDULING' | 'AWAITING' | 'ARCHITECTURE_DESIGNING' | 'CONFIRMING' | 'EXECUTING' | 'INTERRUPTING' | 'ARCHITECTURE_REVISING' | 'SUMMARIZING' | 'ERROR' | 'TERMINATED' export type ClassifyMode = 'regex' | 'llm' export interface MainAgentConfig { session_id: SessionID project_id: ProjectID classify_mode?: ClassifyMode // Alpha default: 'regex'; GA target: 'llm' provider_manager?: any // ProviderManager for LLM-based classify (GA) } export class MainAgent { private config: MainAgentConfig private classify_mode: ClassifyMode private provider_manager?: any state: MainAgentState = 'IDLE' constructor(config: MainAgentConfig) { this.config = config this.classify_mode = config.classify_mode || 'regex' this.provider_manager = config.provider_manager } /** * Handle incoming user message. * Classifies intent → routing decision. */ async handle_user_message(message: string): Promise<{ action: 'answer' | 'delegate' | 'direct' tasks?: string[] response?: string }> { // Classify intent (passes through a Promise.resolve for regex mode) this.state = 'CLASSIFYING' const classification = await Promise.resolve(this.classify(message)) switch (classification) { case 'simple_question': case 'clarification': this.state = 'ANSWERING' return { action: 'answer', response: 'Processing your question...' } case 'implementation_request': case 'task_request': this.state = 'DELEGATING' return { action: 'delegate', tasks: ['task-1'] } case 'direct_command': this.state = 'DIRECT_MODE' return { action: 'direct' } default: this.state = 'ANSWERING' return { action: 'answer', response: 'How can I help?' } } } /** * Classify user message intent. * - regex mode: pattern matching (Alpha scope, deterministic, synchronous) * - llm mode: calls ProviderManager for LLM-based classification (GA target, async) */ classify(message: string): string | Promise { if (this.classify_mode === 'llm' && this.provider_manager) { return this.classify_via_llm(message) } return this.classify_regex(message) } /** * Regex-based intent classification (Alpha scope, 5 patterns). */ private classify_regex(message: string): string { const lower = message.toLowerCase() if (/^(what|how|why|when|where|who|can you|could you|explain)/.test(lower)) { return 'simple_question' } if (/^(\/direct|\/done|implement|create|build|write|add|fix|change|update|remove|delete|refactor)/.test(lower)) { return 'implementation_request' } if (/^(run|execute|test|debug|check|inspect)/.test(lower)) { return 'direct_command' } return 'simple_question' } /** * LLM-based intent classification (GA target). * Calls ProviderManager→Adapter→LLM to classify intent into the state machine route. * TODO(GA): Implement by sending a classification prompt to the configured model. */ private async classify_via_llm(message: string): Promise { const classification_prompt = [ 'Classify this user message into one of:', ' simple_question | implementation_request | direct_command', '', `Message: "${message}"`, '', 'Respond with ONLY the classification string, no other text.', ].join('\n') try { // GA: const result = await this.provider_manager.complete(classification_prompt, ...) // GA: return parse_classification(result.content) // Alpha: prompt is built but not yet sent; fall through to regex as a safety net. void classification_prompt return this.classify_regex(message) } catch { return this.classify_regex(message) } } /** * Handle confirmation from user. */ async handle_confirmation(confirmed: boolean): Promise { if (this.state !== 'CONFIRMING') return if (confirmed) { this.state = 'DELEGATING' } else { this.state = 'IDLE' } } /** * Transition to idle after summarization. */ summarize(): void { this.state = 'SUMMARIZING' // After summarization completes this.state = 'IDLE' } /** * Handle an interruption at the specified change level. * 'execution' → state EXECUTING * 'design' → state ARCHITECTURE_REVISING * 'full' → state ARCHITECTURE_DESIGNING */ handle_interruption(change_level: 'execution' | 'design' | 'full'): void { this.state = 'INTERRUPTING' switch (change_level) { case 'execution': this.state = 'EXECUTING' break case 'design': this.state = 'ARCHITECTURE_REVISING' break case 'full': this.state = 'ARCHITECTURE_DESIGNING' break } } /** * Transition to CONFIRMING state (awaiting user confirmation). */ transition_to_confirming(): void { this.state = 'CONFIRMING' } /** * Transition to EXECUTING state. */ transition_to_executing(): void { this.state = 'EXECUTING' } /** * Transition to INTERRUPTING state. */ transition_to_interrupting(): void { this.state = 'INTERRUPTING' } }