import { createHandler } from './_shared/middleware' import { resolveTypeId, resolveAgentId, resolvePromptConfigId, resolveLinkTypeId } 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, {}) } function extractJsonPlan(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 } } async function buildCreatorMessage(item: any, threadMessages: any[]): Promise { const d = item.data || {} const parentSummary = [ `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}`, ].filter(Boolean).join('\n') const analysis = threadMessages .filter((m: any) => m.data?.message_type === 'agent' || m.direction === 'outbound') .sort((a: any, b: any) => (b.sequence || 0) - (a.sequence || 0))[0] const parts = [ `Create a test plan for the following engineering work item.`, ``, parentSummary, ] if (analysis?.content) { parts.push(``, `Latest analysis:`, analysis.content) } return parts.join('\n') } export const handler = createHandler(async (ctx, body) => { const { item_id } = (body || {}) as { item_id?: 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, testPlanTypeId, linkTypeId, agentId, promptConfigId] = await Promise.all([ resolveTypeId('thread', 'eng_ticket_thread'), resolveTypeId('item', 'workbench_test_plan'), resolveLinkTypeId('workbench_verified_by'), resolveAgentId('Workbench Test Plan Creator'), resolvePromptConfigId('workbench_test_plan_creator'), ]) const linkResult = await adminDataList(ctx, 'links', { source_id: item_id, source_type: 'items', link_type_id: linkTypeId, target_type: 'items', limit: '1', }) const existingLink = Array.isArray(linkResult) ? linkResult[0] : (linkResult as any)?.data?.[0] if (existingLink?.target_id) { const targetResult = await adminDataList(ctx, 'items', { id: existingLink.target_id, limit: '1' }) const target = Array.isArray(targetResult) ? targetResult[0] : (targetResult as any)?.data?.[0] return { test_plan_id: existingLink.target_id, title: target?.title || '', created: false, existing: true, } } const placeholder = await adminCreate(ctx, { entity: 'items', type_id: testPlanTypeId, title: `Draft test plan for ${item.title || 'work item'}`, description: 'AI-generated test plan draft - review required', status: 'draft', data: { work_item_id: item_id, status: 'draft', }, } as any) const testPlanId = (placeholder as any)?.id ?? (placeholder as any)?.data?.id if (!testPlanId) { const e: any = new Error('Failed to create test plan') e.statusCode = 500 throw e } const threadRecord = await adminCreate(ctx, { entity: 'threads', type_id: engTicketThreadId, target_type: 'items', target_id: testPlanId, title: 'Test plan draft', visibility: 'internal', status: 'active', data: { agent_id: agentId, prompt_config_id: promptConfigId, }, } as any) const threadId = (threadRecord as any)?.id ?? (threadRecord as any)?.data?.id if (!threadId) { const e: any = new Error('Failed to create test plan thread') e.statusCode = 500 throw e } const parentThreadResult = await adminDataList(ctx, 'threads', { target_id: item_id, target_type: 'items', limit: '1', }) const parentThreads = Array.isArray(parentThreadResult) ? parentThreadResult : (parentThreadResult as any)?.data const parentThreadId = parentThreads?.[0]?.id const messagesResult = await adminDataList(ctx, 'messages', { thread_id: parentThreadId, limit: '20', sort_field: 'sequence', sort_direction: 'desc', }) const threadMessages = Array.isArray(messagesResult) ? messagesResult : (messagesResult as any)?.data const userMessage = await buildCreatorMessage(item, threadMessages || []) const agentMsg = await runAgent(threadId, userMessage, ctx) const plan = extractJsonPlan(agentMsg?.content || '') if (plan && agentMsg?.id) { const currentData = agentMsg.data || {} await adminDataUpdate(ctx, 'messages', agentMsg.id, { data: { ...currentData, plan }, }) } const updatePayload: any = { title: plan?.title || `Test Plan: ${item.title || 'Work item'}`, description: plan?.description || `Test plan for ${item.title || 'work item'}`, data: { work_item_id: item_id, plan_type: plan?.plan_type || 'manual', status: 'draft', test_case_ids: Array.isArray(plan?.test_cases) ? plan.test_cases.map((tc: any, i: number) => ({ id: `${i + 1}`, ...tc })) : [], notes: plan?.notes || '', }, } await adminDataUpdate(ctx, 'items', testPlanId, updatePayload) await adminCreate(ctx, { entity: 'links', type_slug: 'link', link_type_id: linkTypeId, link_type: 'workbench_verified_by', source_type: 'items', source_id: item_id, target_type: 'items', target_id: testPlanId, } as any) return { test_plan_id: testPlanId, title: updatePayload.title, created: true, existing: false, } })