Files
Agent-System-Backend/internal/store/audit.go
T
weichen 5c52c71f9c Add cmdbus: registry-driven command dispatch with audit and inline confirm
Why: T3 of the interaction foundation - unify button/card/slash/chat entries onto one command bus (D27) composing the Agent-Command library (D30), with danger=high commands gated behind inline confirm cards and every dispatch leaving the same audit envelope.
Evidence: go test green - cmdbus suite (four-entrance audit symmetry, high-danger confirm lifecycle incl. cancel/expiry/handler-reject, audit-unavailable yields unknown, plugin spec fail-closed, schema scalar validation) and server e2e (danger command via POST /api/commands -> confirm card -> card action confirm -> committed receipt -> audit query ordered accepted->committed); legacy /help,/new,/mock,/model,/profile contract preserved through the bus; store migration v7 command_audit (both dialects) with roundtrip test updated.
Impact: new internal/cmdbus (L1 package per D33), server routes POST /api/commands/{id} + GET /api/commands/audit + registry-driven GET /api/commands, postMessage chat-entrance dispatch when the intent gate matches a registered command, plugins host OnCommands overlay, module cards policy/modules/{cmdbus,cardsvc}.md; pending confirm state is process-local (documented limitation).
Refs: docs/proposals/2026-09-30-ui-inbox-command-workspace.md T3; task card feature/command-bus-r1
2026-09-30 07:51:31 +00:00

56 lines
1.5 KiB
Go

package store
import (
"database/sql"
"encoding/json"
"time"
agentcommand "agent-command"
)
// AppendAudit 追加命令审计信封(cmdbus 端口;v7 command_audit)。
func (s *Store) AppendAudit(env agentcommand.AuditEnvelope) error {
raw, err := json.Marshal(env)
if err != nil {
return err
}
_, err = s.db.Exec(s.q(`INSERT INTO command_audit (time, command_id, source, entrance, state, operation_id, conversation_id, payload)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`),
env.Time.UTC().Format(time.RFC3339Nano), env.CommandID, string(env.Source), env.Entrance, string(env.State),
env.OperationID, env.ConversationID, string(raw))
return err
}
// ListAudits 审计查询(时间倒序;conversationID 空 = 全局)。
func (s *Store) ListAudits(conversationID string, limit int) ([]agentcommand.AuditEnvelope, error) {
if limit <= 0 || limit > 500 {
limit = 100
}
var (
rows *sql.Rows
err error
)
if conversationID == "" {
rows, err = s.db.Query(s.q(`SELECT payload FROM command_audit ORDER BY time DESC LIMIT ?`), limit)
} else {
rows, err = s.db.Query(s.q(`SELECT payload FROM command_audit WHERE conversation_id=? ORDER BY time DESC LIMIT ?`), conversationID, limit)
}
if err != nil {
return nil, err
}
defer rows.Close()
out := []agentcommand.AuditEnvelope{}
for rows.Next() {
var raw string
if err := rows.Scan(&raw); err != nil {
return nil, err
}
var env agentcommand.AuditEnvelope
if err := json.Unmarshal([]byte(raw), &env); err != nil {
return nil, err
}
out = append(out, env)
}
return out, rows.Err()
}