Files
teexiii 88a5793030 fix(agents): sync IDENTITY.md Name field on agent rename (#582)
## 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
2026-03-30 20:56:49 +07:00

82 lines
2.4 KiB
Go

package methods
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/nextlevelbuilder/goclaw/internal/bus"
"github.com/nextlevelbuilder/goclaw/internal/gateway"
"github.com/nextlevelbuilder/goclaw/internal/i18n"
"github.com/nextlevelbuilder/goclaw/internal/store"
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
)
// --- agents.delete ---
// Matching TS src/gateway/server-methods/agents.ts:347-398
func (m *AgentsMethods) handleDelete(ctx context.Context, client *gateway.Client, req *protocol.RequestFrame) {
locale := store.LocaleFromContext(ctx)
var params struct {
AgentID string `json:"agentId"`
DeleteFiles bool `json:"deleteFiles"`
}
params.DeleteFiles = true // default
if req.Params != nil {
json.Unmarshal(req.Params, &params)
}
if params.AgentID == "" {
client.SendResponse(protocol.NewErrorResponse(req.ID, protocol.ErrInvalidRequest, i18n.T(locale, i18n.MsgRequired, "agentId")))
return
}
if params.AgentID == "default" {
client.SendResponse(protocol.NewErrorResponse(req.ID, protocol.ErrInvalidRequest, i18n.T(locale, i18n.MsgCannotDeleteDefault)))
return
}
var removedBindings int
if m.agentStore != nil {
// --- DB-backed: delete from store ---
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
if err != nil {
client.SendResponse(protocol.NewErrorResponse(req.ID, protocol.ErrNotFound, i18n.T(locale, i18n.MsgAgentNotFound, params.AgentID)))
return
}
if err := m.agentStore.Delete(ctx, ag.ID); err != nil {
client.SendResponse(protocol.NewErrorResponse(req.ID, protocol.ErrInternal, i18n.T(locale, i18n.MsgFailedToDelete, "agent", fmt.Sprintf("%v", err))))
return
}
m.agents.InvalidateAgent(params.AgentID)
m.agents.Remove(params.AgentID)
// Emit agent:deleted event for async cleanup (e.g. orphaned provider removal)
if m.eventBus != nil {
m.eventBus.Broadcast(bus.Event{
Name: bus.TopicAgentDeleted,
Payload: bus.AgentDeletedPayload{
AgentKey: params.AgentID,
Provider: ag.Provider,
TenantID: store.TenantIDFromContext(ctx),
},
})
}
// Best-effort delete workspace
if params.DeleteFiles && ag.Workspace != "" {
os.RemoveAll(ag.Workspace)
}
}
client.SendResponse(protocol.NewOKResponse(req.ID, map[string]any{
"ok": true,
"agentId": params.AgentID,
"removedBindings": removedBindings,
}))
emitAudit(m.eventBus, client, "agent.deleted", "agent", params.AgentID)
}