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 documentation ticket 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, docsTicketTypeId, linkTypeId, agentId, promptConfigId] = await Promise.all([ resolveTypeId('thread', 'eng_ticket_thread'), resolveTypeId('item', 'workbench_docs_ticket'), resolveLinkTypeId('workbench_documented_by'), resolveAgentId('Workbench Docs Ticket Creator'), resolvePromptConfigId('workbench_docs_ticket_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 { docs_ticket_id: existingLink.target_id, title: target?.title || '', created: false, existing: true, } } const placeholder = await adminCreate(ctx, { entity: 'items', type_id: docsTicketTypeId, title: `Draft docs ticket for ${item.title || 'work item'}`, description: 'AI-generated documentation ticket draft - review required', status: 'draft', data: { work_item_id: item_id, status: 'draft', }, } as any) const docsTicketId = (placeholder as any)?.id ?? (placeholder as any)?.data?.id if (!docsTicketId) { const e: any = new Error('Failed to create docs ticket') e.statusCode = 500 throw e } const threadRecord = await adminCreate(ctx, { entity: 'threads', type_id: engTicketThreadId, target_type: 'items', target_id: docsTicketId, title: 'Docs ticket 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 docs ticket 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 || `Docs: ${item.title || 'Work item'}`, description: plan?.description || `Documentation for ${item.title || 'work item'}`, data: { work_item_id: item_id, doc_type: plan?.doc_type || 'internal_note', audience: plan?.audience || 'engineering', status: 'draft', content_outline: plan?.content_outline || '', known_limitations: Array.isArray(plan?.known_limitations) ? plan.known_limitations : [], notes: plan?.notes || '', }, } await adminDataUpdate(ctx, 'items', docsTicketId, updatePayload) await adminCreate(ctx, { entity: 'links', type_slug: 'link', link_type_id: linkTypeId, link_type: 'workbench_documented_by', source_type: 'items', source_id: item_id, target_type: 'items', target_id: docsTicketId, } as any) return { docs_ticket_id: docsTicketId, title: updatePayload.title, created: true, existing: false, } })