Why: Account disable could be followed by late Backend business writes, API key creation could implicitly create users, account bootstrap locks could invert, and directory cursors were process-local or leaked out-of-scope changes. Evidence: Local go test ./... and isolated PostgreSQL package suites passed on the staged candidate; exact committed-head SELF replay and stage 6 human review remain pending. Impact: Adds final account transactions and external-effect checks across HTTP, Host, Bus and run manager; tightens API key issuance and persists versioned cursor signing. Delegated RDM rename still needs the separately proposed real-operator contract, so A1 and stage 6 are not complete. Refs: Documents U3 r1, approved A1-A4 amendment fb3a821, pending delegated-projection amendment 5783940, Dev feature/iam-u3-account-directory-recovery.
96 lines
3.8 KiB
Go
96 lines
3.8 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"time"
|
|
|
|
core "agent-system-core"
|
|
)
|
|
|
|
func (s *Store) CreateBackgroundTaskAccountChecked(actor, kind, title, conversationID string, allowImplicitDefault bool) (BackgroundTask, error) {
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
task := BackgroundTask{ID: NewID("task"), Owner: actor, Kind: kind, Title: title, Status: "queued",
|
|
ConversationID: conversationID, CreatedAt: now, UpdatedAt: now}
|
|
err := s.withEffectiveAccountWrite(actor, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
_, err := tx.Exec(s.q(`INSERT INTO background_tasks(id,owner,kind,title,status,progress,error,conversation_id,created_at,updated_at)
|
|
VALUES(?,?,?,?,?,?,?,?,?,?)`), task.ID, actor, task.Kind, task.Title, task.Status, task.Progress,
|
|
task.Error, task.ConversationID, task.CreatedAt, task.UpdatedAt)
|
|
return err
|
|
})
|
|
return task, err
|
|
}
|
|
|
|
func (s *Store) UpdateBackgroundTaskAccountChecked(actor, id, status string, progress int, errText string, allowImplicitDefault bool) error {
|
|
return s.withEffectiveAccountWrite(actor, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
res, err := tx.Exec(s.q(`UPDATE background_tasks SET status=?,progress=?,error=?,updated_at=? WHERE id=? AND owner=?`),
|
|
status, progress, errText, time.Now().UTC().Format(time.RFC3339), id, actor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n, err := res.RowsAffected(); err != nil {
|
|
return err
|
|
} else if n != 1 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (s *Store) PutMemoryAccountChecked(actor string, entry core.MemoryEntry, allowImplicitDefault bool) (core.MemoryEntry, error) {
|
|
if entry.ID == "" {
|
|
entry.ID = NewID("mem")
|
|
}
|
|
now := time.Now().UTC()
|
|
if entry.CreatedAt.IsZero() {
|
|
entry.CreatedAt = now
|
|
}
|
|
entry.LastAccessed = now
|
|
expiry := ""
|
|
if entry.ExpiresAt != nil {
|
|
expiry = entry.ExpiresAt.UTC().Format(time.RFC3339)
|
|
}
|
|
err := s.withEffectiveAccountWrite(actor, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
_, err := tx.Exec(s.q(`INSERT INTO memory_entries(id,owner,scope,project_id,typ,content,source,confidence,created_at,last_accessed,expires_at)
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?)`), entry.ID, actor, entry.Scope, string(entry.ProjectID), entry.Type,
|
|
entry.Content, entry.Source, entry.Confidence, entry.CreatedAt.UTC().Format(time.RFC3339),
|
|
entry.LastAccessed.UTC().Format(time.RFC3339), expiry)
|
|
return err
|
|
})
|
|
return entry, err
|
|
}
|
|
|
|
func (s *Store) AppendAuditEventAccountChecked(actor string, event AuditEvent, allowImplicitDefault bool) error {
|
|
event.Owner = actor
|
|
if event.Actor == "" {
|
|
event.Actor = actor
|
|
}
|
|
if event.Time == "" {
|
|
event.Time = time.Now().UTC().Format(time.RFC3339)
|
|
}
|
|
return s.withEffectiveAccountWrite(actor, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
_, err := tx.Exec(s.q(`INSERT INTO audit_events(time,owner,action,actor,target,evidence,trace_id) VALUES(?,?,?,?,?,?,?)`),
|
|
event.Time, actor, event.Action, event.Actor, event.Target, event.Evidence, event.TraceID)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (s *Store) SetPluginEnabledAccountChecked(actor, pluginID string, enabled, allowImplicitDefault bool) error {
|
|
flag := 0
|
|
if enabled {
|
|
flag = 1
|
|
}
|
|
return s.withEffectiveAccountWrite(actor, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
_, err := tx.Exec(s.q(`INSERT INTO plugin_grants(owner,plugin_id,enabled) VALUES(?,?,?)
|
|
ON CONFLICT(owner,plugin_id) DO UPDATE SET enabled=excluded.enabled`), actor, pluginID, flag)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (s *Store) PutPluginKVAccountChecked(actor, pluginID, key, value string, allowImplicitDefault bool) error {
|
|
return s.withEffectiveAccountWrite(actor, allowImplicitDefault, func(tx *sql.Tx, _ Account) error {
|
|
_, err := tx.Exec(s.q(`INSERT INTO plugin_kv(plugin_id,owner,k,v) VALUES(?,?,?,?)
|
|
ON CONFLICT(plugin_id,owner,k) DO UPDATE SET v=excluded.v`), pluginID, actor, key, value)
|
|
return err
|
|
})
|
|
}
|