fix(gateway): clear claude-cli session on sessions.reset RPC (#1232)

The sessions.reset RPC only reset the native session store, leaving the
Claude CLI-backed history (.jsonl + CLAUDE.md) in place. The /reset chat
command already clears it via providers.ResetCLISession, so RPC callers
got an incomplete reset for claude-cli agents — stale history was resumed
on the next turn.

Mirror the chat path by calling ResetCLISession from handleReset. It is a
no-op when the CLI provider is unused. The call is wired through an
overridable package var so the behavior can be regression-tested.
This commit is contained in:
Zezae Oh authored and GitHub committed 2026-06-21 00:36:35 +07:00
1 parent 2bf0f9e172
commit cedd6673c4
2 files changed
+52

No files matched your search

+9
View File
@@ -9,6 +9,7 @@ import (
"github.com/nextlevelbuilder/goclaw/internal/gateway"
httpapi "github.com/nextlevelbuilder/goclaw/internal/http"
"github.com/nextlevelbuilder/goclaw/internal/i18n"
"github.com/nextlevelbuilder/goclaw/internal/providers"
"github.com/nextlevelbuilder/goclaw/internal/store"
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
)
@@ -204,6 +205,10 @@ func (m *SessionsMethods) handleDelete(ctx context.Context, client *gateway.Clie
emitAudit(m.eventBus, client, "session.deleted", "session", params.Key)
}
// cliSessionReset clears the Claude CLI-backed session for a key. It is a
// package var so tests can stub it; defaults to the real provider helper.
var cliSessionReset = providers.ResetCLISession
func (m *SessionsMethods) handleReset(ctx context.Context, client *gateway.Client, req *protocol.RequestFrame) {
locale := store.LocaleFromContext(ctx)
var params sessionKeyParams
@@ -225,6 +230,10 @@ func (m *SessionsMethods) handleReset(ctx context.Context, client *gateway.Clien
}
m.sessions.Reset(ctx, params.Key)
// Also clear the Claude CLI session (.jsonl + CLAUDE.md) so the RPC reset
// matches the /reset chat command for claude-cli-backed agents (no-op when
// the CLI provider is unused).
cliSessionReset("", params.Key)
client.SendResponse(protocol.NewOKResponse(req.ID, map[string]any{
"ok": true,
@@ -0,0 +1,43 @@
package methods
import (
"context"
"testing"
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
)
// TestSessionsReset_ClearsCLISession is a regression guard ensuring the
// sessions.reset RPC also clears the Claude CLI-backed session, matching the
// /reset chat command. Previously the RPC only reset the native session store,
// leaving claude-cli history (.jsonl + CLAUDE.md) in place.
func TestSessionsReset_ClearsCLISession(t *testing.T) {
sess := newStubSessionStore()
// Empty userID matches nullClient()'s empty UserID, so the ownership
// check in handleReset passes and we reach the reset path.
sess.addSession("reset-key", "")
m := buildSessionMethods(t, sess)
client := nullClient()
var called bool
var gotKey string
orig := cliSessionReset
cliSessionReset = func(_, sessionKey string) {
called = true
gotKey = sessionKey
}
t.Cleanup(func() { cliSessionReset = orig })
req := sessionReqFrame(t, protocol.MethodSessionsReset, map[string]any{"key": "reset-key"})
m.handleReset(context.Background(), client, req)
if !called {
t.Fatal("sessions.reset did not clear the Claude CLI session")
}
if gotKey != "reset-key" {
t.Errorf("CLI reset key = %q, want %q", gotKey, "reset-key")
}
if len(sess.resetCalled) != 1 || sess.resetCalled[0] != "reset-key" {
t.Errorf("native store.Reset calls = %v, want [reset-key]", sess.resetCalled)
}
}