package callmemory import ( "context" "github.com/jackc/pgx/v5" ) // The caller holds memory_user_state before expanding or locking any memory. // Resolve original provenance once: a derived provider summary may cover a // whole call, but deleting it as a consequence must not enlarge the requested // source deletion to unrelated utterances from that call. func expandSourceDeletionTargets(ctx context.Context, tx pgx.Tx, access Access, requested []string) ([]string, error) { rows, err := tx.Query(ctx, `WITH refs AS ( SELECT cm.id AS memory_id,r FROM curated_memory cm JOIN memory_version mv ON mv.memory_id=cm.id CROSS JOIN LATERAL jsonb_array_elements(mv.source_refs) r WHERE cm.subject_id=$1::uuid AND cm.tenant_id=$2::uuid AND cm.status<>'DELETED' ), origins AS ( SELECT memory_id,r->>'session_id' AS session_id,r->>'utterance_id' AS utterance_id, NULL::uuid AS turn_id,r->>'story_id' AS story_id FROM refs UNION ALL SELECT refs.memory_id,ss.call_session_id::text,ss.call_utterance_id,ss.conversation_turn_id,NULL::text FROM refs JOIN story_draft sd ON sd.public_id::text=r->>'story_id' AND sd.subject_id=$1::uuid JOIN story_revision sr ON sr.story_id=sd.id JOIN story_source ss ON ss.story_revision_id=sr.id ), requested_origins AS ( SELECT o.* FROM origins o JOIN curated_memory cm ON cm.id=o.memory_id WHERE cm.public_id IN(SELECT value::uuid FROM jsonb_array_elements_text($3::jsonb)) ) SELECT cm.public_id::text,cm.sensitivity FROM curated_memory cm WHERE cm.subject_id=$1::uuid AND cm.tenant_id=$2::uuid AND cm.status<>'DELETED' AND cm.public_id NOT IN(SELECT value::uuid FROM jsonb_array_elements_text($3::jsonb)) AND EXISTS(SELECT 1 FROM origins o JOIN requested_origins wanted ON (o.story_id IS NOT NULL AND o.story_id=wanted.story_id) OR (o.turn_id IS NOT NULL AND o.turn_id=wanted.turn_id) OR (o.session_id IS NOT NULL AND o.session_id=wanted.session_id AND (o.utterance_id IS NULL OR wanted.utterance_id IS NULL OR o.utterance_id=wanted.utterance_id)) WHERE o.memory_id=cm.id) ORDER BY cm.id FOR UPDATE OF cm`, access.Subject, access.Tenant, jsonBytes(requested)) if err != nil { return nil, err } defer rows.Close() ids := append([]string{}, requested...) for rows.Next() { var id, sensitivity string if err = rows.Scan(&id, &sensitivity); err != nil { return nil, err } if sensitivity != "normal" && !access.Sensitive { return nil, fault(403, "SENSITIVE_MEMORY_ACCESS_DENIED") } ids = append(ids, id) } return ids, rows.Err() }