feat(audit): add append-only agent audit trail (.beads/interactions.jsonl)
Implements audit logging for agent interactions to support auditing and dataset generation (fixes #649). New features: - .beads/interactions.jsonl (append-only audit log) - bd audit record: log LLM calls, tool calls, or pipe JSON via stdin - bd audit label <id>: append labels (good/bad) for dataset curation - bd compact --audit: optionally log LLM prompt/response during compaction - bd init: creates empty interactions.jsonl - bd sync: includes interactions.jsonl in staging Audit entries are append-only - labeling creates new entries that reference parent entries by ID. Closes #649 Co-authored-by: Dmitry Chichkov <dchichkov@nvidia.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,11 @@ const (
|
||||
|
||||
// Config holds configuration for the compaction process.
|
||||
type Config struct {
|
||||
APIKey string
|
||||
Concurrency int
|
||||
DryRun bool
|
||||
APIKey string
|
||||
Concurrency int
|
||||
DryRun bool
|
||||
AuditEnabled bool
|
||||
Actor string
|
||||
}
|
||||
|
||||
// Compactor handles issue compaction using AI summarization.
|
||||
@@ -53,6 +55,10 @@ func New(store *sqlite.SQLiteStorage, apiKey string, config *Config) (*Compactor
|
||||
}
|
||||
}
|
||||
}
|
||||
if haikuClient != nil {
|
||||
haikuClient.auditEnabled = config.AuditEnabled
|
||||
haikuClient.auditActor = config.Actor
|
||||
}
|
||||
|
||||
return &Compactor{
|
||||
store: store,
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/anthropics/anthropic-sdk-go"
|
||||
"github.com/anthropics/anthropic-sdk-go/option"
|
||||
"github.com/steveyegge/beads/internal/audit"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
@@ -32,6 +33,8 @@ type HaikuClient struct {
|
||||
tier1Template *template.Template
|
||||
maxRetries int
|
||||
initialBackoff time.Duration
|
||||
auditEnabled bool
|
||||
auditActor string
|
||||
}
|
||||
|
||||
// NewHaikuClient creates a new Haiku API client. Env var ANTHROPIC_API_KEY takes precedence over explicit apiKey.
|
||||
@@ -67,7 +70,23 @@ func (h *HaikuClient) SummarizeTier1(ctx context.Context, issue *types.Issue) (s
|
||||
return "", fmt.Errorf("failed to render prompt: %w", err)
|
||||
}
|
||||
|
||||
return h.callWithRetry(ctx, prompt)
|
||||
resp, callErr := h.callWithRetry(ctx, prompt)
|
||||
if h.auditEnabled {
|
||||
// Best-effort: never fail compaction because audit logging failed.
|
||||
e := &audit.Entry{
|
||||
Kind: "llm_call",
|
||||
Actor: h.auditActor,
|
||||
IssueID: issue.ID,
|
||||
Model: string(h.model),
|
||||
Prompt: prompt,
|
||||
Response: resp,
|
||||
}
|
||||
if callErr != nil {
|
||||
e.Error = callErr.Error()
|
||||
}
|
||||
_, _ = audit.Append(e)
|
||||
}
|
||||
return resp, callErr
|
||||
}
|
||||
|
||||
func (h *HaikuClient) callWithRetry(ctx context.Context, prompt string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user