diff mrjunejune/conversation_store.h @ 264:04fee26ecce0

add authenticated JRPG conversation platform Add reusable auth/session storage, owned conversation recovery, guest quotas, admin workflows, URL-routed conversation UI, mobile frame support, and parallel browser acceptance. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 07:34:12 -0700
parents 1f9877b637e9
children
line wrap: on
line diff
--- a/mrjunejune/conversation_store.h	Thu Aug 06 11:31:30 2026 -0700
+++ b/mrjunejune/conversation_store.h	Fri Aug 07 07:34:12 2026 -0700
@@ -12,6 +12,46 @@
   CONVERSATION_STORE_CONFLICT = 2,
 } Conversation_Store_Result;
 
+/* ------------------------------------------------------------------ */
+/* Ownership types                                                      */
+/* ------------------------------------------------------------------ */
+
+typedef enum {
+  CONVERSATION_OWNER_KIND_USER   = 0,
+  CONVERSATION_OWNER_KIND_GUEST  = 1,
+  CONVERSATION_OWNER_KIND_LEGACY = 2,
+} Conversation_Owner_Kind;
+
+/*
+ * Owner identity for a conversation.
+ * kind=USER/GUEST: id holds user_id or guest_id (UUID, 36 chars).
+ * kind=LEGACY:     id is empty; owned by no one.
+ */
+typedef struct {
+  Conversation_Owner_Kind kind;
+  char id[37]; /* user_id or guest_id; empty string for legacy */
+} Conversation_Owner;
+
+/*
+ * Lightweight summary returned by the listing endpoint.
+ * All string pointers are arena-allocated.
+ * last_message_preview is truncated to 200 chars (UTF-8 may split mid-char;
+ * callers must not rely on it being valid UTF-8 at the boundary).
+ */
+typedef struct {
+  char  *id;
+  char  *title;
+  char  *status;
+  int64  created_at;
+  int64  updated_at;
+  int64  turn_count;
+  char  *last_message_preview; /* at most 200 bytes + NUL */
+} Conversation_Summary;
+
+/* ------------------------------------------------------------------ */
+/* Turn and record types                                                */
+/* ------------------------------------------------------------------ */
+
 typedef struct {
   int64 id;
   int64 sequence;
@@ -36,10 +76,116 @@
   Conversation_Turn *turns;
 } Conversation_Record;
 
+/* ------------------------------------------------------------------ */
+/* Lifecycle                                                            */
+/* ------------------------------------------------------------------ */
+
 Conversation_Store *Conversation_Store_Create(const char *database_path);
 void Conversation_Store_Destroy(Conversation_Store *p_store);
 boolean Conversation_Store_Generate_UUID(char output[37]);
 
+/* ------------------------------------------------------------------ */
+/* Owner-aware APIs (production HTTP handlers must use these)           */
+/* ------------------------------------------------------------------ */
+
+/*
+ * Create a conversation assigned to p_owner.
+ * p_owner->kind must be USER or GUEST; LEGACY is rejected.
+ */
+Conversation_Store_Result Conversation_Store_Create_Owned(
+    Conversation_Store *p_store,
+    const char *title,
+    const Conversation_Owner *p_owner,
+    char output_id[37]);
+
+/*
+ * Get a conversation; returns NOT_FOUND if the conversation does not belong
+ * to p_owner (existence leaks prevented).
+ */
+Conversation_Store_Result Conversation_Store_Get_Owned(
+    Conversation_Store *p_store,
+    const char *conversation_id,
+    const Conversation_Owner *p_owner,
+    Conversation_Record *p_record,
+    Dowa_Arena *p_arena);
+
+Conversation_Store_Result Conversation_Store_Update_Title_Owned(
+    Conversation_Store *p_store,
+    const char *conversation_id,
+    const Conversation_Owner *p_owner,
+    const char *title);
+
+Conversation_Store_Result Conversation_Store_Delete_Owned(
+    Conversation_Store *p_store,
+    const char *conversation_id,
+    const Conversation_Owner *p_owner);
+
+Conversation_Store_Result Conversation_Store_Begin_Turn_Owned(
+    Conversation_Store *p_store,
+    const char *conversation_id,
+    const Conversation_Owner *p_owner,
+    const char *request_id,
+    const char *prompt);
+
+/*
+ * List conversations for an owner, ordered newest-first.
+ * Legacy conversations are never listed (returns empty list for LEGACY owner).
+ *
+ * cursor_updated_at / cursor_id: pass 0 / NULL for the first page.
+ * limit: capped to 50 internally; must be >= 1.
+ *
+ * *pp_summaries is arena-allocated; *p_count is the number of items.
+ * A cursor for the next page (if *p_count == limit) can be derived from
+ * (*pp_summaries)[*p_count - 1].{updated_at, id}.
+ */
+Conversation_Store_Result Conversation_Store_List(
+    Conversation_Store *p_store,
+    const Conversation_Owner *p_owner,
+    int64 cursor_updated_at,
+    const char *cursor_id,
+    int32 limit,
+    Conversation_Summary **pp_summaries,
+    int32 *p_count,
+    Dowa_Arena *p_arena);
+
+/*
+ * Claim a legacy conversation: reassign it to user_id.
+ * Returns NOT_FOUND if the conversation is not a legacy row (or does not
+ * exist), preventing existence leaks.
+ * Returns CONFLICT if the conversation is already owned by someone else.
+ */
+Conversation_Store_Result Conversation_Store_Claim_Legacy(
+    Conversation_Store *p_store,
+    const char *conversation_id,
+    const char *user_id);
+
+/*
+ * Atomically transfer all guest conversations to a user.
+ * Idempotent: re-running with the same (guest_id, user_id) pair is safe.
+ * Returns CONVERSATION_STORE_OK on success (including the no-op case).
+ */
+Conversation_Store_Result Conversation_Store_Transfer_Guest_To_User(
+    Conversation_Store *p_store,
+    const char *guest_id,
+    const char *user_id);
+
+/*
+ * Like Conversation_Store_Transfer_Guest_To_User but also clears outstanding
+ * guest quota reservations atomically in the same transaction.
+ * Use this on login: both the conversation transfer and quota cleanup commit
+ * or roll back together.  The quota tables must reside in the same SQLite
+ * database file as the conversation tables (the shared mrjunejune.db).
+ */
+Conversation_Store_Result Conversation_Store_Transfer_Guest_To_User_Atomic(
+    Conversation_Store *p_store,
+    const char *guest_id,
+    const char *user_id);
+
+/* ------------------------------------------------------------------ */
+/* Legacy/internal APIs (kept only for migration-compatibility tests)   */
+/* Production HTTP handlers must not use these.                         */
+/* ------------------------------------------------------------------ */
+
 Conversation_Store_Result Conversation_Store_Create_Conversation(
     Conversation_Store *p_store,
     const char *title,