feat: implementar audit trail con AuditWriter y emision de eventos
Crea shell/audit/ con Writer que escribe eventos de auditoria a archivo JSONL y opcionalmente a un room Matrix. Integra la emision de eventos en los puntos clave del runtime: - message_received: al recibir cualquier evento Matrix (handler.go) - command_exec: al ejecutar un comando (handler.go) - tool_exec: al ejecutar una tool (tools/registry.go via AuditFunc callback) - llm_request / llm_error: al llamar al LLM (llm.go) El Writer se inicializa en agents/runtime.go si security.audit.enabled=true. Usa patron de inyeccion de dependencias (MatrixSender como funcion, AuditFunc como callback) para evitar acoplamiento entre packages. Incluye tests completos para el Writer: escritura JSONL, filtrado por Include, modo solo-file, modo solo-room, auto-set de timestamp. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
coretypes "github.com/enmanuel/agents/pkg/llm"
|
||||
"github.com/enmanuel/agents/pkg/orchestration"
|
||||
"github.com/enmanuel/agents/pkg/sanitize"
|
||||
"github.com/enmanuel/agents/shell/audit"
|
||||
"github.com/enmanuel/agents/shell/bus"
|
||||
)
|
||||
|
||||
@@ -25,6 +26,15 @@ func (a *Agent) handleEvent(ctx context.Context, msgCtx decision.MessageContext,
|
||||
|
||||
roomID := evt.RoomID.String()
|
||||
|
||||
// Audit: message_received
|
||||
a.emitAudit(audit.Event{
|
||||
AgentID: a.cfg.Agent.ID,
|
||||
EventType: audit.EventMessageReceived,
|
||||
SenderID: msgCtx.SenderID,
|
||||
RoomID: roomID,
|
||||
Detail: fmt.Sprintf("is_dm=%v is_mention=%v", msgCtx.IsDirectMsg, msgCtx.IsMention),
|
||||
})
|
||||
|
||||
// Update room context for memory tools
|
||||
a.roomCtx.Set(roomID)
|
||||
|
||||
@@ -59,6 +69,16 @@ func (a *Agent) handleEvent(ctx context.Context, msgCtx decision.MessageContext,
|
||||
return
|
||||
}
|
||||
a.logger.Info("command_executed", "command", cmdName)
|
||||
|
||||
// Audit: command_exec
|
||||
a.emitAudit(audit.Event{
|
||||
AgentID: a.cfg.Agent.ID,
|
||||
EventType: audit.EventCommandExec,
|
||||
SenderID: msgCtx.SenderID,
|
||||
RoomID: roomID,
|
||||
Detail: fmt.Sprintf("command=%s", cmdName),
|
||||
})
|
||||
|
||||
reply := handler(ctx, msgCtx)
|
||||
_ = a.sendReply(ctx, roomID, msgCtx.EventID, msgCtx.ThreadID, reply)
|
||||
return
|
||||
@@ -341,6 +361,13 @@ func parseSeverity(s string) sanitize.Severity {
|
||||
}
|
||||
}
|
||||
|
||||
// emitAudit writes an audit event if the audit writer is enabled.
|
||||
func (a *Agent) emitAudit(evt audit.Event) {
|
||||
if a.auditWriter != nil {
|
||||
a.auditWriter.Emit(evt)
|
||||
}
|
||||
}
|
||||
|
||||
// sanitizeInput runs prompt injection detection on the message content.
|
||||
// Returns the (possibly modified) content and true if the message should be rejected.
|
||||
func (a *Agent) sanitizeInput(content, roomID, senderID string) (string, bool) {
|
||||
|
||||
Reference in New Issue
Block a user