diff --git a/internal/gateway/methods/sessions.go b/internal/gateway/methods/sessions.go index 8f0bb321..45b258a2 100644 --- a/internal/gateway/methods/sessions.go +++ b/internal/gateway/methods/sessions.go @@ -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, diff --git a/internal/gateway/methods/sessions_reset_cli_test.go b/internal/gateway/methods/sessions_reset_cli_test.go new file mode 100644 index 00000000..3aa74d4c --- /dev/null +++ b/internal/gateway/methods/sessions_reset_cli_test.go @@ -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) + } +}