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
56 lines
1.5 KiB
Go
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()
|
|
}
|