Give every user their own bot face

A bot's face and status line live in bot_user_faces, one row per bot and
user, set by jobs for the user whose conversation they're in. Needs-you
and working only show to that user; every bot read overlays the viewer's
face (botFaceSql) and pushes go to the user a face change is for.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 13:58:26 +01:00
co-authored by Claude Opus 5.5
parent 47c58bc76b
commit ab25158d87
32 changed files with 448 additions and 181 deletions
+60 -12
View File
@@ -233,18 +233,6 @@ const schema: BUN_SQLITE_DatabaseSchemaType = {
fieldDescription: `The bot's job in one line. Null for the coordinator`,
dataType: "TEXT",
},
{
fieldName: "face",
fieldDescription: `The bot's status, shown as its face`,
dataType: "TEXT",
options: BotFaces.map((bf) => bf.value),
defaultValue: "idle",
},
{
fieldName: "status_line",
fieldDescription: `Short status sentence, e.g. "Waiting on you"`,
dataType: "TEXT",
},
{
fieldName: "system_prompt",
fieldDescription: `The bot's core instructions, in markdown. The bot can rewrite it`,
@@ -1064,6 +1052,66 @@ const schema: BUN_SQLITE_DatabaseSchemaType = {
},
],
},
{
tableName: "bot_user_faces",
tableDescription: `A bot's face (its status) and status line as one user sees it, set by the bot's jobs in that user's conversations. No row means idle. Other users never see it, so "needs you" only shows to the user it's for`,
fields: [
{
fieldName: "bot_id",
dataType: "INTEGER",
foreignKey: {
cascadeDelete: true,
destinationTableName: "bots",
destinationTableColumnName: "id",
destinationTableColumnType: "INTEGER",
foreignKeyName: "bot_user_face_bot_fk",
},
},
{
fieldName: "user_id",
dataType: "INTEGER",
foreignKey: {
cascadeDelete: true,
destinationTableName: "users",
destinationTableColumnName: "id",
destinationTableColumnType: "INTEGER",
foreignKeyName: "bot_user_face_user_fk",
},
},
{
fieldName: "face",
fieldDescription: `The bot's status for this user, shown as its face`,
dataType: "TEXT",
options: BotFaces.map((bf) => bf.value),
defaultValue: "idle",
},
{
fieldName: "status_line",
fieldDescription: `Short status sentence, e.g. "Waiting on you"`,
dataType: "TEXT",
},
],
uniqueConstraints: [
{
alias: "one_face_per_bot_user",
constraintName: "A bot has one face per user",
constraintTableFields: [
{ value: "bot_id" },
{ value: "user_id" },
],
},
],
indexes: [
{
indexName: "idx_bot_user_faces_user_id",
indexTableFields: ["user_id"],
},
{
indexName: "idx_bot_user_faces_updated_at",
indexTableFields: ["updated_at"],
},
],
},
{
tableName: "bot_message_media",
tableDescription: `Files attached to a chat message (\`media\` rows, private to the message's owner)`,