mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 20:15:42 +00:00
## Summary - Agent rename now updates Name field in IDENTITY.md (agent-level + per-user copies for open agents) via both HTTP and WS paths - Fixes lossy identity rebuild: previous logic reconstructed IDENTITY.md from only Name/Emoji/Avatar, silently dropping LLM-written fields (Creature, Purpose, Vibe, etc.) - Uses targeted field replacement (bootstrap.UpdateIdentityField) preserving original formatting - Adds ListUserContextFilesByName to AgentContextStore (PG + SQLite) - Fixes LastIndex bug in UpdateIdentityField where values containing colons (e.g. Avatar URLs) would break field replacement - Removes ~335 lines of dead config.json fallback code (agentStore is always set) - Adds 9 unit tests for UpdateIdentityField covering plain/markdown formats, URLs, edge cases
107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
package methods
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/gateway"
|
|
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
|
|
)
|
|
|
|
// --- agent.identity.get ---
|
|
// Matching TS src/gateway/server-methods/agent.ts:601-643
|
|
|
|
func (m *AgentsMethods) handleIdentityGet(ctx context.Context, client *gateway.Client, req *protocol.RequestFrame) {
|
|
var params struct {
|
|
AgentID string `json:"agentId"`
|
|
SessionKey string `json:"sessionKey"`
|
|
}
|
|
if req.Params != nil {
|
|
json.Unmarshal(req.Params, ¶ms)
|
|
}
|
|
if params.AgentID == "" {
|
|
// Try to extract from sessionKey: "agent:{agentId}:..."
|
|
if params.SessionKey != "" {
|
|
parts := strings.SplitN(params.SessionKey, ":", 3)
|
|
if len(parts) >= 2 {
|
|
params.AgentID = parts[1]
|
|
}
|
|
}
|
|
if params.AgentID == "" {
|
|
params.AgentID = "default"
|
|
}
|
|
}
|
|
|
|
result := map[string]any{
|
|
"agentId": params.AgentID,
|
|
}
|
|
|
|
if m.agentStore != nil {
|
|
// --- DB-backed: read identity from store ---
|
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
|
if err == nil {
|
|
result["name"] = ag.DisplayName
|
|
|
|
// Parse IDENTITY.md from DB bootstrap
|
|
dbFiles, _ := m.agentStore.GetAgentContextFiles(ctx, ag.ID)
|
|
for _, f := range dbFiles {
|
|
if f.FileName == "IDENTITY.md" {
|
|
if identity := parseIdentityContent(f.Content); identity != nil {
|
|
if identity["Name"] != "" {
|
|
result["name"] = identity["Name"]
|
|
}
|
|
if identity["Emoji"] != "" {
|
|
result["emoji"] = identity["Emoji"]
|
|
}
|
|
if identity["Avatar"] != "" {
|
|
result["avatar"] = identity["Avatar"]
|
|
}
|
|
if identity["Description"] != "" {
|
|
result["description"] = identity["Description"]
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
client.SendResponse(protocol.NewOKResponse(req.ID, result))
|
|
}
|
|
|
|
// parseIdentityContent parses IDENTITY.md content string and extracts Key: Value fields.
|
|
func parseIdentityContent(content string) map[string]string {
|
|
result := make(map[string]string)
|
|
for line := range strings.SplitSeq(content, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if strings.HasPrefix(line, "#") || line == "" {
|
|
continue
|
|
}
|
|
if idx := strings.Index(line, ":"); idx > 0 {
|
|
key := strings.TrimSpace(line[:idx])
|
|
val := strings.TrimSpace(line[idx+1:])
|
|
if val != "" {
|
|
result[key] = val
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// buildIdentityContent creates the content for IDENTITY.md from fields.
|
|
func buildIdentityContent(name, emoji, avatar string) string {
|
|
var lines []string
|
|
lines = append(lines, "# Identity")
|
|
if name != "" {
|
|
lines = append(lines, "Name: "+name)
|
|
}
|
|
if emoji != "" {
|
|
lines = append(lines, "Emoji: "+emoji)
|
|
}
|
|
if avatar != "" {
|
|
lines = append(lines, "Avatar: "+avatar)
|
|
}
|
|
return strings.Join(lines, "\n") + "\n"
|
|
}
|