Mercurial
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 263:ee04e4e69fed | 264:04fee26ecce0 |
|---|---|
| 9 CONVERSATION_STORE_ERROR = -1, | 9 CONVERSATION_STORE_ERROR = -1, |
| 10 CONVERSATION_STORE_OK = 0, | 10 CONVERSATION_STORE_OK = 0, |
| 11 CONVERSATION_STORE_NOT_FOUND = 1, | 11 CONVERSATION_STORE_NOT_FOUND = 1, |
| 12 CONVERSATION_STORE_CONFLICT = 2, | 12 CONVERSATION_STORE_CONFLICT = 2, |
| 13 } Conversation_Store_Result; | 13 } Conversation_Store_Result; |
| 14 | |
| 15 /* ------------------------------------------------------------------ */ | |
| 16 /* Ownership types */ | |
| 17 /* ------------------------------------------------------------------ */ | |
| 18 | |
| 19 typedef enum { | |
| 20 CONVERSATION_OWNER_KIND_USER = 0, | |
| 21 CONVERSATION_OWNER_KIND_GUEST = 1, | |
| 22 CONVERSATION_OWNER_KIND_LEGACY = 2, | |
| 23 } Conversation_Owner_Kind; | |
| 24 | |
| 25 /* | |
| 26 * Owner identity for a conversation. | |
| 27 * kind=USER/GUEST: id holds user_id or guest_id (UUID, 36 chars). | |
| 28 * kind=LEGACY: id is empty; owned by no one. | |
| 29 */ | |
| 30 typedef struct { | |
| 31 Conversation_Owner_Kind kind; | |
| 32 char id[37]; /* user_id or guest_id; empty string for legacy */ | |
| 33 } Conversation_Owner; | |
| 34 | |
| 35 /* | |
| 36 * Lightweight summary returned by the listing endpoint. | |
| 37 * All string pointers are arena-allocated. | |
| 38 * last_message_preview is truncated to 200 chars (UTF-8 may split mid-char; | |
| 39 * callers must not rely on it being valid UTF-8 at the boundary). | |
| 40 */ | |
| 41 typedef struct { | |
| 42 char *id; | |
| 43 char *title; | |
| 44 char *status; | |
| 45 int64 created_at; | |
| 46 int64 updated_at; | |
| 47 int64 turn_count; | |
| 48 char *last_message_preview; /* at most 200 bytes + NUL */ | |
| 49 } Conversation_Summary; | |
| 50 | |
| 51 /* ------------------------------------------------------------------ */ | |
| 52 /* Turn and record types */ | |
| 53 /* ------------------------------------------------------------------ */ | |
| 14 | 54 |
| 15 typedef struct { | 55 typedef struct { |
| 16 int64 id; | 56 int64 id; |
| 17 int64 sequence; | 57 int64 sequence; |
| 18 char *role; | 58 char *role; |
| 34 int64 created_at; | 74 int64 created_at; |
| 35 int64 updated_at; | 75 int64 updated_at; |
| 36 Conversation_Turn *turns; | 76 Conversation_Turn *turns; |
| 37 } Conversation_Record; | 77 } Conversation_Record; |
| 38 | 78 |
| 79 /* ------------------------------------------------------------------ */ | |
| 80 /* Lifecycle */ | |
| 81 /* ------------------------------------------------------------------ */ | |
| 82 | |
| 39 Conversation_Store *Conversation_Store_Create(const char *database_path); | 83 Conversation_Store *Conversation_Store_Create(const char *database_path); |
| 40 void Conversation_Store_Destroy(Conversation_Store *p_store); | 84 void Conversation_Store_Destroy(Conversation_Store *p_store); |
| 41 boolean Conversation_Store_Generate_UUID(char output[37]); | 85 boolean Conversation_Store_Generate_UUID(char output[37]); |
| 42 | 86 |
| 87 /* ------------------------------------------------------------------ */ | |
| 88 /* Owner-aware APIs (production HTTP handlers must use these) */ | |
| 89 /* ------------------------------------------------------------------ */ | |
| 90 | |
| 91 /* | |
| 92 * Create a conversation assigned to p_owner. | |
| 93 * p_owner->kind must be USER or GUEST; LEGACY is rejected. | |
| 94 */ | |
| 95 Conversation_Store_Result Conversation_Store_Create_Owned( | |
| 96 Conversation_Store *p_store, | |
| 97 const char *title, | |
| 98 const Conversation_Owner *p_owner, | |
| 99 char output_id[37]); | |
| 100 | |
| 101 /* | |
| 102 * Get a conversation; returns NOT_FOUND if the conversation does not belong | |
| 103 * to p_owner (existence leaks prevented). | |
| 104 */ | |
| 105 Conversation_Store_Result Conversation_Store_Get_Owned( | |
| 106 Conversation_Store *p_store, | |
| 107 const char *conversation_id, | |
| 108 const Conversation_Owner *p_owner, | |
| 109 Conversation_Record *p_record, | |
| 110 Dowa_Arena *p_arena); | |
| 111 | |
| 112 Conversation_Store_Result Conversation_Store_Update_Title_Owned( | |
| 113 Conversation_Store *p_store, | |
| 114 const char *conversation_id, | |
| 115 const Conversation_Owner *p_owner, | |
| 116 const char *title); | |
| 117 | |
| 118 Conversation_Store_Result Conversation_Store_Delete_Owned( | |
| 119 Conversation_Store *p_store, | |
| 120 const char *conversation_id, | |
| 121 const Conversation_Owner *p_owner); | |
| 122 | |
| 123 Conversation_Store_Result Conversation_Store_Begin_Turn_Owned( | |
| 124 Conversation_Store *p_store, | |
| 125 const char *conversation_id, | |
| 126 const Conversation_Owner *p_owner, | |
| 127 const char *request_id, | |
| 128 const char *prompt); | |
| 129 | |
| 130 /* | |
| 131 * List conversations for an owner, ordered newest-first. | |
| 132 * Legacy conversations are never listed (returns empty list for LEGACY owner). | |
| 133 * | |
| 134 * cursor_updated_at / cursor_id: pass 0 / NULL for the first page. | |
| 135 * limit: capped to 50 internally; must be >= 1. | |
| 136 * | |
| 137 * *pp_summaries is arena-allocated; *p_count is the number of items. | |
| 138 * A cursor for the next page (if *p_count == limit) can be derived from | |
| 139 * (*pp_summaries)[*p_count - 1].{updated_at, id}. | |
| 140 */ | |
| 141 Conversation_Store_Result Conversation_Store_List( | |
| 142 Conversation_Store *p_store, | |
| 143 const Conversation_Owner *p_owner, | |
| 144 int64 cursor_updated_at, | |
| 145 const char *cursor_id, | |
| 146 int32 limit, | |
| 147 Conversation_Summary **pp_summaries, | |
| 148 int32 *p_count, | |
| 149 Dowa_Arena *p_arena); | |
| 150 | |
| 151 /* | |
| 152 * Claim a legacy conversation: reassign it to user_id. | |
| 153 * Returns NOT_FOUND if the conversation is not a legacy row (or does not | |
| 154 * exist), preventing existence leaks. | |
| 155 * Returns CONFLICT if the conversation is already owned by someone else. | |
| 156 */ | |
| 157 Conversation_Store_Result Conversation_Store_Claim_Legacy( | |
| 158 Conversation_Store *p_store, | |
| 159 const char *conversation_id, | |
| 160 const char *user_id); | |
| 161 | |
| 162 /* | |
| 163 * Atomically transfer all guest conversations to a user. | |
| 164 * Idempotent: re-running with the same (guest_id, user_id) pair is safe. | |
| 165 * Returns CONVERSATION_STORE_OK on success (including the no-op case). | |
| 166 */ | |
| 167 Conversation_Store_Result Conversation_Store_Transfer_Guest_To_User( | |
| 168 Conversation_Store *p_store, | |
| 169 const char *guest_id, | |
| 170 const char *user_id); | |
| 171 | |
| 172 /* | |
| 173 * Like Conversation_Store_Transfer_Guest_To_User but also clears outstanding | |
| 174 * guest quota reservations atomically in the same transaction. | |
| 175 * Use this on login: both the conversation transfer and quota cleanup commit | |
| 176 * or roll back together. The quota tables must reside in the same SQLite | |
| 177 * database file as the conversation tables (the shared mrjunejune.db). | |
| 178 */ | |
| 179 Conversation_Store_Result Conversation_Store_Transfer_Guest_To_User_Atomic( | |
| 180 Conversation_Store *p_store, | |
| 181 const char *guest_id, | |
| 182 const char *user_id); | |
| 183 | |
| 184 /* ------------------------------------------------------------------ */ | |
| 185 /* Legacy/internal APIs (kept only for migration-compatibility tests) */ | |
| 186 /* Production HTTP handlers must not use these. */ | |
| 187 /* ------------------------------------------------------------------ */ | |
| 188 | |
| 43 Conversation_Store_Result Conversation_Store_Create_Conversation( | 189 Conversation_Store_Result Conversation_Store_Create_Conversation( |
| 44 Conversation_Store *p_store, | 190 Conversation_Store *p_store, |
| 45 const char *title, | 191 const char *title, |
| 46 char output_id[37]); | 192 char output_id[37]); |
| 47 | 193 |