feat(bots): reactive multi-agent group chat orchestration with CAS leasing (#1007)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-29 18:44:08 +03:00
1 parent 7e24f5eba3
commit 0d09f43469
9 files changed
+1059 -147

No files matched your search

@@ -157,6 +157,17 @@ data class GroupChatSyncSnapshot(
room.revision?.let { roomMap["revision"] = it }
room.syncRevision?.let { roomMap["syncRevision"] = it }
room.tombstone?.let { roomMap["tombstone"] = it }
room.maxBotMessages?.let { roomMap["maxBotMessages"] = it }
room.maxContinuationPasses?.let { roomMap["maxContinuationPasses"] = it }
room.systemPrompt?.let { roomMap["systemPrompt"] = it }
room.lease?.let { lease ->
roomMap["lease"] =
buildMap<String, Any?> {
lease.driverId?.let { put("driverId", it) }
lease.epoch?.let { put("epoch", it) }
lease.expiresAt?.let { put("expiresAt", it) }
}
}
room.members?.let { members ->
roomMap["members"] =
members.mapNotNull {
@@ -200,6 +211,13 @@ data class GroupChatSyncSnapshot(
}
}
@Serializable
data class GroupChatRoomLease(
val driverId: String? = null,
val epoch: String? = null,
val expiresAt: Long? = null,
)
@Serializable
data class GroupChatRoomMeta(
val id: String? = null,
@@ -214,6 +232,10 @@ data class GroupChatRoomMeta(
val updatedAt: Long? = null,
val createdAt: Long? = null,
val tombstone: Boolean? = null,
val lease: GroupChatRoomLease? = null,
val maxBotMessages: Int? = null,
val maxContinuationPasses: Int? = null,
val systemPrompt: String? = null,
) {
val memberNames: List<String>
get() =
@@ -3,6 +3,8 @@ package com.m57.hermescontrol.data.model
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.longOrNull
@Serializable
data class SessionMessagesResponse(
@@ -52,6 +54,20 @@ data class SessionMessage(
val timestampText: String?
get() = (timestamp as? JsonPrimitive)?.content
val timestampEpochMs: Long?
get() {
val prim = timestamp as? JsonPrimitive ?: return null
val asLong = prim.longOrNull
if (asLong != null) {
return if (asLong < 10_000_000_000L) asLong * 1000L else asLong
}
val asDouble = prim.doubleOrNull
if (asDouble != null) {
return (asDouble * 1000L).toLong()
}
return null
}
val contentText: String
get() =
when (content) {