R4 introduced classify_via_llm() which built classification_prompt but fell through to regex without using it, tripping noUnusedLocals (TS6133). Use 'void classification_prompt' to preserve the GA prompt structure as documentation while satisfying strict lint. Removed console.warn (no @types/node / dom lib in ES2022 target). Regression scope: third-round verification. - 169/169 tests pass - IPC files (B14) bun-build clean (EXIT=0) - MainAgent transpile clean (EXIT=0) - Confirmed remaining tsc errors are environmental (missing @types/node: fs/path/crypto/Buffer) or pre-existing (config write-only field, EventIngestor value-as-type), NOT R4 regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
209 lines
5.6 KiB
TypeScript
Executable File
209 lines
5.6 KiB
TypeScript
Executable File
/**
|
|
* 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<string> {
|
|
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<string> {
|
|
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<void> {
|
|
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'
|
|
}
|
|
}
|