fix(bots): create clean profile without cloning default soul and custom skills (#997)

This commit is contained in:
M57 authored and GitHub committed 2026-08-27 19:06:58 +03:00
1 parent 14fde03083
commit 0cdccad855
2 files changed
+43 -12

No files matched your search

@@ -238,11 +238,11 @@ class BotsViewModel(
CreateProfileRequest( CreateProfileRequest(
name = name, name = name,
description = description.ifBlank { null }, description = description.ifBlank { null },
clone_from_default = true, clone_from_default = false,
) )
val result = safeApiCall { ApiClient.hermesApi.createProfile(req) } val result = safeApiCall { ApiClient.hermesApi.createProfile(req) }
if (result is NetworkResult.Success) { 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 = val botMeta =
BotRosterMeta( BotRosterMeta(
title = title.ifBlank { null }, title = title.ifBlank { null },
@@ -253,7 +253,8 @@ class BotsViewModel(
color = color, color = color,
), ),
) )
wsClientConfigureBot(name, botMeta) val botSoul = composeBotSoul(name, title, description)
wsClientConfigureBot(name, botMeta, soul = botSoul)
loadBots() loadBots()
onSuccess() onSuccess()
} else { } 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<String>()
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( private fun wsClientConfigureBot(
name: String, name: String,
meta: BotRosterMeta, meta: BotRosterMeta,
soul: String? = null,
): kotlinx.coroutines.CompletableDeferred<Any?> { ): kotlinx.coroutines.CompletableDeferred<Any?> {
val metaMap = val metaMap =
buildMap<String, Any?> { buildMap<String, Any> {
put("title", meta.title) meta.title?.let { put("title", it) }
put("description", meta.description) meta.description?.let { put("description", it) }
meta.avatar?.let { av -> meta.avatar?.let { av ->
put( put(
"avatar", "avatar",
buildMap<String, Any?> { buildMap<String, Any> {
av.shape?.let { put("shape", it) } av.shape?.let { put("shape", it) }
av.color?.let { put("color", it) } av.color?.let { put("color", it) }
av.icon?.let { put("icon", it) } av.icon?.let { put("icon", it) }
@@ -386,12 +405,16 @@ class BotsViewModel(
} }
} }
val params =
buildMap<String, Any> {
put("name", name)
put("ui_meta", mapOf("hermes-bots" to metaMap))
soul?.let { put("soul", it) }
}
return HermesWsClient.request( return HermesWsClient.request(
WsMethods.PROFILES_CONFIGURE, WsMethods.PROFILES_CONFIGURE,
mapOf( params,
"name" to name,
"ui_meta" to mapOf("hermes-bots" to metaMap),
),
) )
} }
} }
@@ -165,7 +165,15 @@ class BotsViewModelTest {
advanceUntilIdle() advanceUntilIdle()
assertTrue(successCalled) 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 @Test