diff --git a/app/src/main/java/com/m57/hermescontrol/ui/bots/BotsViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/bots/BotsViewModel.kt index ba3d7a64..db218902 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/bots/BotsViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/bots/BotsViewModel.kt @@ -238,11 +238,11 @@ class BotsViewModel( CreateProfileRequest( name = name, description = description.ifBlank { null }, - clone_from_default = true, + clone_from_default = false, ) val result = safeApiCall { ApiClient.hermesApi.createProfile(req) } if (result is NetworkResult.Success) { - // Configure UI metadata (avatar, custom title) via RPC + // Configure UI metadata (avatar, custom title) and bot SOUL via RPC val botMeta = BotRosterMeta( title = title.ifBlank { null }, @@ -253,7 +253,8 @@ class BotsViewModel( color = color, ), ) - wsClientConfigureBot(name, botMeta) + val botSoul = composeBotSoul(name, title, description) + wsClientConfigureBot(name, botMeta, soul = botSoul) loadBots() onSuccess() } else { @@ -363,18 +364,36 @@ class BotsViewModel( } } + private fun composeBotSoul( + name: String, + title: String, + description: String, + ): String { + val displayName = title.ifBlank { name } + val lines = mutableListOf() + lines.add("# $displayName") + lines.add("") + if (title.isNotBlank()) lines.add("**Role:** $title") + if (description.isNotBlank()) lines.add("**Mission:** $description") + lines.add("") + lines.add("You are $displayName, a persistent named agent (profile `$name`) on this machine.") + lines.add("You keep your own memory, skills, and conversation history across sessions.") + return lines.joinToString("\n") + } + private fun wsClientConfigureBot( name: String, meta: BotRosterMeta, + soul: String? = null, ): kotlinx.coroutines.CompletableDeferred { val metaMap = - buildMap { - put("title", meta.title) - put("description", meta.description) + buildMap { + meta.title?.let { put("title", it) } + meta.description?.let { put("description", it) } meta.avatar?.let { av -> put( "avatar", - buildMap { + buildMap { av.shape?.let { put("shape", it) } av.color?.let { put("color", it) } av.icon?.let { put("icon", it) } @@ -386,12 +405,16 @@ class BotsViewModel( } } + val params = + buildMap { + put("name", name) + put("ui_meta", mapOf("hermes-bots" to metaMap)) + soul?.let { put("soul", it) } + } + return HermesWsClient.request( WsMethods.PROFILES_CONFIGURE, - mapOf( - "name" to name, - "ui_meta" to mapOf("hermes-bots" to metaMap), - ), + params, ) } } diff --git a/app/src/test/java/com/m57/hermescontrol/ui/bots/BotsViewModelTest.kt b/app/src/test/java/com/m57/hermescontrol/ui/bots/BotsViewModelTest.kt index f4b0498e..38c275d2 100644 --- a/app/src/test/java/com/m57/hermescontrol/ui/bots/BotsViewModelTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/ui/bots/BotsViewModelTest.kt @@ -165,7 +165,15 @@ class BotsViewModelTest { advanceUntilIdle() assertTrue(successCalled) - coVerify(exactly = 1) { mockApi.createProfile(match { it.name == "researcher" }) } + coVerify(exactly = 1) { + mockApi.createProfile( + match { + it.name == "researcher" && + it.clone_from_default == false && + it.description == "Deep research agent" + }, + ) + } } @Test