feat: render display_kind timeline markers as chips, not user bubbles (#904) (#909)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-15 14:06:51 +03:00
1 parent 85087c70c7
commit 4be1e965f2
16 files changed
+438 -11

No files matched your search

@@ -40,4 +40,6 @@ data class ChatMessageEntity(
val toolStatus: String? = null,
@ColumnInfo(name = "is_streaming")
val isStreaming: Boolean = false,
@ColumnInfo(name = "display_kind")
val displayKind: String? = null,
)
@@ -34,6 +34,7 @@ fun ChatMessageEntity.toUiModel(): ChatMessage =
"FAILED" -> ToolStatus.FAILED
else -> null
},
displayKind = displayKind,
)
fun ChatMessage.toEntity(sessionId: String): ChatMessageEntity =
@@ -48,4 +49,5 @@ fun ChatMessage.toEntity(sessionId: String): ChatMessageEntity =
toolCallId = toolCallId,
toolStatus = toolStatus?.name,
isStreaming = isStreaming,
displayKind = displayKind,
)
@@ -11,7 +11,7 @@ import java.io.File
@Database(
entities = [ChatMessageEntity::class],
version = 5,
version = 6,
exportSchema = true,
)
abstract class HermesDatabase : RoomDatabase() {
@@ -68,13 +68,30 @@ abstract class HermesDatabase : RoomDatabase() {
}
}
val migration5to6 =
object : Migration(5, 6) {
override fun migrate(db: SupportSQLiteDatabase) {
// Issue #904: timeline markers (display_kind) keep
// their tag through the Room cache so a cached
// marker never degrades back into a user bubble.
db.execSQL(
"ALTER TABLE `chat_messages` ADD COLUMN `display_kind` TEXT",
)
}
}
instance ?: Room
.databaseBuilder(
context.applicationContext,
HermesDatabase::class.java,
"hermes_control.db",
).openHelperFactory(factory)
.addMigrations(migration2to3, migration3to4, migration4to5)
.addMigrations(
migration2to3,
migration3to4,
migration4to5,
migration5to6,
)
.fallbackToDestructiveMigration(false)
.build()
.also { instance = it }
@@ -39,6 +39,15 @@ data class SessionMessage(
val reasoning: JsonElement? = null,
val reasoning_text: JsonElement? = null,
val tool_call_id: String? = null,
/**
* Timeline-marker tag (backend NS-656 lineage, issue #904): markers like
* `model_switch` / `personality_switch` / `auto_continue` ride as
* role=user rows so strict providers accept them mid-conversation, but
* they are NOT user turns. Nullable/additive — old backends omit it.
*/
val display_kind: String? = null,
/** Display-only metadata for the marker (e.g. delegation result counts). */
val display_metadata: JsonElement? = null,
) {
val timestampText: String?
get() = (timestamp as? JsonPrimitive)?.content