Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

1758 lines
48 KiB
Go

package duckdb
import (
"context"
"crypto/rand"
"crypto/sha256"
"database/sql"
"encoding/hex"
"fmt"
"slices"
"sort"
"strconv"
"strings"
"time"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/export"
pricingpkg "go.kenn.io/agentsview/internal/pricing"
)
const projectIdentityPublicationStateKey = "duckdb_project_identity_publication_revision_v2"
func (s *Sync) projectIdentityStateKey(databaseGeneration string) string {
key := projectIdentityPublicationStateKey + ":" + databaseGeneration
if !s.isFiltered() {
return s.syncStateKey(key)
}
projects := append([]string(nil), s.projects...)
excludes := append([]string(nil), s.excludeProjects...)
sort.Strings(projects)
sort.Strings(excludes)
projects = slices.Compact(projects)
excludes = slices.Compact(excludes)
sum := sha256.New()
writeField := func(value string) {
_, _ = fmt.Fprintf(sum, "%d:", len(value))
_, _ = sum.Write([]byte(value))
}
writeField("include")
writeField(strconv.Itoa(len(projects)))
for _, project := range projects {
writeField(project)
}
writeField("exclude")
writeField(strconv.Itoa(len(excludes)))
for _, project := range excludes {
writeField(project)
}
return s.syncStateKey(
key + ":project-filter:" + hex.EncodeToString(sum.Sum(nil)[:8]),
)
}
func (s *Sync) syncModelPricing(ctx context.Context) error {
prices, err := s.local.ListModelPricing(ctx)
if err != nil {
return err
}
if len(prices) == 0 {
prices = duckFallbackPricingRows()
}
if len(prices) == 0 {
return nil
}
existing, err := s.listDuckModelPricing(ctx)
if err != nil {
return err
}
_, prices = db.FilterChangedModelPricing(existing, prices)
if len(prices) == 0 {
return nil
}
tx, err := s.duck.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("beginning duckdb pricing sync: %w", err)
}
defer func() {
_ = tx.Rollback()
}()
for i := 0; i < len(prices); i += duckPricingUpsertBatch {
end := min(i+duckPricingUpsertBatch, len(prices))
batch := prices[i:end]
query, args := duckPricingUpsertStatement(batch)
if err := s.execMutation(ctx, tx, query, args...); err != nil {
return fmt.Errorf(
"syncing duckdb pricing batch starting at %d: %w",
i, err,
)
}
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("committing duckdb pricing sync: %w", err)
}
return nil
}
const duckPricingUpsertBatch = 100
func duckPricingUpsertStatement(prices []db.ModelPricing) (string, []any) {
var b strings.Builder
b.WriteString(`INSERT INTO model_pricing (
model_pattern, input_per_mtok, output_per_mtok,
cache_creation_per_mtok, cache_read_per_mtok, updated_at
) VALUES `)
args := make([]any, 0, len(prices)*6)
for i, p := range prices {
if i > 0 {
b.WriteString(", ")
}
b.WriteString("(?, ?, ?, ?, ?, ?)")
args = append(args,
p.ModelPattern,
p.InputPerMTok,
p.OutputPerMTok,
p.CacheCreationPerMTok,
p.CacheReadPerMTok,
p.UpdatedAt,
)
}
b.WriteString(`
ON CONFLICT(model_pattern) DO UPDATE SET
input_per_mtok = excluded.input_per_mtok,
output_per_mtok = excluded.output_per_mtok,
cache_creation_per_mtok = excluded.cache_creation_per_mtok,
cache_read_per_mtok = excluded.cache_read_per_mtok,
updated_at = excluded.updated_at`)
return b.String(), args
}
func (s *Sync) listDuckModelPricing(ctx context.Context) ([]db.ModelPricing, error) {
rows, err := queryDuckDBContext(
ctx, s.duck, s.connectionKind, s.quack,
`SELECT model_pattern, input_per_mtok,
output_per_mtok, cache_creation_per_mtok,
cache_read_per_mtok, updated_at
FROM model_pricing`,
)
if err != nil {
return nil, fmt.Errorf("listing duckdb pricing: %w", err)
}
defer rows.Close()
var out []db.ModelPricing
for rows.Next() {
var p db.ModelPricing
if err := rows.Scan(
&p.ModelPattern,
&p.InputPerMTok,
&p.OutputPerMTok,
&p.CacheCreationPerMTok,
&p.CacheReadPerMTok,
&p.UpdatedAt,
); err != nil {
return nil, fmt.Errorf("scanning duckdb pricing: %w", err)
}
out = append(out, p)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterating duckdb pricing: %w", err)
}
return out, nil
}
func (s *Sync) syncCursorUsageEvents(ctx context.Context) error {
// Cursor admin rows are global and unattributed, so project-filtered pushes
// cannot sync them honestly.
if s.isFiltered() {
return nil
}
events, err := s.local.GetCursorUsageEvents(ctx)
if err != nil {
return fmt.Errorf("loading local cursor usage events: %w", err)
}
if len(events) == 0 {
return nil
}
tx, err := s.duck.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("beginning duckdb cursor usage sync: %w", err)
}
defer func() {
_ = tx.Rollback()
}()
if err := s.bulkInsertCursorUsageEvents(ctx, tx, events); err != nil {
return err
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("committing duckdb cursor usage sync: %w", err)
}
return nil
}
func (s *Sync) syncProjectIdentityObservations(
ctx context.Context, force bool,
) error {
revision, err := s.local.ProjectIdentityPublicationRevision(ctx)
if err != nil {
return err
}
databaseGeneration, err := s.local.GetDatabaseID(ctx)
if err != nil {
return fmt.Errorf("loading source database generation: %w", err)
}
revisionValue := strconv.FormatInt(revision, 10)
stateKey := s.projectIdentityStateKey(databaseGeneration)
publishedRevisionValue, err := s.local.GetSyncState(stateKey)
if err != nil {
return fmt.Errorf("reading duckdb project identity publication revision: %w", err)
}
archiveID, err := s.local.GetArchiveID(ctx)
if err != nil {
return fmt.Errorf("loading source archive id: %w", err)
}
fullPublication := force || publishedRevisionValue == ""
var publishedRevision int64
if !fullPublication {
publishedRevision, err = strconv.ParseInt(publishedRevisionValue, 10, 64)
if err != nil || publishedRevision < 0 || publishedRevision > revision {
fullPublication = true
} else if publishedRevision == revision {
var archivePresent bool
if err := s.duck.QueryRowContext(ctx, `
SELECT count(*) > 0 FROM source_archives
WHERE source_archive_id = ?`, archiveID).Scan(&archivePresent); err != nil {
return fmt.Errorf("checking duckdb project identity publication: %w", err)
}
if archivePresent {
return nil
}
fullPublication = true
}
}
var observations []export.ProjectIdentityObservation
var snapshots []export.ProjectIdentityObservation
var delta db.ProjectIdentityPublicationDelta
if fullPublication {
observations, err = s.local.ListProjectIdentityObservations(ctx, nil)
if err != nil {
return fmt.Errorf("loading project identity observations: %w", err)
}
if len(s.projects) > 0 || len(s.excludeProjects) > 0 {
out := observations[:0]
for _, obs := range observations {
if projectInSyncScope(obs.Project, s.projects, s.excludeProjects) {
out = append(out, obs)
}
}
observations = out
}
snapshots, err = s.local.ListSessionProjectIdentitySnapshots(ctx)
if err != nil {
return fmt.Errorf("loading session project identity snapshots: %w", err)
}
if len(s.projects) > 0 || len(s.excludeProjects) > 0 {
out := snapshots[:0]
for _, snapshot := range snapshots {
if projectInSyncScope(
snapshot.Project, s.projects, s.excludeProjects,
) {
out = append(out, snapshot)
}
}
snapshots = out
}
} else {
delta, err = s.local.LoadProjectIdentityPublicationDelta(
ctx, publishedRevision, revision, s.projects, s.excludeProjects,
)
if err != nil {
return err
}
observations = delta.Observations
snapshots = delta.Snapshots
}
archiveSalt, err := s.local.GetArchiveSalt(ctx)
if err != nil {
return fmt.Errorf("loading source archive salt: %w", err)
}
tx, err := s.duck.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("beginning duckdb project identity sync: %w", err)
}
defer func() {
_ = tx.Rollback()
}()
if err := upsertSourceArchiveScope(
func(stmt string, args ...any) error {
return s.execMutation(ctx, tx, stmt, args...)
},
func(stmt string, args ...any) *sql.Row {
return tx.QueryRowContext(ctx, stmt, args...)
},
archiveID, archiveSalt,
); err != nil {
return err
}
execDelta := func(stmt string, args ...any) error {
return s.execMutation(ctx, tx, stmt, args...)
}
if fullPublication {
if err := deleteProjectIdentityScope(
execDelta, archiveID, s.projects, s.excludeProjects,
); err != nil {
return err
}
} else if err := deleteProjectIdentityDelta(
execDelta, archiveID, databaseGeneration,
delta.ObservationDeletes, delta.SnapshotDeletes,
); err != nil {
return err
}
for _, obs := range observations {
obs.SourceArchiveID = archiveID
obs.SourceArchiveSalt = archiveSalt
obs = export.SanitizeStoredProjectIdentityObservation(obs)
if err := upsertProjectIdentityObservation(
func(stmt string, args ...any) error {
return s.execMutation(ctx, tx, stmt, args...)
},
func(stmt string, args ...any) *sql.Row {
return tx.QueryRowContext(ctx, stmt, args...)
},
obs, "",
); err != nil {
return fmt.Errorf(
"syncing duckdb project identity observation %s/%s/%s: %w",
obs.Project, obs.Machine, obs.RootPath, err,
)
}
}
for i := range snapshots {
snapshots[i] = export.SanitizeStoredProjectIdentityObservation(snapshots[i])
}
if err := upsertSessionProjectIdentitySnapshots(
func(stmt string, args ...any) error {
return s.execMutation(ctx, tx, stmt, args...)
},
archiveID, databaseGeneration, snapshots,
); err != nil {
return fmt.Errorf("syncing duckdb session project identity snapshots: %w", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("committing duckdb project identity sync: %w", err)
}
if err := s.local.SetSyncState(stateKey, revisionValue); err != nil {
return fmt.Errorf("recording duckdb project identity publication revision: %w", err)
}
return nil
}
func duckFallbackPricingRows() []db.ModelPricing {
src := pricingpkg.FallbackPricing()
out := make([]db.ModelPricing, len(src))
now := time.Now().UTC().Format(time.RFC3339Nano)
for i, p := range src {
out[i] = db.ModelPricing{
ModelPattern: p.ModelPattern,
InputPerMTok: p.InputPerMTok,
OutputPerMTok: p.OutputPerMTok,
CacheCreationPerMTok: p.CacheCreationPerMTok,
CacheReadPerMTok: p.CacheReadPerMTok,
UpdatedAt: now,
}
}
return out
}
func (s *Sync) replaceStarredSessions(
ctx context.Context, tx *sql.Tx, sessions []db.Session,
) error {
ids, err := s.local.ListStarredSessionIDs(ctx)
if err != nil {
return err
}
allowed := make(map[string]bool, len(sessions))
for _, sess := range sessions {
allowed[sess.ID] = true
}
if s.isFiltered() {
for _, sess := range sessions {
if err := s.execMutation(ctx, tx,
`DELETE FROM starred_sessions WHERE session_id = ?`, sess.ID,
); err != nil {
return fmt.Errorf("clearing duckdb starred session %s: %w", sess.ID, err)
}
}
} else {
if err := s.execMutation(ctx, tx, `
DELETE FROM starred_sessions
WHERE session_id IN (
SELECT id FROM sessions WHERE machine = ?
)`, s.machine); err != nil {
return fmt.Errorf("clearing duckdb starred_sessions: %w", err)
}
}
for _, id := range ids {
if !allowed[id] {
continue
}
if _, err := tx.ExecContext(ctx,
`INSERT INTO starred_sessions (session_id, created_at)
VALUES (?, current_timestamp)`,
id,
); err != nil {
return fmt.Errorf("syncing starred session %s: %w", id, err)
}
}
return nil
}
func (s *Sync) pushSession(
ctx context.Context,
exec duckMutationExecutor,
target duckQueryer,
sess db.Session,
full bool,
) (int, error) {
if err := s.upsertSession(ctx, exec, sess); err != nil {
return 0, err
}
msgs, err := s.local.GetAllMessages(ctx, sess.ID)
if err != nil {
return 0, fmt.Errorf("reading local messages for %s: %w", sess.ID, err)
}
if !full {
action, err := s.duckMessagePushAction(ctx, target, sess.ID, msgs)
if err != nil {
return 0, err
}
switch action.kind {
case duckMessagePushSkip:
if err := s.replaceSessionSecretFindings(ctx, exec, sess.ID); err != nil {
return 0, err
}
if err := s.replaceSessionPinnedMessages(ctx, exec, sess.ID); err != nil {
return 0, err
}
return 0, nil
case duckMessagePushAppend:
suffix := messagesAfterDuckOrdinal(msgs, action.maxOrdinal)
if len(suffix) == 0 {
return 0, fmt.Errorf(
"duckdb append %s: no local suffix after ordinal %d",
sess.ID, action.maxOrdinal,
)
}
if err := s.replaceUsageEvents(ctx, exec, sess.ID); err != nil {
return 0, err
}
if err := insertMessages(ctx, exec, suffix); err != nil {
return 0, err
}
if err := insertToolCalls(ctx, exec, suffix); err != nil {
return 0, err
}
if err := insertToolResultEvents(ctx, exec, suffix); err != nil {
return 0, err
}
if err := s.replaceSessionSecretFindings(ctx, exec, sess.ID); err != nil {
return 0, err
}
if err := s.replaceSessionPinnedMessages(ctx, exec, sess.ID); err != nil {
return 0, err
}
return len(suffix), nil
case duckMessagePushReplace:
default:
return 0, fmt.Errorf("unknown duckdb message push action %d", action.kind)
}
}
if err := s.replaceSessionDependents(ctx, exec, sess.ID); err != nil {
return 0, err
}
if err := s.replaceUsageEvents(ctx, exec, sess.ID); err != nil {
return 0, err
}
if err := insertMessages(ctx, exec, msgs); err != nil {
return 0, err
}
if err := s.replaceToolRows(ctx, exec, sess.ID, msgs); err != nil {
return 0, err
}
if err := s.replaceSecretFindings(ctx, exec, sess.ID); err != nil {
return 0, err
}
if err := s.replacePinnedMessages(ctx, exec, sess.ID); err != nil {
return 0, err
}
return len(msgs), nil
}
func (s *Sync) replaceSessionDependents(
ctx context.Context, exec duckMutationExecutor, sessionID string,
) error {
for _, stmt := range []string{
`DELETE FROM pinned_messages WHERE session_id = ?`,
`DELETE FROM secret_findings WHERE session_id = ?`,
`DELETE FROM usage_events WHERE session_id = ?`,
`DELETE FROM messages WHERE session_id = ?`,
} {
if err := s.execMutation(ctx, exec, stmt, sessionID); err != nil {
return fmt.Errorf("clearing duckdb session dependents: %w", err)
}
}
return nil
}
func (s *Sync) deleteHardDeletedMirrorSessions(
ctx context.Context, tx *sql.Tx, localSessions []db.Session,
machine string, projects, excludeProjects []string,
) ([]string, map[string]bool, error) {
localIDs := make(map[string]bool, len(localSessions))
for _, sess := range localSessions {
localIDs[sess.ID] = true
}
mirrorIDs := make(map[string]bool)
rows, err := tx.QueryContext(ctx,
`SELECT id, project FROM sessions WHERE machine = ?`,
machine,
)
if err != nil {
return nil, nil, fmt.Errorf("listing duckdb sessions for deletion reconciliation: %w", err)
}
defer rows.Close()
var stale []string
for rows.Next() {
var id, project string
if err := rows.Scan(&id, &project); err != nil {
return nil, nil, fmt.Errorf("scanning duckdb session for deletion reconciliation: %w", err)
}
mirrorIDs[id] = true
if !projectInSyncScope(project, projects, excludeProjects) {
continue
}
if !localIDs[id] {
stale = append(stale, id)
delete(mirrorIDs, id)
}
}
if err := rows.Err(); err != nil {
return nil, nil, err
}
sort.Strings(stale)
for _, id := range stale {
if err := s.deleteMirrorSession(ctx, tx, id); err != nil {
return nil, nil, err
}
}
return stale, mirrorIDs, nil
}
func (s *Sync) deleteMirrorSession(
ctx context.Context, tx *sql.Tx, sessionID string,
) error {
for _, stmt := range []string{
`DELETE FROM pinned_messages WHERE session_id = ?`,
`DELETE FROM starred_sessions WHERE session_id = ?`,
`DELETE FROM secret_findings WHERE session_id = ?`,
`DELETE FROM tool_result_events WHERE session_id = ?`,
`DELETE FROM tool_calls WHERE session_id = ?`,
`DELETE FROM usage_events WHERE session_id = ?`,
`DELETE FROM messages WHERE session_id = ?`,
`DELETE FROM sessions WHERE id = ?`,
} {
if err := s.execMutation(ctx, tx, stmt, sessionID); err != nil {
return fmt.Errorf("deleting hard-deleted duckdb session %s: %w", sessionID, err)
}
}
return nil
}
func projectInSyncScope(project string, projects, excludeProjects []string) bool {
if len(projects) > 0 {
found := slices.Contains(projects, project)
if !found {
return false
}
}
return !slices.Contains(excludeProjects, project)
}
func (s *Sync) execMutation(
ctx context.Context, exec duckMutationExecutor, stmt string, args ...any,
) error {
if s.connectionKind != duckDBQuackClientConnection {
_, err := exec.ExecContext(ctx, stmt, args...)
return err
}
if _, ok := exec.(*duckRemoteMutationBatch); ok {
_, err := exec.ExecContext(ctx, stmt, args...)
return err
}
// Quack attachments can accept plain inserts, but DELETE, UPDATE, and
// ON CONFLICT are planned against proxy storage and currently fail with
// GetStorageInfo errors. Run those mutations on the server-side base DB.
sqlText, err := duckSQLWithArgs(stmt, args...)
if err != nil {
return err
}
_, err = exec.ExecContext(ctx, "FROM "+quackAttachmentName+".query(?)", sqlText)
return err
}
type duckMutationExecutor interface {
ExecContext(context.Context, string, ...any) (sql.Result, error)
}
const (
duckRemoteMutationCoalesceMaxBytes = 2 << 20
duckRemoteInsertCoalesceMaxRows = 256
duckRemoteInsertCoalesceMaxBytes = 1 << 20
)
type duckRemoteMutationBatch struct {
statements []string
renderedValid bool
renderedStatementsCache []duckRemoteRenderedStatement
renderedStatementBytes int
}
type duckRemoteRenderedStatement struct {
sql string
start, end int
}
func (b *duckRemoteMutationBatch) ExecContext(
_ context.Context, stmt string, args ...any,
) (sql.Result, error) {
sqlText, err := duckSQLWithArgs(stmt, args...)
if err != nil {
return nil, err
}
b.statements = append(b.statements, sqlText)
b.invalidateRendered()
return duckNoopResult{}, nil
}
func (b *duckRemoteMutationBatch) Len() int {
return len(b.statements)
}
func (b *duckRemoteMutationBatch) appendBatch(other *duckRemoteMutationBatch) {
if other == nil || other.Len() == 0 {
return
}
oldLen := len(b.statements)
preserveRendered := b.renderedValid
rendered := b.renderedStatementsCache
renderedBytes := b.renderedStatementBytes
b.statements = append(b.statements, other.statements...)
if !preserveRendered {
b.invalidateRendered()
return
}
for _, stmt := range other.rendered() {
stmt.start += oldLen
stmt.end += oldLen
rendered = append(rendered, stmt)
renderedBytes += len(stmt.sql)
}
b.renderedStatementsCache = rendered
b.renderedStatementBytes = renderedBytes
b.renderedValid = true
}
func (b *duckRemoteMutationBatch) invalidateRendered() {
b.renderedValid = false
b.renderedStatementsCache = nil
b.renderedStatementBytes = 0
}
type duckNoopResult struct{}
func (duckNoopResult) LastInsertId() (int64, error) { return 0, nil }
func (duckNoopResult) RowsAffected() (int64, error) { return 0, nil }
func (s *Sync) execRemoteMutationBatch(
ctx context.Context, label string, batch *duckRemoteMutationBatch,
) error {
return execDuckRemoteMutationBatch(
ctx, s.execRemoteSQLRetry, label, batch, true,
)
}
func appendDuckRemoteMutationBatch(
ctx context.Context,
execCoalesced func(context.Context, string) error,
label string,
current *duckRemoteMutationBatch,
next *duckRemoteMutationBatch,
maxBytes int,
) (*duckRemoteMutationBatch, error) {
if current == nil {
current = &duckRemoteMutationBatch{}
}
if next == nil || next.Len() == 0 {
return current, nil
}
if maxBytes <= 0 {
maxBytes = duckRemoteMutationCoalesceMaxBytes
}
nextBytes := next.transactionBytes()
if nextBytes > maxBytes {
if err := execDuckRemoteMutationBatch(
ctx, execCoalesced, label, current, true,
); err != nil {
return current, err
}
return &duckRemoteMutationBatch{}, fmt.Errorf(
"duckdb remote mutation batch exceeds remote mutation coalesce budget: %d > %d",
nextBytes, maxBytes,
)
}
if current.Len() > 0 && current.combinedTransactionBytes(next) > maxBytes {
if err := execDuckRemoteMutationBatch(ctx, execCoalesced, label, current, true); err != nil {
return current, err
}
current = &duckRemoteMutationBatch{}
}
current.appendBatch(next)
return current, nil
}
func execDuckRemoteMutationBatch(
ctx context.Context,
exec func(context.Context, string) error,
label string,
batch *duckRemoteMutationBatch,
coalesce bool,
) (err error) {
if batch.Len() == 0 {
return nil
}
if coalesce {
if err := exec(ctx, batch.transactionSQL()); err != nil {
if isDuckRemoteMutationTimeoutError(err) {
return err
}
if rollbackErr := exec(ctx, "ROLLBACK"); rollbackErr != nil {
return fmt.Errorf("%w; rollback %s: %v", err, label, rollbackErr)
}
return err
}
return nil
}
return execDuckRemoteMutationStatements(ctx, exec, label, batch.statements)
}
func execDuckRemoteMutationRenderedStatements(
ctx context.Context,
exec func(context.Context, string) error,
label string,
batch *duckRemoteMutationBatch,
) error {
if batch.Len() == 0 {
return nil
}
rendered := batch.rendered()
statements := make([]string, 0, len(rendered))
for _, stmt := range rendered {
statements = append(statements, stmt.sql)
}
return execDuckRemoteMutationStatements(ctx, exec, label, statements)
}
func execDuckRemoteMutationStatements(
ctx context.Context,
exec func(context.Context, string) error,
label string,
statements []string,
) (err error) {
if len(statements) == 0 {
return nil
}
if err := exec(ctx, "BEGIN TRANSACTION"); err != nil {
return fmt.Errorf("begin %s: %w", label, err)
}
needsRollback := true
defer func() {
if !needsRollback {
return
}
rollbackErr := exec(ctx, "ROLLBACK")
if err != nil && rollbackErr != nil {
err = fmt.Errorf("%w; rollback %s: %v", err, label, rollbackErr)
}
}()
for i, stmt := range statements {
if err := exec(ctx, stmt); err != nil {
return fmt.Errorf(
"execute %s statement %d/%d: %w",
label, i+1, len(statements), err,
)
}
}
if err := exec(ctx, "COMMIT"); err != nil {
return fmt.Errorf("commit %s: %w", label, err)
}
needsRollback = false
return nil
}
func execDuckRemoteMutationBatchWithStatementFallback(
ctx context.Context,
execCoalesced func(context.Context, string) error,
execStatement func(context.Context, string) error,
label string,
batch *duckRemoteMutationBatch,
) error {
if err := execDuckRemoteMutationBatch(ctx, execCoalesced, label, batch, true); err != nil {
if ctx.Err() != nil ||
isStaleQuackConnectionError(err) ||
isDuckRemoteMutationTimeoutError(err) {
return err
}
return execDuckRemoteMutationBatch(ctx, execStatement, label, batch, false)
}
return nil
}
func execDuckRemoteMutationBatchOversizeWithStatementFallback(
ctx context.Context,
execCoalesced func(context.Context, string) error,
execStatement func(context.Context, string) error,
label string,
batch *duckRemoteMutationBatch,
maxBytes int,
) error {
if maxBytes <= 0 {
maxBytes = duckRemoteMutationCoalesceMaxBytes
}
if batch.transactionBytes() > maxBytes {
return execDuckRemoteMutationRenderedStatements(
ctx, execStatement, label, batch,
)
}
return execDuckRemoteMutationBatchWithStatementFallback(
ctx, execCoalesced, execStatement, label, batch,
)
}
func (b *duckRemoteMutationBatch) transactionSQL() string {
var sqlText strings.Builder
statements := b.rendered()
sqlText.WriteString("BEGIN TRANSACTION;\n")
for _, stmt := range statements {
sqlText.WriteString(stmt.sql)
sqlText.WriteString(";\n")
}
sqlText.WriteString("COMMIT")
return sqlText.String()
}
func (b *duckRemoteMutationBatch) transactionBytes() int {
if b == nil || b.Len() == 0 {
return 0
}
statements := b.rendered()
return duckRemoteTransactionBytesFor(
b.renderedStatementBytes, len(statements),
)
}
func duckRemoteTransactionBytesFor(statementBytes, statementCount int) int {
if statementCount == 0 {
return 0
}
return len("BEGIN TRANSACTION;\n") +
statementBytes +
len(";\n")*statementCount +
len("COMMIT")
}
func (b *duckRemoteMutationBatch) combinedTransactionBytes(
other *duckRemoteMutationBatch,
) int {
if b == nil || b.Len() == 0 {
return other.transactionBytes()
}
if other == nil || other.Len() == 0 {
return b.transactionBytes()
}
left := b.rendered()
right := other.rendered()
return duckRemoteTransactionBytesFor(
b.renderedStatementBytes+other.renderedStatementBytes,
len(left)+len(right),
)
}
func (b *duckRemoteMutationBatch) rendered() []duckRemoteRenderedStatement {
if b == nil || b.Len() == 0 {
return nil
}
if b.renderedValid {
return b.renderedStatementsCache
}
b.renderedStatementsCache = renderDuckRemoteMutationStatements(
b.statements,
duckRemoteInsertCoalesceMaxRows,
duckRemoteInsertCoalesceMaxBytes,
)
b.renderedStatementBytes = 0
for _, stmt := range b.renderedStatementsCache {
b.renderedStatementBytes += len(stmt.sql)
}
b.renderedValid = true
return b.renderedStatementsCache
}
func renderDuckRemoteMutationStatements(
statements []string, maxRows int, maxBytes int,
) []duckRemoteRenderedStatement {
if len(statements) == 0 {
return nil
}
if maxRows <= 0 {
maxRows = duckRemoteInsertCoalesceMaxRows
}
if maxBytes <= 0 {
maxBytes = duckRemoteInsertCoalesceMaxBytes
}
out := make([]duckRemoteRenderedStatement, 0, len(statements))
var pending duckRemoteInsertGroup
hasPending := false
flush := func() {
if !hasPending {
return
}
out = append(out, pending.RenderedStatement())
pending = duckRemoteInsertGroup{}
hasPending = false
}
for i, stmt := range statements {
prefix, tuple, ok := splitDuckRemoteSimpleInsert(stmt)
if !ok {
flush()
out = append(out, duckRemoteRenderedStatement{
sql: stmt,
start: i,
end: i + 1,
})
continue
}
if !hasPending || pending.prefix != prefix {
flush()
pending = duckRemoteInsertGroup{prefix: prefix, start: i}
hasPending = true
}
if pending.rows > 0 && (pending.rows+1 > maxRows ||
pending.bytes+len(", ")+len(tuple) > maxBytes) {
flush()
pending = duckRemoteInsertGroup{prefix: prefix, start: i}
hasPending = true
}
pending.Append(tuple, i+1)
}
flush()
return out
}
type duckRemoteInsertGroup struct {
prefix string
tuples []string
rows int
bytes int
start int
end int
}
func (g *duckRemoteInsertGroup) Append(tuple string, end int) {
if g.rows == 0 {
g.bytes = len(g.prefix)
} else {
g.bytes += len(", ")
}
g.tuples = append(g.tuples, tuple)
g.rows++
g.bytes += len(tuple)
g.end = end
}
func (g duckRemoteInsertGroup) RenderedStatement() duckRemoteRenderedStatement {
return duckRemoteRenderedStatement{
sql: g.prefix + strings.Join(g.tuples, ", "),
start: g.start,
end: g.end,
}
}
func splitDuckRemoteSimpleInsert(stmt string) (string, string, bool) {
trimmed := strings.TrimSpace(stmt)
if !duckASCIIHasPrefixFold(trimmed, "INSERT INTO ") {
return "", "", false
}
valuesIndex := duckSQLValuesKeywordIndex(trimmed)
if valuesIndex < 0 {
return "", "", false
}
prefix := strings.TrimSpace(trimmed[:valuesIndex]) + " VALUES "
rest := strings.TrimSpace(trimmed[valuesIndex+len(" VALUES "):])
tuple, suffix, ok := duckSQLLeadingParenthesizedExpression(rest)
if !ok || strings.TrimSpace(suffix) != "" {
return "", "", false
}
return prefix, tuple, true
}
func duckSQLValuesKeywordIndex(sqlText string) int {
const valuesKeyword = " VALUES "
for i := len("INSERT INTO "); i+len(valuesKeyword) <= len(sqlText); i++ {
if duckASCIIEqualFoldAt(sqlText, i, valuesKeyword) {
return i
}
}
return -1
}
func duckASCIIHasPrefixFold(s, prefix string) bool {
if len(s) < len(prefix) {
return false
}
return duckASCIIEqualFoldAt(s, 0, prefix)
}
func duckASCIIEqualFoldAt(s string, start int, pattern string) bool {
if start < 0 || start+len(pattern) > len(s) {
return false
}
for i := range len(pattern) {
if duckASCIIFold(s[start+i]) != duckASCIIFold(pattern[i]) {
return false
}
}
return true
}
func duckASCIIFold(b byte) byte {
if b >= 'a' && b <= 'z' {
return b - 'a' + 'A'
}
return b
}
func duckSQLLeadingParenthesizedExpression(sqlText string) (
string, string, bool,
) {
if !strings.HasPrefix(sqlText, "(") {
return "", "", false
}
depth := 0
for i := 0; i < len(sqlText); {
switch sqlText[i] {
case '$':
delimiter, ok := duckDollarQuoteDelimiterAt(sqlText, i)
if ok {
next := strings.Index(sqlText[i+len(delimiter):], delimiter)
if next < 0 {
return "", "", false
}
i += len(delimiter) + next + len(delimiter)
continue
}
case '\'':
next, ok := duckSingleQuotedLiteralEnd(sqlText, i)
if !ok {
return "", "", false
}
i = next
continue
case '(':
depth++
case ')':
depth--
if depth == 0 {
return sqlText[:i+1], sqlText[i+1:], true
}
if depth < 0 {
return "", "", false
}
}
i++
}
return "", "", false
}
func duckDollarQuoteDelimiterAt(sqlText string, start int) (string, bool) {
if start >= len(sqlText) || sqlText[start] != '$' {
return "", false
}
i := start + 1
for i < len(sqlText) && duckDollarQuoteTagByte(sqlText[i]) {
i++
}
if i >= len(sqlText) || sqlText[i] != '$' {
return "", false
}
return sqlText[start : i+1], true
}
func duckDollarQuoteTagByte(b byte) bool {
return b == '_' ||
('0' <= b && b <= '9') ||
('A' <= b && b <= 'Z') ||
('a' <= b && b <= 'z')
}
func duckSingleQuotedLiteralEnd(sqlText string, start int) (int, bool) {
for i := start + 1; i < len(sqlText); i++ {
if sqlText[i] != '\'' {
continue
}
if i+1 < len(sqlText) && sqlText[i+1] == '\'' {
i++
continue
}
return i + 1, true
}
return 0, false
}
func isDuckRemoteMutationTimeoutError(err error) bool {
if err == nil {
return false
}
return strings.Contains(strings.ToLower(err.Error()), "timeout was reached")
}
func (s *Sync) execRemoteSQLRetry(ctx context.Context, sqlText string) error {
if s.quack != nil {
return s.quack.execRemote(ctx, sqlText, true)
}
return s.execRemoteSQLNoRetry(ctx, sqlText)
}
func (s *Sync) execRemoteSQLNoRetry(ctx context.Context, sqlText string) error {
if s.quack != nil {
return s.quack.execRemote(ctx, sqlText, false)
}
_, err := s.duck.ExecContext(ctx, "FROM "+quackAttachmentName+".query(?)", sqlText)
return err
}
func duckSQLWithArgs(stmt string, args ...any) (string, error) {
var b strings.Builder
argIndex := 0
for _, r := range stmt {
if r != '?' {
b.WriteRune(r)
continue
}
if argIndex >= len(args) {
return "", fmt.Errorf("duckdb remote statement missing argument")
}
lit, err := duckValueLiteral(args[argIndex])
if err != nil {
return "", err
}
b.WriteString(lit)
argIndex++
}
if argIndex != len(args) {
return "", fmt.Errorf("duckdb remote statement has unused argument")
}
return b.String(), nil
}
func duckValueLiteral(v any) (string, error) {
switch value := v.(type) {
case nil:
return "NULL", nil
case string:
return duckRemoteStringLiteral(value)
case *string:
if value == nil {
return "NULL", nil
}
return duckRemoteStringLiteral(*value)
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64,
float32, float64:
return fmt.Sprint(value), nil
case *int:
if value == nil {
return "NULL", nil
}
return fmt.Sprint(*value), nil
case *int64:
if value == nil {
return "NULL", nil
}
return fmt.Sprint(*value), nil
case *float64:
if value == nil {
return "NULL", nil
}
return fmt.Sprint(*value), nil
case bool:
if value {
return "TRUE", nil
}
return "FALSE", nil
case time.Time:
return "TIMESTAMP " + duckLiteral(
value.UTC().Format("2006-01-02 15:04:05.999999"),
), nil
default:
return "", fmt.Errorf("unsupported duckdb remote argument type %T", v)
}
}
func duckRemoteStringLiteral(s string) (string, error) {
s = strings.ReplaceAll(s, "\x00", "")
for {
var tagBytes [16]byte
if _, err := rand.Read(tagBytes[:]); err != nil {
return "", fmt.Errorf("generating duckdb string literal tag: %w", err)
}
tag := "agentsview_" + hex.EncodeToString(tagBytes[:])
delimiter := "$" + tag + "$"
if strings.Contains(s, delimiter) {
continue
}
return delimiter + s + delimiter, nil
}
}
func (s *Sync) upsertSession(
ctx context.Context, exec duckMutationExecutor, sess db.Session,
) error {
query := `
INSERT INTO sessions (
id, project, machine, agent,
first_message, display_name, session_name, started_at, ended_at,
message_count, user_message_count,
file_path, file_size, file_mtime, file_inode, file_device,
file_hash, local_modified_at, transcript_revision,
parent_session_id,
relationship_type, total_output_tokens, peak_context_tokens,
has_total_output_tokens, has_peak_context_tokens, is_automated,
tool_failure_signal_count, tool_retry_count, edit_churn_count,
consecutive_failure_max, outcome, outcome_confidence,
ended_with_role, final_failure_streak, signals_pending_since,
compaction_count, mid_task_compaction_count,
context_pressure_max, health_score, health_grade,
has_tool_calls, has_context_data,
quality_signal_version, short_prompt_count, unstructured_start,
missing_success_criteria_count, missing_verification_count,
duplicate_prompt_count, no_code_context_count,
runaway_tool_loop_count, data_version,
cwd, git_branch, source_session_id, source_version, transcript_fidelity,
parser_malformed_lines, is_truncated, deleted_at, created_at,
termination_status, secret_leak_count, secrets_rules_version
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?
)`
query += `
ON CONFLICT(id) DO UPDATE SET
project = excluded.project,
machine = excluded.machine,
agent = excluded.agent,
first_message = excluded.first_message,
display_name = excluded.display_name,
session_name = excluded.session_name,
started_at = excluded.started_at,
ended_at = excluded.ended_at,
message_count = excluded.message_count,
user_message_count = excluded.user_message_count,
file_path = excluded.file_path,
file_size = excluded.file_size,
file_mtime = excluded.file_mtime,
file_inode = excluded.file_inode,
file_device = excluded.file_device,
file_hash = excluded.file_hash,
local_modified_at = excluded.local_modified_at,
transcript_revision = excluded.transcript_revision,
parent_session_id = excluded.parent_session_id,
relationship_type = excluded.relationship_type,
total_output_tokens = excluded.total_output_tokens,
peak_context_tokens = excluded.peak_context_tokens,
has_total_output_tokens = excluded.has_total_output_tokens,
has_peak_context_tokens = excluded.has_peak_context_tokens,
is_automated = excluded.is_automated,
tool_failure_signal_count = excluded.tool_failure_signal_count,
tool_retry_count = excluded.tool_retry_count,
edit_churn_count = excluded.edit_churn_count,
consecutive_failure_max = excluded.consecutive_failure_max,
outcome = excluded.outcome,
outcome_confidence = excluded.outcome_confidence,
ended_with_role = excluded.ended_with_role,
final_failure_streak = excluded.final_failure_streak,
signals_pending_since = excluded.signals_pending_since,
compaction_count = excluded.compaction_count,
mid_task_compaction_count = excluded.mid_task_compaction_count,
context_pressure_max = excluded.context_pressure_max,
health_score = excluded.health_score,
health_grade = excluded.health_grade,
has_tool_calls = excluded.has_tool_calls,
has_context_data = excluded.has_context_data,
quality_signal_version = excluded.quality_signal_version,
short_prompt_count = excluded.short_prompt_count,
unstructured_start = excluded.unstructured_start,
missing_success_criteria_count = excluded.missing_success_criteria_count,
missing_verification_count = excluded.missing_verification_count,
duplicate_prompt_count = excluded.duplicate_prompt_count,
no_code_context_count = excluded.no_code_context_count,
runaway_tool_loop_count = excluded.runaway_tool_loop_count,
data_version = excluded.data_version,
cwd = excluded.cwd,
git_branch = excluded.git_branch,
source_session_id = excluded.source_session_id,
source_version = excluded.source_version,
transcript_fidelity = excluded.transcript_fidelity,
parser_malformed_lines = excluded.parser_malformed_lines,
is_truncated = excluded.is_truncated,
deleted_at = excluded.deleted_at,
created_at = excluded.created_at,
termination_status = excluded.termination_status,
secret_leak_count = excluded.secret_leak_count,
secrets_rules_version = excluded.secrets_rules_version`
if err := s.execMutation(ctx, exec, query, sessionInsertArgs(sess, s.machine)...); err != nil {
return fmt.Errorf("writing duckdb session %s: %w", sess.ID, err)
}
return nil
}
func sessionInsertArgs(sess db.Session, machine string) []any {
return []any{
sess.ID, sess.Project, machine, sess.Agent,
nilString(sess.FirstMessage), nilString(sess.DisplayName),
nilString(sess.SessionName),
nilTime(sess.StartedAt), nilTime(sess.EndedAt),
sess.MessageCount, sess.UserMessageCount,
nilString(sess.FilePath), sess.FileSize, sess.FileMtime,
sess.FileInode, sess.FileDevice, nilString(sess.FileHash),
nilTime(sess.LocalModifiedAt), transcriptRevisionValue(sess.TranscriptRevision),
nilString(sess.ParentSessionID),
sess.RelationshipType, sess.TotalOutputTokens,
sess.PeakContextTokens, sess.HasTotalOutputTokens,
sess.HasPeakContextTokens, sess.IsAutomated,
sess.ToolFailureSignalCount, sess.ToolRetryCount,
sess.EditChurnCount, sess.ConsecutiveFailureMax,
sess.Outcome, sess.OutcomeConfidence,
sess.EndedWithRole, sess.FinalFailureStreak,
nilString(sess.SignalsPendingSince),
sess.CompactionCount, sess.MidTaskCompactionCount,
sess.ContextPressureMax, sess.HealthScore,
nilString(sess.HealthGrade), sess.HasToolCalls,
sess.HasContextData,
sess.QualitySignalVersion, sess.ShortPromptCount,
sess.UnstructuredStart, sess.MissingSuccessCriteriaCount,
sess.MissingVerificationCount, sess.DuplicatePromptCount,
sess.NoCodeContextCount, sess.RunawayToolLoopCount,
sess.DataVersion,
sess.Cwd, sess.GitBranch, sess.SourceSessionID,
sess.SourceVersion, sess.TranscriptFidelity, sess.ParserMalformedLines,
sess.IsTruncated, nilTime(sess.DeletedAt),
timeValue(sess.CreatedAt), nilString(sess.TerminationStatus),
sess.SecretLeakCount, sess.SecretsRulesVersion,
}
}
func insertMessages(
ctx context.Context, exec duckMutationExecutor, msgs []db.Message,
) error {
for _, m := range msgs {
if _, err := exec.ExecContext(ctx, `
INSERT INTO messages (
id, session_id, ordinal, role, content, thinking_text,
timestamp, has_thinking, has_tool_use, content_length,
is_system, model, token_usage, context_tokens, output_tokens,
has_context_tokens, has_output_tokens, claude_message_id,
claude_request_id, source_type, source_subtype, source_uuid,
source_parent_uuid, is_sidechain, is_compact_boundary
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
m.ID, m.SessionID, m.Ordinal, m.Role, m.Content,
m.ThinkingText, timeValue(m.Timestamp),
m.HasThinking, m.HasToolUse, m.ContentLength,
m.IsSystem, m.Model, string(m.TokenUsage),
m.ContextTokens, m.OutputTokens,
m.HasContextTokens, m.HasOutputTokens,
m.ClaudeMessageID, m.ClaudeRequestID,
m.SourceType, m.SourceSubtype, m.SourceUUID,
m.SourceParentUUID, m.IsSidechain, m.IsCompactBoundary,
); err != nil {
return fmt.Errorf("inserting duckdb message %s/%d: %w", m.SessionID, m.Ordinal, err)
}
}
return nil
}
func (s *Sync) replaceToolRows(
ctx context.Context, exec duckMutationExecutor, sessionID string, msgs []db.Message,
) error {
if err := s.execMutation(ctx, exec,
`DELETE FROM tool_result_events WHERE session_id = ?`,
sessionID,
); err != nil {
return fmt.Errorf("clearing duckdb tool_result_events for %s: %w", sessionID, err)
}
if err := s.execMutation(ctx, exec,
`DELETE FROM tool_calls WHERE session_id = ?`,
sessionID,
); err != nil {
return fmt.Errorf("clearing duckdb tool_calls for %s: %w", sessionID, err)
}
if err := insertToolCalls(ctx, exec, msgs); err != nil {
return err
}
if err := insertToolResultEvents(ctx, exec, msgs); err != nil {
return err
}
return nil
}
func insertToolCalls(
ctx context.Context, exec duckMutationExecutor, msgs []db.Message,
) error {
for _, m := range msgs {
for i, tc := range m.ToolCalls {
if _, err := exec.ExecContext(ctx, `
INSERT INTO tool_calls (
message_id, session_id, tool_name, category,
call_index, tool_use_id, input_json, skill_name,
result_content_length, result_content,
subagent_session_id, file_path
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
m.ID, m.SessionID, tc.ToolName, tc.Category,
i, tc.ToolUseID, nilEmpty(tc.InputJSON),
nilEmpty(tc.SkillName), nilZero(tc.ResultContentLength),
nilEmpty(tc.ResultContent), nilEmpty(tc.SubagentSessionID),
nilEmpty(tc.FilePath),
); err != nil {
return fmt.Errorf("inserting duckdb tool_call %s/%d/%d: %w",
m.SessionID, m.Ordinal, i, err)
}
}
}
return nil
}
func insertToolResultEvents(
ctx context.Context, exec duckMutationExecutor, msgs []db.Message,
) error {
for _, m := range msgs {
for i, tc := range m.ToolCalls {
for _, ev := range tc.ResultEvents {
if _, err := exec.ExecContext(ctx, `
INSERT INTO tool_result_events (
session_id, tool_call_message_ordinal, call_index,
tool_use_id, agent_id, subagent_session_id,
source, status, content, content_length,
timestamp, event_index
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
m.SessionID, m.Ordinal, i,
nilEmpty(ev.ToolUseID), nilEmpty(ev.AgentID),
nilEmpty(ev.SubagentSessionID), ev.Source, ev.Status,
ev.Content, ev.ContentLength, timeValue(ev.Timestamp),
ev.EventIndex,
); err != nil {
return fmt.Errorf("inserting duckdb tool_result_event %s/%d/%d: %w",
m.SessionID, m.Ordinal, i, err)
}
}
}
}
return nil
}
func (s *Sync) replaceUsageEvents(
ctx context.Context, exec duckMutationExecutor, sessionID string,
) error {
events, err := s.local.GetUsageEvents(ctx, sessionID)
if err != nil {
return err
}
if err := s.execMutation(ctx, exec,
`DELETE FROM usage_events WHERE session_id = ?`,
sessionID,
); err != nil {
return fmt.Errorf("clearing duckdb usage_events for %s: %w", sessionID, err)
}
for _, ev := range events {
if err := insertUsageEvent(ctx, exec, ev); err != nil {
return fmt.Errorf("inserting duckdb usage_event %s: %w", sessionID, err)
}
}
return nil
}
func insertUsageEvent(
ctx context.Context, exec duckMutationExecutor, ev db.UsageEvent,
) error {
ordinal, cost, occurredAt := usageEventNullableValues(ev)
if _, err := exec.ExecContext(ctx, `
INSERT INTO usage_events (
id, session_id, message_ordinal, source, model,
input_tokens, output_tokens,
cache_creation_input_tokens, cache_read_input_tokens,
reasoning_tokens, cost_usd, cost_status, cost_source,
occurred_at, dedup_key
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
ev.ID, ev.SessionID, ordinal, ev.Source, ev.Model,
ev.InputTokens, ev.OutputTokens,
ev.CacheCreationInputTokens, ev.CacheReadInputTokens,
ev.ReasoningTokens, cost, ev.CostStatus,
ev.CostSource, occurredAt, ev.DedupKey,
); err != nil {
return err
}
return nil
}
func (s *Sync) bulkInsertCursorUsageEvents(
ctx context.Context, tx *sql.Tx, events []db.CursorUsageEvent,
) error {
if len(events) == 0 {
return nil
}
const cursorBatch = 100
for i := 0; i < len(events); i += cursorBatch {
end := min(i+cursorBatch, len(events))
batch := events[i:end]
var b strings.Builder
b.WriteString(`INSERT INTO cursor_usage_events (
occurred_at, model, kind,
input_tokens, output_tokens,
cache_write_tokens, cache_read_tokens,
charged_cents, cursor_token_fee,
user_id, user_email, is_headless, dedup_key
) VALUES `)
args := make([]any, 0, len(batch)*13)
for j, ev := range batch {
if j > 0 {
b.WriteByte(',')
}
b.WriteString("(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
occurredAt, ok := parseTimestamp(ev.OccurredAt)
if !ok {
return fmt.Errorf("parsing cursor usage occurred_at %q", ev.OccurredAt)
}
args = append(args,
occurredAt,
db.SanitizeUTF8(ev.Model),
db.SanitizeUTF8(ev.Kind),
ev.InputTokens,
ev.OutputTokens,
ev.CacheWriteTokens,
ev.CacheReadTokens,
ev.ChargedCents,
ev.CursorTokenFee,
db.SanitizeUTF8(ev.UserID),
db.SanitizeUTF8(ev.UserEmail),
ev.IsHeadless,
db.SanitizeUTF8(ev.DedupKey),
)
}
b.WriteString(` ON CONFLICT DO NOTHING`)
if err := s.execMutation(ctx, tx, b.String(), args...); err != nil {
return fmt.Errorf("bulk inserting duckdb cursor_usage_events: %w", err)
}
}
return nil
}
func usageEventNullableValues(ev db.UsageEvent) (any, any, any) {
var ordinal any
if ev.MessageOrdinal != nil {
ordinal = *ev.MessageOrdinal
}
var cost any
if ev.CostUSD != nil {
cost = *ev.CostUSD
}
var occurredAt any
if ev.OccurredAt != "" {
occurredAt = timeValue(ev.OccurredAt)
}
return ordinal, cost, occurredAt
}
func (s *Sync) replaceSecretFindings(
ctx context.Context, exec duckMutationExecutor, sessionID string,
) error {
findings, err := s.local.SessionSecretFindings(ctx, sessionID)
if err != nil {
return err
}
for _, f := range findings {
if _, err := exec.ExecContext(ctx, `
INSERT INTO secret_findings (
session_id, rule_name, confidence, location_kind,
message_ordinal, call_index, event_index,
match_start, match_end, match_index,
redacted_match, rules_version, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp)`,
f.SessionID, f.RuleName, f.Confidence, f.LocationKind,
f.MessageOrdinal, f.CallIndex, f.EventIndex,
f.MatchStart, f.MatchEnd, f.MatchIndex,
f.RedactedMatch, f.RulesVersion,
); err != nil {
return fmt.Errorf("inserting duckdb secret_finding %s: %w", sessionID, err)
}
}
return nil
}
func (s *Sync) replaceSessionSecretFindings(
ctx context.Context, exec duckMutationExecutor, sessionID string,
) error {
if err := s.execMutation(ctx, exec,
`DELETE FROM secret_findings WHERE session_id = ?`,
sessionID,
); err != nil {
return fmt.Errorf("clearing duckdb secret_findings for %s: %w", sessionID, err)
}
return s.replaceSecretFindings(ctx, exec, sessionID)
}
func (s *Sync) replaceSessionPinnedMessages(
ctx context.Context, exec duckMutationExecutor, sessionID string,
) error {
if err := s.execMutation(ctx, exec,
`DELETE FROM pinned_messages WHERE session_id = ?`,
sessionID,
); err != nil {
return fmt.Errorf("clearing duckdb pinned_messages for %s: %w", sessionID, err)
}
return s.replacePinnedMessages(ctx, exec, sessionID)
}
func (s *Sync) replacePinnedMessages(
ctx context.Context, exec duckMutationExecutor, sessionID string,
) error {
pins, err := s.local.ListPinnedMessages(ctx, sessionID, "")
if err != nil {
return err
}
for _, p := range pins {
if _, err := exec.ExecContext(ctx, `
INSERT INTO pinned_messages (
id, session_id, message_id, ordinal, note, created_at
) VALUES (?, ?, ?, ?, ?, ?)`,
p.ID, p.SessionID, p.MessageID, p.Ordinal,
p.Note, timeValue(p.CreatedAt),
); err != nil {
return fmt.Errorf("inserting duckdb pinned_message %s/%d: %w",
sessionID, p.MessageID, err)
}
}
return nil
}
func (s *Sync) replaceAllPinnedMessages(
ctx context.Context, tx *sql.Tx, sessions []db.Session,
) error {
if err := s.execMutation(ctx, tx, `
DELETE FROM pinned_messages
WHERE session_id IN (
SELECT id FROM sessions WHERE machine = ?
)`, s.machine); err != nil {
return fmt.Errorf("clearing duckdb pinned_messages: %w", err)
}
for _, sess := range sessions {
if err := s.replacePinnedMessages(ctx, tx, sess.ID); err != nil {
return err
}
}
return nil
}
func (s *Sync) replaceScopedPinnedMessages(
ctx context.Context, tx *sql.Tx, sessions []db.Session,
) error {
for _, sess := range sessions {
if err := s.replaceSessionPinnedMessages(ctx, tx, sess.ID); err != nil {
return err
}
}
return nil
}
func nilString(value *string) any {
if value == nil || *value == "" {
return nil
}
return *value
}
func transcriptRevisionValue(value *string) string {
if value == nil || *value == "" {
return "0"
}
return *value
}
func nilEmpty(value string) any {
if value == "" {
return nil
}
return value
}
func nilZero(value int) any {
if value == 0 {
return nil
}
return value
}
func nilTime(value *string) any {
if value == nil || *value == "" {
return nil
}
return timeValue(*value)
}
func timeValue(value string) any {
if value == "" {
return nil
}
if t, ok := parseTimestamp(value); ok {
return t
}
return value
}
func parseTimestamp(value string) (time.Time, bool) {
candidates := []string{
time.RFC3339Nano,
"2006-01-02T15:04:05.000Z",
"2006-01-02 15:04:05",
}
value = strings.TrimSpace(value)
for _, layout := range candidates {
t, err := time.Parse(layout, value)
if err == nil {
return t.UTC(), true
}
}
return time.Time{}, false
}