Add docs/16 for session state machine, incomplete/resume, queue semantics, and agent-loop/harness/MCP gap review. Wire RunOutcome on max-rounds, run_id through audit, TrimMessages/Redact hooks, ConfirmBindHash on write confirm path; session file skeleton without HTTP yet. No push.
59 lines
1.3 KiB
Go
59 lines
1.3 KiB
Go
package aitools
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// AuditEntry MCP/Agent 工具审计;不含密码与大结果全文。 // Logan.Li
|
|
type AuditEntry struct {
|
|
Time string `json:"time"`
|
|
Tool string `json:"tool"`
|
|
ConnectionID string `json:"connection_id,omitempty"`
|
|
Mode string `json:"mode"`
|
|
Source string `json:"source,omitempty"` // mcp | agent
|
|
RunID string `json:"run_id,omitempty"`
|
|
OK bool `json:"ok"`
|
|
Summary string `json:"summary,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
var auditMu sync.Mutex
|
|
|
|
func AuditPath() (string, error) {
|
|
dir, err := opsxdbDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir, "logs", "mcp-audit.jsonl"), nil
|
|
}
|
|
|
|
// AppendAudit 追加一行 JSONL;失败静默(不影响工具主路径)。
|
|
func AppendAudit(e AuditEntry) {
|
|
if e.Time == "" {
|
|
e.Time = time.Now().Format(time.RFC3339)
|
|
}
|
|
path, err := AuditPath()
|
|
if err != nil {
|
|
return
|
|
}
|
|
auditMu.Lock()
|
|
defer auditMu.Unlock()
|
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
|
return
|
|
}
|
|
b, err := json.Marshal(e)
|
|
if err != nil {
|
|
return
|
|
}
|
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
_, _ = f.Write(append(b, '\n'))
|
|
}
|