import { createClientFromRequest } from 'npm:@base44/sdk@0.8.31'; Deno.serve(async (req) => { try { const base44 = createClientFromRequest(req); const user = await base44.auth.me(); if (!user) return Response.json({ error: 'Unauthorized' }, { status: 401 }); const body = await req.json(); const { event_id, event_type, subject_entity_type, subject_entity_id, context_summary, metadata = {}, related_entities = [] } = body; const entities = base44.asServiceRole.entities; // 1. Fetch context from the Workforce Graph let graphContext = {}; try { const graphRes = await base44.asServiceRole.functions.invoke('queryWorkforceGraph', { query_type: 'subgraph', entity_type: subject_entity_type, entity_id: subject_entity_id }); graphContext = graphRes.data?.graph || {}; } catch (e) { console.log('Graph query failed (non-blocking):', e.message); } // 2. Fetch similar past memories (Operational Memory) const similarMemories = await entities.OperationalMemory.filter( { trigger_event_type: event_type }, '-created_date', 5 ); const memoryContext = similarMemories.map(m => ({ decision: m.decision?.recommendation || 'N/A', outcome: m.outcome || 'pending', outcome_score: m.outcome_score, reasoning: m.reasoning?.substring(0, 200) || '' })); // 3. Determine department based on event type const departmentMap = { 'shift.completed': 'operations', 'shift.cancelled': 'operations', 'shift.no_show': 'operations', 'order.created': 'operations', 'order.cancelled': 'operations', 'order.filled': 'operations', 'assignment.cancelled': 'operations', 'invoice.overdue': 'finance', 'vendor.rate_changed': 'procurement', 'worker.availability_changed': 'operations', 'worker.certification_expired': 'compliance', 'budget.threshold_exceeded': 'finance', 'compliance.flagged': 'compliance', 'system.anomaly_detected': 'executive' }; const department = departmentMap[event_type] || 'operations'; // 4. Build the reasoning prompt with full context — CAUSAL CHAIN reasoning const prompt = `You are the KROW Intelligence Engine — an AI workforce reasoning system for contingent labor management. You do not answer. You REASON. When an event occurs, you trace the CAUSAL CHAIN backwards — from the observed event through each contributing factor (connected by ↓) to the root cause(s). You do not give generic analysis. You follow the chain. AN EVENT HAS OCCURRED: Event Type: ${event_type} Summary: ${context_summary || 'No summary provided'} Subject: ${subject_entity_type} (${subject_entity_id}) CURRENT GRAPH STATE (connected entities, relationships, metrics): ${JSON.stringify(graphContext, null, 2).substring(0, 3000)} SIMILAR PAST DECISIONS (Operational Memory — what happened last time): ${memoryContext.length > 0 ? JSON.stringify(memoryContext, null, 2) : 'No similar past decisions found.'} EVENT METADATA: ${JSON.stringify(metadata, null, 2).substring(0, 1000)} REASONING INSTRUCTIONS: 1. OBSERVATION: State what happened as a fact. 2. CAUSAL CHAIN: Trace WHY this event occurred. Walk backwards from the event through contributing factors. Each link in the chain is a factor that CAUSED the next. The chain flows: Factor A → caused → Factor B → caused → Factor C → caused → [Root Cause] Use the graph state, metadata, and past memories as evidence. Reference specific entities, workers, vendors, shifts, or metrics. Do not be generic. Categories: vendor, operations, workforce, training, demand, external, compliance, finance, equipment, transportation, system. 3. ROOT CAUSE(S): The deepest factor(s) — the thing that, if fixed, prevents recurrence. 4. PREDICTION: What will happen next if no action is taken. 5. RECOMMENDATION: Target the root cause, not just the symptom. Respond as JSON.`; // 5. Call the LLM for reasoning let aiDecision = {}; try { const llmResponse = await base44.asServiceRole.integrations.Core.InvokeLLM({ prompt, response_json_schema: { type: 'object', properties: { observation: { type: 'string', description: 'What happened, stated as a fact' }, causal_chain: { type: 'array', description: 'Causal chain traced backwards from the event to root cause(s)', items: { type: 'object', properties: { step: { type: 'number' }, factor: { type: 'string' }, evidence: { type: 'string' }, category: { type: 'string', enum: ['vendor', 'operations', 'workforce', 'training', 'demand', 'external', 'compliance', 'finance', 'equipment', 'transportation', 'system'] }, leads_to: { type: 'string' } } } }, root_causes: { type: 'array', items: { type: 'string' } }, prediction: { type: 'string' }, recommendation: { type: 'string' }, confidence_score: { type: 'number' }, risk_level: { type: 'string' }, proposed_actions: { type: 'array', items: { type: 'object', properties: { action_type: { type: 'string' }, description: { type: 'string' }, auto_execute: { type: 'boolean' } } } }, reasoning: { type: 'string', description: 'Full reasoning chain in natural language' } } } }); aiDecision = typeof llmResponse === 'string' ? JSON.parse(llmResponse) : llmResponse; } catch (llmErr) { console.log('LLM reasoning failed:', llmErr.message); aiDecision = { prediction: 'Unable to generate prediction', recommendation: 'Manual review recommended', confidence_score: 0, risk_level: 'medium', proposed_actions: [], reasoning: 'LLM reasoning failed: ' + llmErr.message }; } // 6. Create Operational Memory — store the causal chain const memory_id = `MEM-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`; const memory = await entities.OperationalMemory.create({ memory_id, trigger_event_id: event_id, trigger_event_type: event_type, context_snapshot: { graph: graphContext, metadata }, decision: aiDecision, reasoning: aiDecision.reasoning || '', causal_chain: aiDecision.causal_chain || [], root_causes: aiDecision.root_causes || [], similar_memories_consulted: similarMemories.map(m => m.memory_id), status: 'pending', agent_name: `${department}_agent`, department, tags: [event_type, department, aiDecision.risk_level || 'medium'] }); // 7. Mark event as reasoning-triggered await entities.WorkforceEvent.update(event_id, { reasoning_triggered: true, memory_id: memory.id }); // 8. Log agent activity await entities.AgentActivity.create({ agent_name: `${department}_agent`, department, activity_type: 'event_processed', trigger_event_id: event_id, memory_id: memory.id, summary: `Processed ${event_type}: ${aiDecision.recommendation || 'No recommendation'}`, details: { confidence: aiDecision.confidence_score, risk: aiDecision.risk_level }, impact_level: aiDecision.risk_level === 'high' ? 'high' : (aiDecision.risk_level === 'medium' ? 'medium' : 'low'), entities_affected: [{ entity_type: subject_entity_type, entity_id: subject_entity_id }] }); // 9. If high risk, queue for human approval; if low risk + auto_execute, execute const hasAutoActions = (aiDecision.proposed_actions || []).some(a => a.auto_execute); if (aiDecision.risk_level === 'high' || (aiDecision.confidence_score || 0) < 60) { await entities.DecisionQueue.create({ memory_id: memory.id, trigger_event_id: event_id, title: `${department} decision: ${event_type}`, summary: aiDecision.recommendation || 'AI recommends manual review', department, agent_name: `${department}_agent`, proposed_actions: aiDecision.proposed_actions || [], confidence_score: aiDecision.confidence_score || 0, risk_level: aiDecision.risk_level || 'medium', status: 'pending_approval', expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), context_snapshot: { graph: graphContext, event_summary: context_summary } }); } return Response.json({ success: true, memory_id: memory.id, decision: aiDecision, graph_nodes: graphContext.nodes?.length || 0, similar_memories: similarMemories.length }); } catch (error) { return Response.json({ error: error.message }, { status: 500 }); } });