import { NextRequest, NextResponse } from 'next/server' import { getDatabase, db_helpers } from '@/lib/db' import { runOpenClaw } from '@/lib/command' import { requireRole } from '@/lib/auth' import { validateBody, createMessageSchema } from '@/lib/validation' import { mutationLimiter } from '@/lib/rate-limit' import { logger } from '@/lib/logger' import { scanForInjection } from '@/lib/injection-guard' import { scanForSecrets } from '@/lib/secret-scanner' import { logSecurityEvent } from '@/lib/security-events' export async function POST(request: NextRequest) { const auth = requireRole(request, 'operator') if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) const rateCheck = mutationLimiter(request) if (rateCheck) return rateCheck try { const result = await validateBody(request, createMessageSchema) if ('error' in result) return result.error const { to, message } = result.data const from = auth.user.display_name || auth.user.username || 'system' // Scan message for injection — this gets forwarded directly to an agent const injectionReport = scanForInjection(message, { context: 'prompt' }) if (!injectionReport.safe) { const criticals = injectionReport.matches.filter(m => m.severity === 'critical') if (criticals.length > 0) { logger.warn({ to, rules: criticals.map(m => m.rule) }, 'Blocked agent message: injection detected') return NextResponse.json( { error: 'Message blocked: potentially unsafe content detected', injection: criticals.map(m => ({ rule: m.rule, description: m.description })) }, { status: 422 } ) } } const secretHits = scanForSecrets(message) if (secretHits.length > 0) { try { logSecurityEvent({ event_type: 'secret_exposure', severity: 'critical', source: 'agent-message', agent_name: from, detail: JSON.stringify({ count: secretHits.length, types: secretHits.map(s => s.type) }), workspace_id: auth.user.workspace_id ?? 1, tenant_id: 1 }) } catch {} } const db = getDatabase() const workspaceId = auth.user.workspace_id ?? 1; const agent = db .prepare('SELECT * FROM agents WHERE name = ? AND workspace_id = ?') .get(to, workspaceId) as any if (!agent) { return NextResponse.json({ error: 'Recipient agent not found' }, { status: 404 }) } if (!agent.session_key) { return NextResponse.json( { error: 'Recipient agent has no session key configured' }, { status: 400 } ) } await runOpenClaw( [ 'gateway', 'sessions_send', '--session', agent.session_key, '--message', `Message from ${from}: ${message}` ], { timeoutMs: 10000 } ) db_helpers.createNotification( to, 'message', 'Direct Message', `${from}: ${message.substring(0, 200)}${message.length > 200 ? '...' : ''}`, 'agent', agent.id, workspaceId ) db_helpers.logActivity( 'agent_message', 'agent', agent.id, from, `Sent message to ${to}`, { to }, workspaceId ) return NextResponse.json({ success: true }) } catch (error) { logger.error({ err: error }, 'POST /api/agents/message error') return NextResponse.json({ error: 'Failed to send message' }, { status: 500 }) } }