import { createHandler } from './_shared/middleware' import { resolveTypeId, resolveAgentId, resolvePromptConfigId } from './_shared/resolve-ids' import { runAgent } from './_shared/index' import { create as adminCreate, update as adminUpdate, list as adminList } from './admin-data' function adminDataUpdate(ctx: any, entity: string, id: string, fields: Record) { return adminUpdate({ ...ctx, query: { ...((ctx as any).query || {}), entity, id } } as any, fields) } function adminDataList(ctx: any, entity: string, filters: Record) { return adminList({ ...ctx, query: { ...((ctx as any).query || {}), entity, ...filters } } as any, {}) } async function getWorkbenchCilConfig(ctx: any) { const appId = ctx.appId const filters: any = { limit: '1' } if (appId) { filters.id = appId } else { filters.slug = 'workbench' } const data = await adminDataList(ctx, 'apps', filters) const app = Array.isArray(data) ? data[0] : (data as any)?.data?.[0] return app?.config?.cil ?? {} } function buildInitialIntakeMessage(item: any): string { const d = item.data || {} const parts = [ `Analyze this work item and produce an intake plan.`, ``, `Title: ${item.title || '(no title)'}`, `Description: ${item.description || '(no description)'}`, d.work_type && `Work Type: ${d.work_type}`, item.status && `Status: ${item.status}`, d.priority && `Priority: ${d.priority}`, d.severity && `Severity: ${d.severity}`, d.team && `Team: ${d.team}`, d.support_summary && `Support Summary: ${d.support_summary}`, d.cil_project_id && `CIL Project ID: ${d.cil_project_id}`, d.kb_article_id && `KB Article ID: ${d.kb_article_id}`, ].filter(Boolean) as string[] return parts.join('\n') } function extractIntakePlan(content: string): any { const match = content.match(/```json\s*([\s\S]*?)\s*```/) if (!match) return null try { return JSON.parse(match[1]) } catch { return null } } export const handler = createHandler(async (ctx, body) => { const { item_id, user_message } = (body || {}) as { item_id?: string; user_message?: string } if (!item_id) { const e: any = new Error('item_id is required') e.statusCode = 400 throw e } const itemResult = await adminDataList(ctx, 'items', { id: item_id, limit: '1' }) const item = Array.isArray(itemResult) ? itemResult[0] : (itemResult as any)?.data?.[0] if (!item) { const e: any = new Error('Work item not found') e.statusCode = 404 throw e } const [engTicketThreadId, agentId, promptConfigId] = await Promise.all([ resolveTypeId('thread', 'eng_ticket_thread'), resolveAgentId('Workbench Intake Agent'), resolvePromptConfigId('workbench_intake'), ]) const threadResult = await adminDataList(ctx, 'threads', { target_id: item_id, target_type: 'items', type_id: engTicketThreadId, limit: '1', }) const existingThread = Array.isArray(threadResult) ? threadResult[0] : (threadResult as any)?.data?.[0] const cilConfig = await getWorkbenchCilConfig(ctx) const threadData: Record = { agent_id: agentId, prompt_config_id: promptConfigId, } if (item.data?.cil_project_id || cilConfig?.enable_cil) { threadData.cil_config = { enabled: true, project_ids: item.data?.cil_project_id ? [item.data.cil_project_id] : Array.isArray(cilConfig.cil_projects) ? cilConfig.cil_projects : [], use_cil_tools: cilConfig.use_cil_tools === true, } } let threadId = existingThread?.id if (!threadId) { const threadRecord = await adminCreate(ctx, { entity: 'threads', type_id: engTicketThreadId, target_type: 'items', target_id: item_id, title: 'Workbench timeline', visibility: 'internal', status: 'active', data: threadData, } as any) threadId = (threadRecord as any)?.id ?? (threadRecord as any)?.data?.id ?? null } else { const currentData = existingThread.data || {} if (currentData.agent_id !== agentId || currentData.prompt_config_id !== promptConfigId) { await adminDataUpdate(ctx, 'threads', threadId, { data: { ...currentData, ...threadData }, }) } } if (!threadId) { const e: any = new Error('Failed to create or resolve timeline thread') e.statusCode = 500 throw e } const message = user_message?.trim() ? user_message.trim() : buildInitialIntakeMessage(item) const agentMsg = await runAgent(threadId, message, ctx) const intakePlan = extractIntakePlan(agentMsg?.content || '') if (intakePlan && agentMsg?.id) { const currentData = agentMsg.data || {} await adminDataUpdate(ctx, 'messages', agentMsg.id, { data: { ...currentData, intake: intakePlan, }, }) } return { thread_id: threadId, message_id: agentMsg?.id, content: agentMsg?.content || '', confidence: agentMsg?.data?.confidence ?? 0, intake: intakePlan, retrieved_sources: agentMsg?.data?.retrieved_sources || [], } })