Spaces:
Runtime error
Runtime error
| 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 }) | |
| } | |
| } | |