feat: implement processDomainConsultation main handler

- Add full conversation flow with session management
- Handle tool call execution
- Support __PASSTHROUGH__ and __SESSION_END__ markers
- Add hasDomainSession helper for routing
- Export executeDomainAction from domain-tool.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-02-05 09:57:32 +09:00
parent 160569bf8c
commit f9f577f25c
4 changed files with 1417 additions and 7 deletions

View File

@@ -593,10 +593,93 @@ export async function processDomainConsultation(
userMessage: string,
env: Env
): Promise<string> {
// TODO: Implement in Task 8
logger.info('도메인 상담 처리 요청 (미구현)', {
userId,
message: userMessage.slice(0, 50),
});
return '__PASSTHROUGH__';
const startTime = Date.now();
logger.info('도메인 상담 시작', { userId, message: userMessage.substring(0, 100) });
try {
// 1. Check for existing session
let session = await getDomainSession(db, userId);
// 2. Create new session if none exists and message seems domain-related
// (For first call, we always try to process - AI will return __PASSTHROUGH__ if not relevant)
if (!session) {
session = createDomainSession(userId, 'gathering');
}
// 3. Add user message to session
addMessageToSession(session, 'user', userMessage);
// 4. Call AI to get response and possible tool calls
const aiResult = await callDomainExpertAI(session, userMessage, env);
// 5. Handle __PASSTHROUGH__ - not domain related
if (aiResult.response === '__PASSTHROUGH__' || aiResult.response.includes('__PASSTHROUGH__')) {
logger.info('도메인 상담 패스스루', { userId });
// Don't save session if passthrough
return '__PASSTHROUGH__';
}
// 6. Execute tool calls if any
let toolResults: string[] = [];
if (aiResult.toolCalls && aiResult.toolCalls.length > 0) {
for (const toolCall of aiResult.toolCalls) {
const result = await executeDomainToolCall(
toolCall.name,
toolCall.arguments,
env,
userId,
db
);
toolResults.push(result);
}
}
// 7. Build final response
let finalResponse = aiResult.response;
if (toolResults.length > 0) {
// If we have tool results, include them in the response
// The AI response might be a summary, but tool results have the actual data
finalResponse = toolResults.join('\n\n');
if (aiResult.response && !aiResult.response.includes('__SESSION_END__')) {
finalResponse = aiResult.response + '\n\n' + finalResponse;
}
}
// 8. Handle __SESSION_END__ - session complete
if (finalResponse.includes('__SESSION_END__')) {
logger.info('도메인 상담 세션 종료', { userId });
await deleteDomainSession(db, userId);
finalResponse = finalResponse.replace('__SESSION_END__', '').trim();
return finalResponse;
}
// 9. Add assistant response to session and save
addMessageToSession(session, 'assistant', finalResponse);
session.updated_at = Date.now();
await saveDomainSession(db, session);
logger.info('도메인 상담 완료', {
userId,
duration: Date.now() - startTime,
hasToolCalls: aiResult.toolCalls?.length || 0
});
return finalResponse;
} catch (error) {
logger.error('도메인 상담 오류', error as Error, { userId });
return '죄송합니다. 도메인 상담 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
}
}
/**
* 도메인 세션 존재 여부 확인 (라우팅용)
*
* @param db - D1 Database
* @param userId - Telegram User ID
* @returns true if active session exists, false otherwise
*/
export async function hasDomainSession(db: D1Database, userId: string): Promise<boolean> {
const session = await getDomainSession(db, userId);
return session !== null && !isSessionExpired(session);
}

View File

@@ -470,7 +470,7 @@ async function callNamecheapApi(
}
// 도메인 작업 직접 실행 (Agent 없이 코드로 처리)
async function executeDomainAction(
export async function executeDomainAction(
action: string,
args: { domain?: string; nameservers?: string[]; tld?: string },
allowedDomains: string[],