feat: add /new and /clear commands to reset current chat session (#572)

This commit is contained in:
Rahul Bansal 2026-03-03 18:56:46 +05:30
parent 4a7605ee14
commit b41667cf06
4 changed files with 120 additions and 0 deletions

View file

@ -1453,6 +1453,35 @@ func (al *AgentLoop) handleCommand(ctx context.Context, msg bus.InboundMessage)
default:
return fmt.Sprintf("Unknown switch target: %s", target), true
}
case "/new", "/clear":
route := al.registry.ResolveRoute(routing.RouteInput{
Channel: msg.Channel,
AccountID: msg.Metadata["account_id"],
Peer: extractPeer(msg),
ParentPeer: extractParentPeer(msg),
GuildID: msg.Metadata["guild_id"],
TeamID: msg.Metadata["team_id"],
})
agent, ok := al.registry.GetAgent(route.AgentID)
if !ok {
agent = al.registry.GetDefaultAgent()
}
if agent == nil {
return "No agent available for this conversation", true
}
sessionKey := route.SessionKey
if msg.SessionKey != "" && strings.HasPrefix(msg.SessionKey, "agent:") {
sessionKey = msg.SessionKey
}
agent.Sessions.Save(sessionKey)
agent.Sessions.ResetSession(sessionKey)
agent.ContextBuilder.InvalidateCache()
return "Started a new conversation. Previous context has been cleared for this chat.", true
}
return "", false

View file

@ -15,6 +15,7 @@ import (
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/media"
"github.com/sipeed/picoclaw/pkg/providers"
"github.com/sipeed/picoclaw/pkg/routing"
"github.com/sipeed/picoclaw/pkg/tools"
)
@ -949,3 +950,38 @@ func TestResolveMediaRefs_UsesMetaContentType(t *testing.T) {
t.Fatalf("expected jpeg prefix, got %q", result[0].Media[0][:30])
}
}
func TestProcessMessage_NewCommandResetsSession(t *testing.T) {
al, _, _, _, cleanup := newTestAgentLoop(t)
defer cleanup()
agent := al.registry.GetDefaultAgent()
if agent == nil {
t.Fatal("no default agent")
}
sessionKey := routing.BuildAgentMainSessionKey(agent.ID)
agent.Sessions.AddMessage(sessionKey, "user", "old message")
agent.Sessions.SetSummary(sessionKey, "old summary")
resp, err := al.processMessage(context.Background(), bus.InboundMessage{
Channel: "webchat",
ChatID: "chat1",
Content: "/new",
})
if err != nil {
t.Fatalf("processMessage error: %v", err)
}
if !strings.Contains(resp, "Started a new conversation") {
t.Fatalf("unexpected response: %q", resp)
}
history := agent.Sessions.GetHistory(sessionKey)
if len(history) != 0 {
t.Fatalf("expected session history to be cleared, got %d messages", len(history))
}
if summary := agent.Sessions.GetSummary(sessionKey); summary != "" {
t.Fatalf("expected summary to be cleared, got %q", summary)
}
}

View file

@ -280,3 +280,26 @@ func (sm *SessionManager) SetHistory(key string, history []providers.Message) {
session.Updated = time.Now()
}
}
// ResetSession clears the conversation history and summary for a session key
// while preserving the same session identity.
func (sm *SessionManager) ResetSession(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
session, ok := sm.sessions[key]
if !ok {
session = &Session{
Key: key,
Messages: []providers.Message{},
Created: time.Now(),
Updated: time.Now(),
}
sm.sessions[key] = session
return
}
session.Messages = []providers.Message{}
session.Summary = ""
session.Updated = time.Now()
}

View file

@ -72,3 +72,35 @@ func TestSave_RejectsPathTraversal(t *testing.T) {
}
}
}
func TestResetSession_ClearsHistoryAndSummary(t *testing.T) {
tmpDir := t.TempDir()
sm := NewSessionManager(tmpDir)
key := "telegram:123456"
sm.AddMessage(key, "user", "hello")
sm.SetSummary(key, "old summary")
sm.ResetSession(key)
history := sm.GetHistory(key)
if len(history) != 0 {
t.Fatalf("expected empty history after reset, got %d", len(history))
}
if got := sm.GetSummary(key); got != "" {
t.Fatalf("expected empty summary after reset, got %q", got)
}
}
func TestResetSession_CreatesSessionIfMissing(t *testing.T) {
tmpDir := t.TempDir()
sm := NewSessionManager(tmpDir)
key := "discord:abc"
sm.ResetSession(key)
history := sm.GetHistory(key)
if len(history) != 0 {
t.Fatalf("expected empty history for new reset session, got %d", len(history))
}
}